Prechádzať zdrojové kódy

Move SSAO to the render graph

Panagiotis Christopoulos Charitos 8 rokov pred
rodič
commit
1e8c6b049f

+ 1 - 2
src/anki/renderer/LightShading.cpp

@@ -161,8 +161,7 @@ void LightShading::run(RenderingContext& ctx)
 	// TODO cmdb->bindTexture(1, 1, m_r->getGBuffer().m_rt1);
 	// TODO cmdb->bindTexture(1, 2, m_r->getGBuffer().m_rt2);
 	// TODO cmdb->bindTexture(1, 3, m_r->getGBuffer().m_depthRt, DepthStencilAspectBit::DEPTH);
-	cmdb->informTextureCurrentUsage(m_r->getSsao().getRt(), TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->bindTexture(1, 4, m_r->getSsao().getRt());
+	// TODO cmdb->bindTexture(1, 4, m_r->getSsao().getRt());
 
 	// TODO: cmdb->bindTexture(0, 0, m_r->getShadowMapping().m_shadowAtlas);
 	// TODO: cmdb->bindTexture(0, 1, m_r->getIndirect().getReflectionTexture());

+ 129 - 143
src/anki/renderer/Ssao.cpp

@@ -16,172 +16,68 @@ namespace anki
 
 const PixelFormat Ssao::RT_PIXEL_FORMAT(ComponentFormat::R8, TransformFormat::UNORM);
 
-Error SsaoMain::init(const ConfigSet& config)
+Ssao::~Ssao()
+{
+}
+
+Error Ssao::initMain(const ConfigSet& config)
 {
 	// Noise
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_main.m_noiseTex));
 
 	// Shader
-	ANKI_CHECK(getResourceManager().loadResource("programs/Ssao.ankiprog", m_prog));
+	ANKI_CHECK(getResourceManager().loadResource("programs/Ssao.ankiprog", m_main.m_prog));
 
