Просмотр исходного кода

Implement GTAO and remove SSGI

Panagiotis Christopoulos Charitos 2 лет назад
Родитель
Сommit
6cf37b8605

+ 9 - 5
AnKi/Collision/Aabb.cpp

@@ -15,13 +15,17 @@ Aabb Aabb::getTransformed(const Transform& trf) const
 		absM[i] = absolute(trf.getRotation()[i]);
 	}
 
-	Vec4 center = (m_min + m_max) * 0.5f;
-	Vec4 extend = (m_max - m_min) * 0.5f;
+	const Vec4 center = (m_min + m_max) * 0.5f;
+	const Vec4 extend = (m_max - m_min) * 0.5f;
 
-	Vec4 newC = trf.transform(center);
-	Vec4 newE = Vec4(absM * (extend * trf.getScale()), 0.0f);
+	const Vec4 newC = trf.transform(center);
+	const Vec4 newE = Vec4(absM * (extend * trf.getScale()), 0.0f);
 
-	return Aabb(newC - newE, newC + newE + Vec4(kEpsilonf, kEpsilonf, kEpsilonf, 0.0f));
+	const Vec4 min = newC - newE;
+	const Vec4 max = newC + newE;
+
+	const F32 epsilon = kEpsilonf * 100.0f;
+	return Aabb(min, max + Vec4(epsilon, epsilon, epsilon, 0.0f));
 }
 
 Aabb Aabb::getCompoundShape(const Aabb& b) const

+ 9 - 2
AnKi/Math/Functions.h

@@ -104,13 +104,20 @@ inline T modf(T x, T& intPart)
 	return std::modf(x, &intPart);
 }
 
-/// The same as abs/fabs. For ints and floats.
-template<typename T, ANKI_ENABLE(std::is_floating_point<T>::value || (std::is_integral<T>::value && std::is_signed<T>::value))>
+/// The same as abs/fabs. For ints.
+template<typename T, ANKI_ENABLE(std::is_integral<T>::value&& std::is_signed<T>::value)>
 inline constexpr T absolute(const T f)
 {
 	return (f < T(0)) ? -f : f;
 }
 
+/// The same as abs/fabs. For floats.
+template<typename T, ANKI_ENABLE(std::is_floating_point<T>::value)>
+inline constexpr T absolute(const T f)
+{
+	return fabs(f);
+}
+
 template<typename T>
 inline constexpr T pow(const T x, const T exp)
 {

+ 1 - 1
AnKi/Renderer.h

@@ -29,6 +29,6 @@
 #include <AnKi/Renderer/VolumetricLightingAccumulation.h>
 #include <AnKi/Renderer/IndirectDiffuseProbes.h>
 #include <AnKi/Renderer/ShadowmapsResolve.h>
-#include <AnKi/Renderer/IndirectDiffuse.h>
+#include <AnKi/Renderer/Ssao.h>
 
 /// @defgroup renderer Renderering system

+ 1 - 2
AnKi/Renderer/Common.h

@@ -15,9 +15,8 @@
 namespace anki {
 
 // Forward
-#define ANKI_RENDERER_OBJECT_DEF(a, b) class a;
+#define ANKI_RENDERER_OBJECT_DEF(name, name2, initCondition) class name;
 #include <AnKi/Renderer/RendererObject.def.h>
-#undef ANKI_RENDERER_OBJECT_DEF
 
 class Renderer;
 class RendererObject;

+ 0 - 391
AnKi/Renderer/IndirectDiffuse.cpp

@@ -1,391 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Renderer/IndirectDiffuse.h>
-#include <AnKi/Renderer/Renderer.h>
-#include <AnKi/Renderer/DepthDownscale.h>
-#include <AnKi/Renderer/GBuffer.h>
-#include <AnKi/Renderer/DownscaleBlur.h>
-#include <AnKi/Renderer/MotionVectors.h>
-#include <AnKi/Renderer/IndirectDiffuseProbes.h>
-#include <AnKi/Renderer/ClusterBinning.h>
-#include <AnKi/Core/CVarSet.h>
-#include <AnKi/Util/Tracer.h>
-
-namespace anki {
-
-static NumericCVar<U32> g_indirectDiffuseSsgiSampleCountCVar(CVarSubsystem::kRenderer, "IndirectDiffuseSsgiSampleCount", 8, 1, 1024,
-															 "SSGI sample count");
-static NumericCVar<F32> g_indirectDiffuseSsgiRadiusCVar(CVarSubsystem::kRenderer, "IndirectDiffuseSsgiRadius", 2.0f, 0.1f, 100.0f,
-														"SSGI radius in meters");
-static NumericCVar<U32> g_indirectDiffuseDenoiseSampleCountCVar(CVarSubsystem::kRenderer, "IndirectDiffuseDenoiseSampleCount", 4, 1, 128,
-																"Indirect diffuse denoise sample count");
-static NumericCVar<F32> g_indirectDiffuseSsaoStrengthCVar(CVarSubsystem::kRenderer, "IndirectDiffuseSsaoStrength", 2.5f, 0.1f, 10.0f,
-														  "SSAO strength");
-static NumericCVar<F32> g_indirectDiffuseSsaoBiasCVar(CVarSubsystem::kRenderer, "IndirectDiffuseSsaoBias", -0.1f, -10.0f, 10.0f, "SSAO bias");
-static NumericCVar<F32> g_indirectDiffuseVrsDistanceThresholdCVar(CVarSubsystem::kRenderer, "IndirectDiffuseVrsDistanceThreshold", 0.01f, 0.00001f,
-																  10.0f, "The meters that control the VRS SRI generation");
-
-Error IndirectDiffuse::init()
-{
-	const Error err = initInternal();
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to initialize indirect diffuse pass");
-	}
-	return err;
-}
-
-Error IndirectDiffuse::initInternal()
-{
-	const UVec2 size = getRenderer().getInternalResolution() / 2;
-	ANKI_ASSERT((getRenderer().getInternalResolution() % 2) == UVec2(0u) && "Needs to be dividable for proper upscaling");
-
-	ANKI_R_LOGV("Initializing indirect diffuse. Resolution %ux%u", size.x(), size.y());
-
-	const Bool preferCompute = g_preferComputeCVar.get();
-
-	// Init textures
-	TextureUsageBit usage = TextureUsageBit::kAllSampled;
-
-	usage |= (preferCompute) ? TextureUsageBit::kUavComputeWrite : TextureUsageBit::kFramebufferWrite;
-	TextureInitInfo texInit =
-		getRenderer().create2DRenderTargetInitInfo(size.x(), size.y(), getRenderer().getHdrFormat(), usage, "IndirectDiffuse #1");
-	m_rts[0] = getRenderer().createAndClearRenderTarget(texInit, TextureUsageBit::kAllSampled);
-	texInit.setName("IndirectDiffuse #2");
-	m_rts[1] = getRenderer().createAndClearRenderTarget(texInit, TextureUsageBit::kAllSampled);
-
-	if(!preferCompute)
-	{
-		m_main.m_fbDescr.m_colorAttachmentCount = 1;
-		m_main.m_fbDescr.bake();
-	}
-
-	// Init VRS SRI generation
-	const Bool enableVrs = GrManager::getSingleton().getDeviceCapabilities().m_vrs && g_vrsCVar.get() && !preferCompute;
-	if(enableVrs)
-	{
-		m_vrs.m_sriTexelDimension = GrManager::getSingleton().getDeviceCapabilities().m_minShadingRateImageTexelSize;
-		ANKI_ASSERT(m_vrs.m_sriTexelDimension == 8 || m_vrs.m_sriTexelDimension == 16);
-
-		const UVec2 rez = (size + m_vrs.m_sriTexelDimension - 1) / m_vrs.m_sriTexelDimension;
-		m_vrs.m_rtHandle = getRenderer().create2DRenderTargetDescription(rez.x(), rez.y(), Format::kR8_Uint, "IndirectDiffuseVrsSri");
-		m_vrs.m_rtHandle.bake();
-
-		ANKI_CHECK(ResourceManager::getSingleton().loadResource("ShaderBinaries/IndirectDiffuseVrsSriGeneration.ankiprogbin", m_vrs.m_prog));
-
-		ShaderProgramResourceVariantInitInfo variantInit(m_vrs.m_prog);
-		variantInit.addMutation("SRI_TEXEL_DIMENSION", m_vrs.m_sriTexelDimension);
-
-		if(m_vrs.m_sriTexelDimension == 16 && GrManager::getSingleton().getDeviceCapabilities().m_minSubgroupSize >= 32)
-		{
-			// Algorithm's workgroup size is 32, GPU's subgroup size is min 32 -> each workgroup has 1 subgroup -> No
-			// need for shared mem
-			variantInit.addMutation("SHARED_MEMORY", 0);
-		}
-		else if(m_vrs.m_sriTexelDimension == 8 && GrManager::getSingleton().getDeviceCapabilities().m_minSubgroupSize >= 16)
-		{
-			// Algorithm's workgroup size is 16, GPU's subgroup size is min 16 -> each workgroup has 1 subgroup -> No
-			// need for shared mem
-			variantInit.addMutation("SHARED_MEMORY", 0);
-		}
-		else
-		{
-			variantInit.addMutation("SHARED_MEMORY", 1);
-		}
-
-		variantInit.addMutation("LIMIT_RATE_TO_2X2", g_vrsLimitTo2x2CVar.get());
-
-		const ShaderProgramResourceVariant* variant;
-		m_vrs.m_prog->getOrCreateVariant(variantInit, variant);
-		m_vrs.m_grProg.reset(&variant->getProgram());
-
-		ANKI_CHECK(loadShaderProgram("ShaderBinaries/VrsSriVisualizeRenderTarget.ankiprogbin", m_vrs.m_visualizeProg, m_vrs.m_visualizeGrProg));
-	}
-
-	// Init SSGI+probes pass
-	ANKI_CHECK(loadShaderProgram("ShaderBinaries/IndirectDiffuse.ankiprogbin", m_main.m_prog, m_main.m_grProg));
-
-	// Init denoise
-	{
-		m_denoise.m_fbDescr.m_colorAttachmentCount = 1;
-		m_denoise.m_fbDescr.bake();
-
-		ANKI_CHECK(loadShaderProgram("ShaderBinaries/IndirectDiffuseDenoise.ankiprogbin", Array<SubMutation, 1>{{"BLUR_ORIENTATION", 0}},
-									 m_denoise.m_prog, m_denoise.m_grProgs[0]));
-		ANKI_CHECK(loadShaderProgram("ShaderBinaries/IndirectDiffuseDenoise.ankiprogbin", Array<SubMutation, 1>{{"BLUR_ORIENTATION", 1}},
-									 m_denoise.m_prog, m_denoise.m_grProgs[1]));
-	}
-
-	return Error::kNone;
-}
-
-void IndirectDiffuse::populateRenderGraph(RenderingContext& ctx)
-{
-	ANKI_TRACE_SCOPED_EVENT(IndirectDiffuse);
-	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
-	const Bool preferCompute = g_preferComputeCVar.get();
-	const Bool enableVrs = GrManager::getSingleton().getDeviceCapabilities().m_vrs && g_vrsCVar.get() && !preferCompute;
-	const Bool fbDescrHasVrs = m_main.m_fbDescr.m_shadingRateAttachmentTexelWidth > 0;
-
-	if(!preferCompute && enableVrs != fbDescrHasVrs)
-	{
-		// Re-bake the FB descriptor if the VRS state has changed
-
-		if(enableVrs)
-		{
-			m_main.m_fbDescr.m_shadingRateAttachmentTexelWidth = m_vrs.m_sriTexelDimension;
-			m_main.m_fbDescr.m_shadingRateAttachmentTexelHeight = m_vrs.m_sriTexelDimension;
-		}
-		else
-		{
-			m_main.m_fbDescr.m_shadingRateAttachmentTexelWidth = 0;
-			m_main.m_fbDescr.m_shadingRateAttachmentTexelHeight = 0;
-		}
-
-		m_main.m_fbDescr.bake();
-	}
-
-	// VRS SRI
-	if(enableVrs)
-	{
-		m_runCtx.m_sriRt = rgraph.newRenderTarget(m_vrs.m_rtHandle);
-
-		ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("IndirectDiffuse VRS SRI gen");
-
-		pass.newTextureDependency(m_runCtx.m_sriRt, TextureUsageBit::kUavComputeWrite);
-		pass.newTextureDependency(getRenderer().getDepthDownscale().getRt(), TextureUsageBit::kSampledCompute,
-								  DepthDownscale::kQuarterInternalResolution);
-
-		pass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
-			const UVec2 viewport = getRenderer().getInternalResolution() / 2u;
-
-			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
-
-			cmdb.bindShaderProgram(m_vrs.m_grProg.get());
-
-			rgraphCtx.bindTexture(0, 0, getRenderer().getDepthDownscale().getRt(), DepthDownscale::kQuarterInternalResolution);
-			cmdb.bindSampler(0, 1, getRenderer().getSamplers().m_nearestNearestClamp.get());
-			rgraphCtx.bindUavTexture(0, 2, m_runCtx.m_sriRt);
-
-			class
-			{
-			public:
-				Vec4 m_v4;
-				Mat4 m_invertedProjectionJitter;
-			} pc;
-
-			pc.m_v4 = Vec4(1.0f / Vec2(viewport), g_indirectDiffuseVrsDistanceThresholdCVar.get(), 0.0f);
-			pc.m_invertedProjectionJitter = ctx.m_matrices.m_invertedProjectionJitter;
-
-			cmdb.setPushConstants(&pc, sizeof(pc));
-
-			dispatchPPCompute(cmdb, m_vrs.m_sriTexelDimension, m_vrs.m_sriTexelDimension, viewport.x(), viewport.y());
-		});
-	}
-
-	// SSGI+probes
-	{
-		// Create RTs
-		const U32 readRtIdx = getRenderer().getFrameCount() & 1;
-		const U32 writeRtIdx = !readRtIdx;
-		if(m_rtsImportedOnce) [[likely]]
-		{
-			m_runCtx.m_mainRtHandles[0] = rgraph.importRenderTarget(m_rts[readRtIdx].get());
-			m_runCtx.m_mainRtHandles[1] = rgraph.importRenderTarget(m_rts[writeRtIdx].get());
-		}
-		else
-		{
-			m_runCtx.m_mainRtHandles[0] = rgraph.importRenderTarget(m_rts[readRtIdx].get(), TextureUsageBit::kAllSampled);
-			m_runCtx.m_mainRtHandles[1] = rgraph.importRenderTarget(m_rts[writeRtIdx].get(), TextureUsageBit::kAllSampled);
-			m_rtsImportedOnce = true;
-		}
-
-		// Create main pass
-		TextureUsageBit readUsage;
-		TextureUsageBit writeUsage;
-		BufferUsageBit readBufferUsage;
-		RenderPassDescriptionBase* prpass;
-		if(preferCompute)
-		{
-			ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("IndirectDiffuse");
-			readUsage = TextureUsageBit::kSampledCompute;
-			writeUsage = TextureUsageBit::kUavComputeWrite;
-			readBufferUsage = BufferUsageBit::kUavComputeRead;
-			prpass = &rpass;
-		}
-		else
-		{
-			GraphicsRenderPassDescription& rpass = rgraph.newGraphicsRenderPass("IndirectDiffuse");
-			rpass.setFramebufferInfo(m_main.m_fbDescr, {m_runCtx.m_mainRtHandles[kWrite]}, {}, (enableVrs) ? m_runCtx.m_sriRt : RenderTargetHandle());
-			readUsage = TextureUsageBit::kSampledFragment;
-			writeUsage = TextureUsageBit::kFramebufferWrite;
-			readBufferUsage = BufferUsageBit::kUavFragmentRead;
-			prpass = &rpass;
-
-			if(enableVrs)
-			{
-				prpass->newTextureDependency(m_runCtx.m_sriRt, TextureUsageBit::kFramebufferShadingRate);
-			}
-		}
-
-		prpass->newTextureDependency(m_runCtx.m_mainRtHandles[kWrite], writeUsage);
-
-		if(getRenderer().getIndirectDiffuseProbes().hasCurrentlyRefreshedVolumeRt())
-		{
-			prpass->newTextureDependency(getRenderer().getIndirectDiffuseProbes().getCurrentlyRefreshedVolumeRt(), readUsage);
-		}
-
-		prpass->newTextureDependency(getRenderer().getGBuffer().getColorRt(2), readUsage);
-		prpass->newTextureDependency(getRenderer().getDepthDownscale().getRt(), readUsage, DepthDownscale::kQuarterInternalResolution);
-		prpass->newTextureDependency(getRenderer().getDownscaleBlur().getRt(), readUsage);
-		prpass->newTextureDependency(getRenderer().getMotionVectors().getMotionVectorsRt(), readUsage);
-		prpass->newTextureDependency(getRenderer().getMotionVectors().getHistoryLengthRt(), readUsage);
-		prpass->newTextureDependency(m_runCtx.m_mainRtHandles[kRead], readUsage);
-
-		prpass->newBufferDependency(
-			getRenderer().getClusterBinning().getPackedObjectsBufferHandle(GpuSceneNonRenderableObjectType::kGlobalIlluminationProbe),
-			readBufferUsage);
-		prpass->newBufferDependency(getRenderer().getClusterBinning().getClustersBufferHandle(), readBufferUsage);
-
-		prpass->setWork([this, &ctx, enableVrs](RenderPassWorkContext& rgraphCtx) {
-			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
-			cmdb.bindShaderProgram(m_main.m_grProg.get());
-
-			BufferOffsetRange buff = getRenderer().getClusterBinning().getClusteredShadingConstants();
-			cmdb.bindConstantBuffer(0, 0, buff.m_buffer, buff.m_offset, buff.m_range);
-
-			buff = getRenderer().getClusterBinning().getPackedObjectsBuffer(GpuSceneNonRenderableObjectType::kGlobalIlluminationProbe);
-			cmdb.bindUavBuffer(0, 1, buff.m_buffer, buff.m_offset, buff.m_range);
-
-			buff = getRenderer().getClusterBinning().getClustersBuffer();
-			cmdb.bindUavBuffer(0, 2, buff.m_buffer, buff.m_offset, buff.m_range);
-
-			cmdb.bindSampler(0, 3, getRenderer().getSamplers().m_trilinearClamp.get());
-			rgraphCtx.bindColorTexture(0, 4, getRenderer().getGBuffer().getColorRt(2));
-			rgraphCtx.bindTexture(0, 5, getRenderer().getDepthDownscale().getRt(), DepthDownscale::kQuarterInternalResolution);
-			rgraphCtx.bindColorTexture(0, 6, getRenderer().getDownscaleBlur().getRt());
-			rgraphCtx.bindColorTexture(0, 7, m_runCtx.m_mainRtHandles[kRead]);
-			rgraphCtx.bindColorTexture(0, 8, getRenderer().getMotionVectors().getMotionVectorsRt());
-			rgraphCtx.bindColorTexture(0, 9, getRenderer().getMotionVectors().getHistoryLengthRt());
-
-			if(g_preferComputeCVar.get())
-			{
-				rgraphCtx.bindUavTexture(0, 10, m_runCtx.m_mainRtHandles[kWrite]);
-			}
-
-			cmdb.bindAllBindless(1);
-
-			// Bind uniforms
-			IndirectDiffuseConstants unis;
-			unis.m_viewportSize = getRenderer().getInternalResolution() / 2u;
-			unis.m_viewportSizef = Vec2(unis.m_viewportSize);
-			const Mat4& pmat = ctx.m_matrices.m_projection;
-			unis.m_projectionMat = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
-			unis.m_radius = g_indirectDiffuseSsgiRadiusCVar.get();
-			unis.m_sampleCount = g_indirectDiffuseSsgiSampleCountCVar.get();
-			unis.m_sampleCountf = F32(unis.m_sampleCount);
-			unis.m_ssaoBias = g_indirectDiffuseSsaoBiasCVar.get();
-			unis.m_ssaoStrength = g_indirectDiffuseSsaoStrengthCVar.get();
-			cmdb.setPushConstants(&unis, sizeof(unis));
-
-			if(g_preferComputeCVar.get())
-			{
-				dispatchPPCompute(cmdb, 8, 8, unis.m_viewportSize.x(), unis.m_viewportSize.y());
-			}
-			else
-			{
-				cmdb.setViewport(0, 0, unis.m_viewportSize.x(), unis.m_viewportSize.y());
-
-				if(enableVrs)
-				{
-					cmdb.setVrsRate(VrsRate::k1x1);
-				}
-
-				cmdb.draw(PrimitiveTopology::kTriangles, 3);
-			}
-		});
-	}
-
-	// Denoise
-	for(U32 dir = 0; dir < 2; ++dir)
-	{
-		const U32 readIdx = (dir == 0) ? kWrite : kRead;
-
-		TextureUsageBit readUsage;
-		TextureUsageBit writeUsage;
-		RenderPassDescriptionBase* prpass;
-		if(preferCompute)
-		{
-			ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass((dir == 0) ? "IndirectDiffuseDenoiseH" : "IndirectDiffuseDenoiseV");
-			readUsage = TextureUsageBit::kSampledCompute;
-			writeUsage = TextureUsageBit::kUavComputeWrite;
-			prpass = &rpass;
-		}
-		else
-		{
-			GraphicsRenderPassDescription& rpass = rgraph.newGraphicsRenderPass((dir == 0) ? "IndirectDiffuseDenoiseH" : "IndirectDiffuseDenoiseV");
-			rpass.setFramebufferInfo(m_denoise.m_fbDescr, {m_runCtx.m_mainRtHandles[!readIdx]});
-			readUsage = TextureUsageBit::kSampledFragment;
-			writeUsage = TextureUsageBit::kFramebufferWrite;
-			prpass = &rpass;
-		}
-
-		prpass->newTextureDependency(m_runCtx.m_mainRtHandles[readIdx], readUsage);
-		prpass->newTextureDependency(getRenderer().getDepthDownscale().getRt(), readUsage, DepthDownscale::kQuarterInternalResolution);
-		prpass->newTextureDependency(m_runCtx.m_mainRtHandles[!readIdx], writeUsage);
-
-		prpass->setWork([this, &ctx, dir, readIdx](RenderPassWorkContext& rgraphCtx) {
-			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
-			cmdb.bindShaderProgram(m_denoise.m_grProgs[dir].get());
-
-			cmdb.bindSampler(0, 0, getRenderer().getSamplers().m_trilinearClamp.get());
-			rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_mainRtHandles[readIdx]);
-			rgraphCtx.bindTexture(0, 2, getRenderer().getDepthDownscale().getRt(), DepthDownscale::kQuarterInternalResolution);
-
-			if(g_preferComputeCVar.get())
-			{
-				rgraphCtx.bindUavTexture(0, 3, m_runCtx.m_mainRtHandles[!readIdx]);
-			}
-
-			IndirectDiffuseDenoiseConstants unis;
-			unis.m_invertedViewProjectionJitterMat = ctx.m_matrices.m_invertedViewProjectionJitter;
-			unis.m_viewportSize = getRenderer().getInternalResolution() / 2u;
-			unis.m_viewportSizef = Vec2(unis.m_viewportSize);
-			unis.m_sampleCountDiv2 = F32(g_indirectDiffuseDenoiseSampleCountCVar.get());
-			unis.m_sampleCountDiv2 = max(1.0f, std::round(unis.m_sampleCountDiv2 / 2.0f));
-
-			cmdb.setPushConstants(&unis, sizeof(unis));
-
-			if(g_preferComputeCVar.get())
-			{
-				dispatchPPCompute(cmdb, 8, 8, unis.m_viewportSize.x(), unis.m_viewportSize.y());
-			}
-			else
-			{
-				cmdb.setViewport(0, 0, unis.m_viewportSize.x(), unis.m_viewportSize.y());
-
-				cmdb.draw(PrimitiveTopology::kTriangles, 3);
-			}
-		});
-	}
-}
-
-void IndirectDiffuse::getDebugRenderTarget(CString rtName, Array<RenderTargetHandle, kMaxDebugRenderTargets>& handles,
-										   ShaderProgramPtr& optionalShaderProgram) const
-{
-	if(rtName == "IndirectDiffuse")
-	{
-		handles[0] = m_runCtx.m_mainRtHandles[kWrite];
-	}
-	else
-	{
-		ANKI_ASSERT(rtName == "IndirectDiffuseVrsSri");
-		handles[0] = m_runCtx.m_sriRt;
-		optionalShaderProgram = m_vrs.m_visualizeGrProg;
-	}
-}
-
-} // end namespace anki

