Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/Lumen/LumenSurfaceCacheFeedback.cpp
2025-05-18 13:04:45 +08:00

483 lines
21 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LumenSurfaceCacheFeedback.h"
#include "SceneRendering.h"
#include "DeferredShadingRenderer.h"
#include "GPUFeedbackCompaction.h"
#include "LumenSceneData.h"
#include "Lumen.h"
#include "LumenReflections.h"
#include "LumenVisualize.h"
#include "ScenePrivate.h"
int32 GLumenSurfaceCacheFeedback = 1;
FAutoConsoleVariableRef CVarLumenSurfaceCacheFeedback(
TEXT("r.LumenScene.SurfaceCache.Feedback"),
GLumenSurfaceCacheFeedback,
TEXT("Whether to use surface cache feedback to selectively map higher quality surface cache pages."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GLumenSurfaceCacheFeedbackTileSize = 16;
FAutoConsoleVariableRef CVarLumenSurfaceCacheFeedbackTileSize(
TEXT("r.LumenScene.SurfaceCache.Feedback.TileSize"),
GLumenSurfaceCacheFeedbackTileSize,
TEXT("One surface cache feedback element will be writen out per tile. Aligned to a power of two."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GLumenSurfaceCacheFeedbackResLevelBias = -0.5f;
FAutoConsoleVariableRef CVarLumenSurfaceCacheFeedbackResLevelBias(
TEXT("r.LumenScene.SurfaceCache.Feedback.ResLevelBias"),
GLumenSurfaceCacheFeedbackResLevelBias,
TEXT("Bias resolution of on demand surface cache pages."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* InVariable)
{
Lumen::DebugResetSurfaceCache();
}),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GLumenSurfaceCacheFeedbackFeedbackMinPageHits = 16;
FAutoConsoleVariableRef CVarLumenSurfaceCacheFeedbackMinPageHits(
TEXT("r.LumenScene.SurfaceCache.Feedback.MinPageHits"),
GLumenSurfaceCacheFeedbackFeedbackMinPageHits,
TEXT("Min number of page hits to demand a new page."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GLumenSurfaceCacheFeedbackMaxUniqueElements = 1024;
FAutoConsoleVariableRef CVarLumenSurfaceCacheFeedbackUniqueElements(
TEXT("r.LumenScene.SurfaceCache.Feedback.UniqueElements"),
GLumenSurfaceCacheFeedbackMaxUniqueElements,
TEXT("Limit of unique surface cache feedback elements. Used to resize buffers."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
uint32 Lumen::GetFeedbackBufferTileSize()
{
return FMath::RoundUpToPowerOfTwo(FMath::Clamp(GLumenSurfaceCacheFeedbackTileSize, 1, 256));
}
uint32 Lumen::GetFeedbackBufferTileWrapMask()
{
// Index & TileWrapMask = Index % TileSize
return GetFeedbackBufferTileSize() - 1;
}
uint32 Lumen::GetFeedbackBufferSize(const FViewFamilyInfo& ViewFamily)
{
const FSceneTexturesConfig& SceneTexturesConfig = ViewFamily.SceneTexturesConfig;
const FIntPoint SceneTextureExtentInTiles = FIntPoint::DivideAndRoundUp(SceneTexturesConfig.Extent, Lumen::GetFeedbackBufferTileSize());
const uint32 FeedbackBufferSize = SceneTextureExtentInTiles.X * SceneTextureExtentInTiles.Y;
return FeedbackBufferSize;
}
uint32 Lumen::GetCompactedFeedbackBufferSize()
{
return FMath::RoundUpToPowerOfTwo(FMath::Clamp(GLumenSurfaceCacheFeedbackMaxUniqueElements, 1, 16 * 1024));
}
FLumenSurfaceCacheFeedback::FLumenSurfaceCacheFeedback()
{
ReadbackBuffers.AddZeroed(MaxReadbackBuffers);
}
FLumenSurfaceCacheFeedback::~FLumenSurfaceCacheFeedback()
{
for (int32 BufferIndex = 0; BufferIndex < ReadbackBuffers.Num(); ++BufferIndex)
{
if (ReadbackBuffers[BufferIndex])
{
delete ReadbackBuffers[BufferIndex];
ReadbackBuffers[BufferIndex] = nullptr;
}
}
}
void FLumenSurfaceCacheFeedback::AllocateFeedbackResources(FRDGBuilder& GraphBuilder, FFeedbackResources& Resources, const FViewFamilyInfo& ViewFamily) const
{
Resources.BufferSize = Lumen::GetFeedbackBufferSize(ViewFamily);
FRDGBuffer* BufferAllocator = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1),
TEXT("Lumen.FeedbackAllocator"));
Resources.BufferAllocatorUAV = GraphBuilder.CreateUAV(BufferAllocator, ERDGUnorderedAccessViewFlags::SkipBarrier);
Resources.BufferAllocatorSRV = GraphBuilder.CreateSRV(BufferAllocator, PF_R32_UINT);
FRDGBuffer* Buffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32) * Lumen::FeedbackBufferElementStride, Resources.BufferSize),
TEXT("Lumen.Feedback"));
Resources.BufferUAV = GraphBuilder.CreateUAV(Buffer, ERDGUnorderedAccessViewFlags::SkipBarrier);
Resources.BufferSRV = GraphBuilder.CreateSRV(Buffer, PF_R32G32_UINT);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(BufferAllocator, PF_R32_UINT), 0);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Buffer, PF_R32G32_UINT), 0);
}
FRDGBufferUAVRef FLumenSurfaceCacheFeedback::GetDummyFeedbackAllocatorUAV(FRDGBuilder& GraphBuilder) const
{
FRDGBufferRef DummyBufferAllocator = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1),
TEXT("Lumen.DummyFeedbackAllocator"));
return GraphBuilder.CreateUAV(DummyBufferAllocator, ERDGUnorderedAccessViewFlags::SkipBarrier);
}
FRDGBufferUAVRef FLumenSurfaceCacheFeedback::GetDummyFeedbackUAV(FRDGBuilder& GraphBuilder) const
{
FRDGBufferRef DummyBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32) * Lumen::FeedbackBufferElementStride, 1),
TEXT("Lumen.DummyFeedback"));
return GraphBuilder.CreateUAV(DummyBuffer, ERDGUnorderedAccessViewFlags::SkipBarrier);
}
void FLumenSurfaceCacheFeedback::SubmitFeedbackBuffer(
const FViewInfo& View,
FRDGBuilder& GraphBuilder,
FLumenSurfaceCacheFeedback::FFeedbackResources& FeedbackResources)
{
if (ReadbackBuffersNumPending == MaxReadbackBuffers)
{
// Return when queue is full. It is NOT safe to EnqueueCopy on a buffer that already has a pending copy.
return;
}
RDG_EVENT_SCOPE(GraphBuilder, "Submit Lumen surface cache feedback");
const uint32 CompactedFeedbackBufferSize = Lumen::GetCompactedFeedbackBufferSize();
const uint32 HashTableSize = 2 * CompactedFeedbackBufferSize;
const uint32 HashTableIndexWrapMask = HashTableSize - 1;
FRDGBufferDesc CompactedFeedbackBufferDesc(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32) * Lumen::FeedbackBufferElementStride, CompactedFeedbackBufferSize));
CompactedFeedbackBufferDesc.Usage |= BUF_SourceCopy;
FRDGBufferRef CompactedFeedbackBuffer = GraphBuilder.CreateBuffer(CompactedFeedbackBufferDesc, TEXT("Lumen.CompactedFeedback"));
// Need to clear this buffer, as first element will be used as an allocator
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CompactedFeedbackBuffer, PF_R32_UINT), 0);
FRDGBufferDesc HashTableBufferDesc(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), HashTableSize));
FRDGBufferRef HashTableKeyBuffer = GraphBuilder.CreateBuffer(HashTableBufferDesc, TEXT("Lumen.HashTableKeys"));
FRDGBufferRef HashTableElementIndexBuffer = GraphBuilder.CreateBuffer(HashTableBufferDesc, TEXT("Lumen.HashTableElementIndices"));
FRDGBufferRef HashTableElementCountBuffer = GraphBuilder.CreateBuffer(HashTableBufferDesc, TEXT("Lumen.HashTableElementCounts"));
// Hash table depends on empty slots to be 0
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HashTableKeyBuffer, PF_R32_UINT), 0);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HashTableElementCountBuffer, PF_R32_UINT), 0);
FRDGBufferRef BuildHashTableIndirectArgBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc<FRHIDispatchIndirectParameters>(1), TEXT("Lumen.BuildHashTableIndirectArgs"));
// Set indirect dispatch arguments for hash table building
{
FBuildFeedbackHashTableIndirectArgsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FBuildFeedbackHashTableIndirectArgsCS::FParameters>();
PassParameters->RWBuildHashTableIndirectArgs = GraphBuilder.CreateUAV(BuildHashTableIndirectArgBuffer, PF_R32_UINT);
PassParameters->FeedbackBufferAllocator = FeedbackResources.BufferAllocatorSRV;
PassParameters->FeedbackBuffer = FeedbackResources.BufferSRV;
PassParameters->FeedbackBufferSize = FeedbackResources.BufferSize;
FBuildFeedbackHashTableIndirectArgsCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FBuildFeedbackHashTableIndirectArgsCS::FFeedbackBufferStride>(Lumen::FeedbackBufferElementStride);
auto ComputeShader = View.ShaderMap->GetShader<FBuildFeedbackHashTableIndirectArgsCS>(PermutationVector);
const FIntVector GroupSize = FIntVector(1, 1, 1);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Hash table indirect arguments"),
ComputeShader,
PassParameters,
GroupSize);
}
// Build hash table of feedback elements
{
FBuildFeedbackHashTableCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FBuildFeedbackHashTableCS::FParameters>();
PassParameters->BuildHashTableIndirectArgs = BuildHashTableIndirectArgBuffer;
PassParameters->RWHashTableKeys = GraphBuilder.CreateUAV(HashTableKeyBuffer);
PassParameters->RWHashTableElementIndices = GraphBuilder.CreateUAV(HashTableElementIndexBuffer);
PassParameters->RWHashTableElementCounts = GraphBuilder.CreateUAV(HashTableElementCountBuffer);
PassParameters->HashTableSize = HashTableSize;
PassParameters->HashTableIndexWrapMask = HashTableIndexWrapMask;
PassParameters->FeedbackBufferAllocator = FeedbackResources.BufferAllocatorSRV;
PassParameters->FeedbackBuffer = FeedbackResources.BufferSRV;
PassParameters->FeedbackBufferSize = FeedbackResources.BufferSize;
FBuildFeedbackHashTableCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FBuildFeedbackHashTableCS::FFeedbackBufferStride>(Lumen::FeedbackBufferElementStride);
auto ComputeShader = View.ShaderMap->GetShader<FBuildFeedbackHashTableCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Build feedback hash table"),
ComputeShader,
PassParameters,
BuildHashTableIndirectArgBuffer,
0);
}
// Compact hash table into an array of unique feedback elements
{
FCompactFeedbackHashTableCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FCompactFeedbackHashTableCS::FParameters>();
PassParameters->RWCompactedFeedbackBuffer = GraphBuilder.CreateUAV(CompactedFeedbackBuffer);
PassParameters->CompactedFeedbackBufferSize = CompactedFeedbackBufferSize;
PassParameters->CompactedFeedbackCountShiftBits = 16;
PassParameters->HashTableElementIndices = GraphBuilder.CreateSRV(HashTableElementIndexBuffer, PF_R32_UINT);
PassParameters->HashTableElementCounts = GraphBuilder.CreateSRV(HashTableElementCountBuffer, PF_R32_UINT);
PassParameters->HashTableSize = HashTableSize;
PassParameters->HashTableIndexWrapMask = HashTableIndexWrapMask;
PassParameters->FeedbackBufferAllocator = FeedbackResources.BufferAllocatorSRV;
PassParameters->FeedbackBuffer = FeedbackResources.BufferSRV;
PassParameters->FeedbackBufferSize = FeedbackResources.BufferSize;
FCompactFeedbackHashTableCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FCompactFeedbackHashTableCS::FFeedbackBufferStride>(Lumen::FeedbackBufferElementStride);
auto ComputeShader = View.ShaderMap->GetShader<FCompactFeedbackHashTableCS>(PermutationVector);
const FIntVector GroupSize = FComputeShaderUtils::GetGroupCount(HashTableSize, FCompactFeedbackHashTableCS::GetGroupSize());
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Compact feedback hash table"),
ComputeShader,
PassParameters,
GroupSize);
}
if (ReadbackBuffers[ReadbackBuffersWriteIndex] == nullptr)
{
FRHIGPUBufferReadback* GPUBufferReadback = new FRHIGPUBufferReadback(TEXT("Lumen.SurfaceCacheFeedbackBuffer"));
ReadbackBuffers[ReadbackBuffersWriteIndex] = GPUBufferReadback;
}
FRHIGPUBufferReadback* ReadbackBuffer = ReadbackBuffers[ReadbackBuffersWriteIndex];
AddReadbackBufferPass(GraphBuilder, RDG_EVENT_NAME("Readback"), CompactedFeedbackBuffer,
[ReadbackBuffer, CompactedFeedbackBuffer](FRDGAsyncTask, FRHICommandList& RHICmdList)
{
ReadbackBuffer->EnqueueCopy(RHICmdList, CompactedFeedbackBuffer->GetRHI(), 0u);
});
ReadbackBuffersWriteIndex = (ReadbackBuffersWriteIndex + 1) % MaxReadbackBuffers;
ReadbackBuffersNumPending = FMath::Min(ReadbackBuffersNumPending + 1, MaxReadbackBuffers);
++FrameIndex;
}
FRHIGPUBufferReadback* FLumenSurfaceCacheFeedback::GetLatestReadbackBuffer()
{
FRHIGPUBufferReadback* LatestReadbackBuffer = nullptr;
// Find latest buffer that is ready
while (ReadbackBuffersNumPending > 0)
{
uint32 Index = (ReadbackBuffersWriteIndex + MaxReadbackBuffers - ReadbackBuffersNumPending) % MaxReadbackBuffers;
if (ReadbackBuffers[Index]->IsReady())
{
--ReadbackBuffersNumPending;
LatestReadbackBuffer = ReadbackBuffers[Index];
}
else
{
break;
}
}
return LatestReadbackBuffer;
}
void FLumenSceneData::UpdateSurfaceCacheFeedback(FFeedbackData FeedbackData, const TArray<FVector, TInlineAllocator<2>>& LumenSceneCameraOrigins, TArray<FSurfaceCacheRequest>& SurfaceCacheRequests, const FViewFamilyInfo& ViewFamily, int32 RequestHistogram[Lumen::NumDistanceBuckets])
{
TRACE_CPUPROFILER_EVENT_SCOPE(UpdateSurfaceCacheFeedback);
NumHiResPagesToAdd = 0;
if (FeedbackData.Data)
{
const int32 HeaderSize = 1;
const int32 NumFeedbackElements = FMath::Min<int32>(FeedbackData.Data[0], FeedbackData.NumElements - HeaderSize);
for (int32 FeedbackElementIndex = 0; FeedbackElementIndex < NumFeedbackElements; ++FeedbackElementIndex)
{
const uint32 PackedA = FeedbackData.Data[(FeedbackElementIndex + HeaderSize) * Lumen::FeedbackBufferElementStride + 0];
const uint32 PackedB = FeedbackData.Data[(FeedbackElementIndex + HeaderSize) * Lumen::FeedbackBufferElementStride + 1];
int32 CardIndex = PackedA & 0xFFFFFF;
uint16 DesiredResLevel = FMath::Clamp(PackedA >> 24, Lumen::MinResLevel, Lumen::MaxResLevel);
FIntPoint LocalPageCoord;
LocalPageCoord.X = (PackedB >> 0) & 0xFF;
LocalPageCoord.Y = (PackedB >> 8) & 0xFF;
const uint32 PageHitNum = (PackedB >> 16) & 0xFFFF;
if (PageHitNum > GLumenSurfaceCacheFeedbackFeedbackMinPageHits
&& CardIndex < Cards.Num()
&& Cards.IsAllocated(CardIndex))
{
FLumenCard& Card = Cards[CardIndex];
FLumenMipMapDesc MipMapDesc;
Card.GetMipMapDesc(DesiredResLevel, MipMapDesc);
LocalPageCoord.X = FMath::Clamp(LocalPageCoord.X, 0, MipMapDesc.SizeInPages.X - 1);
LocalPageCoord.Y = FMath::Clamp(LocalPageCoord.Y, 0, MipMapDesc.SizeInPages.Y - 1);
const uint16 LocalPageIndex = LocalPageCoord.X + LocalPageCoord.Y * MipMapDesc.SizeInPages.X;
FVirtualPageIndex PageIndex(CardIndex, DesiredResLevel, LocalPageIndex);
const FLumenSurfaceMipMap MipMap = Card.GetMipMap(PageIndex.ResLevel);
const int32 PageTableIndex = MipMap.PageTableSpanSize > 0 ? MipMap.GetPageTableIndex(PageIndex.LocalPageIndex) : -1;
if (PageTableIndex >= 0 && PageTable[PageTableIndex].IsMapped())
{
// Update last used time for existing pages
if (!MipMap.bLocked)
{
UnlockedAllocationHeap.Update(SurfaceCacheFeedback.GetFrameIndex(), PageTableIndex);
}
}
else
{
float DistanceSquared = FLT_MAX; // LWC_TODO
for (FVector CameraOrigin : LumenSceneCameraOrigins)
{
DistanceSquared = FMath::Min(DistanceSquared, Card.WorldOBB.ComputeSquaredDistanceToPoint(CameraOrigin));
}
float Distance = FMath::Sqrt(DistanceSquared);
// Change priority based on the normalized number of hits and make those request less important than low res resident pages
const float NormalizeNumberOfHits = PageHitNum / float(Lumen::GetFeedbackBufferSize(ViewFamily));
Distance += 2500.0f + 2500.0f * (1.0f - NormalizeNumberOfHits);
// Requested missing page
FSurfaceCacheRequest Request;
Request.CardIndex = PageIndex.CardIndex;
Request.ResLevel = PageIndex.ResLevel;
Request.LocalPageIndex = PageIndex.LocalPageIndex;
Request.Distance = Distance;
SurfaceCacheRequests.Add(Request);
uint32 Bin = Lumen::GetMeshCardDistanceBin(Distance);
RequestHistogram[Bin]++;
ensure(!Request.IsLockedMip());
++NumHiResPagesToAdd;
}
}
}
}
}
FIntPoint FLumenSurfaceCacheFeedback::GetFeedbackBufferTileJitter() const
{
const uint32 TileSize = Lumen::GetFeedbackBufferTileSize();
const uint32 TileSizeLog2 = FMath::CeilLogTwo(TileSize);
const uint32 SequenceSize = FMath::Square(TileSize);
const uint32 PixelIndex = FrameIndex % SequenceSize;
const uint32 PixelAddress = ReverseBits(PixelIndex) >> (32U - 2 * TileSizeLog2);
FIntPoint TileJitter;
TileJitter.X = FMath::ReverseMortonCode2(PixelAddress);
TileJitter.Y = FMath::ReverseMortonCode2(PixelAddress >> 1);
return TileJitter;
}
void FDeferredShadingSceneRenderer::BeginGatheringLumenSurfaceCacheFeedback(FRDGBuilder& GraphBuilder, const FViewInfo& View, FLumenSceneFrameTemporaries& FrameTemporaries)
{
const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View);
const bool bLumenActive = ViewPipelineState.DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen || ViewPipelineState.ReflectionsMethod == EReflectionsMethod::Lumen;
if (bLumenActive && GLumenSurfaceCacheFeedback != 0)
{
FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View);
const bool bVisualizeUsesFeedback = LumenVisualize::UseSurfaceCacheFeedback(ViewFamily.EngineShowFlags);
const bool bReflectionsUseFeedback = ViewPipelineState.ReflectionsMethod == EReflectionsMethod::Lumen && LumenReflections::UseSurfaceCacheFeedback();
if (!Lumen::IsSurfaceCacheFrozen() && (bVisualizeUsesFeedback || bReflectionsUseFeedback))
{
ensure(FrameTemporaries.SurfaceCacheFeedbackResources.BufferUAV == nullptr);
LumenSceneData.SurfaceCacheFeedback.AllocateFeedbackResources(GraphBuilder, FrameTemporaries.SurfaceCacheFeedbackResources, ViewFamily);
}
if (LumenSceneData.CardPageLastUsedBuffer && LumenSceneData.CardPageHighResLastUsedBuffer)
{
FRDGBuffer* CardPageLastUsedBuffer = GraphBuilder.RegisterExternalBuffer(LumenSceneData.CardPageLastUsedBuffer);
FrameTemporaries.CardPageLastUsedBufferSRV = GraphBuilder.CreateSRV(CardPageLastUsedBuffer);
FrameTemporaries.CardPageLastUsedBufferUAV = GraphBuilder.CreateUAV(CardPageLastUsedBuffer, ERDGUnorderedAccessViewFlags::SkipBarrier);
FRDGBuffer* CardPageHighResLastUsedBuffer = GraphBuilder.RegisterExternalBuffer(LumenSceneData.CardPageHighResLastUsedBuffer);
FrameTemporaries.CardPageHighResLastUsedBufferSRV = GraphBuilder.CreateSRV(CardPageHighResLastUsedBuffer);
FrameTemporaries.CardPageHighResLastUsedBufferUAV = GraphBuilder.CreateUAV(CardPageHighResLastUsedBuffer, ERDGUnorderedAccessViewFlags::SkipBarrier);
}
}
}
void FDeferredShadingSceneRenderer::FinishGatheringLumenSurfaceCacheFeedback(FRDGBuilder& GraphBuilder, const FViewInfo& View, FLumenSceneFrameTemporaries& FrameTemporaries)
{
const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View);
const bool bLumenActive = ViewPipelineState.DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen || ViewPipelineState.ReflectionsMethod == EReflectionsMethod::Lumen;
if (bLumenActive && GLumenSurfaceCacheFeedback != 0)
{
FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View);
if (FrameTemporaries.SurfaceCacheFeedbackResources.BufferUAV)
{
LumenSceneData.SurfaceCacheFeedback.SubmitFeedbackBuffer(Views[0], GraphBuilder, FrameTemporaries.SurfaceCacheFeedbackResources);
FrameTemporaries.SurfaceCacheFeedbackResources = {};
}
if (FrameTemporaries.CardPageLastUsedBufferUAV && FrameTemporaries.CardPageHighResLastUsedBufferUAV)
{
GraphBuilder.QueueBufferExtraction(FrameTemporaries.CardPageLastUsedBufferUAV->GetParent(), &LumenSceneData.CardPageLastUsedBuffer);
GraphBuilder.QueueBufferExtraction(FrameTemporaries.CardPageHighResLastUsedBufferUAV->GetParent(), &LumenSceneData.CardPageHighResLastUsedBuffer);
}
}
if (FrameTemporaries.AlbedoAtlas)
{
FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.DepthAtlas, &LumenSceneData.DepthAtlas);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.AlbedoAtlas, &LumenSceneData.AlbedoAtlas);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.OpacityAtlas, &LumenSceneData.OpacityAtlas);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.NormalAtlas, &LumenSceneData.NormalAtlas);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.EmissiveAtlas, &LumenSceneData.EmissiveAtlas);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.DirectLightingAtlas, &LumenSceneData.DirectLightingAtlas);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.IndirectLightingAtlas, &LumenSceneData.IndirectLightingAtlas);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.RadiosityNumFramesAccumulatedAtlas, &LumenSceneData.RadiosityNumFramesAccumulatedAtlas);
GraphBuilder.QueueTextureExtraction(FrameTemporaries.FinalLightingAtlas, &LumenSceneData.FinalLightingAtlas);
GraphBuilder.QueueBufferExtraction(FrameTemporaries.TileShadowDownsampleFactorAtlas, &LumenSceneData.TileShadowDownsampleFactorAtlas);
if (FrameTemporaries.DiffuseLightingAndSecondMomentHistoryAtlas && FrameTemporaries.DiffuseLightingAndSecondMomentHistoryAtlas->HasBeenProduced())
{
GraphBuilder.QueueTextureExtraction(FrameTemporaries.DiffuseLightingAndSecondMomentHistoryAtlas, &LumenSceneData.DiffuseLightingAndSecondMomentHistoryAtlas);
}
if (FrameTemporaries.NumFramesAccumulatedHistoryAtlas && FrameTemporaries.NumFramesAccumulatedHistoryAtlas->HasBeenProduced())
{
GraphBuilder.QueueTextureExtraction(FrameTemporaries.NumFramesAccumulatedHistoryAtlas, &LumenSceneData.NumFramesAccumulatedHistoryAtlas);
}
}
QueueExtractStochasticLighting(GraphBuilder, FrameTemporaries);
}