From ec62763e8762353fa1553a813ec2a4c13670c6c8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Nov 2022 00:37:01 +0100 Subject: [PATCH] Optimize Global SDF cascades updates intervals (max 1 cascade per frame) --- Source/Engine/Graphics/RenderTools.cpp | 89 +++++++++++++++++++ Source/Engine/Graphics/RenderTools.h | 11 +++ .../Renderer/GlobalSignDistanceFieldPass.cpp | 11 ++- 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp index 184eb82f7..912b0ed48 100644 --- a/Source/Engine/Graphics/RenderTools.cpp +++ b/Source/Engine/Graphics/RenderTools.cpp @@ -489,6 +489,95 @@ int32 RenderTools::ComputeSkinnedModelLOD(const SkinnedModel* model, const Float return 0; } +void RenderTools::ComputeCascadeUpdateFrequency(int32 cascadeIndex, int32 cascadeCount, int32& updateFrequency, int32& updatePhrase, int32 updateMaxCountPerFrame) +{ + switch (updateMaxCountPerFrame) + { + case 1: // 1 cascade update per frame + switch (cascadeIndex) + { + case 0: // Cascade 0 + updateFrequency = 2; + updatePhrase = 0; + break; + case 1: // Cascade 1 + updateFrequency = 4; + updatePhrase = 1; + break; + case 2: // Cascade 2 + updateFrequency = 8; + updatePhrase = 3; + break; + default: + if (cascadeCount > 4) + { + if (cascadeIndex == 3) + { + // Cascade 3 + updateFrequency = 16; + updatePhrase = 7; + } + else + { + // Other + updateFrequency = 16; + updatePhrase = 15; + } + } + else + { + // Cascade 3 + updateFrequency = 8; + updatePhrase = 7; + } + } + break; + case 2: // 2 cascade2 update per frame + switch (cascadeIndex) + { + case 0: // Cascade 0 + updateFrequency = 1; + updatePhrase = 0; + break; + case 1: // Cascade 1 + updateFrequency = 2; + updatePhrase = 0; + break; + case 2: // Cascade 2 + updateFrequency = 4; + updatePhrase = 1; + break; + default: + if (cascadeCount > 4) + { + if (cascadeIndex == 3) + { + // Cascade 3 + updateFrequency = 8; + updatePhrase = 3; + } + else + { + // Other + updateFrequency = 8; + updatePhrase = 7; + } + } + else + { + // Cascade 3 + updateFrequency = 4; + updatePhrase = 3; + } + } + break; + default: // Every frame + updateFrequency = 1; + updatePhrase = 1; + break; + } +} + int32 MipLevelsCount(int32 width, bool useMipLevels) { if (!useMipLevels) diff --git a/Source/Engine/Graphics/RenderTools.h b/Source/Engine/Graphics/RenderTools.h index c3088a96d..da2c26c35 100644 --- a/Source/Engine/Graphics/RenderTools.h +++ b/Source/Engine/Graphics/RenderTools.h @@ -108,6 +108,17 @@ public: const uint32 distanceI = *((uint32*)&distance); return ((uint32)(-(int32)(distanceI >> 31)) | 0x80000000) ^ distanceI; } + + // Calculates the update frequency and phrase for the given cached data (eg. cascaded shadow map or global sdf cascade contents). Lower data indices are updated first and more frequent. + static void ComputeCascadeUpdateFrequency(int32 cascadeIndex, int32 cascadeCount, int32& updateFrequency, int32& updatePhrase, int32 updateMaxCountPerFrame = 1); + + // Checks if cached data should be updated during the given frame. + FORCE_INLINE static bool ShouldUpdateCascade(int32 frameIndex, int32 cascadeIndex, int32 cascadeCount, int32 updateMaxCountPerFrame = 1, bool updateForce = false) + { + int32 updateFrequency, updatePhrase; + ComputeCascadeUpdateFrequency(cascadeIndex, cascadeCount, updateFrequency, updatePhrase, updateMaxCountPerFrame); + return (frameIndex % updateFrequency == updatePhrase) || updateForce; + } }; // Calculate mip levels count for a texture 1D diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index 7e6031252..fd748104c 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -11,6 +11,7 @@ #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderTargetPool.h" +#include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Level/Scene/SceneRendering.h" #include "Engine/Level/Actors/StaticModel.h" @@ -171,6 +172,7 @@ struct CascadeData class GlobalSignDistanceFieldCustomBuffer : public RenderBuffers::CustomBuffer, public ISceneRenderingListener { public: + int32 FrameIndex = 0; int32 Resolution = 0; GPUTexture* Texture = nullptr; GPUTexture* TextureMip = nullptr; @@ -415,6 +417,7 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex { sdfData.Cascades.Resize(cascadesCount); sdfData.Resolution = resolution; + sdfData.FrameIndex = 0; updated = true; auto desc = GPUTextureDescription::New3D(resolution * cascadesCount, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); { @@ -489,14 +492,16 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex auto& chunks = ChunksCache; chunks.EnsureCapacity(rasterizeChunks * rasterizeChunks, false); bool anyDraw = false; - const uint64 cascadeFrequencies[] = { 2, 3, 5, 11 }; - //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 }; + const bool updateEveryFrame = false; // true if update all cascades every frame + const int32 maxCascadeUpdatesPerFrame = 1; // maximum cascades to update at a single frame GPUTextureView* textureView = sdfData.Texture->ViewVolume(); GPUTextureView* textureMipView = sdfData.TextureMip->ViewVolume(); + if (sdfData.FrameIndex++ > 128) + sdfData.FrameIndex = 0; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { // Reduce frequency of the updates - if (useCache && (Engine::FrameCount % cascadeFrequencies[cascadeIndex]) != 0) + if (useCache && !RenderTools::ShouldUpdateCascade(sdfData.FrameIndex, cascadeIndex, cascadesCount, maxCascadeUpdatesPerFrame, updateEveryFrame)) continue; auto& cascade = sdfData.Cascades[cascadeIndex]; const float cascadeDistance = distanceExtent * cascadesDistanceScales[cascadeIndex];