+ 0 - 86
AnKi/Renderer/IndirectDiffuse.h

@@ -1,86 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Renderer/RendererObject.h>
-#include <AnKi/Resource/ImageResource.h>
-#include <AnKi/Gr.h>
-
-namespace anki {
-
-/// @addtogroup renderer
-/// @{
-
-/// Global illumination.
-class IndirectDiffuse : public RendererObject
-{
-public:
-	IndirectDiffuse()
-	{
-		registerDebugRenderTarget("IndirectDiffuse");
-		registerDebugRenderTarget("IndirectDiffuseVrsSri");
-	}
-
-	Error init();
-
-	void populateRenderGraph(RenderingContext& ctx);
-
-	void getDebugRenderTarget(CString rtName, Array<RenderTargetHandle, kMaxDebugRenderTargets>& handles,
-							  ShaderProgramPtr& optionalShaderProgram) const override;
-
-	RenderTargetHandle getRt() const
-	{
-		return m_runCtx.m_mainRtHandles[kWrite];
-	}
-
-private:
-	Array<TexturePtr, 2> m_rts;
-	Bool m_rtsImportedOnce = false;
-
-	static constexpr U32 kRead = 0;
-	static constexpr U32 kWrite = 1;
-
-	class
-	{
-	public:
-		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
-		RenderTargetDescription m_rtHandle;
-
-		ShaderProgramResourcePtr m_visualizeProg;
-		ShaderProgramPtr m_visualizeGrProg;
-
-		U32 m_sriTexelDimension = 16;
-	} m_vrs;
-
-	class
-	{
-	public:
-		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
-		FramebufferDescription m_fbDescr;
-	} m_main;
-
-	class
-	{
-	public:
-		ShaderProgramResourcePtr m_prog;
-		Array<ShaderProgramPtr, 2> m_grProgs;
-		FramebufferDescription m_fbDescr;
-	} m_denoise;
-
-	class
-	{
-	public:
-		RenderTargetHandle m_sriRt;
-		Array<RenderTargetHandle, 2> m_mainRtHandles;
-	} m_runCtx;
-
-	Error initInternal();
-};
-/// @}
-
-} // namespace anki

+ 15 - 12
AnKi/Renderer/LightShading.cpp

@@ -14,9 +14,9 @@
 #include <AnKi/Renderer/IndirectSpecular.h>
 #include <AnKi/Renderer/ShadowmapsResolve.h>
 #include <AnKi/Renderer/RtShadows.h>
-#include <AnKi/Renderer/IndirectDiffuse.h>
 #include <AnKi/Renderer/VrsSriGeneration.h>
 #include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/Ssao.h>
 #include <AnKi/Core/CVarSet.h>
 #include <AnKi/Util/Tracer.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
@@ -137,16 +137,20 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 		// Bind all
 		cmdb.bindConstantBuffer(0, 0, getRenderer().getClusterBinning().getClusteredShadingConstants());
 		cmdb.bindUavBuffer(0, 1, getRenderer().getClusterBinning().getPackedObjectsBuffer(GpuSceneNonRenderableObjectType::kLight));
-		rgraphCtx.bindColorTexture(0, 2, getRenderer().getShadowMapping().getShadowmapRt());
-		cmdb.bindUavBuffer(0, 3, getRenderer().getClusterBinning().getClustersBuffer());
+		cmdb.bindUavBuffer(0, 2, getRenderer().getClusterBinning().getPackedObjectsBuffer(GpuSceneNonRenderableObjectType::kGlobalIlluminationProbe));
+		rgraphCtx.bindColorTexture(0, 3, getRenderer().getShadowMapping().getShadowmapRt());
+		cmdb.bindUavBuffer(0, 4, getRenderer().getClusterBinning().getClustersBuffer());
 
