From 9c161121b37da41b2cfd237e7d37c2c067b0ef0c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 19 Jun 2024 14:03:55 +0200 Subject: [PATCH] Add `Graphics::SpreadWorkload` to disable graphics workloads amortization over several frames when debugging graphics perf --- Source/Engine/Graphics/Graphics.cpp | 1 + Source/Engine/Graphics/Graphics.h | 13 +++++++++++++ .../GI/DynamicDiffuseGlobalIllumination.cpp | 2 +- .../Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp | 5 +++-- .../Engine/Renderer/GlobalSignDistanceFieldPass.cpp | 2 +- Source/Engine/Renderer/ShadowsPass.cpp | 5 +++++ 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index 8483d1f6c..9535f3ecf 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -22,6 +22,7 @@ bool Graphics::AllowCSMBlending = false; Quality Graphics::GlobalSDFQuality = Quality::High; Quality Graphics::GIQuality = Quality::High; PostProcessSettings Graphics::PostProcessSettings; +bool Graphics::SpreadWorkload = true; #if GRAPHICS_API_NULL extern GPUDevice* CreateGPUDeviceNull(); diff --git a/Source/Engine/Graphics/Graphics.h b/Source/Engine/Graphics/Graphics.h index 55ded56e5..b7f1fbcd7 100644 --- a/Source/Engine/Graphics/Graphics.h +++ b/Source/Engine/Graphics/Graphics.h @@ -69,9 +69,22 @@ public: /// API_FIELD() static PostProcessSettings PostProcessSettings; +public: + /// + /// Debug utility to toggle graphics workloads amortization over several frames by systems such as shadows mapping, global illumination or surface atlas. Can be used to test performance in the worst-case scenario (eg. camera-cut). + /// + API_FIELD() static bool SpreadWorkload; + public: /// /// Disposes the device. /// static void DisposeDevice(); }; + +// Skip disabling workload spreading in Release builds +#if BUILD_RELEASE +#define GPU_SPREAD_WORKLOAD true +#else +#define GPU_SPREAD_WORKLOAD Graphics::SpreadWorkload +#endif diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 3fbf2dc47..f82123a46 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -402,7 +402,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont bool cascadeSkipUpdate[4]; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { - cascadeSkipUpdate[cascadeIndex] = !clear && (ddgiData.LastFrameUsed % cascadeFrequencies[cascadeIndex]) != 0; + cascadeSkipUpdate[cascadeIndex] = !clear && (ddgiData.LastFrameUsed % cascadeFrequencies[cascadeIndex]) != 0 && GPU_SPREAD_WORKLOAD; } // Compute scrolling (probes are placed around camera but are scrolling to increase stability during movement) diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index a85269fcd..b1ed55eec 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -13,6 +13,7 @@ #include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPUDevice.h" +#include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderTargetPool.h" @@ -536,7 +537,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co context->SetRenderTarget(depthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers))); { PROFILE_GPU_CPU_NAMED("Clear"); - if (noCache || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES) + if (noCache || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES || !GPU_SPREAD_WORKLOAD) { // Full-atlas hardware clear context->ClearDepth(depthBuffer); @@ -1268,7 +1269,7 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con object->Bounds = OrientedBoundingBox(localBounds); object->Bounds.Transform(localToWorld); object->Radius = (float)actorObjectBounds.Radius; - if (dirty || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES) + if (dirty || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES || !GPU_SPREAD_WORKLOAD) { object->LastFrameUpdated = surfaceAtlasData.CurrentFrame; object->LightingUpdateFrame = surfaceAtlasData.CurrentFrame; diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index 4b5ee7816..2d35ac09e 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -493,7 +493,7 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex // Rasterize world geometry into Global SDF renderContext.View.Pass = DrawPass::GlobalSDF; uint32 viewMask = renderContext.View.RenderLayersMask; - const bool useCache = !updated && !GLOBAL_SDF_DEBUG_FORCE_REDRAW; + const bool useCache = !updated && !GLOBAL_SDF_DEBUG_FORCE_REDRAW && GPU_SPREAD_WORKLOAD; static_assert(GLOBAL_SDF_RASTERIZE_CHUNK_SIZE % GLOBAL_SDF_RASTERIZE_GROUP_SIZE == 0, "Invalid chunk size for Global SDF rasterization group size."); const int32 rasterizeChunks = Math::CeilToInt((float)resolution / (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE); auto& chunks = ChunksCache; diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index d779e0b83..25c4b993d 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -231,6 +231,11 @@ struct ShadowAtlasLight float CalculateUpdateRateInv(const RenderLightData& light, float distanceFromView, bool& freezeUpdate) const { + if (!GPU_SPREAD_WORKLOAD) + { + freezeUpdate = false; + return 1.0f; + } const float shadowsUpdateRate = light.ShadowsUpdateRate; const float shadowsUpdateRateAtDistance = shadowsUpdateRate * light.ShadowsUpdateRateAtDistance; float updateRate = Math::Lerp(shadowsUpdateRate, shadowsUpdateRateAtDistance, Math::Saturate(distanceFromView / Distance));