Explorar o código

Move Volumetic to the render graph

Panagiotis Christopoulos Charitos %!s(int64=8) %!d(string=hai) anos
pai
achega
af6a0c60b2
Modificáronse 3 ficheiros con 209 adicións e 297 borrados
  1. 1 3
      src/anki/renderer/ForwardShading.cpp
  2. 134 180
      src/anki/renderer/Volumetric.cpp
  3. 74 114
      src/anki/renderer/Volumetric.h

+ 1 - 3
src/anki/renderer/ForwardShading.cpp

@@ -109,14 +109,12 @@ void ForwardShading::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb
 	Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	computeLinearizeDepthOptimal(ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, unis->x(), unis->y());
 
-	cmdb->informTextureSurfaceCurrentUsage(
-		m_r->getVolumetric().m_main.getRt(), TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
 	cmdb->informTextureSurfaceCurrentUsage(
 		m_r->getDepthDownscale().m_qd.m_colorRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
 
 	cmdb->bindTextureAndSampler(0, 0, m_r->getDepthDownscale().m_hd.m_colorRt, m_r->getNearestSampler());
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_qd.m_colorRt, m_r->getNearestSampler());
-	cmdb->bindTexture(0, 2, m_r->getVolumetric().m_main.getRt());
+	// TODO cmdb->bindTexture(0, 2, m_r->getVolumetric().m_main.getRt());
 	cmdb->bindTexture(0, 3, m_vol.m_noiseTex->getGrTexture());
 
 	m_r->drawQuad(cmdb);

+ 134 - 180
src/anki/renderer/Volumetric.cpp

@@ -14,80 +14,119 @@
 namespace anki
 {
 
-Error VolumetricMain::init(const ConfigSet& config)
+Error Volumetric::initMain(const ConfigSet& config)
 {
 	// Misc
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
-
-	for(U i = 0; i < 2; ++i)
-	{
-		// RT
-		TextureInitInfo rtInit = m_r->create2DRenderTargetInitInfo(m_vol->m_width,
-			m_vol->m_height,
-			LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
-			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			SamplingFilter::LINEAR,
-			1,
-			"volmain");
-		rtInit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
-		m_rt[i] = m_r->createAndClearRenderTarget(rtInit);
-
-		// FB
-		FramebufferInitInfo fbInit("volmain");
-		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);
-	}
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_main.m_noiseTex));
 
 	// Shaders
-	ANKI_CHECK(getResourceManager().loadResource("programs/VolumetricFog.ankiprog", m_prog));
+	ANKI_CHECK(getResourceManager().loadResource("programs/VolumetricFog.ankiprog", m_main.m_prog));
 
-	ShaderProgramResourceMutationInitList<1> mutators(m_prog);
+	ShaderProgramResourceMutationInitList<1> mutators(m_main.m_prog);
 	mutators.add("ENABLE_SHADOWS", 1);
 
-	ShaderProgramResourceConstantValueInitList<3> consts(m_prog);
-	consts.add("FB_SIZE", UVec2(m_vol->m_width, m_vol->m_height))
+	ShaderProgramResourceConstantValueInitList<3> consts(m_main.m_prog);
+	consts.add("FB_SIZE", UVec2(m_width, m_height))
 		.add("CLUSTER_COUNT",
 			UVec3(m_r->getLightShading().getLightBin().getClusterer().getClusterCountX(),
 				m_r->getLightShading().getLightBin().getClusterer().getClusterCountY(),
 				m_r->getLightShading().getLightBin().getClusterer().getClusterCountZ()))
-		.add("NOISE_MAP_SIZE", U32(m_noiseTex->getWidth()));
+		.add("NOISE_MAP_SIZE", U32(m_main.m_noiseTex->getWidth()));
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
-	m_grProg = variant->getProgram();
+	m_main.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
+	m_main.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-TexturePtr VolumetricMain::getRt() const
+Error Volumetric::initHBlur(const ConfigSet& config)
 {
-	return m_rt[m_r->getFrameCount() & 1];
+	// Progs
+	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/LumaAwareBlur.ankiprog", m_hblur.m_prog));
+
+	ShaderProgramResourceMutationInitList<3> mutators(m_hblur.m_prog);
+	mutators.add("HORIZONTAL", 1).add("KERNEL_SIZE", 11).add("COLOR_COMPONENTS", 3);
+	ShaderProgramResourceConstantValueInitList<1> consts(m_hblur.m_prog);
+	consts.add("TEXTURE_SIZE", UVec2(m_width, m_height));
+
+	const ShaderProgramResourceVariant* variant;
+	m_hblur.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
+	m_hblur.m_grProg = variant->getProgram();
+
+	return Error::NONE;
 }
 