-		cmdb.bindSampler(0, 4, getRenderer().getSamplers().m_nearestNearestClamp.get());
-		cmdb.bindSampler(0, 5, getRenderer().getSamplers().m_trilinearClamp.get());
-		rgraphCtx.bindColorTexture(0, 6, getRenderer().getGBuffer().getColorRt(0));
-		rgraphCtx.bindColorTexture(0, 7, getRenderer().getGBuffer().getColorRt(1));
-		rgraphCtx.bindColorTexture(0, 8, getRenderer().getGBuffer().getColorRt(2));
-		rgraphCtx.bindTexture(0, 9, getRenderer().getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::kDepth));
-		rgraphCtx.bindColorTexture(0, 10, getRenderer().getShadowmapsResolve().getRt());
+		cmdb.bindSampler(0, 5, getRenderer().getSamplers().m_nearestNearestClamp.get());
+		cmdb.bindSampler(0, 6, getRenderer().getSamplers().m_trilinearClamp.get());
+		rgraphCtx.bindColorTexture(0, 7, getRenderer().getGBuffer().getColorRt(0));
+		rgraphCtx.bindColorTexture(0, 8, getRenderer().getGBuffer().getColorRt(1));
+		rgraphCtx.bindColorTexture(0, 9, getRenderer().getGBuffer().getColorRt(2));
+		rgraphCtx.bindTexture(0, 10, getRenderer().getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::kDepth));
+		rgraphCtx.bindColorTexture(0, 11, getRenderer().getShadowmapsResolve().getRt());
+		rgraphCtx.bindColorTexture(0, 12, getRenderer().getSsao().getRt());
+
+		cmdb.bindAllBindless(1);
 
 		// Draw
 		drawQuad(cmdb);
@@ -159,7 +163,6 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 
 		cmdb.bindSampler(0, 0, getRenderer().getSamplers().m_nearestNearestClamp.get());
 		cmdb.bindSampler(0, 1, getRenderer().getSamplers().m_trilinearClamp.get());
-		rgraphCtx.bindColorTexture(0, 2, getRenderer().getIndirectDiffuse().getRt());
 		rgraphCtx.bindColorTexture(0, 3, getRenderer().getIndirectSpecular().getRt());
 		rgraphCtx.bindColorTexture(0, 4, getRenderer().getDepthDownscale().getRt());
 		rgraphCtx.bindTexture(0, 5, getRenderer().getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::kDepth));
@@ -338,9 +341,9 @@ void LightShading::populateRenderGraph(RenderingContext& ctx)
 	pass.newBufferDependency(getRenderer().getClusterBinning().getClustersBufferHandle(), BufferUsageBit::kUavFragmentRead);
 	pass.newBufferDependency(getRenderer().getClusterBinning().getPackedObjectsBufferHandle(GpuSceneNonRenderableObjectType::kLight),
 							 BufferUsageBit::kUavFragmentRead);
+	pass.newTextureDependency(getRenderer().getSsao().getRt(), readUsage);
 
 	// Apply indirect
-	pass.newTextureDependency(getRenderer().getIndirectDiffuse().getRt(), readUsage);
 	pass.newTextureDependency(getRenderer().getDepthDownscale().getRt(), readUsage);
 	pass.newTextureDependency(getRenderer().getIndirectSpecular().getRt(), readUsage);
 

+ 9 - 87
AnKi/Renderer/Renderer.cpp

@@ -39,10 +39,10 @@
 #include <AnKi/Renderer/AccelerationStructureBuilder.h>
 #include <AnKi/Renderer/MotionVectors.h>
 #include <AnKi/Renderer/Scale.h>
-#include <AnKi/Renderer/IndirectDiffuse.h>
 #include <AnKi/Renderer/VrsSriGeneration.h>
 #include <AnKi/Renderer/PrimaryNonRenderableVisibility.h>
 #include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/Ssao.h>
 #include <AnKi/Core/StatsSet.h>
 
 namespace anki {
@@ -184,92 +184,13 @@ Error Renderer::initInternal(UVec2 swapchainResolution)
 	}
 
 	// Init the stages. Careful with the order!!!!!!!!!!
