You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
224 lines
7.3 KiB
C++
224 lines
7.3 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
RHIGPUReadback.cpp: Convenience function implementations for async GPU
|
|
memory updates and readbacks
|
|
=============================================================================*/
|
|
|
|
#include "RHIGPUReadback.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
////////////////////// FGenericRHIGPUFence /////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
FGenericRHIGPUFence::FGenericRHIGPUFence(FName InName)
|
|
: FRHIGPUFence(InName)
|
|
, InsertedFrameNumber(MAX_uint32)
|
|
{}
|
|
|
|
void FGenericRHIGPUFence::Clear()
|
|
{
|
|
InsertedFrameNumber = MAX_uint32;
|
|
}
|
|
|
|
void FGenericRHIGPUFence::WriteInternal()
|
|
{
|
|
// GPU generally overlap the game. This overlap increases when using AFR. In normal mode this can make us appear to be further behind the gpu than we actually are.
|
|
InsertedFrameNumber = GFrameNumberRenderThread + GNumAlternateFrameRenderingGroups;
|
|
}
|
|
|
|
bool FGenericRHIGPUFence::Poll() const
|
|
{
|
|
const uint32 CurrentFrameNumber = GFrameNumberRenderThread;
|
|
if (CurrentFrameNumber > InsertedFrameNumber)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//////////////////// FGenericRHIStagingBuffer //////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void* FGenericRHIStagingBuffer::Lock(uint32 InOffset, uint32 NumBytes)
|
|
{
|
|
check(ShadowBuffer);
|
|
check(!bIsLocked);
|
|
bIsLocked = true;
|
|
return reinterpret_cast<void*>(reinterpret_cast<uint8*>(RHILockBuffer(ShadowBuffer, InOffset, NumBytes, RLM_ReadOnly)) + Offset);
|
|
}
|
|
|
|
void FGenericRHIStagingBuffer::Unlock()
|
|
{
|
|
check(bIsLocked);
|
|
RHIUnlockBuffer(ShadowBuffer);
|
|
bIsLocked = false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//////////////////// FRHIGPUBufferReadback /////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
FRHIGPUBufferReadback::FRHIGPUBufferReadback(FName RequestName) : FRHIGPUMemoryReadback(RequestName)
|
|
{
|
|
}
|
|
|
|
void FRHIGPUBufferReadback::EnqueueCopy(FRHICommandList& RHICmdList, FRHIBuffer* SourceBuffer, uint32 NumBytes)
|
|
{
|
|
Fence->Clear();
|
|
LastCopyGPUMask = RHICmdList.GetGPUMask();
|
|
|
|
for (uint32 GPUIndex : LastCopyGPUMask)
|
|
{
|
|
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::FromIndex(GPUIndex));
|
|
|
|
if (!DestinationStagingBuffers[GPUIndex])
|
|
{
|
|
DestinationStagingBuffers[GPUIndex] = RHICreateStagingBuffer();
|
|
}
|
|
|
|
RHICmdList.CopyToStagingBuffer(SourceBuffer, DestinationStagingBuffers[GPUIndex], 0, NumBytes ? NumBytes : SourceBuffer->GetSize());
|
|
RHICmdList.WriteGPUFence(Fence);
|
|
}
|
|
}
|
|
|
|
void* FRHIGPUBufferReadback::Lock(uint32 NumBytes)
|
|
{
|
|
// We arbitrarily read from the first GPU set in the mask. The assumption is that in cases where the buffer is written on multiple GPUs,
|
|
// that it will have the same data generated in lockstep on all GPUs, so it doesn't matter which GPU we read the data from. We could
|
|
// easily in the future allow the caller to pass in a GPU index (defaulting to INDEX_NONE) to allow reading from a specific GPU index.
|
|
uint32 GPUIndex = LastCopyGPUMask.GetFirstIndex();
|
|
|
|
if (DestinationStagingBuffers[GPUIndex])
|
|
{
|
|
LastLockGPUIndex = GPUIndex;
|
|
|
|
ensure(Fence->Poll());
|
|
return RHILockStagingBuffer(DestinationStagingBuffers[GPUIndex], Fence.GetReference(), 0, NumBytes);
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void FRHIGPUBufferReadback::Unlock()
|
|
{
|
|
ensure(DestinationStagingBuffers[LastLockGPUIndex]);
|
|
RHIUnlockStagingBuffer(DestinationStagingBuffers[LastLockGPUIndex]);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//////////////////// FRHIGPUTextureReadback ////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
FRHIGPUTextureReadback::FRHIGPUTextureReadback(FName RequestName) : FRHIGPUMemoryReadback(RequestName)
|
|
{
|
|
}
|
|
|
|
void FRHIGPUTextureReadback::EnqueueCopyRDG(FRHICommandList& RHICmdList, FRHITexture* SourceTexture, FResolveRect Rect)
|
|
{
|
|
EnqueueCopy(RHICmdList, SourceTexture, Rect);
|
|
}
|
|
|
|
void FRHIGPUTextureReadback::EnqueueCopy(FRHICommandList& RHICmdList, FRHITexture* SourceTexture, FResolveRect Rect)
|
|
{
|
|
Fence->Clear();
|
|
|
|
if (SourceTexture)
|
|
{
|
|
check(SourceTexture->GetTexture2D());
|
|
check(!SourceTexture->IsMultisampled());
|
|
|
|
// Assume for now that every enqueue happens on a texture of the same format and size (when reused).
|
|
if (!DestinationStagingTexture)
|
|
{
|
|
FIntVector TextureSize = SourceTexture->GetSizeXYZ();
|
|
FString FenceName = Fence->GetFName().ToString();
|
|
|
|
const FRHITextureCreateDesc Desc =
|
|
FRHITextureCreateDesc::Create2D(*FenceName, TextureSize.X, TextureSize.Y, SourceTexture->GetFormat())
|
|
.SetFlags(ETextureCreateFlags::CPUReadback | ETextureCreateFlags::HideInVisualizeTexture)
|
|
.SetInitialState(ERHIAccess::CopyDest);
|
|
|
|
DestinationStagingTexture = RHICreateTexture(Desc);
|
|
}
|
|
|
|
FRHICopyTextureInfo CopyInfo;
|
|
|
|
if (Rect.IsValid())
|
|
{
|
|
CopyInfo.SourcePosition = FIntVector(Rect.X1, Rect.Y1, 1);
|
|
CopyInfo.DestPosition = CopyInfo.SourcePosition;
|
|
CopyInfo.Size = FIntVector(Rect.X2 - Rect.X1, Rect.Y2 - Rect.Y1, 1);
|
|
}
|
|
|
|
RHICmdList.CopyTexture(SourceTexture, DestinationStagingTexture, CopyInfo);
|
|
|
|
RHICmdList.WriteGPUFence(Fence);
|
|
|
|
LastCopyGPUMask = RHICmdList.GetGPUMask();
|
|
}
|
|
}
|
|
|
|
void* FRHIGPUTextureReadback::Lock(uint32 NumBytes)
|
|
{
|
|
if (DestinationStagingTexture)
|
|
{
|
|
LastLockGPUIndex = LastCopyGPUMask.ToIndex();
|
|
|
|
void* ResultsBuffer = nullptr;
|
|
int32 BufferWidth = 0, BufferHeight = 0;
|
|
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
|
RHICmdList.Transition(FRHITransitionInfo(DestinationStagingTexture, ERHIAccess::CopyDest, ERHIAccess::CPURead));
|
|
RHICmdList.MapStagingSurface(DestinationStagingTexture, Fence.GetReference(), ResultsBuffer, BufferWidth, BufferHeight, LastLockGPUIndex);
|
|
return ResultsBuffer;
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void* FRHIGPUTextureReadback::Lock(int32& OutRowPitchInPixels, int32 *OutBufferHeight)
|
|
{
|
|
if (DestinationStagingTexture)
|
|
{
|
|
LastLockGPUIndex = LastCopyGPUMask.ToIndex();
|
|
|
|
void* ResultsBuffer = nullptr;
|
|
int32 BufferWidth = 0, BufferHeight = 0;
|
|
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
|
RHICmdList.Transition(FRHITransitionInfo(DestinationStagingTexture, ERHIAccess::CopyDest, ERHIAccess::CPURead));
|
|
RHICmdList.MapStagingSurface(DestinationStagingTexture, Fence.GetReference(), ResultsBuffer, BufferWidth, BufferHeight, LastLockGPUIndex);
|
|
if (OutBufferHeight)
|
|
{
|
|
*OutBufferHeight = BufferHeight;
|
|
}
|
|
OutRowPitchInPixels = BufferWidth;
|
|
return ResultsBuffer;
|
|
}
|
|
|
|
OutRowPitchInPixels = 0;
|
|
if (OutBufferHeight)
|
|
{
|
|
*OutBufferHeight = 0;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void FRHIGPUTextureReadback::LockTexture(FRHICommandListImmediate& RHICmdList, void*& OutBufferPtr, int32& OutRowPitchInPixels)
|
|
{
|
|
OutBufferPtr = Lock(OutRowPitchInPixels);
|
|
}
|
|
|
|
void FRHIGPUTextureReadback::Unlock()
|
|
{
|
|
ensure(DestinationStagingTexture);
|
|
|
|
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
|
RHICmdList.UnmapStagingSurface(DestinationStagingTexture, LastLockGPUIndex);
|
|
RHICmdList.Transition(FRHITransitionInfo(DestinationStagingTexture, ERHIAccess::CPURead, ERHIAccess::CopyDest));
|
|
}
|