Improve env probes rendering if DDGI is enabled

This commit is contained in:
Wojciech Figat
2022-07-15 16:11:00 +02:00
parent a8579cadcc
commit a2677a25a9
11 changed files with 97 additions and 103 deletions

BIN
Content/Shaders/GI/DDGI.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -403,7 +403,7 @@ void SceneRenderTask::OnBegin(GPUContext* context)
void SceneRenderTask::OnRender(GPUContext* context)
{
if (Buffers && Buffers->GetWidth() > 0)
if (!IsCustomRendering && Buffers && Buffers->GetWidth() > 0)
Renderer::Render(this);
RenderTask::OnRender(context);

View File

@@ -228,6 +228,11 @@ public:
/// </summary>
bool IsCameraCut = true;
/// <summary>
/// True if the task is used for custom scene rendering and default scene drawing into output should be skipped. Enable it if you use Render event and draw scene manually.
/// </summary>
API_FIELD() bool IsCustomRendering = false;
/// <summary>
/// Marks the next rendered frame as camera cut. Used to clear the temporal effects history and prevent visual artifacts blended from the previous frames.
/// </summary>

View File

@@ -88,7 +88,7 @@ namespace AtmospherePreComputeImpl
GPUPipelineState* _psCopyInscatterNAdd = nullptr;
GPUPipelineState* _psInscatterS = nullptr;
GPUPipelineState* _psInscatterN = nullptr;
RenderTask* _task = nullptr;
SceneRenderTask* _task = nullptr;
//
GPUTexture* AtmosphereTransmittance = nullptr;
@@ -166,12 +166,10 @@ bool init()
return true;
}
auto shader = _shader->GetShader();
// Validate shader constant buffers sizes
ASSERT(shader->GetCB(0) != nullptr);
if (shader->GetCB(0)->GetSize() != sizeof(Data))
{
LOG(Fatal, "Shader {0} has incorrect constant buffer {1} size: {2} bytes. Expected: {3} bytes", _shader->ToString(), 0, shader->GetCB(0)->GetSize(), sizeof(Data));
REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
return true;
}
@@ -252,6 +250,7 @@ bool init()
// Init rendering pipeline
_task = New<SceneRenderTask>();
_task->Enabled = false;
_task->IsCustomRendering = true;
_task->Render.Bind(onRender);
// Init render targets

View File