-	m_volumetricLightingAccumulation.reset(newInstance<VolumetricLightingAccumulation>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_volumetricLightingAccumulation->init());
-
-	m_indirectDiffuseProbes.reset(newInstance<IndirectDiffuseProbes>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_indirectDiffuseProbes->init());
-
-	m_probeReflections.reset(newInstance<ProbeReflections>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_probeReflections->init());
-
-	m_vrsSriGeneration.reset(newInstance<VrsSriGeneration>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_vrsSriGeneration->init());
-
-	m_scale.reset(newInstance<Scale>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_scale->init());
-
-	m_gbuffer.reset(newInstance<GBuffer>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_gbuffer->init());
-
-	m_gbufferPost.reset(newInstance<GBufferPost>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_gbufferPost->init());
-
-	m_shadowMapping.reset(newInstance<ShadowMapping>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_shadowMapping->init());
-
-	m_volumetricFog.reset(newInstance<VolumetricFog>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_volumetricFog->init());
-
-	m_lightShading.reset(newInstance<LightShading>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_lightShading->init());
-
-	m_depthDownscale.reset(newInstance<DepthDownscale>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_depthDownscale->init());
-
-	m_forwardShading.reset(newInstance<ForwardShading>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_forwardShading->init());
-
-	m_lensFlare.reset(newInstance<LensFlare>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_lensFlare->init());
-
-	m_downscaleBlur.reset(newInstance<DownscaleBlur>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_downscaleBlur->init());
-
-	m_indirectSpecular.reset(newInstance<IndirectSpecular>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_indirectSpecular->init());
-
-	m_tonemapping.reset(newInstance<Tonemapping>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_tonemapping->init());
-
-	m_temporalAA.reset(newInstance<TemporalAA>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_temporalAA->init());
-
-	m_bloom.reset(newInstance<Bloom>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_bloom->init());
-
-	m_finalComposite.reset(newInstance<FinalComposite>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_finalComposite->init());
-
-	m_dbg.reset(newInstance<Dbg>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_dbg->init());
-
-	m_uiStage.reset(newInstance<UiStage>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_uiStage->init());
-
-	m_indirectDiffuse.reset(newInstance<IndirectDiffuse>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_indirectDiffuse->init());
-
-	if(GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled && g_rayTracedShadowsCVar.get())
-	{
-		m_accelerationStructureBuilder.reset(newInstance<AccelerationStructureBuilder>(RendererMemoryPool::getSingleton()));
-		ANKI_CHECK(m_accelerationStructureBuilder->init());
-
-		m_rtShadows.reset(newInstance<RtShadows>(RendererMemoryPool::getSingleton()));
-		ANKI_CHECK(m_rtShadows->init());
+#define ANKI_RENDERER_OBJECT_DEF(name, name2, initCondition) \
+	if(initCondition) \
+	{ \
+		m_##name2.reset(newInstance<name>(RendererMemoryPool::getSingleton())); \
+		ANKI_CHECK(m_##name2->init()); \
 	}
-
-	m_shadowmapsResolve.reset(newInstance<ShadowmapsResolve>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_shadowmapsResolve->init());
-
-	m_motionVectors.reset(newInstance<MotionVectors>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_motionVectors->init());
-
-	m_clusterBinning2.reset(newInstance<ClusterBinning>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_clusterBinning2->init());
-
-	m_primaryNonRenderableVisibility.reset(newInstance<PrimaryNonRenderableVisibility>(RendererMemoryPool::getSingleton()));
-	ANKI_CHECK(m_primaryNonRenderableVisibility->init());
+#include <AnKi/Renderer/RendererObject.def.h>
 
 	// Init samplers
 	{
@@ -352,6 +273,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	jitter *= ndcPixelSize;
 	ctx.m_matrices.m_jitter = Mat4::getIdentity();
 	ctx.m_matrices.m_jitter.setTranslationPart(Vec4(jitter, 0.0f, 1.0f));
+	ctx.m_matrices.m_jitterOffsetNdc = jitter;
 
 	ctx.m_matrices.m_projectionJitter = ctx.m_matrices.m_jitter * ctx.m_matrices.m_projection;
 	ctx.m_matrices.m_viewProjectionJitter = ctx.m_matrices.m_projectionJitter * Mat4(ctx.m_matrices.m_view, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
@@ -397,7 +319,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	m_volumetricFog->populateRenderGraph(ctx);
 	m_lensFlare->populateRenderGraph(ctx);
 	m_indirectSpecular->populateRenderGraph(ctx);
-	m_indirectDiffuse->populateRenderGraph(ctx);
+	m_ssao->populateRenderGraph(ctx);
 	m_lightShading->populateRenderGraph(ctx);
 	if(!getScale().getUsingGrUpscaler())
 	{

+ 4 - 6
AnKi/Renderer/Renderer.h

@@ -54,13 +54,12 @@ public:
 
 	~Renderer();
 
-#define ANKI_RENDERER_OBJECT_DEF(a, b) \
-	a& get##a() \
+#define ANKI_RENDERER_OBJECT_DEF(name, name2, initCondition) \
+	name& get##name() \
 	{ \
-		return *m_##b; \
+		return *m_##name2; \
 	}
 #include <AnKi/Renderer/RendererObject.def.h>
-#undef ANKI_RENDERER_OBJECT_DEF
 
 	Bool getRtShadowsEnabled() const
 	{
@@ -214,9 +213,8 @@ public:
 private:
 	/// @name Rendering stages
 	/// @{
-#define ANKI_RENDERER_OBJECT_DEF(a, b) UniquePtr<a, SingletonMemoryPoolDeleter<RendererMemoryPool>> m_##b;
+#define ANKI_RENDERER_OBJECT_DEF(name, name2, initCondition) UniquePtr<name, SingletonMemoryPoolDeleter<RendererMemoryPool>> m_##name2;
 #include <AnKi/Renderer/RendererObject.def.h>
-#undef ANKI_RENDERER_OBJECT_DEF
 	/// @}
 
 	UVec2 m_tileCounts = UVec2(0u);

+ 1 - 1
AnKi/Renderer/RendererObject.cpp

@@ -21,7 +21,7 @@ void RendererObject::registerDebugRenderTarget(CString rtName)
 }
 
 Error RendererObject::loadShaderProgram(CString filename, ConstWeakArray<SubMutation> mutators, ShaderProgramResourcePtr& rsrc,
-										ShaderProgramPtr& grProg, ShaderTypeBit shaderTypes, CString technique)
+										ShaderProgramPtr& grProg, CString technique, ShaderTypeBit shaderTypes)
 {
 	if(!rsrc.isCreated())
 	{

+ 33 - 28
AnKi/Renderer/RendererObject.def.h

@@ -3,31 +3,36 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-ANKI_RENDERER_OBJECT_DEF(GBuffer, gbuffer)
-ANKI_RENDERER_OBJECT_DEF(GBufferPost, gbufferPost)
-ANKI_RENDERER_OBJECT_DEF(ShadowMapping, shadowMapping)
-ANKI_RENDERER_OBJECT_DEF(LightShading, lightShading)
-ANKI_RENDERER_OBJECT_DEF(ForwardShading, forwardShading)
-ANKI_RENDERER_OBJECT_DEF(LensFlare, lensFlare)
-ANKI_RENDERER_OBJECT_DEF(Tonemapping, tonemapping)
-ANKI_RENDERER_OBJECT_DEF(Bloom, bloom)
-ANKI_RENDERER_OBJECT_DEF(FinalComposite, finalComposite)
-ANKI_RENDERER_OBJECT_DEF(Dbg, dbg)
-ANKI_RENDERER_OBJECT_DEF(ProbeReflections, probeReflections)
-ANKI_RENDERER_OBJECT_DEF(DownscaleBlur, downscaleBlur)
-ANKI_RENDERER_OBJECT_DEF(VolumetricFog, volumetricFog)
-ANKI_RENDERER_OBJECT_DEF(DepthDownscale, depthDownscale)
-ANKI_RENDERER_OBJECT_DEF(TemporalAA, temporalAA)
-ANKI_RENDERER_OBJECT_DEF(UiStage, uiStage)
-ANKI_RENDERER_OBJECT_DEF(IndirectSpecular, indirectSpecular)
-ANKI_RENDERER_OBJECT_DEF(VolumetricLightingAccumulation, volumetricLightingAccumulation)
-ANKI_RENDERER_OBJECT_DEF(IndirectDiffuseProbes, indirectDiffuseProbes)
-ANKI_RENDERER_OBJECT_DEF(ShadowmapsResolve, shadowmapsResolve)
-ANKI_RENDERER_OBJECT_DEF(RtShadows, rtShadows)
-ANKI_RENDERER_OBJECT_DEF(AccelerationStructureBuilder, accelerationStructureBuilder)
-ANKI_RENDERER_OBJECT_DEF(MotionVectors, motionVectors)
-ANKI_RENDERER_OBJECT_DEF(Scale, scale)
-ANKI_RENDERER_OBJECT_DEF(IndirectDiffuse, indirectDiffuse)
-ANKI_RENDERER_OBJECT_DEF(VrsSriGeneration, vrsSriGeneration)
-ANKI_RENDERER_OBJECT_DEF(PrimaryNonRenderableVisibility, primaryNonRenderableVisibility)
-ANKI_RENDERER_OBJECT_DEF(ClusterBinning, clusterBinning2)
+// ANKI_RENDERER_OBJECT_DEF(Name, name, initCondition)
+
+ANKI_RENDERER_OBJECT_DEF(GBuffer, gbuffer, 1)
+ANKI_RENDERER_OBJECT_DEF(GBufferPost, gbufferPost, 1)
+ANKI_RENDERER_OBJECT_DEF(ShadowMapping, shadowMapping, 1)
+ANKI_RENDERER_OBJECT_DEF(LightShading, lightShading, 1)
+ANKI_RENDERER_OBJECT_DEF(ForwardShading, forwardShading, 1)
+ANKI_RENDERER_OBJECT_DEF(LensFlare, lensFlare, 1)
+ANKI_RENDERER_OBJECT_DEF(DownscaleBlur, downscaleBlur, 1)
+ANKI_RENDERER_OBJECT_DEF(Tonemapping, tonemapping, 1)
+ANKI_RENDERER_OBJECT_DEF(Bloom, bloom, 1)
+ANKI_RENDERER_OBJECT_DEF(FinalComposite, finalComposite, 1)
+ANKI_RENDERER_OBJECT_DEF(Dbg, dbg, 1)
+ANKI_RENDERER_OBJECT_DEF(ProbeReflections, probeReflections, 1)
+ANKI_RENDERER_OBJECT_DEF(VolumetricFog, volumetricFog, 1)
+ANKI_RENDERER_OBJECT_DEF(DepthDownscale, depthDownscale, 1)
+ANKI_RENDERER_OBJECT_DEF(TemporalAA, temporalAA, 1)
+ANKI_RENDERER_OBJECT_DEF(UiStage, uiStage, 1)
+ANKI_RENDERER_OBJECT_DEF(IndirectSpecular, indirectSpecular, 1)
+ANKI_RENDERER_OBJECT_DEF(VolumetricLightingAccumulation, volumetricLightingAccumulation, 1)
+ANKI_RENDERER_OBJECT_DEF(IndirectDiffuseProbes, indirectDiffuseProbes, 1)
+ANKI_RENDERER_OBJECT_DEF(ShadowmapsResolve, shadowmapsResolve, 1)
+ANKI_RENDERER_OBJECT_DEF(RtShadows, rtShadows, GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled&& g_rayTracedShadowsCVar.get())
+ANKI_RENDERER_OBJECT_DEF(AccelerationStructureBuilder, accelerationStructureBuilder,
+						 GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled&& g_rayTracedShadowsCVar.get())
+ANKI_RENDERER_OBJECT_DEF(MotionVectors, motionVectors, 1)
+ANKI_RENDERER_OBJECT_DEF(Scale, scale, 1)
+ANKI_RENDERER_OBJECT_DEF(VrsSriGeneration, vrsSriGeneration, 1)
+ANKI_RENDERER_OBJECT_DEF(PrimaryNonRenderableVisibility, primaryNonRenderableVisibility, 1)
+ANKI_RENDERER_OBJECT_DEF(ClusterBinning, clusterBinning2, 1)
+ANKI_RENDERER_OBJECT_DEF(Ssao, ssao, 1)
+
+#undef ANKI_RENDERER_OBJECT_DEF

+ 1 - 1
AnKi/Renderer/RendererObject.h

@@ -97,7 +97,7 @@ protected:
 	};
 
 	static Error loadShaderProgram(CString filename, ConstWeakArray<SubMutation> mutators, ShaderProgramResourcePtr& rsrc, ShaderProgramPtr& grProg,
-								   ShaderTypeBit shaderTypes = ShaderTypeBit::kNone, CString technique = {});
+								   CString technique = {}, ShaderTypeBit shaderTypes = ShaderTypeBit::kNone);
 
 	static void zeroBuffer(Buffer* buff);
 };

+ 230 - 0
AnKi/Renderer/Ssao.cpp

@@ -0,0 +1,230 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Renderer/Ssao.h>
+#include <AnKi/Renderer/Renderer.h>
+#include <AnKi/Renderer/GBuffer.h>
+#include <AnKi/Renderer/MotionVectors.h>
+#include <AnKi/Renderer/DepthDownscale.h>
+#include <AnKi/Util/Tracer.h>
+
+namespace anki {
+
+static NumericCVar<U32> g_ssaoSampleCountCVar(CVarSubsystem::kRenderer, "SsaoSampleCount", 4, 1, 1024, "SSAO sample count");
+static NumericCVar<F32> g_ssaoRadiusCVar(CVarSubsystem::kRenderer, "SsaoRadius", 2.0f, 0.1f, 100.0f, "SSAO radius in meters");
+static BoolCVar g_ssaoQuarterRez(CVarSubsystem::kRenderer, "SsaoQuarterResolution", ANKI_PLATFORM_MOBILE, "Render SSAO in quarter rez");
+static NumericCVar<F32> g_ssaoPower(CVarSubsystem::kRenderer, "SsaoPower", 3.0f, 0.1f, 100.0f, "SSAO power");
+
+Error Ssao::init()
+{
+	const Error err = initInternal();
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize SSAO");
+	}
+	return err;
+}
+
+Error Ssao::initInternal()
+{
+	const UVec2 rez = (g_ssaoQuarterRez.get()) ? getRenderer().getInternalResolution() / 2 : getRenderer().getInternalResolution();
+
+	ANKI_R_LOGV("Initializing SSAO. Resolution %ux%u", rez.x(), rez.y());
+
+	const Bool preferCompute = g_preferComputeCVar.get();
+
+	{
+		TextureUsageBit usage = TextureUsageBit::kAllSampled;
+		usage |= (preferCompute) ? TextureUsageBit::kUavComputeWrite : TextureUsageBit::kFramebufferWrite;
+		TextureInitInfo texInit = getRenderer().create2DRenderTargetInitInfo(rez.x(), rez.y(), Format::kR8_Unorm, usage, "SSAO #1");
+		m_rts[0] = getRenderer().createAndClearRenderTarget(texInit, TextureUsageBit::kAllSampled);
+		texInit.setName("SSAO #2");
+		m_rts[1] = getRenderer().createAndClearRenderTarget(texInit, TextureUsageBit::kAllSampled);
+	}
+
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.bake();
+
+	ANKI_CHECK(loadShaderProgram("ShaderBinaries/Ssao.ankiprogbin", Array<SubMutation, 1>{{{"SAMPLE_COUNT", 3}}}, m_prog, m_grProg, "Ssao"));
+
+	ANKI_CHECK(loadShaderProgram("ShaderBinaries/Ssao.ankiprogbin", Array<SubMutation, 1>{{{"SAMPLE_COUNT", 5}}}, m_prog, m_denoiseGrProgs[0],
+								 "SsaoDenoiseHorizontal"));
+	ANKI_CHECK(loadShaderProgram("ShaderBinaries/Ssao.ankiprogbin", Array<SubMutation, 1>{{{"SAMPLE_COUNT", 5}}}, m_prog, m_denoiseGrProgs[1],
+								 "SsaoDenoiseVertical"));
+
+	ANKI_CHECK(ResourceManager::getSingleton().loadResource("EngineAssets/BlueNoise_Rgba8_64x64.png", m_noiseImage));
+
+	return Error::kNone;
+}
+
+void Ssao::populateRenderGraph(RenderingContext& ctx)
+{
+	ANKI_TRACE_SCOPED_EVENT(Ssao);
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+	const Bool preferCompute = g_preferComputeCVar.get();
+
+	const U32 readRtIdx = getRenderer().getFrameCount() & 1;
+	const U32 writeRtIdx = !readRtIdx;
+	if(m_rtsImportedOnce) [[likely]]
+	{
+		m_runCtx.m_ssaoRts[0] = rgraph.importRenderTarget(m_rts[readRtIdx].get());
+		m_runCtx.m_ssaoRts[1] = rgraph.importRenderTarget(m_rts[writeRtIdx].get());
+	}
+	else
+	{
+		m_runCtx.m_ssaoRts[0] = rgraph.importRenderTarget(m_rts[readRtIdx].get(), TextureUsageBit::kAllSampled);
+		m_runCtx.m_ssaoRts[1] = rgraph.importRenderTarget(m_rts[writeRtIdx].get(), TextureUsageBit::kAllSampled);
+		m_rtsImportedOnce = true;
+	}
+
+	TextureUsageBit readUsage;
+	TextureUsageBit writeUsage;
+	if(preferCompute)
+	{
+		readUsage = TextureUsageBit::kSampledCompute;
+		writeUsage = TextureUsageBit::kUavComputeWrite;
+	}
+	else
+	{
+		readUsage = TextureUsageBit::kSampledFragment;
+		writeUsage = TextureUsageBit::kFramebufferWrite;
+	}
+
+	// Main pass
+	{
+		RenderPassDescriptionBase* ppass;
+		if(preferCompute)
+		{
+			ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("SSAO");
+			ppass = &pass;
+		}
+		else
+		{
+			GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SSAO");
+			pass.setFramebufferInfo(m_fbDescr, {m_runCtx.m_ssaoRts[1]}, {});
+			ppass = &pass;
+		}
+
+		ppass->newTextureDependency(m_runCtx.m_ssaoRts[1], writeUsage);
+		ppass->newTextureDependency(m_runCtx.m_ssaoRts[0], readUsage);
+		ppass->newTextureDependency(getRenderer().getGBuffer().getColorRt(2), readUsage);
+		ppass->newTextureDependency(getRenderer().getGBuffer().getDepthRt(), readUsage);
+		ppass->newTextureDependency(getRenderer().getMotionVectors().getHistoryLengthRt(), readUsage);
+		ppass->newTextureDependency(getRenderer().getMotionVectors().getMotionVectorsRt(), readUsage);
+
+		ppass->setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
+			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+
+			cmdb.bindShaderProgram(m_grProg.get());
+
+			rgraphCtx.bindColorTexture(0, 0, getRenderer().getGBuffer().getColorRt(2));
+			rgraphCtx.bindTexture(0, 1, getRenderer().getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::kDepth));
+
+			cmdb.bindTexture(0, 2, &m_noiseImage->getTextureView());
+			cmdb.bindSampler(0, 3, getRenderer().getSamplers().m_trilinearRepeat.get());
+			cmdb.bindSampler(0, 4, getRenderer().getSamplers().m_trilinearClamp.get());
+			rgraphCtx.bindColorTexture(0, 5, m_runCtx.m_ssaoRts[0]);
+			rgraphCtx.bindColorTexture(0, 6, getRenderer().getMotionVectors().getMotionVectorsRt());
+			rgraphCtx.bindColorTexture(0, 7, getRenderer().getMotionVectors().getHistoryLengthRt());
+
+			const UVec2 rez = (g_ssaoQuarterRez.get()) ? getRenderer().getInternalResolution() / 2 : getRenderer().getInternalResolution();
+
+			SsaoConstants consts;
+			consts.m_radius = g_ssaoRadiusCVar.get();
+			consts.m_sampleCount = g_ssaoSampleCountCVar.get();
+			consts.m_viewportSizef = Vec2(rez);
+			consts.m_unprojectionParameters = ctx.m_matrices.m_unprojectionParameters;
+			consts.m_projectionMat00 = ctx.m_matrices.m_projection(0, 0);
+			consts.m_projectionMat11 = ctx.m_matrices.m_projection(1, 1);
+			consts.m_projectionMat22 = ctx.m_matrices.m_projection(2, 2);
+			consts.m_projectionMat23 = ctx.m_matrices.m_projection(2, 3);
+			consts.m_frameCount = getRenderer().getFrameCount() % kMaxU32;
+			consts.m_ssaoPower = g_ssaoPower.get();
+			consts.m_viewMat = ctx.m_matrices.m_view;
+			consts.m_prevJitterUv = ctx.m_matrices.m_jitterOffsetNdc / 2.0f;
+			cmdb.setPushConstants(&consts, sizeof(consts));
+
+			if(g_preferComputeCVar.get())
+			{
+				rgraphCtx.bindUavTexture(0, 8, m_runCtx.m_ssaoRts[1]);
+
+				dispatchPPCompute(cmdb, 8, 8, rez.x(), rez.y());
+			}
+			else
+			{
+				cmdb.setViewport(0, 0, rez.x(), rez.y());
+
+				drawQuad(cmdb);
+			}
+		});
+	}
+
+	// Vertical and horizontal blur
+	for(U32 dir = 0; dir < 2; ++dir)
+	{
+		RenderPassDescriptionBase* ppass;
+
+		const U32 readRt = (dir == 0) ? 1 : 0;
+		const U32 writeRt = !readRt;
+
+		CString passName = (dir == 0) ? "SSAO vert blur" : "SSAO horiz blur";
+		if(preferCompute)
+		{
+			ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass(passName);
+			ppass = &pass;
+		}
+		else
+		{
+			GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass(passName);
+			pass.setFramebufferInfo(m_fbDescr, {m_runCtx.m_ssaoRts[writeRt]}, {});
+			ppass = &pass;
+		}
+
+		ppass->newTextureDependency(m_runCtx.m_ssaoRts[readRt], readUsage);
+		ppass->newTextureDependency(m_runCtx.m_ssaoRts[writeRt], writeUsage);
+		if(g_ssaoQuarterRez.get())
+		{
+			ppass->newTextureDependency(getRenderer().getDepthDownscale().getRt(), readUsage);
+		}
+		else
+		{
+			ppass->newTextureDependency(getRenderer().getGBuffer().getDepthRt(), readUsage);
+		}
+
+		ppass->setWork([this, dir, readRt](RenderPassWorkContext& rgraphCtx) {
+			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+
+			cmdb.bindShaderProgram(m_denoiseGrProgs[dir].get());
+
+			cmdb.bindSampler(0, 0, getRenderer().getSamplers().m_trilinearClamp.get());
+			rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_ssaoRts[readRt]);
+			if(g_ssaoQuarterRez.get())
+			{
+				rgraphCtx.bindColorTexture(0, 2, getRenderer().getDepthDownscale().getRt());
+			}
+			else
+			{
+				rgraphCtx.bindTexture(0, 2, getRenderer().getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::kDepth));
+			}
+
+			const UVec2 rez = (g_ssaoQuarterRez.get()) ? getRenderer().getInternalResolution() / 2 : getRenderer().getInternalResolution();
+
+			if(g_preferComputeCVar.get())
+			{
+				rgraphCtx.bindUavTexture(0, 3, m_runCtx.m_ssaoRts[!readRt]);
+
+				dispatchPPCompute(cmdb, 8, 8, rez.x(), rez.y());
+			}
+			else
+			{
+				cmdb.setViewport(0, 0, rez.x(), rez.y());
+
+				drawQuad(cmdb);
+			}
+		});
+	}
+}
+
+} // end namespace anki

+ 63 - 0
AnKi/Renderer/Ssao.h

@@ -0,0 +1,63 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Renderer/RendererObject.h>
+#include <AnKi/Resource/ImageResource.h>
+#include <AnKi/Gr.h>
+
+namespace anki {
+
+/// @addtogroup renderer
+/// @{
+
+/// Resolves shadowmaps into a single texture.
+class Ssao : public RendererObject
+{
+public:
+	Ssao()
+	{
+		registerDebugRenderTarget("Ssao");
+	}
+
+	Error init();
+
+	void populateRenderGraph(RenderingContext& ctx);
+
+	void getDebugRenderTarget([[maybe_unused]] CString rtName, Array<RenderTargetHandle, kMaxDebugRenderTargets>& handles,
+							  [[maybe_unused]] ShaderProgramPtr& optionalShaderProgram) const override
+	{
+		ANKI_ASSERT(rtName == "Ssao");
+		handles[0] = m_runCtx.m_ssaoRts[1];
+	}
+
+	RenderTargetHandle getRt() const
+	{
+		return m_runCtx.m_ssaoRts[1];
+	}
+
+public:
+	ShaderProgramResourcePtr m_prog;
+	ShaderProgramPtr m_grProg;
+	Array<ShaderProgramPtr, 2> m_denoiseGrProgs;
+
+	FramebufferDescription m_fbDescr;
+	ImageResourcePtr m_noiseImage;
+
+	Array<TexturePtr, 2> m_rts;
+	Bool m_rtsImportedOnce = false;
+
+	class
+	{
+	public:
+		Array<RenderTargetHandle, 2> m_ssaoRts;
+	} m_runCtx;
+
+	Error initInternal();
+};
+/// @}
+
+} // end namespace anki