-	ShaderProgramResourceConstantValueInitList<6> consts(m_prog);
-	consts.add("NOISE_MAP_SIZE", U32(m_noiseTex->getWidth()))
-		.add("FB_SIZE", UVec2(m_ssao->m_width, m_ssao->m_height))
+	ShaderProgramResourceConstantValueInitList<6> consts(m_main.m_prog);
+	consts.add("NOISE_MAP_SIZE", U32(m_main.m_noiseTex->getWidth()))
+		.add("FB_SIZE", UVec2(m_width, m_height))
 		.add("RADIUS", 3.0f)
 		.add("BIAS", 0.0f)
 		.add("STRENGTH", 2.0f)
 		.add("HISTORY_FEEDBACK", 1.0f / 4.0f);
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(consts.get(), variant);
-	m_grProg = variant->getProgram();
+	m_main.m_prog->getOrCreateVariant(consts.get(), variant);
+	m_main.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void SsaoMain::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void SsaoMain::run(RenderingContext& ctx)
-{
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->beginRenderPass(m_ssao->m_fb[m_r->getFrameCount() & 1]);
-	cmdb->setViewport(0, 0, m_ssao->m_width, m_ssao->m_height);
-	cmdb->bindShaderProgram(m_grProg);
-
-	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_colorRt);
-	// TODO cmdb->bindTextureAndSampler(0, 1, m_r->getGBuffer().m_rt2, m_r->getLinearSampler());
-	cmdb->bindTexture(0, 2, m_noiseTex->getGrTexture());
-	cmdb->informTextureCurrentUsage(m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1], TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->bindTexture(0, 3, m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1]);
-
-	struct Unis
-	{
-		Vec4 m_unprojectionParams;
-		Vec4 m_projectionMat;
-		Vec4 m_noiseLayerPad3;
-		Mat4 m_prevViewProjMatMulInvViewProjMat;
-	};
-
-	Unis* unis = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 0);
-	const Mat4& pmat = ctx.m_renderQueue->m_projectionMatrix;
-	unis->m_unprojectionParams = ctx.m_unprojParams;
-	unis->m_projectionMat = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
-	unis->m_noiseLayerPad3 = Vec4(m_r->getFrameCount() % m_noiseTex->getLayerCount(), 0.0, 0.0, 0.0);
-	unis->m_prevViewProjMatMulInvViewProjMat =
-		ctx.m_prevViewProjMat * ctx.m_renderQueue->m_viewProjectionMatrix.getInverse();
-
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-void SsaoMain::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-Error SsaoHBlur::init(const ConfigSet& config)
+Error Ssao::initHBlur(const ConfigSet& config)
 {
 	// shader
-	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/DepthAwareBlur.ankiprog", m_prog));
+	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/DepthAwareBlur.ankiprog", m_hblur.m_prog));
 
-	ShaderProgramResourceMutationInitList<3> mutators(m_prog);
+	ShaderProgramResourceMutationInitList<3> mutators(m_hblur.m_prog);
 	mutators.add("HORIZONTAL", 1).add("KERNEL_SIZE", 9).add("COLOR_COMPONENTS", 1);
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add("TEXTURE_SIZE", UVec2(m_ssao->m_width, m_ssao->m_height));
+	ShaderProgramResourceConstantValueInitList<1> consts(m_hblur.m_prog);
+	consts.add("TEXTURE_SIZE", UVec2(m_width, m_height));
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
+	m_hblur.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
 
-	m_grProg = variant->getProgram();
+	m_hblur.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void SsaoHBlur::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void SsaoHBlur::run(RenderingContext& ctx)
-{
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->setViewport(0, 0, m_ssao->m_width, m_ssao->m_height);
-	cmdb->beginRenderPass(m_ssao->m_fb[(m_r->getFrameCount() + 1) & 1]);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTexture(0, 0, m_ssao->m_rt[m_r->getFrameCount() & 1]);
-	cmdb->bindTexture(0, 1, m_r->getDepthDownscale().m_qd.m_colorRt);
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-void SsaoHBlur::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-Error SsaoVBlur::init(const ConfigSet& config)
+Error Ssao::initVBlur(const ConfigSet& config)
 {
 	// shader
-	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/DepthAwareBlur.ankiprog", m_prog));
+	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/DepthAwareBlur.ankiprog", m_vblur.m_prog));
 
-	ShaderProgramResourceMutationInitList<3> mutators(m_prog);
+	ShaderProgramResourceMutationInitList<3> mutators(m_vblur.m_prog);
 	mutators.add("HORIZONTAL", 0).add("KERNEL_SIZE", 9).add("COLOR_COMPONENTS", 1);
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add("TEXTURE_SIZE", UVec2(m_ssao->m_width, m_ssao->m_height));
+	ShaderProgramResourceConstantValueInitList<1> consts(m_vblur.m_prog);
+	consts.add("TEXTURE_SIZE", UVec2(m_width, m_height));
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
+	m_vblur.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
 
-	m_grProg = variant->getProgram();
+	m_vblur.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void SsaoVBlur::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void SsaoVBlur::run(RenderingContext& ctx)
-{
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->setViewport(0, 0, m_ssao->m_width, m_ssao->m_height);
-	cmdb->beginRenderPass(m_ssao->m_fb[m_r->getFrameCount() & 1]);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTexture(0, 0, m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1]);
-	cmdb->bindTexture(0, 1, m_r->getDepthDownscale().m_qd.m_colorRt);
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-void SsaoVBlur::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
 Error Ssao::init(const ConfigSet& config)
 {
 	m_width = m_r->getWidth() / SSAO_FRACTION;
@@ -192,32 +88,30 @@ Error Ssao::init(const ConfigSet& config)
 	for(U i = 0; i < 2; ++i)
 	{
 		// RT
-		m_rt[i] = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_width,
+		m_rtTextures[i] = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_width,
 			m_height,
 			Ssao::RT_PIXEL_FORMAT,
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::CLEAR,
 			SamplingFilter::LINEAR,
 			1,
 			"ssaomain"));
-
-		// FB
-		FramebufferInitInfo fbInit("ssaomain");
-		fbInit.m_colorAttachmentCount = 1;
-		fbInit.m_colorAttachments[0].m_texture = m_rt[i];
-		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-		m_fb[i] = getGrManager().newInstance<Framebuffer>(fbInit);
 	}
 
-	Error err = m_main.init(config);
+	// FB descr
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_fbDescr.bake();
+
+	Error err = initMain(config);
 
 	if(!err)
 	{
-		err = m_hblur.init(config);
+		err = initHBlur(config);
 	}
 
 	if(!err)
 	{
-		err = m_vblur.init(config);
+		err = initVBlur(config);
 	}
 
 	if(err)
@@ -228,9 +122,101 @@ Error Ssao::init(const ConfigSet& config)
 	return err;
 }
 
-TexturePtr Ssao::getRt() const
+void Ssao::runMain(CommandBufferPtr& cmdb, const RenderingContext& ctx, const RenderGraph& rgraph)
 {
-	return m_rt[m_r->getFrameCount() & 1];
+	cmdb->setViewport(0, 0, m_width, m_height);
+	cmdb->bindShaderProgram(m_main.m_grProg);
+
+	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_colorRt);
+	// TODO cmdb->bindTextureAndSampler(0, 1, m_r->getGBuffer().m_rt2, m_r->getLinearSampler());
+	cmdb->bindTexture(0, 2, m_main.m_noiseTex->getGrTexture());
+	cmdb->bindTexture(0, 3, rgraph.getTexture(m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1]));
+
+	struct Unis
+	{
+		Vec4 m_unprojectionParams;
+		Vec4 m_projectionMat;
+		Vec4 m_noiseLayerPad3;
+		Mat4 m_prevViewProjMatMulInvViewProjMat;
+	};
+
+	Unis* unis = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 0);
+	const Mat4& pmat = ctx.m_renderQueue->m_projectionMatrix;
+	unis->m_unprojectionParams = ctx.m_unprojParams;
+	unis->m_projectionMat = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
+	unis->m_noiseLayerPad3 = Vec4(m_r->getFrameCount() % m_main.m_noiseTex->getLayerCount(), 0.0, 0.0, 0.0);
+	unis->m_prevViewProjMatMulInvViewProjMat =
+		ctx.m_prevViewProjMat * ctx.m_renderQueue->m_viewProjectionMatrix.getInverse();
+
+	m_r->drawQuad(cmdb);
+}
+
+void Ssao::runHBlur(CommandBufferPtr& cmdb, const RenderGraph& rgraph)
+{
+	cmdb->setViewport(0, 0, m_width, m_height);
+	cmdb->bindShaderProgram(m_hblur.m_grProg);
+	cmdb->bindTexture(0, 0, rgraph.getTexture(m_runCtx.m_rts[m_r->getFrameCount() & 1]));
+	cmdb->bindTexture(0, 1, m_r->getDepthDownscale().m_qd.m_colorRt);
+	m_r->drawQuad(cmdb);
+}
+
+void Ssao::runVBlur(CommandBufferPtr& cmdb, const RenderGraph& rgraph)
+{
+	cmdb->setViewport(0, 0, m_width, m_height);
+	cmdb->bindShaderProgram(m_vblur.m_grProg);
+	cmdb->bindTexture(0, 0, rgraph.getTexture(m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1]));
+	cmdb->bindTexture(0, 1, m_r->getDepthDownscale().m_qd.m_colorRt);
+	m_r->drawQuad(cmdb);
+}
+
+void Ssao::populateRenderGraph(RenderingContext& ctx)
+{
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	// Create RTs
+	const U rtToRenderIdx = m_r->getFrameCount() & 1;
+	m_runCtx.m_rts[rtToRenderIdx] =
+		rgraph.importRenderTarget("SSAO #1", m_rtTextures[rtToRenderIdx], TextureUsageBit::NONE);
+	const U rtToReadIdx = !rtToRenderIdx;
+	m_runCtx.m_rts[rtToReadIdx] =
+		rgraph.importRenderTarget("SSAO #2", m_rtTextures[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT);
+
+	// Create main render pass
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SSAO main");
+
+		pass.setWork(runMain, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToRenderIdx]}}, {});
+
+		pass.newConsumer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT});
+	}
+
+	// Create HBlur pass
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SSAO hblur");
+
+		pass.setWork(runHBlur, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToReadIdx]}}, {});
+
+		pass.newConsumer({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::SAMPLED_FRAGMENT});
+	}
+
+	// Create VBlur pass
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SSAO vblur");
+
+		pass.setWork(runVBlur, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToRenderIdx]}}, {});
+
+		pass.newConsumer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT});
+	}
 }
 
 } // end namespace anki