@@ -82,6 +82,7 @@ public:
int32 CascadesCount = 0;
int32 ProbeRaysCount = 0;
int32 ProbesCountTotal = 0;
Int3 ProbeCounts = Int3::Zero;
GPUTexture* ProbesTrace = nullptr; // Probes ray tracing: (RGB: hit radiance, A: hit distance)
GPUTexture* ProbesState = nullptr; // Probes state: (RGB: world-space offset, A: state)
@@ -194,7 +195,7 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources()
{
_psIndirectLighting = device->CreatePipelineState();
psDesc.PS = shader->GetPS("PS_IndirectLighting");
psDesc.BlendMode = BlendingMode::Additive;
psDesc.BlendMode = BlendingMode::Add;
if (_psIndirectLighting->Init(psDesc))
return true;
}
@@ -251,15 +252,8 @@ bool DynamicDiffuseGlobalIlluminationPass::Get(const RenderBuffers* buffers, Bin
return true;
}
bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer)
bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderContext, GPUContext* context, DDGICustomBuffer& ddgiData)
{
// Skip if not supported
if (checkIfSkipPass())
return true;
if (renderContext.List->Scenes.Count() == 0)
return true;
auto& ddgiData = *renderContext.Buffers->GetCustomBuffer<DDGICustomBuffer>(TEXT("DDGI"));
// Render Global SDF and Global Surface Atlas for software raytracing
GlobalSignDistanceFieldPass::BindingData bindingDataSDF;
if (GlobalSignDistanceFieldPass::Instance()->Render(renderContext, context, bindingDataSDF))
@@ -269,13 +263,6 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
return true;
GPUTextureView* skybox = GBufferPass::Instance()->RenderSkybox(renderContext, context);
// Skip if already done in the current frame
const auto currentFrame = Engine::FrameCount;
if (ddgiData.LastFrameUsed == currentFrame)
return false;
ddgiData.LastFrameUsed = currentFrame;
PROFILE_GPU_CPU("Dynamic Diffuse Global Illumination");
// Setup options
auto& settings = renderContext.List->Settings.GlobalIllumination;
auto* graphicsSettings = GraphicsSettings::Get();
@@ -299,7 +286,6 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
return true;
}
ASSERT_LOW_LAYER(probeRaysCount <= DDGI_TRACE_RAYS_LIMIT);
bool debugProbes = renderContext.View.Mode == ViewMode::GlobalIllumination;
const float indirectLightingIntensity = settings.Intensity;
const float probeHistoryWeight = Math::Clamp(settings.TemporalResponse, 0.0f, 0.98f);
const float distance = settings.Distance;
@@ -314,7 +300,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
idealDistance *= 2;
cascadesCount++;
}
// Calculate the probes count based on the amount of cascades and the distance to cover
const float cascadesDistanceScales[] = { 1.0f, 3.0f, 6.0f, 10.0f }; // Scales each cascade further away from the camera origin
const float distanceExtent = distance / cascadesDistanceScales[cascadesCount - 1];
@@ -369,6 +355,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
ddgiData.Release();
ddgiData.CascadesCount = cascadesCount;
ddgiData.ProbeRaysCount = probeRaysCount;
ddgiData.ProbesCountTotal = probesCountTotal;
ddgiData.ProbeCounts = probesCounts;
for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
{
@@ -408,6 +395,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
context->ClearUA(ddgiData.ProbesIrradiance, Float4::Zero);
context->ClearUA(ddgiData.ProbesDistance, Float4::Zero);
}
ddgiData.LastFrameUsed = Engine::FrameCount;
// Calculate which cascades should be updated this frame
const uint64 cascadeFrequencies[] = { 2, 3, 5, 7 };
@@ -416,7 +404,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
bool cascadeSkipUpdate[4];
for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
{
cascadeSkipUpdate[cascadeIndex] = !clear && (currentFrame % cascadeFrequencies[cascadeIndex]) != 0;
cascadeSkipUpdate[cascadeIndex] = !clear && (ddgiData.LastFrameUsed % cascadeFrequencies[cascadeIndex]) != 0;
}
// Compute scrolling (probes are placed around camera but are scrolling to increase stability during movement)
@@ -622,6 +610,54 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
}
}
return false;
}
bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer)
{
if (checkIfSkipPass())
return true;
if (renderContext.List->Scenes.Count() == 0)
return true;
RenderBuffers* renderBuffers = renderContext.Buffers;
bool render = true;
if (renderContext.View.IsOfflinePass)
{
// During offline pass (eg. probes rendering) we can try reuse main game viewport or editor viewport DDGI probes
// TODO: apply it for transparency too (in DynamicDiffuseGlobalIlluminationPass::Get)
for (auto* task : RenderTask::Tasks)
{
if (auto* sceneTask = ScriptingObject::Cast<SceneRenderTask>(task))
{
auto* sceneTaskDDGI = sceneTask->Buffers ? sceneTask->Buffers->FindCustomBuffer<DDGICustomBuffer>(TEXT("DDGI")) : nullptr;
if (sceneTaskDDGI && sceneTaskDDGI->LastFrameUsed + 1 >= Engine::FrameCount)
{
// Reuse DDGI from this task
renderBuffers = sceneTask->Buffers;
render = false;
break;
}
}
}
}
auto& ddgiData = *renderBuffers->GetCustomBuffer<DDGICustomBuffer>(TEXT("DDGI"));
if (render && ddgiData.LastFrameUsed == Engine::FrameCount)
render = false;
PROFILE_GPU_CPU("Dynamic Diffuse Global Illumination");
if (render)
{
if (RenderInner(renderContext, context, ddgiData))
{
context->ResetRenderTarget();
context->ResetSR();
context->ResetUA();
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
return true;
}
}
// Render indirect lighting
if (lightBuffer)
{
@@ -630,6 +666,15 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
// DDGI indirect lighting debug preview
context->Clear(lightBuffer, Color::Transparent);
#endif
if (!render)
{
Data0 data;
data.DDGI = ddgiData.Result.Constants;
data.TemporalTime = 0.0f;
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
context->UpdateCB(_cb0, &data);
context->BindCB(0, _cb0);
}
context->BindSR(0, renderContext.Buffers->GBuffer0->View());
context->BindSR(1, renderContext.Buffers->GBuffer1->View());
context->BindSR(2, renderContext.Buffers->GBuffer2->View());
@@ -645,7 +690,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
#if USE_EDITOR
// Probes debug drawing
if (debugProbes && lightBuffer)
if (renderContext.View.Mode == ViewMode::GlobalIllumination && lightBuffer)
{
PROFILE_GPU_CPU("Debug Probes");
if (!_debugModel)
@@ -661,7 +706,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
Matrix world;
Matrix::Scaling(Float3(0.2f), world);
const Mesh& debugMesh = _debugModel->LODs[0].Meshes[0];
for (int32 probeIndex = 0; probeIndex < probesCountTotal; probeIndex++)
for (int32 probeIndex = 0; probeIndex < ddgiData.ProbesCountTotal; probeIndex++)
debugMesh.Draw(debugRenderContext, _debugMaterial, world, StaticFlags::None, true, DrawPass::GBuffer, (float)probeIndex);
debugRenderContext.List->SortDrawCalls(debugRenderContext, false, DrawCallsListType::GBuffer);
context->SetViewportAndScissors(debugRenderContext.View.ScreenSize.X, debugRenderContext.View.ScreenSize.Y);
@@ -688,14 +733,13 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
}
debugRenderContext.List->ExecuteDrawCalls(debugRenderContext, DrawCallsListType::GBuffer);
RenderList::ReturnToPool(debugRenderContext.List);
context->UnBindCB(3);
context->ResetRenderTarget();
}
}
#endif
context->ResetRenderTarget();
context->ResetSR();
context->ResetUA();
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
return false;
}