+ 10 - 15
AnKi/Shaders/DepthAwareBlur.ankiprog

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <AnKi/Shaders/Common.hlsl>
+#include <AnKi/Shaders/BilateralFilter.hlsl>
 
 #pragma anki mutator ORIENTATION 0 1 2 // 0: VERTICAL, 1: HORIZONTAL, 2: BOX
 #pragma anki mutator SAMPLE_COUNT 3 5 7 9 11 13 15
@@ -31,11 +32,11 @@ typedef F32 ColorType;
 
 [[vk::binding(0)]] SamplerState g_linearAnyClampSampler;
 [[vk::binding(1)]] Texture2D<ColorType> g_inTex;
-[[vk::binding(2)]] Texture2D g_depthTex;
+[[vk::binding(2)]] Texture2D<Vec4> g_depthTex;
 
 #	if defined(ANKI_COMPUTE_SHADER)
 #		define THREADGROUP_SQRT_SIZE 8
-[[vk::binding(2)]] RWTexture2D<ColorType> g_outImg;
+[[vk::binding(3)]] RWTexture2D<ColorType> g_outImg;
 #	endif
 
 F32 computeDepthWeight(F32 refDepth, F32 depth)
@@ -53,13 +54,13 @@ F32 readDepth(Vec2 uv)
 void sampleTex(Vec2 uv, F32 refDepth, inout ColorType col, inout F32 weight)
 {
 	const ColorType color = g_inTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0);
-	const F32 w = computeDepthWeight(refDepth, readDepth(uv));
+	const F32 w = calculateBilateralWeightDepth(refDepth, readDepth(uv), 1.0f);
 	col += color * w;
 	weight += w;
 }
 
 #	if defined(ANKI_COMPUTE_SHADER)
-[numthreads(THREADGROUP_SQRT_SIZE, THREADGROUP_SQRT_SIZE, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID)
+[numthreads(THREADGROUP_SQRT_SIZE, THREADGROUP_SQRT_SIZE, 1)] void main(UVec2 svDispatchThreadId : SV_DISPATCHTHREADID)
 #	else
 ColorType main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 #	endif
@@ -70,13 +71,7 @@ ColorType main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 
 	// Set UVs
 #	if defined(ANKI_COMPUTE_SHADER)
-	[branch] if(svDispatchThreadId.x >= textureSize.x || svDispatchThreadId.y >= textureSize.y)
-	{
-		// Out of bounds
-		return;
-	}
-
-	const Vec2 uv = (Vec2(svDispatchThreadId.xy) + 0.5) / Vec2(textureSize);
+	const Vec2 uv = (Vec2(svDispatchThreadId) + 0.5) / Vec2(textureSize);
 #	endif
 
 	const Vec2 texelSize = 1.0 / Vec2(textureSize);
@@ -95,15 +90,15 @@ ColorType main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 #			define X_OR_Y y
 #		endif
 
-	Vec2 uvOffset = Vec2(0.0, 0.0);
-	uvOffset.X_OR_Y = 1.5 * texelSize.X_OR_Y;
+	Vec2 uvOffset = 0.0f;
+	uvOffset.X_OR_Y = 1.0f * texelSize.X_OR_Y;
 
 	[unroll] for(U32 i = 0u; i < (SAMPLE_COUNT - 1u) / 2u; ++i)
 	{
 		sampleTex(uv + uvOffset, refDepth, color, weight);
 		sampleTex(uv - uvOffset, refDepth, color, weight);
 
-		uvOffset.X_OR_Y += 2.0 * texelSize.X_OR_Y;
+		uvOffset.X_OR_Y += 1.0f * texelSize.X_OR_Y;
 	}
 #	else
 	// Do box
@@ -125,7 +120,7 @@ ColorType main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 
 	// Write value
 #	if defined(ANKI_COMPUTE_SHADER)
-	g_outImg[svDispatchThreadId.xy] = color;
+	g_outImg[svDispatchThreadId] = color;
 #	else
 	return color;
 #	endif

+ 40 - 0
AnKi/Shaders/FastMathFunctions.hlsl

@@ -0,0 +1,40 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Shaders/Common.hlsl>
+
+/// Sin approximation: https://www.desmos.com/calculator/svgcjfskne
+F32 fastSin(F32 x)
+{
+	const F32 k2Pi = 2.0 * kPi;
+	const F32 kPiOver2 = kPi / 2.0;
+
+	x = (x + kPiOver2) / (k2Pi) + 0.75;
+	x = frac(x);
+	x = x * 2.0 - 1.0;
+	x = x * abs(x) - x;
+	x *= 4.0;
+	return x;
+}
+
+/// Cos approximation
+F32 fastCos(F32 x)
+{
+	return fastSin(x + kPi / 2.0);
+}
+
+F32 fastSqrt(F32 x)
+{
+	return asfloat(0x1FBD1DF5 + (asint(x) >> 1));
+}
+
+F32 fastAcos(F32 x)
+{
+	F32 res = -0.156583f * abs(x) + kPi / 2.0f;
+	res *= fastSqrt(1.0f - abs(x));
+	return (x > 0.0f) ? res : kPi - res;
+}

+ 22 - 20
AnKi/Shaders/Functions.hlsl

@@ -665,26 +665,6 @@ RVec3 filmGrain(RVec3 color, Vec2 uv, F32 strength, F32 time)
 	return color * grain;
 }
 
-/// Sin approximation: https://www.desmos.com/calculator/svgcjfskne
-F32 fastSin(F32 x)
-{
-	const F32 k2Pi = 2.0 * kPi;
-	const F32 kPiOver2 = kPi / 2.0;
-
-	x = (x + kPiOver2) / (k2Pi) + 0.75;
-	x = frac(x);
-	x = x * 2.0 - 1.0;
-	x = x * abs(x) - x;
-	x *= 4.0;
-	return x;
-}
-
-/// Cos approximation
-F32 fastCos(F32 x)
-{
-	return fastSin(x + kPi / 2.0);
-}
-
 #if defined(ANKI_COMPUTE_SHADER)
 /// HLSL doesn't have SubgroupID so compute it. It's a macro because we can't have functions that InterlockedAdd on local variables (the compiler
 /// can't see it's groupshared).
@@ -736,3 +716,25 @@ RVec3 perturbNormal(RVec3 tangentNormal, Vec3 viewDir, Vec2 uv, Vec3 geometricNo
 	TBN.setColumns(T * invmax, B * invmax, geometricNormal);
 	return normalize(mul(TBN, tangentNormal));
 }
+
+/// Project a sphere into NDC. Sphere in view space. The sphere should be in front of the near plane (-sphereCenter.z > sphereRadius + znear)
+/// @param P00 projection matrix's [0,0]
+/// @param P11 projection matrix's [1,1]
+void projectSphereView(Vec3 sphereCenter, F32 sphereRadius, F32 P00, F32 P11, out Vec2 aabbMin, out Vec2 aabbMax)
+{
+	sphereCenter.z = abs(sphereCenter.z);
+
+	const Vec3 cr = sphereCenter * sphereRadius;
+	const F32 czr2 = sphereCenter.z * sphereCenter.z - sphereRadius * sphereRadius;
+
+	const F32 vx = sqrt(sphereCenter.x * sphereCenter.x + czr2);
+	const F32 minx = (vx * sphereCenter.x - cr.z) / (vx * sphereCenter.z + cr.x);
+	const F32 maxx = (vx * sphereCenter.x + cr.z) / (vx * sphereCenter.z - cr.x);
+
+	const F32 vy = sqrt(sphereCenter.y * sphereCenter.y + czr2);
+	const F32 miny = (vy * sphereCenter.y - cr.z) / (vy * sphereCenter.z + cr.y);
+	const F32 maxy = (vy * sphereCenter.y + cr.z) / (vy * sphereCenter.z - cr.y);
+
+	aabbMin = Vec2(minx * P00, miny * P11);
+	aabbMax = Vec2(maxx * P00, maxy * P11);
+}

+ 36 - 0
AnKi/Shaders/ImportanceSampling.hlsl

@@ -75,3 +75,39 @@ U32 hashPcg(U32 u)
 	const U32 word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
 	return (word >> 22u) ^ word;
 }
+
+U32 hilbertIndex(U32 posX, U32 posY)
+{
+	const U32 level = 3u;
+	const U32 width = 1u << level;
+
+	U32 index = 0u;
+	for(U32 curLevel = width / 2u; curLevel > 0u; curLevel /= 2u)
+	{
+		const U32 regionX = (posX & curLevel) > 0u;
+		const U32 regionY = (posY & curLevel) > 0u;
+		index += curLevel * curLevel * ((3u * regionX) ^ regionY);
+		if(regionY == 0U)
+		{
+			if(regionX == 1U)
+			{
+				posX = U32(width - 1u) - posX;
+				posY = U32(width - 1U) - posY;
+			}
+
+			const U32 temp = posX;
+			posX = posY;
+			posY = temp;
+		}
+	}
+
+	return index;
+}
+
+/// Taken from XeGTAO code
+Vec2 spatioTemporalNoise(UVec2 fragCoord, U32 temporalIdx)
+{
+	U32 index = hilbertIndex(fragCoord.x, fragCoord.y);
+	index += 288u * (temporalIdx % 64u);
+	return Vec2(frac(0.5f + index * Vec2(0.75487766624669276005f, 0.5698402909980532659114f)));
+}

+ 3 - 2
AnKi/Shaders/Include/ClusteredShadingTypes.h

@@ -148,9 +148,10 @@ struct CommonMatrices
 	/// pos = Vec3(xy, z);
 	/// @endcode
 	Vec4 m_unprojectionParameters;
+
+	Vec2 m_jitterOffsetNdc;
+	Vec2 m_padding;
 };
-constexpr U32 kSizeof_CommonMatrices = 43u * sizeof(Vec4);
-static_assert(sizeof(CommonMatrices) == kSizeof_CommonMatrices);
 
 /// Common uniforms for light shading passes.
 struct ClusteredShadingConstants

+ 21 - 0
AnKi/Shaders/Include/MiscRendererTypes.h

@@ -124,4 +124,25 @@ struct VolumetricLightingConstants
 	F32 m_maxZSplitsToProcessf;
 };
 
+// SSAO
+struct SsaoConstants
+{
+	F32 m_radius; ///< In meters.
+	U32 m_sampleCount;
+	Vec2 m_viewportSizef;
+
+	Vec4 m_unprojectionParameters;
+
+	F32 m_projectionMat00;
+	F32 m_projectionMat11;
+	F32 m_projectionMat22;
+	F32 m_projectionMat23;
+
+	Vec2 m_prevJitterUv;
+	F32 m_ssaoPower;
+	U32 m_frameCount;
+
+	Mat3x4 m_viewMat;
+};
+
 ANKI_END_NAMESPACE

+ 0 - 241
AnKi/Shaders/IndirectDiffuse.ankiprog