+ 74 - 94
src/anki/renderer/Ssao.h

@@ -8,129 +8,109 @@
 #include <anki/renderer/RendererObject.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/Gr.h>
-#include <anki/core/Timestamp.h>
 
 namespace anki
 {
 
-// Forward
-class Ssao;
-
 /// @addtogroup renderer
 /// @{
 
 /// Screen space ambient occlusion pass
-class SsaoMain : public RendererObject
+class Ssao : public RendererObject
 {
-	friend class SsaoVBlur;
-	friend class SsaoHBlur;
-
 anki_internal:
-	SsaoMain(Renderer* r, Ssao* ssao)
+	static const PixelFormat RT_PIXEL_FORMAT;
+
+	Ssao(Renderer* r)
 		: RendererObject(r)
-		, m_ssao(ssao)
 	{
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	void setPreRunBarriers(RenderingContext& ctx);
+	~Ssao();
 
-	void run(RenderingContext& ctx);
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
 
-	void setPostRunBarriers(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
 private:
-	Ssao* m_ssao;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-	TextureResourcePtr m_noiseTex;
-};
-
-/// Screen space ambient occlusion blur pass.
-class SsaoHBlur : public RendererObject
-{
-	friend class SsaoVBlur;
+	U32 m_width, m_height;
 
-anki_internal:
-	SsaoHBlur(Renderer* r, Ssao* ssao)
-		: RendererObject(r)
-		, m_ssao(ssao)
+	class
 	{
-	}
-
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	void setPreRunBarriers(RenderingContext& ctx);
-
-	void run(RenderingContext& ctx);
-
-	void setPostRunBarriers(RenderingContext& ctx);
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+		TextureResourcePtr m_noiseTex;
+	} m_main; ///< Main noisy pass.
 
-private:
-	Ssao* m_ssao;
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_hblur; ///< Horizontal blur.
 
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_vblur; ///< Vertical blur.
 
-/// Screen space ambient occlusion blur pass.
-class SsaoVBlur : public RendererObject
-{
-anki_internal:
-	SsaoVBlur(Renderer* r, Ssao* ssao)
-		: RendererObject(r)
-		, m_ssao(ssao)
+	class
 	{
+	public:
+		Array<RenderTargetHandle, 2> m_rts;
+		const RenderingContext* m_ctx = nullptr;
+	} m_runCtx; ///< Runtime context.
+
+	Array<TexturePtr, 2> m_rtTextures;
+	GraphicsRenderPassFramebufferDescription m_fbDescr;
+
+	ANKI_USE_RESULT Error initMain(const ConfigSet& set);
+	ANKI_USE_RESULT Error initVBlur(const ConfigSet& set);
+	ANKI_USE_RESULT Error initHBlur(const ConfigSet& set);
+
+	void runMain(CommandBufferPtr& cmdb, const RenderingContext& ctx, const RenderGraph& rgraph);
+	void runHBlur(CommandBufferPtr& cmdb, const RenderGraph& rgraph);
+	void runVBlur(CommandBufferPtr& cmdb, const RenderGraph& rgraph);
+
+	/// A RenderPassWorkCallback for SSAO main pass.
+	static void runMain(void* userData,
+		CommandBufferPtr cmdb,
+		U32 secondLevelCmdbIdx,
+		U32 secondLevelCmdbCount,
+		const RenderGraph& rgraph)
+	{
+		ANKI_ASSERT(userData);
+		Ssao* self = static_cast<Ssao*>(userData);
+		self->runMain(cmdb, *self->m_runCtx.m_ctx, rgraph);
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	void setPreRunBarriers(RenderingContext& ctx);
-
-	void run(RenderingContext& ctx);
-
-	void setPostRunBarriers(RenderingContext& ctx);
-
-private:
-	Ssao* m_ssao;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
-
-/// Screen space ambient occlusion pass
-class Ssao : public RendererObject
-{
-	friend class SsaoMain;
-	friend class SsaoHBlur;
-	friend class SsaoVBlur;
-
-anki_internal:
-	static const PixelFormat RT_PIXEL_FORMAT;
-
-	SsaoMain m_main;
-	SsaoHBlur m_hblur;
-	SsaoVBlur m_vblur;
-
-	Ssao(Renderer* r)
-		: RendererObject(r)
-		, m_main(r, this)
-		, m_hblur(r, this)
-		, m_vblur(r, this)
+	/// A RenderPassWorkCallback for SSAO HBlur.
+	static void runHBlur(void* userData,
+		CommandBufferPtr cmdb,
+		U32 secondLevelCmdbIdx,
+		U32 secondLevelCmdbCount,
+		const RenderGraph& rgraph)
 	{
+		ANKI_ASSERT(userData);
+		Ssao* self = static_cast<Ssao*>(userData);
+		self->runHBlur(cmdb, rgraph);
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	TexturePtr getRt() const;
-
-private:
-	U32 m_width, m_height;
-
-	Array<TexturePtr, 2> m_rt;
-	Array<FramebufferPtr, 2> m_fb;
+	/// A RenderPassWorkCallback for SSAO VBlur.
+	static void runVBlur(void* userData,
+		CommandBufferPtr cmdb,
+		U32 secondLevelCmdbIdx,
+		U32 secondLevelCmdbCount,
+		const RenderGraph& rgraph)
+	{
+		ANKI_ASSERT(userData);
+		Ssao* self = static_cast<Ssao*>(userData);
+		self->runVBlur(cmdb, rgraph);
+	}
 };
 /// @}