View File

@@ -82,6 +82,7 @@ private:
uint64 LastFrameShaderReload = 0;
void OnShaderReloading(Asset* obj);
#endif
bool RenderInner(RenderContext& renderContext, GPUContext* context, class DDGICustomBuffer& ddgiData);
public:
// [RendererPass]

View File

@@ -1043,14 +1043,14 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
}
}
}
context->ResetSR();
context->ResetRenderTarget();
}
// TODO: explore atlas tiles optimization with feedback from renderer (eg. when tile is sampled by GI/Reflections mark it as used, then sort tiles by importance and prioritize updates for ones frequently used)
#undef WRITE_TILE
context->ResetSR();
context->ResetRenderTarget();
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
return notReady;
}

View File

@@ -29,7 +29,6 @@
/// <summary>
/// Custom task called after downloading probe texture data to save it.
/// </summary>
/// <seealso cref="ThreadPoolTask" />
class DownloadProbeTask : public ThreadPoolTask
{
private:
@@ -38,31 +37,19 @@ private:
ProbesRenderer::Entry _entry;
public:
/// <summary>
/// Initializes a new instance of the <see cref="DownloadProbeTask"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="entry">The entry.</param>
DownloadProbeTask(GPUTexture* target, const ProbesRenderer::Entry& entry)
: _texture(target)
, _entry(entry)
{
}
public:
/// <summary>
/// Gets the texture data container.
/// </summary>
FORCE_INLINE TextureData& GetData()
{
return _data;
}
protected:
// [ThreadPoolTask]
bool Run() override
{
// Switch type
if (_entry.Type == ProbesRenderer::EntryType::EnvProbe)
{
if (_entry.Actor)
@@ -78,7 +65,6 @@ protected:
return true;
}
// Fire event
ProbesRenderer::OnFinishBake(_entry);
return false;
@@ -239,13 +225,9 @@ bool ProbesRenderer::Init()
if (!_shader->IsLoaded())
return false;
const auto shader = _shader->GetShader();
LOG(Info, "Starting Probes Renderer service");
// Validate shader constant buffers sizes
if (shader->GetCB(0)->GetSize() != sizeof(Data))
{
LOG(Fatal, "Shader {0} has incorrect constant buffer {1} size: {2} bytes. Expected: {3} bytes", _shader.ToString(), 0, shader->GetCB(0)->GetSize(), sizeof(Data));
REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
return true;
}
@@ -296,6 +278,7 @@ bool ProbesRenderer::Init()
_task = New<SceneRenderTask>();
auto task = _task;
task->Enabled = false;
task->IsCustomRendering = true;
task->Output = _output;
auto& view = task->View;
view.Flags =
@@ -310,6 +293,7 @@ bool ProbesRenderer::Init()
ViewFlags::Fog;
view.Mode = ViewMode::NoPostFx;
view.IsOfflinePass = true;
view.IsSingleFrame = true;
view.StaticFlagsMask = StaticFlags::ReflectionProbe;
view.MaxShadowsQuality = Quality::Low;
task->IsCameraCut = true;
@@ -320,7 +304,7 @@ bool ProbesRenderer::Init()
_probe = GPUDevice::Instance->CreateTexture(TEXT("ProbesUpdate.Probe"));
if (_probe->Init(GPUTextureDescription::NewCube(probeResolution, _output->Format(), GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews, 0)))
return true;
_tmpFace = GPUDevice::Instance->CreateTexture(TEXT("ProbesUpdate.TmpFae"));
_tmpFace = GPUDevice::Instance->CreateTexture(TEXT("ProbesUpdate.TmpFace"));
if (_tmpFace->Init(GPUTextureDescription::New2D(probeResolution, probeResolution, 0, _output->Format(), GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews)))
return true;
@@ -335,8 +319,6 @@ void ProbesRenderer::Release()
return;
ASSERT(_updateFrameNumber == 0);
LOG(Info, "Disposing Probes Renderer service");
// Release GPU data
if (_output)
_output->ReleaseGPU();
@@ -471,7 +453,6 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context)
return;
}
bool setLowerHemisphereToBlack = false;
auto shader = _shader->GetShader();
PROFILE_GPU("Render Probe");
@@ -487,7 +468,6 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context)
float nearPlane = Math::Max(0.1f, envProbe->CaptureNearPlane);
// Fix far plane distance
// TODO: investigate performance of this action, maybe we could skip it?
float farPlane = Math::Max(radius, nearPlane + 100.0f);
Function<bool(Actor*, const Vector3&, float&)> f(&fixFarPlaneTreeExecute);
SceneQuery::TreeExecute<const Vector3&, float&>(f, position, farPlane);
@@ -505,12 +485,11 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context)
float farPlane = Math::Max(nearPlane + 1000.0f, skyLight->SkyDistanceThreshold * 2.0f);
customCullingNear = skyLight->SkyDistanceThreshold;
// TODO: use setLowerHemisphereToBlack feature for SkyLight?
// Setup view
LargeWorlds::UpdateOrigin(_task->View.Origin, position);
_task->View.SetUpCube(nearPlane, farPlane, position - _task->View.Origin);
}
_task->CameraCut();
// Resize buffers
bool resizeFailed = _output->Resize(probeResolution, probeResolution);
@@ -527,7 +506,6 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context)
// Render scene for all faces
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
// Set view
_task->View.SetFace(faceIndex);
// Handle custom frustum for the culling (used to skip objects near the camera)
@@ -547,21 +525,7 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context)
PROFILE_GPU("Copy Face");
context->SetRenderTarget(_probe->View(faceIndex));
context->SetViewportAndScissors((float)probeResolution, (float)probeResolution);
auto probeFrame = _output->View();
if (setLowerHemisphereToBlack && faceIndex != 2)
{
MISSING_CODE("set lower hemisphere of the probe to black");
/*
ProbesFilter_Tmp.Set(_core.Rendering.Pool.RT2_FloatRGB.Handle);
ProbesFilter_CoefficientMask2.Set(f != 3 ? 1.0f : 0.0f);
ProbesFilter_Shader.Apply(5);
_device.DrawFullscreenTriangle();
*/
}
else
{
context->Draw(probeFrame);
}
context->Draw(_output->View());
context->ResetRenderTarget();
}
}
@@ -577,16 +541,9 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context)
auto cb = shader->GetCB(0);
for (int32 mipIndex = 1; mipIndex < mipLevels; mipIndex++)
{
// Cache data
int32 srcMipIndex = mipIndex - 1;
int32 mipSize = 1 << (mipLevels - mipIndex - 1);
data.SourceMipIndex = srcMipIndex;
// Set viewport
float mipSizeFloat = (float)mipSize;
context->SetViewportAndScissors(mipSizeFloat, mipSizeFloat);
// Filter all faces
data.SourceMipIndex = mipIndex - 1;
context->SetViewportAndScissors((float)mipSize, (float)mipSize);
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
context->ResetSR();
@@ -600,12 +557,14 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context)
context->SetRenderTarget(_tmpFace->View(0, mipIndex));
context->SetState(_psFilterFace);
context->DrawFullscreenTriangle();
context->ResetSR();
context->ResetRenderTarget();
// Copy face back to the cubemap
copyTmpToFace(context, mipIndex, faceIndex);
context->SetRenderTarget(_probe->View(faceIndex, mipIndex));
context->BindSR(1, _tmpFace->View(0, mipIndex));
context->SetState(_psCopyFace);
context->DrawFullscreenTriangle();
}
}
}
@@ -618,17 +577,4 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context)
_task->Enabled = false;
}
void ProbesRenderer::copyTmpToFace(GPUContext* context, int32 mipIndex, int32 faceIndex)
{
// Set destination render target
context->SetRenderTarget(_probe->View(faceIndex, mipIndex));
// Setup shader constants
context->BindSR(1, _tmpFace->View(0, mipIndex));
// Copy pixels
context->SetState(_psCopyFace);
context->DrawFullscreenTriangle();
}
#endif

View File

@@ -105,7 +105,6 @@ public:
private:
static void onRender(RenderTask* task, GPUContext* context);
static void copyTmpToFace(GPUContext* context, int32 mipIndex, int32 faceIndex);
};
#endif

View File

@@ -112,4 +112,4 @@ class RendererPass : public Singleton<T>, public RendererPassBase
{
};
#define REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, index, dataType) LOG(Fatal, "Shader {0} has incorrect constant buffer {1} size: {2} bytes. Expected: {3} bytes", ToString(), index, shader->GetCB(index)->GetSize(), sizeof(dataType));
#define REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, index, dataType) LOG(Fatal, "Shader {0} has incorrect constant buffer {1} size: {2} bytes. Expected: {3} bytes", shader->ToString(), index, shader->GetCB(index)->GetSize(), sizeof(dataType));

View File

@@ -569,7 +569,7 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0)
// Calculate lighting
float3 diffuseColor = GetDiffuseColor(gBuffer);
float3 diffuse = Diffuse_Lambert(diffuseColor);
output = float4(diffuse * irradiance * gBuffer.AO, 1);
output = float4(diffuse * irradiance * gBuffer.AO, saturate(length(irradiance)));
}
#endif