diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h
index 6e2074870..53bf89a61 100644
--- a/Source/Engine/Core/Config/GraphicsSettings.h
+++ b/Source/Engine/Core/Config/GraphicsSettings.h
@@ -62,11 +62,17 @@ public:
API_FIELD(Attributes="EditorOrder(1320), DefaultValue(false), EditorDisplay(\"Quality\", \"Allow CSM Blending\")")
bool AllowCSMBlending = false;
+ ///
+ /// If checked, enables Global SDF rendering. This can be used in materials, shaders, and particles.
+ ///
+ API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Global SDF\")")
+ bool EnableGlobalSDF = false;
+
#if USE_EDITOR
///
/// If checked, the 'Generate SDF' option will be checked on model import options by default. Use it if your project uses Global SDF (eg. for Global Illumination or particles).
///
- API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Global SDF\")")
+ API_FIELD(Attributes="EditorOrder(2010), EditorDisplay(\"Global SDF\")")
bool GenerateSDFOnModelImport = false;
#endif
diff --git a/Source/Engine/Graphics/RenderBuffers.h b/Source/Engine/Graphics/RenderBuffers.h
index b99ec5308..b0cbcd321 100644
--- a/Source/Engine/Graphics/RenderBuffers.h
+++ b/Source/Engine/Graphics/RenderBuffers.h
@@ -150,6 +150,17 @@ public:
return _viewport;
}
+ template
+ const T* FindCustomBuffer(const StringView& name) const
+ {
+ for (CustomBuffer* e : CustomBuffers)
+ {
+ if (e->Name == name)
+ return (const T*)e;
+ }
+ return nullptr;
+ }
+
template
T* GetCustomBuffer(const StringView& name)
{
diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp
index 4442e5b15..2fe47bc48 100644
--- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp
+++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp
@@ -110,6 +110,7 @@ public:
GPUTexture* CascadeMips[4] = {};
Vector3 Positions[4];
HashSet NonEmptyChunks[4];
+ GlobalSignDistanceFieldPass::BindingData Result;
~GlobalSignDistanceFieldCustomBuffer()
{
@@ -212,6 +213,17 @@ void GlobalSignDistanceFieldPass::Dispose()
ChunksCache.SetCapacity(0);
}
+bool GlobalSignDistanceFieldPass::Get(const RenderBuffers* buffers, BindingData& result)
+{
+ auto* sdfData = buffers->FindCustomBuffer(TEXT("GlobalSignDistanceField"));
+ if (sdfData && sdfData->LastFrameUsed == Engine::FrameCount)
+ {
+ result = sdfData->Result;
+ return false;
+ }
+ return true;
+}
+
bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContext* context, BindingData& result)
{
// Skip if not supported
@@ -221,6 +233,16 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
return true;
auto& sdfData = *renderContext.Buffers->GetCustomBuffer(TEXT("GlobalSignDistanceField"));
+ // Skip if already done in the current frame
+ const auto currentFrame = Engine::FrameCount;
+ if (sdfData.LastFrameUsed == currentFrame)
+ {
+ result = sdfData.Result;
+ return false;
+ }
+
+ PROFILE_GPU_CPU("Global SDF");
+
// TODO: configurable via graphics settings
const int32 resolution = 256;
const int32 mipFactor = 4;
@@ -229,238 +251,231 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
const float distanceExtent = 2500.0f;
const float cascadesDistances[] = { distanceExtent, distanceExtent * 2.0f, distanceExtent * 4.0f, distanceExtent * 8.0f };
- // Skip if already done in the current frame
- const auto currentFrame = Engine::FrameCount;
- if (sdfData.LastFrameUsed != currentFrame)
+ // Initialize buffers
+ sdfData.LastFrameUsed = currentFrame;
+ auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1);
+ bool updated = false;
+ for (GPUTexture*& cascade : sdfData.Cascades)
{
- PROFILE_GPU_CPU("Global SDF");
-
- // Initialize buffers
- sdfData.LastFrameUsed = currentFrame;
- auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1);
- bool updated = false;
- for (GPUTexture*& cascade : sdfData.Cascades)
+ if (cascade && cascade->Width() != desc.Width)
{
- if (cascade && cascade->Width() != desc.Width)
- {
- RenderTargetPool::Release(cascade);
- cascade = nullptr;
- }
+ RenderTargetPool::Release(cascade);
+ cascade = nullptr;
+ }
+ if (!cascade)
+ {
+ cascade = RenderTargetPool::Get(desc);
if (!cascade)
- {
- cascade = RenderTargetPool::Get(desc);
- if (!cascade)
- return true;
- updated = true;
- PROFILE_GPU_CPU("Init");
- context->ClearUA(cascade, Vector4::One);
- }
+ return true;
+ updated = true;
+ PROFILE_GPU_CPU("Init");
+ context->ClearUA(cascade, Vector4::One);
}
- desc = GPUTextureDescription::New3D(resolutionMip, resolutionMip, resolutionMip, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1);
- for (GPUTexture*& cascadeMip : sdfData.CascadeMips)
+ }
+ desc = GPUTextureDescription::New3D(resolutionMip, resolutionMip, resolutionMip, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1);
+ for (GPUTexture*& cascadeMip : sdfData.CascadeMips)
+ {
+ if (cascadeMip && cascadeMip->Width() != desc.Width)
{
- if (cascadeMip && cascadeMip->Width() != desc.Width)
- {
- RenderTargetPool::Release(cascadeMip);
- cascadeMip = nullptr;
- }
+ RenderTargetPool::Release(cascadeMip);
+ cascadeMip = nullptr;
+ }
+ if (!cascadeMip)
+ {
+ cascadeMip = RenderTargetPool::Get(desc);
if (!cascadeMip)
+ return true;
+ updated = true;
+ }
+ }
+ GPUTexture* tmpMip = nullptr;
+ if (updated)
+ LOG(Info, "Global SDF memory usage: {0} MB", (sdfData.Cascades[0]->GetMemoryUsage() + sdfData.CascadeMips[0]->GetMemoryUsage()) * ARRAY_COUNT(sdfData.Cascades) / 1024 / 1024);
+
+ // Rasterize world geometry into Global SDF
+ renderContext.View.Pass = DrawPass::GlobalSDF;
+ uint32 viewMask = renderContext.View.RenderLayersMask;
+ const bool useCache = !updated && !renderContext.Task->IsCameraCut;
+ 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;
+ chunks.EnsureCapacity(rasterizeChunks * rasterizeChunks, false);
+ bool anyDraw = false;
+ const uint64 cascadeFrequencies[] = { 2, 3, 5, 11 };
+ //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 };
+ for (int32 cascade = 0; cascade < 4; cascade++)
+ {
+ // Reduce frequency of the updates
+ if (useCache && (Engine::FrameCount % cascadeFrequencies[cascade]) != 0)
+ continue;
+ const float distance = cascadesDistances[cascade];
+ const float maxDistance = distance * 2;
+ const float voxelSize = maxDistance / resolution;
+ const float snapping = voxelSize * mipFactor;
+ const Vector3 center = Vector3::Floor(renderContext.View.Position / snapping) * snapping;
+ // TODO: cascade scrolling on movement to reduce dirty chunks?
+ //const Vector3 center = Vector3::Zero;
+ sdfData.Positions[cascade] = center;
+ BoundingBox cascadeBounds(center - distance, center + distance);
+ // TODO: add scene detail scale factor to PostFx settings (eg. to increase or decrease scene details and quality)
+ const float minObjectRadius = Math::Max(20.0f, voxelSize * 0.5f); // Skip too small objects for this cascade
+ const float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
+ GPUTextureView* cascadeView = sdfData.Cascades[cascade]->ViewVolume();
+ GPUTextureView* cascadeMipView = sdfData.CascadeMips[cascade]->ViewVolume();
+
+ // Clear cascade before rasterization
+ {
+ PROFILE_CPU_NAMED("Clear");
+ chunks.Clear();
+ _modelsBuffer->Clear();
+ _modelsTextures.Clear();
+ }
+
+ // Draw all objects from all scenes into the cascade
+ _modelsBufferCount = 0;
+ _voxelSize = voxelSize;
+ _cascadeBounds = cascadeBounds;
+ for (auto* scene : renderContext.List->Scenes)
+ {
+ // TODO: optimize for moving camera (copy sdf)
+ // TODO: if chunk is made of static objects only then mark it as static and skip from rendering during the next frame (will need to track objects dirty state in the SceneRendering)
+ for (auto& e : scene->Actors)
{
- cascadeMip = RenderTargetPool::Get(desc);
- if (!cascadeMip)
- return true;
- updated = true;
+ if (viewMask & e.LayerMask && e.Bounds.Radius >= minObjectRadius && CollisionsHelper::BoxIntersectsSphere(cascadeBounds, e.Bounds))
+ {
+ e.Actor->Draw(renderContext);
+ }
}
}
- GPUTexture* tmpMip = nullptr;
- if (updated)
- LOG(Info, "Global SDF memory usage: {0} MB", (sdfData.Cascades[0]->GetMemoryUsage() + sdfData.CascadeMips[0]->GetMemoryUsage()) * ARRAY_COUNT(sdfData.Cascades) / 1024 / 1024);
- // Rasterize world geometry into Global SDF
- renderContext.View.Pass = DrawPass::GlobalSDF;
- uint32 viewMask = renderContext.View.RenderLayersMask;
- const bool useCache = !updated && !renderContext.Task->IsCameraCut;
- 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;
- chunks.EnsureCapacity(rasterizeChunks * rasterizeChunks, false);
- bool anyDraw = false;
- const uint64 cascadeFrequencies[] = { 2, 3, 5, 11 };
- //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 };
- for (int32 cascade = 0; cascade < 4; cascade++)
+ // Send models data to the GPU
{
- // Reduce frequency of the updates
- if (useCache && (Engine::FrameCount % cascadeFrequencies[cascade]) != 0)
- continue;
- const float distance = cascadesDistances[cascade];
- const float maxDistance = distance * 2;
- const float voxelSize = maxDistance / resolution;
- const float snapping = voxelSize * mipFactor;
- const Vector3 center = Vector3::Floor(renderContext.View.Position / snapping) * snapping;
- // TODO: cascade scrolling on movement to reduce dirty chunks?
- //const Vector3 center = Vector3::Zero;
- sdfData.Positions[cascade] = center;
- BoundingBox cascadeBounds(center - distance, center + distance);
- // TODO: add scene detail scale factor to PostFx settings (eg. to increase or decrease scene details and quality)
- const float minObjectRadius = Math::Max(20.0f, voxelSize * 0.5f); // Skip too small objects for this cascade
- const float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
- GPUTextureView* cascadeView = sdfData.Cascades[cascade]->ViewVolume();
- GPUTextureView* cascadeMipView = sdfData.CascadeMips[cascade]->ViewVolume();
+ PROFILE_GPU_CPU("Update Models");
+ _modelsBuffer->Flush(context);
+ }
- // Clear cascade before rasterization
+ // Perform batched chunks rasterization
+ if (!anyDraw)
+ {
+ anyDraw = true;
+ context->ResetSR();
+ tmpMip = RenderTargetPool::Get(desc);
+ if (!tmpMip)
+ return true;
+ }
+ ModelsRasterizeData data;
+ data.CascadeCoordToPosMul = cascadeBounds.GetSize() / resolution;
+ data.CascadeCoordToPosAdd = cascadeBounds.Minimum + voxelSize * 0.5f;
+ data.MaxDistance = maxDistance;
+ data.CascadeResolution = resolution;
+ data.CascadeMipResolution = resolutionMip;
+ data.CascadeMipFactor = mipFactor;
+ context->BindUA(0, cascadeView);
+ context->BindSR(0, _modelsBuffer->GetBuffer() ? _modelsBuffer->GetBuffer()->View() : nullptr);
+ if (_cb1)
+ context->BindCB(1, _cb1);
+ const int32 chunkDispatchGroups = GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / GLOBAL_SDF_RASTERIZE_GROUP_SIZE;
+ auto& nonEmptyChunks = sdfData.NonEmptyChunks[cascade];
+ {
+ PROFILE_GPU_CPU("Clear Chunks");
+ for (auto it = nonEmptyChunks.Begin(); it.IsNotEnd(); ++it)
{
- PROFILE_CPU_NAMED("Clear");
- chunks.Clear();
- _modelsBuffer->Clear();
- _modelsTextures.Clear();
- }
+ auto& key = it->Item;
+ if (chunks.ContainsKey(key))
+ continue;
- // Draw all objects from all scenes into the cascade
- _modelsBufferCount = 0;
- _voxelSize = voxelSize;
- _cascadeBounds = cascadeBounds;
- for (auto* scene : renderContext.List->Scenes)
- {
- // TODO: optimize for moving camera (copy sdf)
- // TODO: if chunk is made of static objects only then mark it as static and skip from rendering during the next frame (will need to track objects dirty state in the SceneRendering)
- for (auto& e : scene->Actors)
- {
- if (viewMask & e.LayerMask && e.Bounds.Radius >= minObjectRadius && CollisionsHelper::BoxIntersectsSphere(cascadeBounds, e.Bounds))
- {
- e.Actor->Draw(renderContext);
- }
- }
- }
-
- // Send models data to the GPU
- {
- PROFILE_GPU_CPU("Update Models");
- _modelsBuffer->Flush(context);
- }
-
- // Perform batched chunks rasterization
- if (!anyDraw)
- {
- anyDraw = true;
- context->ResetSR();
- tmpMip = RenderTargetPool::Get(desc);
- if (!tmpMip)
- return true;
- }
- ModelsRasterizeData data;
- data.CascadeCoordToPosMul = cascadeBounds.GetSize() / resolution;
- data.CascadeCoordToPosAdd = cascadeBounds.Minimum + voxelSize * 0.5f;
- data.MaxDistance = maxDistance;
- data.CascadeResolution = resolution;
- data.CascadeMipResolution = resolutionMip;
- data.CascadeMipFactor = mipFactor;
- context->BindUA(0, cascadeView);
- context->BindSR(0, _modelsBuffer->GetBuffer() ? _modelsBuffer->GetBuffer()->View() : nullptr);
- if (_cb1)
- context->BindCB(1, _cb1);
- const int32 chunkDispatchGroups = GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / GLOBAL_SDF_RASTERIZE_GROUP_SIZE;
- auto& nonEmptyChunks = sdfData.NonEmptyChunks[cascade];
- {
- PROFILE_GPU_CPU("Clear Chunks");
- for (auto it = nonEmptyChunks.Begin(); it.IsNotEnd(); ++it)
- {
- auto& key = it->Item;
- if (chunks.ContainsKey(key))
- continue;
-
- // Clear empty chunk
- nonEmptyChunks.Remove(it);
- data.ChunkCoord = key.Coord * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
- if (_cb1)
- context->UpdateCB(_cb1, &data);
- context->Dispatch(_csClearChunk, chunkDispatchGroups, chunkDispatchGroups, chunkDispatchGroups);
- // TODO: don't stall with UAV barrier on D3D12/Vulkan if UAVs don't change between dispatches
- }
- }
- // TODO: rasterize models into global sdf relative to the cascade origin to prevent fp issues on large worlds
- {
- PROFILE_GPU_CPU("Rasterize Chunks");
- for (auto& e : chunks)
- {
- // Rasterize non-empty chunk
- auto& key = e.Key;
- auto& chunk = e.Value;
- for (int32 i = 0; i < chunk.ModelsCount; i++)
- {
- int32 model = chunk.Models[i];
- data.Models[i] = model;
- context->BindSR(i + 1, _modelsTextures[model]);
- }
- ASSERT_LOW_LAYER(chunk.ModelsCount != 0);
- data.ChunkCoord = key.Coord * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
- data.ModelsCount = chunk.ModelsCount;
- if (_cb1)
- context->UpdateCB(_cb1, &data);
- GPUShaderProgramCS* cs;
- if (key.Layer == 0)
- {
- // First layer so can override existing chunk data
- cs = _csRasterizeModel0;
- nonEmptyChunks.Add(key);
- }
- else
- {
- // Another layer so need combine with existing chunk data
- cs = _csRasterizeModel1;
- }
- context->Dispatch(cs, chunkDispatchGroups, chunkDispatchGroups, chunkDispatchGroups);
- // TODO: don't stall with UAV barrier on D3D12/Vulkan if UAVs don't change between dispatches - only for a sequence of _csRasterizeModel0 dispatches (maybe cache per-shader write/read flags for all UAVs?)
-
-#if GLOBAL_SDF_DEBUG_CHUNKS
- // Debug draw chunk bounds in world space with number of models in it
- if (cascade + 1 == GLOBAL_SDF_DEBUG_CHUNKS)
- {
- Vector3 chunkMin = cascadeBounds.Minimum + Vector3(key.Coord) * chunkSize;
- BoundingBox chunkBounds(chunkMin, chunkMin + chunkSize);
- DebugDraw::DrawWireBox(chunkBounds, Color::Red, 0, false);
- DebugDraw::DrawText(StringUtils::ToString(chunk.ModelsCount), chunkBounds.GetCenter() + Vector3(0, 50.0f * key.Layer, 0), Color::Red);
- }
-#endif
- }
- }
-
- // Generate mip out of cascade (empty chunks have distance value 1 which is incorrect so mip will be used as a fallback - lower res)
- {
- PROFILE_GPU_CPU("Generate Mip");
+ // Clear empty chunk
+ nonEmptyChunks.Remove(it);
+ data.ChunkCoord = key.Coord * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
if (_cb1)
context->UpdateCB(_cb1, &data);
- context->ResetUA();
- context->BindSR(0, cascadeView);
- context->BindUA(0, cascadeMipView);
- const int32 mipDispatchGroups = Math::DivideAndRoundUp(resolutionMip, GLOBAL_SDF_MIP_GROUP_SIZE);
- int32 floodFillIterations = chunks.Count() == 0 ? 1 : GLOBAL_SDF_MIP_FLOODS;
- context->Dispatch(_csGenerateMip0, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups);
- context->UnBindSR(0);
- GPUTextureView* tmpMipView = tmpMip->ViewVolume();
- for (int32 i = 1; i < floodFillIterations; i++)
+ context->Dispatch(_csClearChunk, chunkDispatchGroups, chunkDispatchGroups, chunkDispatchGroups);
+ // TODO: don't stall with UAV barrier on D3D12/Vulkan if UAVs don't change between dispatches
+ }
+ }
+ // TODO: rasterize models into global sdf relative to the cascade origin to prevent fp issues on large worlds
+ {
+ PROFILE_GPU_CPU("Rasterize Chunks");
+ for (auto& e : chunks)
+ {
+ // Rasterize non-empty chunk
+ auto& key = e.Key;
+ auto& chunk = e.Value;
+ for (int32 i = 0; i < chunk.ModelsCount; i++)
{
- context->ResetUA();
- context->BindSR(0, cascadeMipView);
- context->BindUA(0, tmpMipView);
- context->Dispatch(_csGenerateMip1, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups);
- Swap(tmpMipView, cascadeMipView);
+ int32 model = chunk.Models[i];
+ data.Models[i] = model;
+ context->BindSR(i + 1, _modelsTextures[model]);
}
- if (floodFillIterations % 2 == 0)
- Swap(tmpMipView, cascadeMipView);
+ ASSERT_LOW_LAYER(chunk.ModelsCount != 0);
+ data.ChunkCoord = key.Coord * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
+ data.ModelsCount = chunk.ModelsCount;
+ if (_cb1)
+ context->UpdateCB(_cb1, &data);
+ GPUShaderProgramCS* cs;
+ if (key.Layer == 0)
+ {
+ // First layer so can override existing chunk data
+ cs = _csRasterizeModel0;
+ nonEmptyChunks.Add(key);
+ }
+ else
+ {
+ // Another layer so need combine with existing chunk data
+ cs = _csRasterizeModel1;
+ }
+ context->Dispatch(cs, chunkDispatchGroups, chunkDispatchGroups, chunkDispatchGroups);
+ // TODO: don't stall with UAV barrier on D3D12/Vulkan if UAVs don't change between dispatches - only for a sequence of _csRasterizeModel0 dispatches (maybe cache per-shader write/read flags for all UAVs?)
+
+#if GLOBAL_SDF_DEBUG_CHUNKS
+ // Debug draw chunk bounds in world space with number of models in it
+ if (cascade + 1 == GLOBAL_SDF_DEBUG_CHUNKS)
+ {
+ Vector3 chunkMin = cascadeBounds.Minimum + Vector3(key.Coord) * chunkSize;
+ BoundingBox chunkBounds(chunkMin, chunkMin + chunkSize);
+ DebugDraw::DrawWireBox(chunkBounds, Color::Red, 0, false);
+ DebugDraw::DrawText(StringUtils::ToString(chunk.ModelsCount), chunkBounds.GetCenter() + Vector3(0, 50.0f * key.Layer, 0), Color::Red);
+ }
+#endif
}
}
- RenderTargetPool::Release(tmpMip);
- if (anyDraw)
+ // Generate mip out of cascade (empty chunks have distance value 1 which is incorrect so mip will be used as a fallback - lower res)
{
- context->UnBindCB(1);
+ PROFILE_GPU_CPU("Generate Mip");
+ if (_cb1)
+ context->UpdateCB(_cb1, &data);
context->ResetUA();
- context->FlushState();
- context->ResetSR();
- context->FlushState();
+ context->BindSR(0, cascadeView);
+ context->BindUA(0, cascadeMipView);
+ const int32 mipDispatchGroups = Math::DivideAndRoundUp(resolutionMip, GLOBAL_SDF_MIP_GROUP_SIZE);
+ int32 floodFillIterations = chunks.Count() == 0 ? 1 : GLOBAL_SDF_MIP_FLOODS;
+ context->Dispatch(_csGenerateMip0, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups);
+ context->UnBindSR(0);
+ GPUTextureView* tmpMipView = tmpMip->ViewVolume();
+ for (int32 i = 1; i < floodFillIterations; i++)
+ {
+ context->ResetUA();
+ context->BindSR(0, cascadeMipView);
+ context->BindUA(0, tmpMipView);
+ context->Dispatch(_csGenerateMip1, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups);
+ Swap(tmpMipView, cascadeMipView);
+ }
+ if (floodFillIterations % 2 == 0)
+ Swap(tmpMipView, cascadeMipView);
}
}
+ RenderTargetPool::Release(tmpMip);
+ if (anyDraw)
+ {
+ context->UnBindCB(1);
+ context->ResetUA();
+ context->FlushState();
+ context->ResetSR();
+ context->FlushState();
+ }
+
// Copy results
static_assert(ARRAY_COUNT(result.Cascades) == ARRAY_COUNT(sdfData.Cascades), "Invalid cascades count.");
static_assert(ARRAY_COUNT(result.CascadeMips) == ARRAY_COUNT(sdfData.CascadeMips), "Invalid cascades count.");
@@ -476,6 +491,7 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
result.GlobalSDF.CascadeVoxelSize.Raw[cascade] = voxelSize;
}
result.GlobalSDF.Resolution = (float)resolution;
+ sdfData.Result = result;
return false;
}
diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h
index b4a9cf984..f4c503222 100644
--- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h
+++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h
@@ -46,6 +46,14 @@ private:
BoundingBox _cascadeBounds;
public:
+ ///
+ /// Gets the Global SDF (only if enabled in Graphics Settings).
+ ///
+ /// The rendering context buffers.
+ /// The result Global SDF data for binding to the shaders.
+ /// True if there is no valid Global SDF rendered during this frame, otherwise false.
+ bool Get(const RenderBuffers* buffers, BindingData& result);
+
///
/// Renders the Global SDF.
///
diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp
index 8174ebeb5..d6d21c1be 100644
--- a/Source/Engine/Renderer/Renderer.cpp
+++ b/Source/Engine/Renderer/Renderer.cpp
@@ -29,6 +29,7 @@
#include "AntiAliasing/SMAA.h"
#include "Engine/Level/Actor.h"
#include "Engine/Level/Level.h"
+#include "Engine/Core/Config/GraphicsSettings.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Editor/QuadOverdrawPass.h"
@@ -289,6 +290,7 @@ void Renderer::DrawPostFxMaterial(GPUContext* context, const RenderContext& rend
void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
{
auto context = GPUDevice::Instance->GetMainContext();
+ auto* graphicsSettings = GraphicsSettings::Get();
auto& view = renderContext.View;
ASSERT(renderContext.Buffers && renderContext.Buffers->GetWidth() > 0);
@@ -338,6 +340,13 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
}
#endif
+ // Global SDF rendering (can be used by materials later on)
+ if (graphicsSettings->EnableGlobalSDF)
+ {
+ GlobalSignDistanceFieldPass::BindingData bindingData;
+ GlobalSignDistanceFieldPass::Instance()->Render(renderContext, context, bindingData);
+ }
+
// Fill GBuffer
GBufferPass::Instance()->Fill(renderContext, lightBuffer->View());