diff --git a/Source/Engine/Graphics/PostProcessSettings.cpp b/Source/Engine/Graphics/PostProcessSettings.cpp
index 029925840..c86a0f31a 100644
--- a/Source/Engine/Graphics/PostProcessSettings.cpp
+++ b/Source/Engine/Graphics/PostProcessSettings.cpp
@@ -22,6 +22,7 @@ void AmbientOcclusionSettings::BlendWith(AmbientOcclusionSettings& other, float
BLEND_FLOAT(Radius);
BLEND_FLOAT(FadeOutDistance);
BLEND_FLOAT(FadeDistance);
+ BLEND_ENUM(DepthResolution);
}
void GlobalIlluminationSettings::BlendWith(GlobalIlluminationSettings& other, float weight)
diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h
index d7b5a9cb4..e05eda2a2 100644
--- a/Source/Engine/Graphics/PostProcessSettings.h
+++ b/Source/Engine/Graphics/PostProcessSettings.h
@@ -217,10 +217,15 @@ API_ENUM(Attributes="Flags") enum class AmbientOcclusionSettingsOverride : int32
///
FadeDistance = 1 << 5,
+ ///
+ /// Overrides property.
+ ///
+ DepthResolution = 1 << 6,
+
///
/// All properties.
///
- All = Enabled | Intensity | Power | Radius | FadeOutDistance | FadeDistance,
+ All = Enabled | Intensity | Power | Radius | FadeOutDistance | FadeDistance | DepthResolution,
};
///
@@ -274,6 +279,12 @@ API_STRUCT() struct FLAXENGINE_API AmbientOcclusionSettings : ISerializable
API_FIELD(Attributes="Limit(0.0f), EditorOrder(5), PostProcessSetting((int)AmbientOcclusionSettingsOverride.FadeDistance)")
float FadeDistance = 500.0f;
+ ///
+ /// The depth buffer downscale option to optimize rendering performance. Full gives better quality, but half improves performance.
+ ///
+ API_FIELD(Attributes="EditorOrder(6), PostProcessSetting((int)AmbientOcclusionSettingsOverride.DepthResolution)")
+ ResolutionMode DepthResolution = ResolutionMode::Half;
+
public:
///
/// Blends the settings using given weight.
diff --git a/Source/Engine/Graphics/RenderBuffers.cpp b/Source/Engine/Graphics/RenderBuffers.cpp
index 22eace596..c5dc2ee31 100644
--- a/Source/Engine/Graphics/RenderBuffers.cpp
+++ b/Source/Engine/Graphics/RenderBuffers.cpp
@@ -83,16 +83,17 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
if (LastFrameHalfResDepth == currentFrame)
return HalfResDepth;
- const int32 halfDepthWidth = _width / 2;
- const int32 halfDepthHeight = _height / 2;
+ const int32 halfDepthWidth = (_width + 1) / 2;
+ const int32 halfDepthHeight = (_height + 1) / 2;
const PixelFormat halfDepthFormat = GPU_DEPTH_BUFFER_PIXEL_FORMAT;
+ auto tempDesc = GPUTextureDescription::New2D(halfDepthWidth, halfDepthHeight, halfDepthFormat);
+ if (EnumHasAnyFlags(DepthBuffer->Flags(), GPUTextureFlags::ReadOnlyDepthView))
+ tempDesc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::DepthStencil | GPUTextureFlags::ReadOnlyDepthView;
LastFrameHalfResDepth = currentFrame;
if (HalfResDepth == nullptr)
{
// Missing buffer
- auto tempDesc = GPUTextureDescription::New2D(halfDepthWidth, halfDepthHeight, halfDepthFormat);
- tempDesc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::DepthStencil;
HalfResDepth = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(HalfResDepth, "HalfResDepth");
}
@@ -100,8 +101,6 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
{
// Wrong size buffer
RenderTargetPool::Release(HalfResDepth);
- auto tempDesc = GPUTextureDescription::New2D(halfDepthWidth, halfDepthHeight, halfDepthFormat);
- tempDesc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::DepthStencil;
HalfResDepth = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(HalfResDepth, "HalfResDepth");
}
diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp
index 5c026ab4e..8d4a2fbe9 100644
--- a/Source/Engine/Graphics/RenderTools.cpp
+++ b/Source/Engine/Graphics/RenderTools.cpp
@@ -695,6 +695,15 @@ Float2 RenderTools::GetDepthBounds(const RenderView& view, const OrientedBoundin
return GetDepthBounds(view, Span(corners, 8));
}
+float RenderTools::GetDepthBounds(const RenderView& view, const Float3& point)
+{
+ const Float4 pointClip = Matrix::TransformPosition(view.ViewProjection(), Float4(point, 1.0));
+ float depth = pointClip.Z / pointClip.W;
+ if (depth >= 1.0f)
+ depth = 0.0f; // Point is behind the view
+ return Math::Clamp(depth, 0.0f, 1.0f);
+}
+
Float3 RenderTools::GetColorQuantizationError(PixelFormat format)
{
Float3 mantissaBits;
diff --git a/Source/Engine/Graphics/RenderTools.h b/Source/Engine/Graphics/RenderTools.h
index 2c9f65fed..967913a12 100644
--- a/Source/Engine/Graphics/RenderTools.h
+++ b/Source/Engine/Graphics/RenderTools.h
@@ -148,6 +148,7 @@ public:
static Float2 GetDepthBounds(const RenderView& view, const Span& points);
static Float2 GetDepthBounds(const RenderView& view, const BoundingBox& bounds);
static Float2 GetDepthBounds(const RenderView& view, const OrientedBoundingBox& bounds);
+ static float GetDepthBounds(const RenderView& view, const Float3& point);
static constexpr float DepthBoundMaxBackground = 1.0f - 0.0000001f; // Skip background/sky pixels from shading
// Calculates error for a given render target format to reduce floating-point precision artifacts via QuantizeColor (from Noise.hlsl).
diff --git a/Source/Engine/Renderer/AmbientOcclusionPass.cpp b/Source/Engine/Renderer/AmbientOcclusionPass.cpp
index 08a8cefcb..34a512fe3 100644
--- a/Source/Engine/Renderer/AmbientOcclusionPass.cpp
+++ b/Source/Engine/Renderer/AmbientOcclusionPass.cpp
@@ -10,6 +10,7 @@
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/RenderTask.h"
+#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/RenderTargetPool.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Utilities/StringConverter.h"
@@ -75,6 +76,7 @@ bool AmbientOcclusionPass::Init()
_psNonSmartBlur = GPUDevice::Instance->CreatePipelineState();
_psApply = GPUDevice::Instance->CreatePipelineState();
_psApplyHalf = GPUDevice::Instance->CreatePipelineState();
+ _depthBounds = GPUDevice::Instance->Limits.HasDepthBounds && GPUDevice::Instance->Limits.HasReadOnlyDepth;
// Load shader
_shader = Content::LoadAsyncInternal(TEXT("Shaders/SSAO"));
@@ -96,8 +98,12 @@ bool AmbientOcclusionPass::setupResources()
CHECK_INVALID_SHADER_PASS_CB_SIZE(shader, 0, ASSAOConstants);
// Create pipeline states
- GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
- // Prepare Depths
+ auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
+ if (_depthBounds)
+ {
+ psDesc.DepthEnable = psDesc.DepthBoundsEnable = true;
+ psDesc.DepthWriteEnable = false;
+ }
if (!_psPrepareDepths->IsValid())
{
psDesc.PS = shader->GetPS("PS_PrepareDepths");
@@ -110,7 +116,6 @@ bool AmbientOcclusionPass::setupResources()
if (_psPrepareDepthsHalf->Init(psDesc))
return true;
}
- // Prepare Depth Mips
for (int32 i = 0; i < ARRAY_COUNT(_psPrepareDepthMip); i++)
{
if (!_psPrepareDepthMip[i]->IsValid())
@@ -122,7 +127,6 @@ bool AmbientOcclusionPass::setupResources()
return true;
}
}
- // AO Generate
for (int32 i = 0; i < ARRAY_COUNT(_psGenerate); i++)
{
if (!_psGenerate[i]->IsValid())
@@ -134,7 +138,6 @@ bool AmbientOcclusionPass::setupResources()
return true;
}
}
- // Blur
if (!_psSmartBlur->IsValid())
{
psDesc.PS = shader->GetPS("PS_SmartBlur");
@@ -153,7 +156,6 @@ bool AmbientOcclusionPass::setupResources()
if (_psNonSmartBlur->Init(psDesc))
return true;
}
- // Apply AO
psDesc.BlendMode = BlendingMode::Multiply;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::Alpha;
if (!_psApply->IsValid())
@@ -257,6 +259,12 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
m_sizeY = (float)renderContext.Buffers->GetHeight();
m_halfSizeX = (m_sizeX + 1) / 2;
m_halfSizeY = (m_sizeY + 1) / 2;
+ GPUTexture* depthBuffer = aoSettings.DepthResolution == ResolutionMode::Full ? renderContext.Buffers->DepthBuffer : renderContext.Buffers->RequestHalfResDepth(context);
+ const bool depthBufferReadOnly = EnumHasAnyFlags(depthBuffer->Flags(), GPUTextureFlags::ReadOnlyDepthView) && _depthBounds;
+ GPUTextureView* depthBufferRTV = depthBufferReadOnly ? depthBuffer->ViewReadOnlyDepth() : nullptr;
+ GPUTextureView* depthBufferSRV = depthBufferReadOnly ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
+ Float3 depthBoundPoint = renderContext.View.Position + renderContext.View.Direction * (aoSettings.FadeOutDistance * 2.0f);
+ context->SetDepthBounds(0.0f, RenderTools::GetDepthBounds(renderContext.View, depthBoundPoint));
// Request temporary buffers
InitRTs(renderContext);
@@ -266,20 +274,22 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
context->BindCB(SSAO_CONSTANTS_BUFFER_SLOT, _shader->GetShader()->GetCB(SSAO_CONSTANTS_BUFFER_SLOT));
// Generate depths
- PrepareDepths(renderContext);
+ PrepareDepths(renderContext, depthBufferRTV, depthBufferSRV);
// Generate SSAO
- GenerateSSAO(renderContext);
+ GenerateSSAO(renderContext, depthBufferRTV);
// Apply
context->BindSR(SSAO_TEXTURE_SLOT0, m_finalResults->ViewArray());
context->SetViewportAndScissors(m_sizeX, m_sizeY);
context->SetState(settings.SkipHalfPixels ? _psApplyHalf : _psApply);
- context->SetRenderTarget(renderContext.Buffers->GBuffer0->View());
+ context->SetRenderTarget(depthBufferReadOnly ? renderContext.Buffers->DepthBuffer->ViewReadOnlyDepth() : nullptr, renderContext.Buffers->GBuffer0->View());
context->DrawFullscreenTriangle();
// Release and cleanup
ReleaseRTs(renderContext);
+ if (_depthBounds)
+ context->SetDepthBounds(0, 1);
context->ResetRenderTarget();
context->ResetSR();
}
@@ -405,14 +415,14 @@ void AmbientOcclusionPass::UpdateCB(const RenderContext& renderContext, GPUConte
context->BindCB(SSAO_CONSTANTS_BUFFER_SLOT, cb);
}
-void AmbientOcclusionPass::PrepareDepths(const RenderContext& renderContext)
+void AmbientOcclusionPass::PrepareDepths(const RenderContext& renderContext, GPUTextureView* depthBufferRTV, GPUTextureView* depthBufferSRV)
{
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
// Bind scene depth buffer and set proper viewport
- context->BindSR(SSAO_TEXTURE_SLOT0, renderContext.Buffers->DepthBuffer);
+ context->BindSR(SSAO_TEXTURE_SLOT0, depthBufferSRV);
context->SetViewportAndScissors(m_halfSizeX, m_halfSizeY);
// Prepare depth in half resolution
@@ -424,7 +434,7 @@ void AmbientOcclusionPass::PrepareDepths(const RenderContext& renderContext)
m_halfDepths[0]->View(),
m_halfDepths[3]->View()
};
- context->SetRenderTarget(nullptr, ToSpan(twoDepths, ARRAY_COUNT(twoDepths)));
+ context->SetRenderTarget(depthBufferRTV, ToSpan(twoDepths, ARRAY_COUNT(twoDepths)));
context->SetState(_psPrepareDepthsHalf);
}
else
@@ -436,7 +446,7 @@ void AmbientOcclusionPass::PrepareDepths(const RenderContext& renderContext)
m_halfDepths[2]->View(),
m_halfDepths[3]->View()
};
- context->SetRenderTarget(nullptr, ToSpan(fourDepths, ARRAY_COUNT(fourDepths)));
+ context->SetRenderTarget(depthBufferRTV, ToSpan(fourDepths, ARRAY_COUNT(fourDepths)));
context->SetState(_psPrepareDepths);
}
context->DrawFullscreenTriangle();
@@ -455,7 +465,7 @@ void AmbientOcclusionPass::PrepareDepths(const RenderContext& renderContext)
m_halfDepths[2]->View(0, i),
m_halfDepths[3]->View(0, i)
};
- context->SetRenderTarget(nullptr, ToSpan(fourDepthMips, ARRAY_COUNT(fourDepthMips)));
+ context->SetRenderTarget(depthBufferRTV, ToSpan(fourDepthMips, ARRAY_COUNT(fourDepthMips)));
int32 mipWidth, mipHeight;
m_halfDepths[0]->GetMipSize(i, mipWidth, mipHeight);
@@ -473,7 +483,7 @@ void AmbientOcclusionPass::PrepareDepths(const RenderContext& renderContext)
}
}
-void AmbientOcclusionPass::GenerateSSAO(const RenderContext& renderContext)
+void AmbientOcclusionPass::GenerateSSAO(const RenderContext& renderContext, GPUTextureView* depthBufferRTV)
{
// Cache data
auto device = GPUDevice::Instance;
@@ -516,7 +526,7 @@ void AmbientOcclusionPass::GenerateSSAO(const RenderContext& renderContext)
if (blurPasses == 0)
rts = m_finalResults->View(pass);
- context->SetRenderTarget(rts);
+ context->SetRenderTarget(depthBufferRTV, rts);
context->BindSR(SSAO_TEXTURE_SLOT0, m_halfDepths[pass]);
context->BindSR(SSAO_TEXTURE_SLOT1, normalMapSRV);
context->SetState(_psGenerate[settings.QualityLevel]);
@@ -537,7 +547,7 @@ void AmbientOcclusionPass::GenerateSSAO(const RenderContext& renderContext)
if (i == (blurPasses - 1))
rts = m_finalResults->View(pass);
- context->SetRenderTarget(rts);
+ context->SetRenderTarget(depthBufferRTV, rts);
context->BindSR(SSAO_TEXTURE_SLOT0, pPingRT);
if (settings.QualityLevel == 0)
diff --git a/Source/Engine/Renderer/AmbientOcclusionPass.h b/Source/Engine/Renderer/AmbientOcclusionPass.h
index c44454806..c3d496960 100644
--- a/Source/Engine/Renderer/AmbientOcclusionPass.h
+++ b/Source/Engine/Renderer/AmbientOcclusionPass.h
@@ -100,6 +100,7 @@ private:
float m_halfSizeY;
ASSAOConstants _constantsBufferData;
ASSAO_Settings settings;
+ bool _depthBounds;
public:
@@ -121,8 +122,8 @@ private:
void InitRTs(const RenderContext& renderContext);
void ReleaseRTs(const RenderContext& renderContext);
void UpdateCB(const RenderContext& renderContext, GPUContext* context, const int32 passIndex);
- void PrepareDepths(const RenderContext& renderContext);
- void GenerateSSAO(const RenderContext& renderContext);
+ void PrepareDepths(const RenderContext& renderContext, GPUTextureView* depthBufferRTV, GPUTextureView* depthBufferSRV);
+ void GenerateSSAO(const RenderContext& renderContext, GPUTextureView* depthBufferRTV);
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj)
{
diff --git a/Source/Engine/Renderer/LightPass.cpp b/Source/Engine/Renderer/LightPass.cpp
index e62305980..e75247e76 100644
--- a/Source/Engine/Renderer/LightPass.cpp
+++ b/Source/Engine/Renderer/LightPass.cpp
@@ -425,6 +425,8 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
RenderTargetPool::Release(shadowMask);
// Restore state
+ if (_depthBounds)
+ context->SetDepthBounds(0, 1);
context->ResetRenderTarget();
context->ResetSR();
context->ResetCB();