-void VolumetricMain::setPreRunBarriers(RenderingContext& ctx)
+Error Volumetric::initVBlur(const ConfigSet& config)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
+	// Progs
+	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/LumaAwareBlur.ankiprog", m_vblur.m_prog));
+
+	ShaderProgramResourceMutationInitList<3> mutators(m_vblur.m_prog);
+	mutators.add("HORIZONTAL", 0).add("KERNEL_SIZE", 11).add("COLOR_COMPONENTS", 3);
+	ShaderProgramResourceConstantValueInitList<1> consts(m_vblur.m_prog);
+	consts.add("TEXTURE_SIZE", UVec2(m_width, m_height));
+
+	const ShaderProgramResourceVariant* variant;
+	m_vblur.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
+	m_vblur.m_grProg = variant->getProgram();
+
+	return Error::NONE;
 }
 
-void VolumetricMain::run(RenderingContext& ctx)
+Error Volumetric::init(const ConfigSet& config)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	m_width = m_r->getWidth() / VOLUMETRIC_FRACTION;
+	m_height = m_r->getHeight() / VOLUMETRIC_FRACTION;
+
+	ANKI_R_LOGI("Initializing volumetric pass. Size %ux%u", m_width, m_height);
+
+	for(U i = 0; i < 2; ++i)
+	{
+		// RT
+		TextureInitInfo rtInit = m_r->create2DRenderTargetInitInfo(m_width,
+			m_height,
+			LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
+			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+			SamplingFilter::LINEAR,
+			1,
+			"volmain");
+		rtInit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
+		m_rtTextures[i] = m_r->createAndClearRenderTarget(rtInit);
+	}
+
+	// FB
+	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 = initHBlur(config);
+	}
+
+	if(!err)
+	{
+		err = initVBlur(config);
+	}
+
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize volumetric pass");
+	}
+
+	return err;
+}
 
-	//
-	// Main pass
-	//
-	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
+void Volumetric::runMain(CommandBufferPtr& cmdb, const RenderingContext& ctx, const RenderGraph& rgraph)
+{
+	cmdb->setViewport(0, 0, m_width, m_height);
 
 	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_colorRt);
-	cmdb->bindTexture(0, 1, m_noiseTex->getGrTexture());
-	TexturePtr& history = m_rt[(m_r->getFrameCount() + 1) & 1];
-	cmdb->informTextureCurrentUsage(history, TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->bindTexture(0, 2, history);
+	cmdb->bindTexture(0, 1, m_main.m_noiseTex->getGrTexture());
+	cmdb->bindTexture(0, 2, rgraph.getTexture(m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1]));
 	// TODO: cmdb->bindTexture(0, 3, m_r->getShadowMapping().m_shadowAtlas);
 
 	bindUniforms(cmdb, 0, 0, ctx.m_lightShading.m_commonToken);
@@ -106,172 +145,87 @@ void VolumetricMain::run(RenderingContext& ctx)
 		ctx.m_renderQueue->m_cameraFar,
 		uniforms->m_linearizeNoiseTexOffsetLayer.x(),
 		uniforms->m_linearizeNoiseTexOffsetLayer.y());
-	F32 texelOffset = 1.0 / m_noiseTex->getWidth();
+	F32 texelOffset = 1.0 / m_main.m_noiseTex->getWidth();
 	uniforms->m_linearizeNoiseTexOffsetLayer.z() = m_r->getFrameCount() * texelOffset;
-	uniforms->m_linearizeNoiseTexOffsetLayer.w() = m_r->getFrameCount() & (m_noiseTex->getLayerCount() - 1);
-	uniforms->m_fogParticleColorPad1 = Vec4(m_fogParticleColor, 0.0);
+	uniforms->m_linearizeNoiseTexOffsetLayer.w() = m_r->getFrameCount() & (m_main.m_noiseTex->getLayerCount() - 1);
+	uniforms->m_fogParticleColorPad1 = Vec4(m_main.m_fogParticleColor, 0.0);
 	uniforms->m_prevViewProjMatMulInvViewProjMat =
 		ctx.m_prevViewProjMat * ctx.m_renderQueue->m_viewProjectionMatrix.getInverse();
 
 	bindStorage(cmdb, 0, 0, ctx.m_lightShading.m_clustersToken);
 	bindStorage(cmdb, 0, 1, ctx.m_lightShading.m_lightIndicesToken);
 
-	cmdb->bindShaderProgram(m_grProg);
+	cmdb->bindShaderProgram(m_main.m_grProg);
 
-	cmdb->beginRenderPass(m_fb[m_r->getFrameCount() & 1]);
 	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-void VolumetricMain::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
 }
 