@@ -1,241 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-// Does SSGI and GI probe sampling
-
-#if defined(ANKI_COMPUTE_SHADER) || defined(ANKI_FRAGMENT_SHADER)
-#	include <AnKi/Shaders/Functions.hlsl>
-#	include <AnKi/Shaders/PackFunctions.hlsl>
-#	include <AnKi/Shaders/ImportanceSampling.hlsl>
-#	include <AnKi/Shaders/TonemappingFunctions.hlsl>
-#	include <AnKi/Shaders/Include/MiscRendererTypes.h>
-#	include <AnKi/Shaders/ClusteredShadingFunctions.hlsl>
-
-#	define ENABLE_SSGI true
-#	define ENABLE_PROBES true
-#	define REMOVE_FIREFLIES false
-#	define REPROJECT_LIGHTBUFFER false
-#	define SSGI_PROBE_COMBINE(ssgiColor, probeColor) ((ssgiColor) + (probeColor))
-
-[[vk::binding(0)]] ConstantBuffer<ClusteredShadingConstants> g_clusteredShading;
-[[vk::binding(1)]] StructuredBuffer<GlobalIlluminationProbe> g_giProbes;
-[[vk::binding(2)]] StructuredBuffer<Cluster> g_clusters;
-[[vk::binding(3)]] SamplerState g_linearAnyClampSampler;
-[[vk::binding(4)]] Texture2D<Vec4> g_gbufferRt2;
-[[vk::binding(5)]] Texture2D<Vec4> g_depthTex;
-[[vk::binding(6)]] Texture2D<RVec4> g_lightBufferRt;
-[[vk::binding(7)]] Texture2D<RVec4> g_historyTex;
-[[vk::binding(8)]] Texture2D<Vec4> g_motionVectorsTex;
-[[vk::binding(9)]] Texture2D<Vec4> g_historyLengthTex;
-
-#	if defined(ANKI_COMPUTE_SHADER)
-[[vk::binding(10)]] RWTexture2D<RVec4> g_outUav;
-#	endif
-
-ANKI_BINDLESS_SET(1)
-
-[[vk::push_constant]] ConstantBuffer<IndirectDiffuseConstants> g_consts;
-
-Vec4 cheapProject(Vec4 point_)
-{
-	return projectPerspective(point_, g_consts.m_projectionMat.x, g_consts.m_projectionMat.y, g_consts.m_projectionMat.z, g_consts.m_projectionMat.w);
-}
-
-#	if defined(ANKI_COMPUTE_SHADER)
-[numthreads(8, 8, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID)
-#	else
-RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITION) : SV_TARGET0
-#	endif
-{
-#	if defined(ANKI_COMPUTE_SHADER)
-	if(svDispatchThreadId.x >= g_consts.m_viewportSize.x || svDispatchThreadId.y >= g_consts.m_viewportSize.y)
-	{
-		return;
-	}
-
-	const Vec2 fragCoord = Vec2(svDispatchThreadId.xy) + 0.5;
-	const Vec2 uv = fragCoord / g_consts.m_viewportSizef;
-#	else
-	const Vec2 fragCoord = svPosition.xy;
-#	endif
-
-	const Vec2 ndc = uvToNdc(uv);
-
-	// Get normal
-	const Vec3 worldNormal = unpackNormalFromGBuffer(g_gbufferRt2.SampleLevel(g_linearAnyClampSampler, uv, 0.0));
-	const Vec3 viewNormal = mul(g_clusteredShading.m_matrices.m_view, Vec4(worldNormal, 0.0));
-
-	// Get origin
-	const F32 depth = g_depthTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).r;
-	Vec4 v4 = mul(g_clusteredShading.m_matrices.m_invertedViewProjectionJitter, Vec4(ndc, depth, 1.0));
-	const Vec3 worldPos = v4.xyz / v4.w;
-	v4 = mul(g_clusteredShading.m_matrices.m_invertedProjectionJitter, Vec4(ndc, depth, 1.0));
-	const Vec3 viewPos = v4.xyz / v4.w;
-
-	// SSGI
-	RVec3 outColor = Vec3(0.0, 0.0, 0.0);
-	RF32 ssao = 0.0;
-	if(ENABLE_SSGI)
-	{
-		// Find the projected radius
-		const RVec3 sphereLimit = viewPos + Vec3(g_consts.m_radius, 0.0, 0.0);
-		const RVec4 projSphereLimit = cheapProject(Vec4(sphereLimit, 1.0));
-		const RVec2 projSphereLimit2 = projSphereLimit.xy / projSphereLimit.w;
-		const RF32 projRadius = length(projSphereLimit2 - ndc);
-
-		// Loop to compute
-#	if defined(ANKI_COMPUTE_SHADER)
-		const UVec2 globalInvocation = svDispatchThreadId.xy;
-#	else
-		const UVec2 globalInvocation = UVec2(svPosition.xy);
-#	endif
-		const UVec2 random = rand3DPCG16(UVec3(globalInvocation, g_clusteredShading.m_frame)).xy;
-		const F32 aspectRatio = g_consts.m_viewportSizef.x / g_consts.m_viewportSizef.y;
-		for(U32 i = 0u; i < g_consts.m_sampleCount; ++i)
-		{
-			const Vec2 point_ = uvToNdc(hammersleyRandom16(i, g_consts.m_sampleCount, random)) * Vec2(1.0, aspectRatio);
-			const Vec2 finalDiskPoint = ndc + point_ * projRadius;
-
-			// Do a cheap unproject in view space
-			const F32 d = g_depthTex.SampleLevel(g_linearAnyClampSampler, ndcToUv(finalDiskPoint), 0.0).r;
-			const F32 z = g_clusteredShading.m_matrices.m_unprojectionParameters.z / (g_clusteredShading.m_matrices.m_unprojectionParameters.w + d);
-			const Vec2 xy = finalDiskPoint * g_clusteredShading.m_matrices.m_unprojectionParameters.xy * z;
-			const Vec3 s = Vec3(xy, z);
-
-			// Compute factor
-			const Vec3 dir = s - viewPos;
-			const F32 len = length(dir);
-			const Vec3 n = normalize(dir);
-			const F32 NoL = max(0.0, dot(viewNormal, n));
-			// const F32 distFactor = 1.0 - sin(min(1.0, len / g_consts.m_radius) * kPi / 2.0);
-			const F32 distFactor = 1.0 - min(1.0, len / g_consts.m_radius);
-
-			// Compute the UV for sampling the pyramid
-			const Vec2 crntFrameUv = ndcToUv(finalDiskPoint);
-			Vec2 lastFrameUv;
-			if(REPROJECT_LIGHTBUFFER)
-			{
-				lastFrameUv = crntFrameUv + g_motionVectorsTex.SampleLevel(g_linearAnyClampSampler, crntFrameUv, 0.0).xy;
-			}
-			else
-			{
-				lastFrameUv = crntFrameUv;
-			}
-
-			// Append color
-			const F32 w = distFactor * NoL;
-			const RVec3 c = g_lightBufferRt.SampleLevel(g_linearAnyClampSampler, lastFrameUv, 100.0).xyz;
-			outColor += c * w;
-
-			// Compute SSAO as well
-			ssao += max(dot(viewNormal, dir) + g_consts.m_ssaoBias, kEpsilonF32) / max(len * len, kEpsilonF32);
-		}
-
-		const RF32 scount = 1.0 / g_consts.m_sampleCountf;
-		outColor *= scount * 2.0 * kPi;
-		ssao *= scount;
-	}
-
-	ssao = min(1.0, 1.0 - ssao * g_consts.m_ssaoStrength);
-
-	if(ENABLE_PROBES)
-	{
-		// Sample probes
-
-		RVec3 probeColor = Vec3(0.0, 0.0, 0.0);
-
-		// Get the cluster
-		Cluster cluster = getClusterFragCoord(g_clusters, g_clusteredShading, Vec3(fragCoord * 2.0, depth));
-
-		const U32 oneProbe = WaveActiveAllTrue(countbits(cluster.m_giProbesMask) == 1);
-		if(oneProbe)
-		{
-			// All subgroups point to the same probe and there is only one probe, do a fast path without blend weight
-
-			const GlobalIlluminationProbe probe = g_giProbes[firstbitlow2(cluster.m_giProbesMask)];
-
-			// Sample
-			probeColor =
-				sampleGlobalIllumination(worldPos, worldNormal, probe, g_bindlessTextures3dF32[probe.m_volumeTexture], g_linearAnyClampSampler);
-		}
-		else
-		{
-			// Zero or more than one probes, do a slow path that blends them together
-
-			F32 totalBlendWeight = kEpsilonF32;
-
-			// Loop probes
-			[loop] while(cluster.m_giProbesMask != 0u)
-			{
-				const U32 idx = U32(firstbitlow2(cluster.m_giProbesMask));
-				cluster.m_giProbesMask &= ~(1u << idx);
-				const GlobalIlluminationProbe probe = g_giProbes[idx];
-
-				// Compute blend weight
-				const F32 blendWeight = computeProbeBlendWeight(worldPos, probe.m_aabbMin, probe.m_aabbMax, probe.m_fadeDistance);
-				totalBlendWeight += blendWeight;
-
-				// Sample
-				const RVec3 c = sampleGlobalIllumination(
-					worldPos, worldNormal, probe, g_bindlessTextures3dF32[NonUniformResourceIndex(probe.m_volumeTexture)], g_linearAnyClampSampler);
-				probeColor += c * blendWeight;
-			}
-
-			// Normalize
-			probeColor /= totalBlendWeight;
-		}
-
-		outColor = SSGI_PROBE_COMBINE(outColor, probeColor);
-	}
-
-	// Remove fireflies
-	if(REMOVE_FIREFLIES)
-	{
-		const F32 lum = computeLuminance(outColor) + 0.001;
-		const F32 averageLum = (WaveActiveSum(lum) / F32(WaveGetLaneCount())) * 2.0;
-		const F32 newLum = min(lum, averageLum);
-		outColor *= newLum / lum;
-	}
-
-	// Apply SSAO
-	outColor *= ssao;
-
-	// Blend color with history
-	{
-		const Vec2 historyUv = uv + g_motionVectorsTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).xy;
-		const F32 historyLength = g_historyLengthTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).x;
-
-		const F32 lowestBlendFactor = 0.05;
-		const F32 maxHistoryLength = 16.0;
-		const F32 stableFrames = 4.0;
-		const F32 lerpVal = min(1.0, (historyLength * maxHistoryLength - 1.0) / stableFrames);
-		const F32 blendFactor = lerp(1.0, lowestBlendFactor, lerpVal);
-
-		// Blend with history
-		if(blendFactor < 1.0)
-		{
-			const RVec3 history = g_historyTex.SampleLevel(g_linearAnyClampSampler, historyUv, 0.0).rgb;
-			outColor = lerp(history, outColor, blendFactor);
-		}
-	}
-
-	// Store color
-#	if defined(ANKI_COMPUTE_SHADER)
-	g_outUav[svDispatchThreadId.xy] = RVec4(outColor, 0.0f);
-#	else
-	return outColor;
-#	endif
-}
-#endif // defined(ANKI_COMPUTE_SHADER) || defined(ANKI_FRAGMENT_SHADER)
-
-#pragma anki technique_start vert
-#include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki technique_end vert
-
-#pragma anki technique_start frag
-#pragma anki technique_end frag
-
-#pragma anki technique_start comp
-#pragma anki technique_end comp

+ 0 - 100
AnKi/Shaders/IndirectDiffuseDenoise.ankiprog

@@ -1,100 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma anki mutator BLUR_ORIENTATION 0 1 // 0: in X axis, 1: in Y axis
-
-#include <AnKi/Shaders/Include/MiscRendererTypes.h>
-#include <AnKi/Shaders/PackFunctions.hlsl>
-#include <AnKi/Shaders/Functions.hlsl>
-#include <AnKi/Shaders/BilateralFilter.hlsl>
-
-#if defined(ANKI_COMPUTE_SHADER) || defined(ANKI_FRAGMENT_SHADER)
-[[vk::binding(0)]] SamplerState g_linearAnyClampSampler;
-[[vk::binding(1)]] Texture2D<RVec4> g_toDenoiseTex;
-[[vk::binding(2)]] Texture2D g_depthTex;
-
-#	if defined(ANKI_COMPUTE_SHADER)
-#		define THREAD_GROUP_SIZE_SQRT 8
-[[vk::binding(3)]] RWTexture2D<RVec4> g_outUav;
-#	endif
-
-[[vk::push_constant]] ConstantBuffer<IndirectDiffuseDenoiseConstants> g_consts;
-
-Vec3 unproject(Vec2 ndc, F32 depth)
-{
-	const Vec4 worldPos4 = mul(g_consts.m_invertedViewProjectionJitterMat, Vec4(ndc, depth, 1.0));
-	const Vec3 worldPos = worldPos4.xyz / worldPos4.w;
-	return worldPos;
-}
-
-#	if defined(ANKI_COMPUTE_SHADER)
-[numthreads(THREAD_GROUP_SIZE_SQRT, THREAD_GROUP_SIZE_SQRT, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID)
-#	else
-RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
-#	endif
-{
-#	if defined(ANKI_COMPUTE_SHADER)
-	if(skipOutOfBoundsInvocations(UVec2(THREAD_GROUP_SIZE_SQRT, THREAD_GROUP_SIZE_SQRT), g_consts.m_viewportSize, svDispatchThreadId))
-	{
-		return;
-	}
-
-	const Vec2 uv = (Vec2(svDispatchThreadId.xy) + 0.5) / g_consts.m_viewportSizef;
-#	endif
-
-	// Reference
-	const F32 depthCenter = g_depthTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).r;
-	if(depthCenter == 1.0)
-	{
-#	if defined(ANKI_COMPUTE_SHADER)
-		g_outUav[svDispatchThreadId.xy] = 0.0f;
-		return;
-#	else
-		return 0.0f;
-#	endif
-	}
-
-	// Sample
-	RF32 weight = kEpsilonRF32;
-	Vec3 color = Vec3(0.0, 0.0, 0.0); // Keep it full precision because it may go to inf
-
-	for(F32 i = -g_consts.m_sampleCountDiv2; i <= g_consts.m_sampleCountDiv2; i += 1.0)
-	{
-		const Vec2 texelSize = 1.0 / g_consts.m_viewportSizef;
-#	if BLUR_ORIENTATION == 0
-		const Vec2 sampleUv = Vec2(uv.x + i * texelSize.x, uv.y);
-#	else
-		const Vec2 sampleUv = Vec2(uv.x, uv.y + i * texelSize.y);
-#	endif
-
-		const F32 depthTap = g_depthTex.SampleLevel(g_linearAnyClampSampler, sampleUv, 0.0).r;
-
-		RF32 w = calculateBilateralWeightDepth(depthCenter, depthTap, 1.0);
-		// w *= gaussianWeight(0.4, abs(F32(i)) / (g_consts.m_sampleCountDiv2 * 2.0 + 1.0));
-		weight += w;
-
-		color += g_toDenoiseTex.SampleLevel(g_linearAnyClampSampler, sampleUv, 0.0).xyz * w;
-	}
-
-	// Normalize and store
-	color /= weight;
-
-#	if defined(ANKI_COMPUTE_SHADER)
-	g_outUav[svDispatchThreadId.xy] = RVec4(color, 0.0f);
-#	else
-	return color;
-#	endif
-}
-#endif // defined(ANKI_COMPUTE_SHADER) || defined(ANKI_FRAGMENT_SHADER)
-
-#pragma anki technique_start vert
-#include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki technique_end vert
-
-#pragma anki technique_start frag
-#pragma anki technique_end frag
-
-#pragma anki technique_start comp
-#pragma anki technique_end comp

+ 0 - 167
AnKi/Shaders/IndirectDiffuseVrsSriGeneration.ankiprog

