Add option to always render Global SDF

This commit is contained in:
Wojciech Figat
2022-03-24 14:59:14 +01:00
parent 92ab3d005e
commit d5297f9047
5 changed files with 257 additions and 207 deletions

View File

@@ -62,11 +62,17 @@ public:
API_FIELD(Attributes="EditorOrder(1320), DefaultValue(false), EditorDisplay(\"Quality\", \"Allow CSM Blending\")")
bool AllowCSMBlending = false;
/// <summary>
/// If checked, enables Global SDF rendering. This can be used in materials, shaders, and particles.
/// </summary>
API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Global SDF\")")
bool EnableGlobalSDF = false;
#if USE_EDITOR
/// <summary>
/// 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).
/// </summary>
API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Global SDF\")")
API_FIELD(Attributes="EditorOrder(2010), EditorDisplay(\"Global SDF\")")
bool GenerateSDFOnModelImport = false;
#endif

View File

@@ -150,6 +150,17 @@ public:
return _viewport;
}
template<class T>
const T* FindCustomBuffer(const StringView& name) const
{
for (CustomBuffer* e : CustomBuffers)
{
if (e->Name == name)
return (const T*)e;
}
return nullptr;
}
template<class T>
T* GetCustomBuffer(const StringView& name)
{

View File

@@ -110,6 +110,7 @@ public:
GPUTexture* CascadeMips[4] = {};
Vector3 Positions[4];
HashSet<RasterizeChunkKey> 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<GlobalSignDistanceFieldCustomBuffer>(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<GlobalSignDistanceFieldCustomBuffer>(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;
}

View File

@@ -46,6 +46,14 @@ private:
BoundingBox _cascadeBounds;
public:
/// <summary>
/// Gets the Global SDF (only if enabled in Graphics Settings).
/// </summary>
/// <param name="buffers">The rendering context buffers.</param>
/// <param name="result">The result Global SDF data for binding to the shaders.</param>
/// <returns>True if there is no valid Global SDF rendered during this frame, otherwise false.</returns>
bool Get(const RenderBuffers* buffers, BindingData& result);
/// <summary>
/// Renders the Global SDF.
/// </summary>

View File

@@ -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());