-Error VolumetricHBlur::init(const ConfigSet& config)
+void Volumetric::runHBlur(CommandBufferPtr& cmdb, const RenderGraph& rgraph)
 {
-	// Create RTs
-	m_rt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_vol->m_width,
-		m_vol->m_height,
-		LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		SamplingFilter::LINEAR,
-		1,
-		"volblur"));
-
-	// Create FBs
-	FramebufferInitInfo fbInit("volblur");
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
-
-	// Progs
-	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/LumaAwareBlur.ankiprog", m_prog));
+	cmdb->bindTexture(0, 0, rgraph.getTexture(m_runCtx.m_rts[m_r->getFrameCount() & 1]));
+	cmdb->bindShaderProgram(m_hblur.m_grProg);
+	cmdb->setViewport(0, 0, m_width, m_height);
 
-	ShaderProgramResourceMutationInitList<3> mutators(m_prog);
-	mutators.add("HORIZONTAL", 1).add("KERNEL_SIZE", 11).add("COLOR_COMPONENTS", 3);
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add("TEXTURE_SIZE", UVec2(m_vol->m_width, m_vol->m_height));
-
-	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
-	m_grProg = variant->getProgram();
-
-	return Error::NONE;
-}
-
-void VolumetricHBlur::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(
-		m_rt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+	m_r->drawQuad(cmdb);
 }
 
-void VolumetricHBlur::run(RenderingContext& ctx)
+void Volumetric::runVBlur(CommandBufferPtr& cmdb, const RenderGraph& rgraph)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->bindTexture(0, 0, m_vol->m_main.m_rt[m_r->getFrameCount() & 1]);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
+	cmdb->bindTexture(0, 0, rgraph.getTexture(m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1]));
+	cmdb->bindShaderProgram(m_vblur.m_grProg);
+	cmdb->setViewport(0, 0, m_width, m_height);
 
-	cmdb->beginRenderPass(m_fb);
 	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
 }
 
-void VolumetricHBlur::setPostRunBarriers(RenderingContext& ctx)
+void Volumetric::populateRenderGraph(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
-Error VolumetricVBlur::init(const ConfigSet& config)
-{
-	// Create FBs
-	for(U i = 0; i < 2; ++i)
+	// Create RTs
+	const U rtToRenderIdx = m_r->getFrameCount() & 1;
+	m_runCtx.m_rts[rtToRenderIdx] =
+		rgraph.importRenderTarget("VOL #1", m_rtTextures[rtToRenderIdx], TextureUsageBit::NONE);
+	const U rtToReadIdx = !rtToRenderIdx;
+	m_runCtx.m_rts[rtToReadIdx] =
+		rgraph.importRenderTarget("VOL #2", m_rtTextures[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT);
+
+	// Create main render pass
 	{
-		FramebufferInitInfo fbInit;
-		fbInit.m_colorAttachmentCount = 1;
-		fbInit.m_colorAttachments[0].m_texture = m_vol->m_main.m_rt[i];
-		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-		m_fb[i] = getGrManager().newInstance<Framebuffer>(fbInit);
-	}
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("VOL main");
 
-	// Progs
-	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/LumaAwareBlur.ankiprog", m_prog));
-
-	ShaderProgramResourceMutationInitList<3> mutators(m_prog);
-	mutators.add("HORIZONTAL", 0).add("KERNEL_SIZE", 11).add("COLOR_COMPONENTS", 3);
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add("TEXTURE_SIZE", UVec2(m_vol->m_width, m_vol->m_height));
-
-	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
-	m_grProg = variant->getProgram();
-
-	return Error::NONE;
-}
+		pass.setWork(runMain, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToRenderIdx]}}, {});
 
-void VolumetricVBlur::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vol->m_main.m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void VolumetricVBlur::run(RenderingContext& ctx)
-{
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->bindTexture(0, 0, m_vol->m_hblur.m_rt);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
-
-	cmdb->beginRenderPass(m_fb[m_r->getFrameCount() & 1]);
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-void VolumetricVBlur::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vol->m_main.m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-Error Volumetric::init(const ConfigSet& config)
-{
-	m_width = m_r->getWidth() / VOLUMETRIC_FRACTION;
-	m_height = m_r->getHeight() / VOLUMETRIC_FRACTION;
+		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});
+	}
 
-	ANKI_R_LOGI("Initializing volumetric pass. Size %ux%u", m_width, m_height);
+	// Create HBlur pass
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("VOL hblur");
 
-	Error err = m_main.init(config);
+		pass.setWork(runHBlur, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToReadIdx]}}, {});
 
-	if(!err)
-	{
-		err = m_hblur.init(config);
+		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});
 	}
 
-	if(!err)
+	// Create VBlur pass
 	{
-		err = m_vblur.init(config);
-	}
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("VOL vblur");
 
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to initialize volumetric pass");
-	}
+		pass.setWork(runVBlur, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToRenderIdx]}}, {});
 
-	return err;
+		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 - 114
src/anki/renderer/Volumetric.h