@@ -1,167 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma anki mutator SRI_TEXEL_DIMENSION 8 16
-#pragma anki mutator SHARED_MEMORY 0 1
-#pragma anki mutator LIMIT_RATE_TO_2X2 0 1
-
-#pragma anki technique_start comp
-
-#include <AnKi/Shaders/Functions.hlsl>
-
-[[vk::binding(0)]] Texture2D g_inputTex;
-[[vk::binding(1)]] SamplerState g_nearestClampSampler;
-
-#if SRI_TEXEL_DIMENSION == 8
-#	define REGION_SIZE_X 2
-#	define REGION_SIZE_Y 2
-#else
-#	define REGION_SIZE_X 2
-#	define REGION_SIZE_Y 4
-#endif
-
-#define THREAD_GROUP_SIZE_X (SRI_TEXEL_DIMENSION / REGION_SIZE_X)
-#define THREAD_GROUP_SIZE_Y (SRI_TEXEL_DIMENSION / REGION_SIZE_Y)
-
-[[vk::binding(2)]] RWTexture2D<UVec4> g_sriImg;
-
-struct Constants
-{
-	Vec2 m_oneOverViewportSize;
-	F32 m_thresholdMeters;
-	F32 m_padding0;
-	Mat4 m_invertedProjectionJitter;
-};
-
-[[vk::push_constant]] ConstantBuffer<Constants> g_consts;
-
-#if SHARED_MEMORY
-// Ideally, we'd be able to calculate the min/max/average using subgroup operations, but there's no guarantee
-// subgroupSize is large enough so we need shared memory as a fallback. We need gl_NumSubgroups entries, but it is not a
-// constant, so estimate it assuming a subgroupSize of at least 8.
-constexpr U32 kSharedMemoryEntries = THREAD_GROUP_SIZE_X * THREAD_GROUP_SIZE_Y / 8u;
-groupshared Vec2 s_maxDerivative[kSharedMemoryEntries];
-groupshared U32 s_waveIndexInsideThreadGroup;
-#endif
-
-F32 sampleViewPositionZ(Vec2 uv, I32 offsetX, I32 offsetY)
-{
-	uv += Vec2(offsetX, offsetY) * g_consts.m_oneOverViewportSize;
-	const Vec2 ndc = uvToNdc(uv);
-	const F32 depth = g_inputTex.SampleLevel(g_nearestClampSampler, uv, 0.0).x;
-
-	const Vec4 v4 = mul(g_consts.m_invertedProjectionJitter, Vec4(ndc, depth, 1.0));
-	return v4.z / v4.w;
-}
-
-[numthreads(THREAD_GROUP_SIZE_X, THREAD_GROUP_SIZE_Y, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID, U32 svGroupIndex : SV_GROUPINDEX,
-																	UVec3 svGroupId : SV_GROUPID)
-{
-#if SHARED_MEMORY
-	U32 wavesPerThreadGroup;
-	U32 waveIndexInsideThreadGroup;
-	ANKI_COMPUTE_WAVE_INDEX_INSIDE_THREADGROUP(svGroupIndex, s_waveIndexInsideThreadGroup, waveIndexInsideThreadGroup, wavesPerThreadGroup);
-#endif
-
-	const Vec2 uv = (Vec2(svDispatchThreadId.xy) * Vec2(REGION_SIZE_X, REGION_SIZE_Y) + 0.5) * g_consts.m_oneOverViewportSize;
-
-#if SRI_TEXEL_DIMENSION == 8
-	// Get positions
-	// l0.z  l0.w
-	// l0.x  l0.y
-	Vec4 l0;
-	l0.x = sampleViewPositionZ(uv, 0, 0);
-	l0.y = sampleViewPositionZ(uv, 1, 0);
-	l0.z = sampleViewPositionZ(uv, 0, 1);
-	l0.w = sampleViewPositionZ(uv, 1, 1);
-
-	// Calculate derivatives.
-	Vec2 a = Vec2(l0.y, l0.z);
-	Vec2 b = Vec2(l0.x, l0.w);
-	const Vec2 dx = abs(a - b);
-
-	a = Vec2(l0.z, l0.w);
-	b = Vec2(l0.x, l0.y);
-	const Vec2 dy = abs(a - b);
-
-	F32 maxDerivativeX = max(dx.x, dx.y);
-	F32 maxDerivativeY = max(dy.x, dy.y);
-#else
-	// Get positions
-	// l1.z  l1.w
-	// l1.x  l1.y
-	// l0.z  l0.w
-	// l0.x  l0.y
-	Vec4 l0;
-	l0.x = sampleViewPositionZ(uv, 0, 0);
-	l0.y = sampleViewPositionZ(uv, 1, 0);
-	l0.z = sampleViewPositionZ(uv, 0, 1);
-	l0.w = sampleViewPositionZ(uv, 1, 1);
-
-	Vec4 l1;
-	l1.x = sampleViewPositionZ(uv, 0, 2);
-	l1.y = sampleViewPositionZ(uv, 1, 2);
-	l1.z = sampleViewPositionZ(uv, 0, 3);
-	l1.w = sampleViewPositionZ(uv, 1, 3);
-
-	// Calculate derivatives.
-	Vec4 a = Vec4(l0.y, l0.z, l1.y, l1.z);
-	Vec4 b = Vec4(l0.x, l0.w, l1.x, l1.w);
-	const Vec4 dx = abs(a - b);
-
-	a = Vec4(l0.z, l0.w, l1.z, l1.y);
-	b = Vec4(l0.x, l0.y, l1.x, l1.w);
-	const Vec4 dy = abs(a - b);
-
-	F32 maxDerivativeX = max(max(dx.x, dx.y), max(dx.z, dx.w));
-	F32 maxDerivativeY = max(max(dy.x, dy.y), max(dy.z, dy.w));
-#endif
-
-	maxDerivativeX = WaveActiveMax(maxDerivativeX);
-	maxDerivativeY = WaveActiveMax(maxDerivativeY);
-
-#if SHARED_MEMORY
-	// Store results in shared memory.
-	[branch] if(WaveIsFirstLane())
-	{
-		s_maxDerivative[waveIndexInsideThreadGroup] = Vec2(maxDerivativeX, maxDerivativeY);
-	}
-
-	GroupMemoryBarrierWithGroupSync();
-#endif
-
-	// Write the result
-	[branch] if(svGroupIndex == 0u)
-	{
-		// Get max across all subgroups.
-#if SHARED_MEMORY
-		Vec2 maxDerivative = s_maxDerivative[0];
-
-		for(U32 i = 1u; i < wavesPerThreadGroup; ++i)
-		{
-			maxDerivative = max(maxDerivative, s_maxDerivative[i]);
-		}
-#else
-		const Vec2 maxDerivative = Vec2(maxDerivativeX, maxDerivativeY);
-#endif
-
-		// Determine shading rate.
-		const F32 threshold1 = g_consts.m_thresholdMeters;
-		const F32 threshold2 = threshold1 * 0.4;
-
-		UVec2 rate;
-		rate.x = (maxDerivative.x > threshold1) ? 1u : ((maxDerivative.x > threshold2) ? 2u : 4u);
-		rate.y = (maxDerivative.y > threshold1) ? 1u : ((maxDerivative.y > threshold2) ? 2u : 4u);
-
-#if LIMIT_RATE_TO_2X2
-		rate = min(rate, UVec2(2u, 2u));
-#endif
-
-		const UVec2 outTexelCoord = svGroupId.xy;
-		g_sriImg[outTexelCoord] = UVec4(encodeVrsRate(rate), 0, 0, 0);
-	}
-}
-
-#pragma anki technique_end comp

+ 2 - 0
AnKi/Shaders/Intellisense.hlsl

@@ -216,6 +216,8 @@ U32 asuint(float f);
 
 F32 asfloat(U32 u);
 
+I32 asint(float u);
+
 U32 NonUniformResourceIndex(U32 x);
 
 template<typename T>

+ 66 - 12
AnKi/Shaders/LightShading.ankiprog

@@ -16,17 +16,21 @@
 [[vk::binding(0)]] ConstantBuffer<ClusteredShadingConstants> g_clusteredShading;
 [[vk::binding(1)]] StructuredBuffer<PointLight> g_pointLights;
 [[vk::binding(1)]] StructuredBuffer<SpotLight> g_spotLights;
-[[vk::binding(2)]] Texture2D<Vec4> g_shadowAtlasTex;
-[[vk::binding(3)]] StructuredBuffer<Cluster> g_clusters;
+[[vk::binding(2)]] StructuredBuffer<GlobalIlluminationProbe> g_giProbes;
+[[vk::binding(3)]] Texture2D<Vec4> g_shadowAtlasTex;
+[[vk::binding(4)]] StructuredBuffer<Cluster> g_clusters;
 
-[[vk::binding(4)]] SamplerState g_nearestAnyClampSampler;
-[[vk::binding(5)]] SamplerState g_trilinearClampSampler;
+[[vk::binding(5)]] SamplerState g_nearestAnyClampSampler;
+[[vk::binding(6)]] SamplerState g_trilinearClampSampler;
 
-[[vk::binding(6)]] Texture2D<Vec4> g_gbuffer0Tex;
-[[vk::binding(7)]] Texture2D<Vec4> g_gbuffer1Tex;
-[[vk::binding(8)]] Texture2D<Vec4> g_gbuffer2Tex;
-[[vk::binding(9)]] Texture2D g_depthTex;
-[[vk::binding(10)]] Texture2D<RVec4> g_resolvedShadowsTex;
+[[vk::binding(7)]] Texture2D<Vec4> g_gbuffer0Tex;
+[[vk::binding(8)]] Texture2D<Vec4> g_gbuffer1Tex;
+[[vk::binding(9)]] Texture2D<Vec4> g_gbuffer2Tex;
+[[vk::binding(10)]] Texture2D g_depthTex;
+[[vk::binding(11)]] Texture2D<RVec4> g_resolvedShadowsTex;
+[[vk::binding(12)]] Texture2D<RVec4> g_ssaoTex;
+
+ANKI_BINDLESS_SET(1)
 
 // Common code for lighting
 #define LIGHTING_COMMON_BRDF() \
@@ -63,13 +67,63 @@ RVec3 main(Vec4 svPosition : SV_POSITION, Vec2 uv : TEXCOORD) : SV_TARGET0
 							g_gbuffer2Tex.SampleLevel(g_nearestAnyClampSampler, uv, 0.0), gbuffer);
 	gbuffer.m_subsurface = max(gbuffer.m_subsurface, kSubsurfaceMin);
 
-	// SM
-	RVec4 resolvedSm = g_resolvedShadowsTex.SampleLevel(g_trilinearClampSampler, uv, 0.0);
-	U32 resolvedSmIdx = 0u;
+	// Apply SSAO
+	const RF32 ssao = g_ssaoTex.SampleLevel(g_nearestAnyClampSampler, uv, 0.0f).x;
+	gbuffer.m_diffuse *= ssao;
 
 	// Ambient and emissive color
 	RVec3 outColor = gbuffer.m_emission;
 
+	// Indirect diffuse
+	{
+		RVec3 probeColor = Vec3(0.0, 0.0, 0.0);
+
+		const U32 oneProbe = WaveActiveAllTrue(countbits(cluster.m_giProbesMask) == 1);
+		if(oneProbe)
+		{
+			// All subgroups point to the same probe and there is only one probe, do a fast path without blend weight
+
+			const GlobalIlluminationProbe probe = g_giProbes[firstbitlow2(cluster.m_giProbesMask)];
+
+			// Sample
+			probeColor =
+				sampleGlobalIllumination(worldPos, gbuffer.m_normal, probe, g_bindlessTextures3dF32[probe.m_volumeTexture], g_trilinearClampSampler);
+		}
+		else
+		{
+			// Zero or more than one probes, do a slow path that blends them together
+
+			F32 totalBlendWeight = kEpsilonF32;
+
+			// Loop probes
+			[loop] while(cluster.m_giProbesMask != 0u)
+			{
+				const U32 idx = U32(firstbitlow2(cluster.m_giProbesMask));
+				cluster.m_giProbesMask &= ~(1u << idx);
+				const GlobalIlluminationProbe probe = g_giProbes[idx];
+
+				// Compute blend weight
+				const F32 blendWeight = computeProbeBlendWeight(worldPos, probe.m_aabbMin, probe.m_aabbMax, probe.m_fadeDistance);
+				totalBlendWeight += blendWeight;
+
+				// Sample
+				const RVec3 c =
+					sampleGlobalIllumination(worldPos, gbuffer.m_normal, probe,
+											 g_bindlessTextures3dF32[NonUniformResourceIndex(probe.m_volumeTexture)], g_trilinearClampSampler);
+				probeColor += c * blendWeight;
+			}
+
+			// Normalize
+			probeColor /= totalBlendWeight;
+		}
+
+		outColor += probeColor * gbuffer.m_diffuse;
+	}
+
+	// SM
+	RVec4 resolvedSm = g_resolvedShadowsTex.SampleLevel(g_trilinearClampSampler, uv, 0.0);
+	U32 resolvedSmIdx = 0u;
+
 	// Dir light
 	const RVec3 viewDir = normalize(g_clusteredShading.m_cameraPosition - worldPos);
 	const DirectionalLight dirLight = g_clusteredShading.m_directionalLight;

+ 1 - 12
AnKi/Shaders/LightShadingApplyIndirect.ankiprog

@@ -16,7 +16,6 @@
 
 [[vk::binding(0)]] SamplerState g_nearestAnyClampSampler;
 [[vk::binding(1)]] SamplerState g_linearAnyClampSampler;
-[[vk::binding(2)]] Texture2D<RVec4> g_quarterDiffuseIndirectTex;
 [[vk::binding(3)]] Texture2D<RVec4> g_quarterSpecularIndirectTex;
 [[vk::binding(4)]] Texture2D<Vec4> g_quarterDepthTex;
 [[vk::binding(5)]] Texture2D<Vec4> g_fullDepthTex;
@@ -64,21 +63,15 @@ RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 	const F32 depthThreshold = 0.2 / (g_consts.m_far - g_consts.m_near);
 
 	// Do a neareset depth upscale
-	RVec3 diffuse = RVec3(0.0, 0.0, 0.0);
 	RVec3 specular = RVec3(0.0, 0.0, 0.0);
 	if(maxDiff <= depthThreshold)
 	{
-		diffuse = g_quarterDiffuseIndirectTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).xyz;
 		specular = g_quarterSpecularIndirectTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).xyz;
 	}
 	else
 	{
 		// Some discontinuites, need to pick the one closest to depth reference
 
-		const RVec4 diffuseR = g_quarterDiffuseIndirectTex.GatherRed(g_linearAnyClampSampler, uv);
-		const RVec4 diffuseG = g_quarterDiffuseIndirectTex.GatherGreen(g_linearAnyClampSampler, uv);
-		const RVec4 diffuseB = g_quarterDiffuseIndirectTex.GatherBlue(g_linearAnyClampSampler, uv);
-
 		const RVec4 specularR = g_quarterSpecularIndirectTex.GatherRed(g_linearAnyClampSampler, uv);
 		const RVec4 specularG = g_quarterSpecularIndirectTex.GatherGreen(g_linearAnyClampSampler, uv);
 		const RVec4 specularB = g_quarterSpecularIndirectTex.GatherBlue(g_linearAnyClampSampler, uv);
@@ -98,13 +91,9 @@ RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 			minDiff = diffs.z;
 		}
 
-		diffuse = Vec3(diffuseR[comp], diffuseG[comp], diffuseB[comp]);
 		specular = Vec3(specularR[comp], specularG[comp], specularB[comp]);
 	}
 
-	// Do diffuse
-	diffuse *= gbuffer.m_diffuse;
-
 	// Do specular
 	const Vec2 ndc = uvToNdc(uv);
 	const Vec4 worldPos4 = mul(g_clusteredShading.m_matrices.m_invertedViewProjectionJitter, Vec4(ndc, depthCenter, 1.0));
@@ -115,6 +104,6 @@ RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 	specular *= env;
 
 	// Writeout
-	return min(diffuse + specular, RVec3(kMaxRF32, kMaxRF32, kMaxRF32));
+	return min(specular, RVec3(kMaxRF32, kMaxRF32, kMaxRF32));
 }
 #pragma anki technique_end frag

+ 296 - 0
AnKi/Shaders/Ssao.ankiprog

