Files
UnrealEngine/Engine/Source/Runtime/Landscape/Private/LandscapeAsyncTextureReadback.h
2025-05-18 13:04:45 +08:00

92 lines
4.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "RHIGPUReadback.h"
#include "RenderGraphFwd.h"
#include <atomic>
// Class that performs an async GPU texture readback for landscape purposes.
// Only supports BGRA8 texture format, and returns the results as an array of FColors.
class FLandscapeAsyncTextureReadback
{
private:
// game thread / render thread shared state
std::atomic_int PendingRenderThreadCommands = 0; // number of render thread commands queued and not yet executed (for sanity checks)
std::atomic_bool bCancel = false; // set by game thread to signal cancel readback: we don't need to produce valid results
// render thread state
std::atomic_bool bStartedOnRenderThread = false; // render thread start command has sent the readback command to the GPU
std::atomic_bool bFinishedOnRenderThread = false; // render thread finish command has made the data available to game thread
TUniquePtr<FRHIGPUTextureReadback> AsyncReadback; // render thread managed async readback structure
// results - readable by game thread when bFinishedOnRenderThread
int32 TextureWidth = 0;
int32 TextureHeight = 0;
TArray<FColor> ReadbackResults;
public:
// construct on the game thread
FLandscapeAsyncTextureReadback() {}
// destruct on the render thread. From game thread, call QueueDeletion() instead
~FLandscapeAsyncTextureReadback()
{
check(IsInRenderingThread());
}
// use this to start an async readback operation from the render thread (on a render graph texture)
void StartReadback_RenderThread(FRDGBuilder& GraphBuilder, FRDGTextureRef RDGTexture);
// checks if the operation is ready to finish, and if it is, calls FinishReadback_RenderThread() to complete the readback.
// bInForceFinish will force the readback to finish by stalling the render thread until it is ready
void CheckAndUpdate_RenderThread(const bool bInForceFinish);
// use this to finish an async readback operation from the render thread
// Calling this when !IsComplete() will cause a stall until the GPU has completed the readback
void FinishReadback_RenderThread();
// Non-blocking call from the game thread to check readback status and start the finish command if needed.
// Returns true when the AsyncReadbackResults are available to the game thread, at which point you can call TakeResults() to access them.
// bOutRenderCommandQueued is set to true if any render command was queued in this call, othwerwise unchanged.
// You must call this function occasionally or the readback will never complete, as the finish command is required.
// bInForceFinish will force the finish process to be queued on the render thread (potentially stalling render thread, but forcing it to finish the readback)
// bInForceFinish DOES NOT generally make the results immediately available, but it will ensure they are available after the render thread executes the command.
bool CheckAndUpdate(bool& bOutRenderCommandQueued, const bool bInForceFinish);
// Call from game thread to terminate any readback in flight and queue deletion of this (FLandscapeAsyncTextureReadback) on the render thread
void CancelAndSelfDestruct();
// Returns true when async readback results are available. Call TakeResults() to retrieve them.
bool IsComplete()
{
return bFinishedOnRenderThread;
}
// Retrieve the async readback results. Requires readback to be complete.
// This function returns its internal memory buffer, relinquishing control over it, so this function can only be called once.
TArray<FColor> TakeResults(FIntPoint* OutSize)
{
check(bFinishedOnRenderThread);
if (OutSize)
{
*OutSize = FIntPoint(TextureWidth, TextureHeight);
}
return MoveTemp(ReadbackResults);
}
FString ToString()
{
FString Result;
Result.Appendf(TEXT("FLandscapeAsyncTextureReadback { RTStart: %d RTComplete: %d PendingCommands: %d Cancel: %d AsyncReadback: %p }"), bStartedOnRenderThread.load(), bFinishedOnRenderThread.load(), PendingRenderThreadCommands.load(), bCancel.load(), AsyncReadback.Get());
return Result;
}
// once complete, call this to queue deletion of the readback object on the render thread
// (this must be deleted on the render thread to avoid other render-thread queued commands from accessing a deallocated pointer)
void QueueDeletionFromGameThread();
};