@@ -10,156 +10,116 @@
 namespace anki
 {
 
-// Forward
-class Volumetric;
-
 /// @addtogroup renderer
 /// @{
 
-/// Volumetic main pass.
-class VolumetricMain : public RendererObject
+/// Volumetric effects.
+class Volumetric : public RendererObject
 {
-	friend class Volumetric;
-	friend class VolumetricHBlur;
-	friend class VolumetricVBlur;
-
-anki_internal:
-	VolumetricMain(Renderer* r, Volumetric* vol)
-		: RendererObject(r)
-		, m_vol(vol)
-	{
-	}
-
-	~VolumetricMain()
+public:
+	void setFogParticleColor(const Vec3& col)
 	{
+		m_main.m_fogParticleColor = col;
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
-
-	TexturePtr getRt() const;
-
-private:
-	Volumetric* m_vol;
-
-	Vec3 m_fogParticleColor = Vec3(1.0);
-	Mat3x4 m_prevCameraRot = Mat3x4::getIdentity();
-
-	Array<TexturePtr, 2> m_rt; ///< vRT
-	Array<FramebufferPtr, 2> m_fb;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-
-	TextureResourcePtr m_noiseTex;
-};
-
-/// Volumetric blur pass.
-class VolumetricHBlur : public RendererObject
-{
-	friend class Volumetric;
-	friend class VolumetricVBlur;
-
 anki_internal:
-	VolumetricHBlur(Renderer* r, Volumetric* vol)
+
+	Volumetric(Renderer* r)
 		: RendererObject(r)
-		, m_vol(vol)
 	{
 	}
 
-	~VolumetricHBlur()
+	~Volumetric()
 	{
 	}
 
 	ANKI_USE_RESULT Error init(const ConfigSet& config);
 
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
 private:
-	Volumetric* m_vol;
+	U32 m_width = 0, m_height = 0;
 
-	TexturePtr m_rt;
-	FramebufferPtr m_fb;
+	class
+	{
+	public:
+		Vec3 m_fogParticleColor = Vec3(1.0);
+		Mat3x4 m_prevCameraRot = Mat3x4::getIdentity();
 
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
 
-/// Volumetric blur pass.
-class VolumetricVBlur : public RendererObject
-{
-	friend class Volumetric;
+		TextureResourcePtr m_noiseTex;
+	} m_main; ///< Main noisy pass.
 
-anki_internal:
-	VolumetricVBlur(Renderer* r, Volumetric* vol)
-		: RendererObject(r)
-		, m_vol(vol)
+	class
 	{
-	}
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_hblur; ///< Horizontal blur.
 
-	~VolumetricVBlur()
+	class
 	{
-	}
-
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
-
-private:
-	Volumetric* m_vol;
-
-	Array<FramebufferPtr, 2> m_fb;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
-
-/// Volumetric effects.
-class Volumetric : public RendererObject
-{
-	friend class VolumetricMain;
-	friend class VolumetricHBlur;
-	friend class VolumetricVBlur;
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_vblur; ///< Vertical blur.
 
-public:
-	void setFogParticleColor(const Vec3& col)
+	class
 	{
-		m_main.m_fogParticleColor = col;
-	}
-
-anki_internal:
-	VolumetricMain m_main;
-	VolumetricHBlur m_hblur;
-	VolumetricVBlur m_vblur;
-
-	Volumetric(Renderer* r)
-		: RendererObject(r)
-		, m_main(r, this)
-		, m_hblur(r, this)
-		, m_vblur(r, this)
+	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);
+		Volumetric* self = static_cast<Volumetric*>(userData);
+		self->runMain(cmdb, *self->m_runCtx.m_ctx, rgraph);
 	}
 
-	~Volumetric()
+	/// A RenderPassWorkCallback for SSAO HBlur.
+	static void runHBlur(void* userData,
+		CommandBufferPtr cmdb,
+		U32 secondLevelCmdbIdx,
+		U32 secondLevelCmdbCount,
+		const RenderGraph& rgraph)
 	{
+		ANKI_ASSERT(userData);
+		Volumetric* self = static_cast<Volumetric*>(userData);
+		self->runHBlur(cmdb, rgraph);
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	TexturePtr getRt() const
+	/// A RenderPassWorkCallback for SSAO VBlur.
+	static void runVBlur(void* userData,
+		CommandBufferPtr cmdb,
+		U32 secondLevelCmdbIdx,
+		U32 secondLevelCmdbCount,
+		const RenderGraph& rgraph)
 	{
-		return m_main.getRt();
+		ANKI_ASSERT(userData);
+		Volumetric* self = static_cast<Volumetric*>(userData);
+		self->runVBlur(cmdb, rgraph);
 	}
-
-private:
-	U32 m_width = 0, m_height = 0;
 };
 /// @}