@@ -0,0 +1,296 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+// Ground truth ambiend occlusion
+
+#pragma anki mutator SAMPLE_COUNT 3 5 7 9 11 13 15
+
+#include <AnKi/Shaders/Common.hlsl>
+
+// ===========================================================================
+// SSAO                                                                      =
+// ===========================================================================
+#if defined(ANKI_TECHNIQUE_Ssao) && (defined(ANKI_COMPUTE_SHADER) || defined(ANKI_FRAGMENT_SHADER))
+#	include <AnKi/Shaders/Include/MiscRendererTypes.h>
+#	include <AnKi/Shaders/Functions.hlsl>
+#	include <AnKi/Shaders/PackFunctions.hlsl>
+#	include <AnKi/Shaders/FastMathFunctions.hlsl>
+#	include <AnKi/Shaders/ImportanceSampling.hlsl>
+
+[[vk::binding(0)]] Texture2D<Vec4> g_gbufferRt2;
+[[vk::binding(1)]] Texture2D<Vec4> g_depthTex;
+
+[[vk::binding(2)]] Texture2D<RVec4> g_noiseTex;
+[[vk::binding(3)]] SamplerState g_trilinearRepeatSampler;
+[[vk::binding(4)]] SamplerState g_linearAnyClampSampler;
+
+[[vk::binding(5)]] Texture2D<RVec4> g_historyTex;
+[[vk::binding(6)]] Texture2D<Vec4> g_motionVectorsTex;
+[[vk::binding(7)]] Texture2D<Vec4> g_historyLengthTex;
+
+#	if defined(ANKI_COMPUTE_SHADER)
+[[vk::binding(8)]] RWTexture2D<RVec4> g_outUav;
+#	endif
+
+[[vk::push_constant]] ConstantBuffer<SsaoConstants> g_consts;
+
+Vec3 unproject(Vec2 ndc)
+{
+	const F32 d = g_depthTex.SampleLevel(g_linearAnyClampSampler, ndcToUv(ndc), 0.0).r;
+	const F32 z = g_consts.m_unprojectionParameters.z / (g_consts.m_unprojectionParameters.w + d);
+	const Vec2 xy = ndc * g_consts.m_unprojectionParameters.xy * z;
+	return Vec3(xy, z);
+}
+
+Vec4 project(Vec4 p)
+{
+	return projectPerspective(p, g_consts.m_projectionMat00, g_consts.m_projectionMat11, g_consts.m_projectionMat22, g_consts.m_projectionMat23);
+}
+
+F32 computeFalloff(F32 len)
+{
+	return sqrt(1.0f - min(1.0f, len / g_consts.m_radius));
+}
+
+#	if defined(ANKI_COMPUTE_SHADER)
+[numthreads(8, 8, 1)] void main(UVec2 svDispatchThreadId : SV_DISPATCHTHREADID)
+#	else
+RF32 main([[vk::location(0)]] Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITION) : SV_TARGET0
+#	endif
+{
+#	if defined(ANKI_COMPUTE_SHADER)
+	const Vec2 uv = (Vec2(svDispatchThreadId) + 0.5) / g_consts.m_viewportSizef;
+#	else
+	const UVec2 svDispatchThreadId = svPosition;
+	ANKI_MAYBE_UNUSED(svDispatchThreadId);
+#	endif
+
+	const Vec2 ndc = uvToNdc(uv);
+	const Vec3 Pc = unproject(ndc);
+	const Vec3 V = normalize(-Pc); // View vector
+
+	// Get noise
+#	if 0
+	Vec2 noiseTexSize;
+	g_noiseTex.GetDimensions(noiseTexSize.x, noiseTexSize.y);
+	const Vec2 noiseUv = Vec2(g_consts.m_viewportSizef) / noiseTexSize * uv;
+	const Vec2 noise2 = animateBlueNoise(g_noiseTex.SampleLevel(g_trilinearRepeatSampler, noiseUv, 0.0).xyz, g_consts.m_frameCount).yx;
+#	else
+	const Vec2 noise2 = spatioTemporalNoise(svDispatchThreadId, g_consts.m_frameCount);
+#	endif
+
+	// Rand slice direction
+	const F32 randAng = noise2.x * kPi;
+#	if 0
+	const F32 aspect = g_consts.m_viewportSizef.x / g_consts.m_viewportSizef.y;
+	const Vec2 dir2d = normalize(Vec2(cos(randAng), sin(randAng)) * Vec2(1.0f, aspect));
+#	else
+	const Vec2 dir2d = Vec2(cos(randAng), sin(randAng));
+#	endif
+
+	// Project the view normal to the slice
+	const Vec3 worldNormal = unpackNormalFromGBuffer(g_gbufferRt2.SampleLevel(g_linearAnyClampSampler, uv, 0.0));
+	const Vec3 viewNormal = mul(g_consts.m_viewMat, Vec4(worldNormal, 0.0));
+
+	const Vec3 directionVec = Vec3(dir2d, 0.0f);
+	const Vec3 orthoDirectionVec = directionVec - (dot(directionVec, V) * V);
+	const Vec3 axisVec = normalize(cross(orthoDirectionVec, V));
+	const Vec3 projectedNormalVec = viewNormal - axisVec * dot(viewNormal, axisVec);
+	const F32 signNorm = (F32)sign(dot(orthoDirectionVec, projectedNormalVec));
+	const F32 projectedNormalVecLength = length(projectedNormalVec);
+	const F32 cosNorm = saturate(dot(projectedNormalVec, V) / projectedNormalVecLength);
+	const F32 n = -signNorm * fastAcos(cosNorm);
+
+	// Find the projected radius
+	const Vec3 sphereLimit = Pc + Vec3(g_consts.m_radius, 0.0, 0.0);
+	const Vec4 projSphereLimit = project(Vec4(sphereLimit, 1.0));
+	const Vec2 projSphereLimit2 = projSphereLimit.xy / projSphereLimit.w;
+	const F32 projRadius = length(projSphereLimit2 - ndc);
+
+	// Compute the inner integral (Slide 54)
+	const U32 stepCount = max(1u, g_consts.m_sampleCount / 2u);
+
+	const F32 lowHorizonCos1 = cos(n - kPi / 2.0f);
+	const F32 lowHorizonCos2 = cos(n + kPi / 2.0f);
+
+	F32 cosH1 = lowHorizonCos1;
+	F32 cosH2 = lowHorizonCos2;
+
+	for(U32 i = 0u; i < stepCount; ++i)
+	{
+		const F32 stepBaseNoise = F32(i * stepCount) * 0.6180339887498948482;
+		const F32 stepNoise = frac(noise2.y + stepBaseNoise);
+		F32 s = (i + stepNoise) / F32(stepCount);
+		s *= s;
+		const Vec2 sampleOffset = dir2d * projRadius * s;
+
+		// h1
+		const Vec3 Ps = unproject(ndc + sampleOffset);
+		const Vec3 Ds = Ps - Pc;
+		const F32 DsLen = length(Ds);
+		cosH1 = max(cosH1, lerp(lowHorizonCos1, dot(V, Ds) / DsLen, computeFalloff(DsLen)));
+
+		// h2
+		const Vec3 Pt = unproject(ndc - sampleOffset);
+		const Vec3 Dt = Pt - Pc;
+		const F32 DtLen = length(Dt);
+		cosH2 = max(cosH2, lerp(lowHorizonCos2, dot(V, Dt) / DtLen, computeFalloff(DtLen)));
+	}
+
+	// Compute the h1 and h2
+	const F32 h1 = n + max(-fastAcos(cosH1) - n, -kPi / 2);
+	const F32 h2 = n + min(fastAcos(cosH2) - n, kPi / 2);
+
+	// Compute the final value (Slide 61)
+	F32 Vd = -cos(2.0f * h1 - n) + cos(n) + 2.0f * h1 * sin(n);
+	Vd += -cos(2.0f * h2 - n) + cos(n) + 2.0f * h2 * sin(n);
+	Vd *= 0.25;
+	Vd *= projectedNormalVecLength;
+
+	// Apply power
+	Vd = pow(Vd, g_consts.m_ssaoPower);
+
+	// Blend color with history
+	{
+		const Vec2 historyUv = uv + g_motionVectorsTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0f).xy;
+
+		// History length creates black trails so it doesn't work correctly
+#	if 0
+		const Vec4 historyLengths = g_historyLengthTex.GatherRed(g_linearAnyClampSampler, uv + g_consts.m_prevJitterUv);
+		const F32 historyLength = max4(historyLengths);
+#	else
+		const F32 historyLength = (any(historyUv < 0.0f) || any(historyUv > 1.0f)) ? 0.0f : 1.0f;
+#	endif
+
+		const F32 lowestBlendFactor = 0.1f;
+		const F32 maxHistoryLength = 16.0f;
+		const F32 stableFrames = 4.0f;
+		const F32 lerpVal = min(1.0f, (historyLength * maxHistoryLength - 1.0f) / stableFrames);
+		const F32 blendFactor = lerp(1.0f, lowestBlendFactor, lerpVal);
+
+		// Blend with history
+		if(blendFactor < 1.0)
+		{
+			const F32 history = g_historyTex.SampleLevel(g_linearAnyClampSampler, historyUv, 0.0f).r;
+			Vd = lerp(history, Vd, blendFactor);
+		}
+	}
+
+#	if defined(ANKI_COMPUTE_SHADER)
+	g_outUav[svDispatchThreadId] = Vd;
+#	else
+	return Vd;
+#	endif
+}
+#endif // defined(ANKI_TECHNIQUE_Ssao) && (defined(ANKI_COMPUTE_SHADER) || defined(ANKI_FRAGMENT_SHADER))
+
+// ===========================================================================
+// SSAO denoise                                                              =
+// ===========================================================================
+#if(defined(ANKI_TECHNIQUE_SsaoDenoiseVertical) || defined(ANKI_TECHNIQUE_SsaoDenoiseHorizontal)) \
+	&& (defined(ANKI_COMPUTE_SHADER) || defined(ANKI_FRAGMENT_SHADER))
+#	include <AnKi/Shaders/BilateralFilter.hlsl>
+
+[[vk::binding(0)]] SamplerState g_linearAnyClampSampler;
+[[vk::binding(1)]] Texture2D<Vec4> g_inTex;
+[[vk::binding(2)]] Texture2D<Vec4> g_depthTex;
+
+#	if defined(ANKI_COMPUTE_SHADER)
+[[vk::binding(3)]] RWTexture2D<Vec4> g_outImg;
+#	endif
+
+F32 readDepth(Vec2 uv)
+{
+	return g_depthTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).x;
+}
+
+void sampleTex(Vec2 uv, F32 refDepth, inout F32 col, inout F32 weight)
+{
+	const F32 color = g_inTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).x;
+	const F32 w = calculateBilateralWeightDepth(refDepth, readDepth(uv), 1.0f);
+	col += color * w;
+	weight += w;
+}
+
+#	if defined(ANKI_COMPUTE_SHADER)
+[numthreads(8, 8, 1)] void main(UVec2 svDispatchThreadId : SV_DISPATCHTHREADID)
+#	else
+F32 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
+#	endif
+{
+	UVec2 textureSize;
+	U32 mipCount;
+	g_inTex.GetDimensions(0, textureSize.x, textureSize.y, mipCount);
+
+	// Set UVs
+#	if defined(ANKI_COMPUTE_SHADER)
+	const Vec2 uv = (Vec2(svDispatchThreadId) + 0.5) / Vec2(textureSize);
+#	endif
+
+	const Vec2 texelSize = 1.0 / Vec2(textureSize);
+
+	// Sample
+	F32 color = g_inTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).r;
+	const F32 refDepth = readDepth(uv);
+	F32 weight = 1.0;
+
+#	if defined(ANKI_TECHNIQUE_SsaoDenoiseHorizontal)
+#		define X_OR_Y x
+#	else
+#		define X_OR_Y y
+#	endif
+
+	Vec2 uvOffset = 0.0f;
+	uvOffset.X_OR_Y = 1.0f * texelSize.X_OR_Y;
+
+	[unroll] for(U32 i = 0u; i < (SAMPLE_COUNT - 1u) / 2u; ++i)
+	{
+		sampleTex(uv + uvOffset, refDepth, color, weight);
+		sampleTex(uv - uvOffset, refDepth, color, weight);
+
+		uvOffset.X_OR_Y += 1.0f * texelSize.X_OR_Y;
+	}
+
+	color /= weight;
+
+	// Write value
+#	if defined(ANKI_COMPUTE_SHADER)
+	g_outImg[svDispatchThreadId] = color;
+#	else
+	return color;
+#	endif
+}
+#endif // (defined(ANKI_TECHNIQUE_SsaoDenoiseVertical) || defined(ANKI_TECHNIQUE_SsaoDenoiseHorizontal)) && (defined(ANKI_COMPUTE_SHADER) ||
+	   // defined(ANKI_FRAGMENT_SHADER))
+
+#pragma anki technique_start vert Ssao uses_mutators
+#include <AnKi/Shaders/QuadVert.hlsl>
+#pragma anki technique_end vert Ssao
+
+#pragma anki technique_start frag Ssao uses_mutators
+#pragma anki technique_end frag Ssao
+
+#pragma anki technique_start comp Ssao
+#pragma anki technique_end comp Ssao
+
+#pragma anki technique_start vert SsaoDenoiseVertical
+#include <AnKi/Shaders/QuadVert.hlsl>
+#pragma anki technique_end vert SsaoDenoiseVertical
+
+#pragma anki technique_start frag SsaoDenoiseVertical
+#pragma anki technique_end frag SsaoDenoiseVertical
+
+#pragma anki technique_start comp SsaoDenoiseVertical
+#pragma anki technique_end comp SsaoDenoiseVertical
+
+#pragma anki technique_start vert SsaoDenoiseHorizontal
+#include <AnKi/Shaders/QuadVert.hlsl>
+#pragma anki technique_end vert SsaoDenoiseHorizontal
+
+#pragma anki technique_start frag SsaoDenoiseHorizontal
+#pragma anki technique_end frag SsaoDenoiseHorizontal
+
+#pragma anki technique_start comp SsaoDenoiseHorizontal
+#pragma anki technique_end comp SsaoDenoiseHorizontal

+ 1 - 1
Samples/Common/SampleApp.cpp

@@ -73,7 +73,7 @@ Error SampleApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::kI) == 1)
 	{
-		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "SSR") ? "" : "SSR");
+		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "Ssao") ? "" : "Ssao");
 	}
 
 	if(in.getKey(KeyCode::kO) == 1)

+ 2 - 2
Sandbox/Main.cpp

@@ -347,7 +347,7 @@ Error MyApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::kI) == 1)
 	{
-		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "SSR") ? "" : "SSR");
+		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "Ssao") ? "" : "Ssao");
 	}
 
 	if(in.getKey(KeyCode::kO) == 1)
@@ -357,7 +357,7 @@ Error MyApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::kH) == 1)
 	{
-		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "GBufferAlbedo") ? "" : "GBufferAlbedo");
+		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "LightShading") ? "" : "LightShading");
 	}
 
 	/*if(in.getKey(KeyCode::J) == 1)