Browse Source

Renderer: Add blur in volumetric fog

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
6dcd672b63

+ 2 - 2
sandbox/Main.cpp

@@ -118,8 +118,8 @@ Error MyApp::userMainLoop(Bool& quit)
 	{
 	{
 		/*Vec3 origin = mover->getWorldTransform().getOrigin().xyz();
 		/*Vec3 origin = mover->getWorldTransform().getOrigin().xyz();
 		printf("%f %f %f\n", origin.x(), origin.y(), origin.z());*/
 		printf("%f %f %f\n", origin.x(), origin.y(), origin.z());*/
-		mover->setLocalOrigin(Vec4(0.0));
-		mover->setLocalRotation(Mat3x4::getIdentity());
+		mover->setLocalOrigin(Vec4(81.169312, -2.309618, 17.088392, 0.0));
+		// mover->setLocalRotation(Mat3x4::getIdentity());
 	}
 	}
 
 
 	if(in.getKey(KeyCode::F1) == 1)
 	if(in.getKey(KeyCode::F1) == 1)

+ 96 - 0
shaders/DepthAwareBlurGeneric.frag.glsl

@@ -0,0 +1,96 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "shaders/Functions.glsl"
+#include "shaders/GaussianBlurCommon.glsl"
+
+// Preprocessor switches sanity checks
+#if !defined(VPASS) && !defined(HPASS)
+#error See file
+#endif
+
+#if !(defined(COL_RGBA) || defined(COL_RGB) || defined(COL_R))
+#error See file
+#endif
+
+#if !defined(TEXTURE_SIZE)
+#error See file
+#endif
+
+// Determine color type
+#if defined(COL_RGBA)
+#define COL_TYPE vec4
+#elif defined(COL_RGB)
+#define COL_TYPE vec3
+#elif defined(COL_R)
+#define COL_TYPE float
+#endif
+
+// Determine tex fetch
+#if defined(COL_RGBA)
+#define TEX_FETCH rgba
+#elif defined(COL_RGB)
+#define TEX_FETCH rgb
+#elif defined(COL_R)
+#define TEX_FETCH r
+#endif
+
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_colorTex;
+layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_depthTex;
+
+layout(location = 0) in vec2 in_uv;
+layout(location = 0) out COL_TYPE out_color;
+
+layout(std140, ANKI_UBO_BINDING(0, 0)) uniform ubo0_
+{
+	vec4 u_linearizeDepthCfDepthThresholdPad1;
+};
+
+#define u_linearizeDepthCf u_linearizeDepthCfDepthThresholdPad1.xy
+#define u_depthThreshold u_linearizeDepthCfDepthThresholdPad1.z
+
+float readLinearDepth(vec2 uv)
+{
+	float d = texture(u_depthTex, uv).r;
+	return linearizeDepthOptimal(d, u_linearizeDepthCf.x, u_linearizeDepthCf.y);
+}
+
+float computeDepthWeight(vec2 uv, float refDepth)
+{
+	float d = readLinearDepth(uv);
+	float diff = abs(refDepth - d);
+	float depthWeight = 1.0 / (EPSILON + diff);
+	return depthWeight;
+}
+
+void main()
+{
+#if defined(VPASS)
+	const vec2 TEXEL_SIZE = vec2(0.0, 1.0 / TEXTURE_SIZE.y);
+#else
+	const vec2 TEXEL_SIZE = vec2(1.0 / TEXTURE_SIZE.x, 0.0);
+#endif
+
+	out_color = COL_TYPE(0.0);
+	float refDepth = readLinearDepth(in_uv);
+	float weight = 0.0;
+
+	for(uint i = 0u; i < STEP_COUNT; ++i)
+	{
+		vec2 texCoordOffset = OFFSETS[i] * TEXEL_SIZE;
+
+		vec2 uv = in_uv + texCoordOffset;
+		float w = WEIGHTS[i] * computeDepthWeight(uv, refDepth);
+		out_color += texture(u_colorTex, uv).TEX_FETCH * w;
+		weight += w;
+
+		uv = in_uv - texCoordOffset;
+		w = WEIGHTS[i] * computeDepthWeight(uv, refDepth);
+		out_color += texture(u_colorTex, uv).TEX_FETCH * w;
+		weight += w;
+	}
+
+	out_color = out_color / weight;
+}

+ 12 - 9
shaders/FsUpscale.frag.glsl

@@ -6,6 +6,8 @@
 #include "shaders/Common.glsl"
 #include "shaders/Common.glsl"
 #include "shaders/Functions.glsl"
 #include "shaders/Functions.glsl"
 
 
+#define BLUE_NOISE 1
+
 layout(location = 0) in vec2 in_uv;
 layout(location = 0) in vec2 in_uv;
 
 
 layout(location = 0) out vec4 out_color;
 layout(location = 0) out vec4 out_color;
@@ -13,8 +15,9 @@ layout(location = 0) out vec4 out_color;
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthFullTex;
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthFullTex;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_depthHalfTex;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_depthHalfTex;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_fsRt;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_fsRt;
-
-const float DEPTH_THRESHOLD = 1.0 / 4000.0;
+#if BLUE_NOISE
+layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2DArray u_noiseTex;
+#endif
 
 
 layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
 layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
 {
 {
@@ -23,13 +26,13 @@ layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
 
 
 void main()
 void main()
 {
 {
-#if 0
-	// Get the depth of the current fragment
-	vec3 color = nearestDepthUpscale(in_uv, u_depthFullTex, u_depthHalfTex, u_fsRt, DEPTH_THRESHOLD);
-#else
-	vec4 color =
+	out_color =
 		bilateralUpsample(u_depthFullTex, u_depthHalfTex, u_fsRt, 1.0 / vec2(SRC_SIZE), in_uv, u_linearizeCfPad2.xy);
 		bilateralUpsample(u_depthFullTex, u_depthHalfTex, u_fsRt, 1.0 / vec2(SRC_SIZE), in_uv, u_linearizeCfPad2.xy);
-#endif
 
 
-	out_color = color;
+#if BLUE_NOISE
+	vec3 blueNoise = texture(u_noiseTex, vec3(FB_SIZE / vec2(NOISE_TEX_SIZE) * in_uv, 0.0), 0.0).rgb;
+	blueNoise = blueNoise * 2.0 - 1.0;
+	blueNoise = sign(blueNoise) * (1.0 - sqrt(1.0 - abs(blueNoise)));
+	out_color.rgb += blueNoise / 32.0;
+#endif
 }
 }

+ 84 - 0
shaders/LumaAwareBlurGeneric.frag.glsl

@@ -0,0 +1,84 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "shaders/Functions.glsl"
+#include "shaders/GaussianBlurCommon.glsl"
+#include "shaders/Tonemapping.glsl"
+
+// Preprocessor switches sanity checks
+#if !defined(VPASS) && !defined(HPASS)
+#error See file
+#endif
+
+#if !(defined(COL_RGBA) || defined(COL_RGB) || defined(COL_R))
+#error See file
+#endif
+
+#if !defined(TEXTURE_SIZE)
+#error See file
+#endif
+
+// Determine color type
+#if defined(COL_RGBA)
+#define COL_TYPE vec4
+#elif defined(COL_RGB)
+#define COL_TYPE vec3
+#elif defined(COL_R)
+#define COL_TYPE float
+#endif
+
+// Determine tex fetch
+#if defined(COL_RGBA)
+#define TEX_FETCH rgba
+#elif defined(COL_RGB)
+#define TEX_FETCH rgb
+#elif defined(COL_R)
+#define TEX_FETCH r
+#endif
+
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_colorTex;
+
+layout(location = 0) in vec2 in_uv;
+layout(location = 0) out COL_TYPE out_color;
+
+float computeLumaWeight(float refLuma, COL_TYPE col)
+{
+	float l = computeLuminance(col);
+	float diff = abs(refLuma - l);
+	float weight = 1.0 / (EPSILON + diff);
+	return weight;
+}
+
+void main()
+{
+#if defined(VPASS)
+	const vec2 TEXEL_SIZE = vec2(0.0, 1.0 / TEXTURE_SIZE.y);
+#else
+	const vec2 TEXEL_SIZE = vec2(1.0 / TEXTURE_SIZE.x, 0.0);
+#endif
+
+	out_color = COL_TYPE(0.0);
+	float refLuma = computeLuminance(texture(u_colorTex, in_uv).TEX_FETCH);
+	float weight = 0.0;
+
+	for(uint i = 0u; i < STEP_COUNT; ++i)
+	{
+		vec2 texCoordOffset = OFFSETS[i] * TEXEL_SIZE;
+
+		vec2 uv = in_uv + texCoordOffset;
+		COL_TYPE col = texture(u_colorTex, uv).TEX_FETCH;
+		float w = WEIGHTS[i] * computeLumaWeight(refLuma, col);
+		out_color += col * w;
+		weight += w;
+
+		uv = in_uv - texCoordOffset;
+		col = texture(u_colorTex, uv).TEX_FETCH;
+		w = WEIGHTS[i] * computeLumaWeight(refLuma, col);
+		out_color += col * w;
+		weight += w;
+	}
+
+	out_color = out_color / weight;
+}

+ 1 - 1
shaders/Volumetric.frag.glsl

@@ -154,5 +154,5 @@ void main()
 	}
 	}
 
 
 	newCol *= u_fogParticleColor;
 	newCol *= u_fogParticleColor;
-	out_color = vec4(newCol, 1.0 / 2.0);
+	out_color = vec4(newCol, 1.0 / 3.0);
 }
 }

+ 11 - 5
shaders/VolumetricUpscale.frag.glsl

@@ -5,14 +5,17 @@
 
 
 #include "shaders/Functions.glsl"
 #include "shaders/Functions.glsl"
 
 
+#define BLUE_NOISE 0
+
 layout(location = 0) in vec2 in_uv;
 layout(location = 0) in vec2 in_uv;
 layout(location = 0) out vec3 out_color;
 layout(location = 0) out vec3 out_color;
 
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthFullTex;
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthFullTex;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_depthHalfTex;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_depthHalfTex;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_colorTex;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_colorTex;
-
-const float DEPTH_THRESHOLD = 1.0 / 5000.0;
+#if BLUE_NOISE
+layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2DArray u_noiseTex;
+#endif
 
 
 layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
 layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
 {
 {
@@ -21,10 +24,13 @@ layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
 
 
 void main()
 void main()
 {
 {
-#if 0
-	out_color = nearestDepthUpscale(in_uv, u_depthFullTex, u_depthHalfTex, u_colorTex, DEPTH_THRESHOLD);
-#else
 	out_color =
 	out_color =
 		bilateralUpsample(u_depthFullTex, u_depthHalfTex, u_colorTex, 1.0 / SRC_SIZE, in_uv, u_linearizeCfPad2.xy).rgb;
 		bilateralUpsample(u_depthFullTex, u_depthHalfTex, u_colorTex, 1.0 / SRC_SIZE, in_uv, u_linearizeCfPad2.xy).rgb;
+
+#if BLUE_NOISE
+	vec3 blueNoise = texture(u_noiseTex, vec3(FB_SIZE / vec2(NOISE_TEX_SIZE) * in_uv, 0.0), 0.0).rgb;
+	blueNoise = blueNoise * 2.0 - 1.0;
+	blueNoise = sign(blueNoise) * (1.0 - sqrt(1.0 - abs(blueNoise)));
+	out_color += blueNoise / 16.0;
 #endif
 #endif
 }
 }

+ 21 - 6
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -109,12 +109,10 @@ void CommandBufferImpl::beginRenderPass(FramebufferPtr fb)
 
 
 void CommandBufferImpl::beginRenderPassInternal()
 void CommandBufferImpl::beginRenderPassInternal()
 {
 {
-// TODO
-#if 0
+	FramebufferImpl& impl = *m_activeFb->m_impl;
+
 	VkRenderPassBeginInfo bi = {};
 	VkRenderPassBeginInfo bi = {};
 	bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
 	bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
-	FramebufferImpl& impl = *m_activeFb->m_impl;
-	bi.renderPass = impl.getRenderPassHandle();
 	bi.clearValueCount = impl.getAttachmentCount();
 	bi.clearValueCount = impl.getAttachmentCount();
 	bi.pClearValues = impl.getClearValues();
 	bi.pClearValues = impl.getClearValues();
 
 
@@ -123,8 +121,26 @@ void CommandBufferImpl::beginRenderPassInternal()
 		// Bind a non-default FB
 		// Bind a non-default FB
 
 
 		bi.framebuffer = impl.getFramebufferHandle(0);
 		bi.framebuffer = impl.getFramebufferHandle(0);
-
 		impl.getAttachmentsSize(bi.renderArea.extent.width, bi.renderArea.extent.height);
 		impl.getAttachmentsSize(bi.renderArea.extent.width, bi.renderArea.extent.height);
+
+		// Calc the usage
+		Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS> colAttUsages;
+		for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
+		{
+			Bool found = m_texUsageTracker.findUsage(*impl.getColorAttachment(i), colAttUsages[i]);
+			ANKI_ASSERT(found);
+			(void)found;
+		}
+
+		TextureUsageBit dsAttUsage = TextureUsageBit::NONE;
+		if(impl.hasDepthStencil())
+		{
+			Bool found = m_texUsageTracker.findUsage(*impl.getDepthStencilAttachment(), dsAttUsage);
+			ANKI_ASSERT(found);
+			(void)found;
+		}
+
+		bi.renderPass = impl.getRenderPassHandle(colAttUsages, dsAttUsage);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -148,7 +164,6 @@ void CommandBufferImpl::beginRenderPassInternal()
 	}
 	}
 
 
 	ANKI_CMD(vkCmdBeginRenderPass(m_handle, &bi, m_subpassContents), ANY_OTHER_COMMAND);
 	ANKI_CMD(vkCmdBeginRenderPass(m_handle, &bi, m_subpassContents), ANY_OTHER_COMMAND);
-#endif
 }
 }
 
 
 void CommandBufferImpl::endRenderPass()
 void CommandBufferImpl::endRenderPass()

+ 48 - 0
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -7,6 +7,8 @@
 
 
 #include <anki/gr/vulkan/VulkanObject.h>
 #include <anki/gr/vulkan/VulkanObject.h>
 #include <anki/gr/CommandBuffer.h>
 #include <anki/gr/CommandBuffer.h>
+#include <anki/gr/Texture.h>
+#include <anki/gr/vulkan/TextureImpl.h>
 #include <anki/util/List.h>
 #include <anki/util/List.h>
 
 
 namespace anki
 namespace anki
@@ -216,6 +218,52 @@ private:
 	U8 m_deferredDsetBindingMask = 0;
 	U8 m_deferredDsetBindingMask = 0;
 	VkPipelineLayout m_crntPplineLayout = VK_NULL_HANDLE;
 	VkPipelineLayout m_crntPplineLayout = VK_NULL_HANDLE;
 
 
+	/// Track texture usage.
+	class TextureUsageTracker
+	{
+	public:
+		Bool findUsage(const Texture& tex, TextureUsageBit& usage) const
+		{
+			auto it = m_map.find(tex.getUuid());
+			if(it != m_map.getEnd())
+			{
+				usage = (*it);
+				return true;
+			}
+			else if(tex.m_impl->m_usageWhenEncountered != TextureUsageBit::NONE)
+			{
+				usage = tex.m_impl->m_usageWhenEncountered;
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		void setUsage(const Texture& tex, TextureUsageBit usage, StackAllocator<U8>& alloc)
+		{
+			ANKI_ASSERT(usage != TextureUsageBit::NONE);
+			auto it = m_map.find(tex.getUuid());
+			if(it != m_map.getEnd())
+			{
+				(*it) = usage;
+			}
+			else
+			{
+				m_map.pushBack(alloc, tex.getUuid(), usage);
+			}
+		}
+
+		void destroy(StackAllocator<U8>& alloc)
+		{
+			m_map.destroy(alloc);
+		}
+
+	private:
+		HashMap<U64, TextureUsageBit> m_map;
+	} m_texUsageTracker;
+
 	/// Some common operations per command.
 	/// Some common operations per command.
 	void commandCommon();
 	void commandCommon();
 
 

+ 60 - 4
src/anki/gr/vulkan/FramebufferImpl.cpp

@@ -27,6 +27,40 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 	// Create the FBs
 	// Create the FBs
 	ANKI_CHECK(initFbs(init));
 	ANKI_CHECK(initFbs(init));
 
 
+	// Set clear values
+	for(U i = 0; i < m_colorAttCount; ++i)
+	{
+		if(init.m_colorAttachments[i].m_loadOperation == AttachmentLoadOperation::CLEAR)
+		{
+			F32* col = &m_clearVals[i].color.float32[0];
+			col[0] = init.m_colorAttachments[i].m_clearValue.m_colorf[0];
+			col[1] = init.m_colorAttachments[i].m_clearValue.m_colorf[1];
+			col[2] = init.m_colorAttachments[i].m_clearValue.m_colorf[2];
+			col[3] = init.m_colorAttachments[i].m_clearValue.m_colorf[3];
+		}
+		else
+		{
+			m_clearVals[i] = {};
+		}
+	}
+
+	if(hasDepthStencil())
+	{
+		if(init.m_depthStencilAttachment.m_loadOperation == AttachmentLoadOperation::CLEAR
+			|| init.m_depthStencilAttachment.m_stencilLoadOperation == AttachmentLoadOperation::CLEAR)
+		{
+			m_clearVals[m_colorAttCount].depthStencil.depth =
+				init.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth;
+
+			m_clearVals[m_colorAttCount].depthStencil.stencil =
+				init.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_stencil;
+		}
+		else
+		{
+			m_clearVals[m_colorAttCount] = {};
+		}
+	}
+
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
@@ -57,6 +91,7 @@ Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 		}
 		}
 
 
 		m_colorAttachmentMask.set(0);
 		m_colorAttachmentMask.set(0);
+		m_colorAttCount = 1;
 	}
 	}
 	else
 	else
 	{
 	{
@@ -78,6 +113,7 @@ Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 
 
 			m_refs[i] = att.m_texture;
 			m_refs[i] = att.m_texture;
 			m_colorAttachmentMask.set(i);
 			m_colorAttachmentMask.set(i);
+			m_colorAttCount = i + 1;
 		}
 		}
 
 
 		if(hasDepthStencil)
 		if(hasDepthStencil)
@@ -177,14 +213,34 @@ void FramebufferImpl::initRpassCreateInfo(const FramebufferInitInfo& init)
 	m_rpassCi.pSubpasses = &m_subpassDescr;
 	m_rpassCi.pSubpasses = &m_subpassDescr;
 }
 }
 
 
-VkRenderPass FramebufferImpl::getRenderPass(WeakArray<TextureUsageBit> usages)
+VkRenderPass FramebufferImpl::getRenderPassHandle(
+	const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorUsages, TextureUsageBit dsUsage)
 {
 {
 	VkRenderPass out;
 	VkRenderPass out;
 
 
 	if(!m_defaultFb)
 	if(!m_defaultFb)
 	{
 	{
 		// Create hash
 		// Create hash
-		U64 hash = computeHash(&usages[0], usages.getSize() * sizeof(usages[0]));
+		Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS + 1> allUsages;
+		U allUsageCount = 0;
+		for(U i = 0; i < MAX_COLOR_ATTACHMENTS; ++i)
+		{
+			if(m_colorAttachmentMask.get(i))
+			{
+				ANKI_ASSERT(!!(colorUsages[i]));
+				allUsages[allUsageCount++] = colorUsages[i];
+			}
+		}
+
+		if(m_depthAttachment || m_stencilAttachment)
+		{
+			ANKI_ASSERT(!!(dsUsage));
+			allUsages[allUsageCount++] = dsUsage;
+		}
+
+		U64 hash = computeHash(&allUsages[0], allUsageCount * sizeof(allUsages[0]));
+
+		// Get or create
 		LockGuard<Mutex> lock(m_rpassesMtx);
 		LockGuard<Mutex> lock(m_rpassesMtx);
 		auto it = m_rpasses.find(hash);
 		auto it = m_rpasses.find(hash);
 		if(it != m_rpasses.getEnd())
 		if(it != m_rpasses.getEnd())
@@ -208,7 +264,7 @@ VkRenderPass FramebufferImpl::getRenderPass(WeakArray<TextureUsageBit> usages)
 
 
 			for(U i = 0; i < subpassDescr.colorAttachmentCount; ++i)
 			for(U i = 0; i < subpassDescr.colorAttachmentCount; ++i)
 			{
 			{
-				const VkImageLayout lay = m_refs[i]->m_impl->computeLayout(usages[i], m_attachedMipLevels[i]);
+				const VkImageLayout lay = m_refs[i]->m_impl->computeLayout(colorUsages[i], m_attachedMipLevels[i]);
 
 
 				attachmentDescriptions[i].initialLayout = lay;
 				attachmentDescriptions[i].initialLayout = lay;
 				attachmentDescriptions[i].finalLayout = lay;
 				attachmentDescriptions[i].finalLayout = lay;
@@ -219,7 +275,7 @@ VkRenderPass FramebufferImpl::getRenderPass(WeakArray<TextureUsageBit> usages)
 			if(m_refs[MAX_COLOR_ATTACHMENTS])
 			if(m_refs[MAX_COLOR_ATTACHMENTS])
 			{
 			{
 				const U i = MAX_COLOR_ATTACHMENTS;
 				const U i = MAX_COLOR_ATTACHMENTS;
-				const VkImageLayout lay = m_refs[i]->m_impl->computeLayout(usages[i], m_attachedMipLevels[i]);
+				const VkImageLayout lay = m_refs[i]->m_impl->computeLayout(dsUsage, m_attachedMipLevels[i]);
 
 
 				attachmentDescriptions[i].initialLayout = lay;
 				attachmentDescriptions[i].initialLayout = lay;
 				attachmentDescriptions[i].finalLayout = lay;
 				attachmentDescriptions[i].finalLayout = lay;

+ 49 - 11
src/anki/gr/vulkan/FramebufferImpl.h

@@ -39,9 +39,10 @@ public:
 	}
 	}
 
 
 	/// Use it for binding.
 	/// Use it for binding.
-	VkRenderPass getRenderPass(WeakArray<TextureUsageBit> usages);
+	VkRenderPass getRenderPassHandle(
+		const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorUsages, TextureUsageBit dsUsage);
 
 
-	VkFramebuffer getFramebuffer(U frame) const
+	VkFramebuffer getFramebufferHandle(U frame) const
 	{
 	{
 		ANKI_ASSERT(m_fbs[frame]);
 		ANKI_ASSERT(m_fbs[frame]);
 		return m_fbs[frame];
 		return m_fbs[frame];
@@ -54,16 +55,50 @@ public:
 		stencil = m_stencilAttachment;
 		stencil = m_stencilAttachment;
 	}
 	}
 
 
-private:
-	class Hasher
+	U getColorAttachmentCount() const
 	{
 	{
-	public:
-		U64 operator()(const U64 key) const
-		{
-			return key;
-		}
-	};
+		return m_colorAttCount;
+	}
 
 
+	Bool hasDepthStencil() const
+	{
+		return m_refs[MAX_COLOR_ATTACHMENTS].get() != nullptr;
+	}
+
+	U getAttachmentCount() const
+	{
+		return m_colorAttCount + (hasDepthStencil() ? 1 : 0);
+	}
+
+	TexturePtr getColorAttachment(U att) const
+	{
+		ANKI_ASSERT(m_refs[att].get());
+		return m_refs[att];
+	}
+
+	TexturePtr getDepthStencilAttachment() const
+	{
+		ANKI_ASSERT(m_refs[MAX_COLOR_ATTACHMENTS].get());
+		return m_refs[MAX_COLOR_ATTACHMENTS];
+	}
+
+	const VkClearValue* getClearValues() const
+	{
+		return &m_clearVals[0];
+	}
+
+	Bool isDefaultFramebuffer() const
+	{
+		return m_defaultFb;
+	}
+
+	void getAttachmentsSize(U32& width, U32& height) const
+	{
+		width = m_width;
+		height = m_height;
+	}
+
+private:
 	U32 m_width = 0;
 	U32 m_width = 0;
 	U32 m_height = 0;
 	U32 m_height = 0;
 
 
@@ -73,6 +108,9 @@ private:
 	Bool8 m_depthAttachment = false;
 	Bool8 m_depthAttachment = false;
 	Bool8 m_stencilAttachment = false;
 	Bool8 m_stencilAttachment = false;
 
 
+	U8 m_colorAttCount = 0;
+	Array<VkClearValue, MAX_COLOR_ATTACHMENTS + 1> m_clearVals;
+
 	Array<TexturePtr, MAX_COLOR_ATTACHMENTS + 1> m_refs; ///< @note The pos of every attachment is fixed.
 	Array<TexturePtr, MAX_COLOR_ATTACHMENTS + 1> m_refs; ///< @note The pos of every attachment is fixed.
 	Array<U32, MAX_COLOR_ATTACHMENTS + 1> m_attachedMipLevels = {};
 	Array<U32, MAX_COLOR_ATTACHMENTS + 1> m_attachedMipLevels = {};
 
 
@@ -84,7 +122,7 @@ private:
 
 
 	// VK objects
 	// VK objects
 	VkRenderPass m_rpass = {}; ///< Compatible renderpass or default FB's renderpass.
 	VkRenderPass m_rpass = {}; ///< Compatible renderpass or default FB's renderpass.
-	HashMap<U64, VkRenderPass, Hasher> m_rpasses;
+	HashMap<U64, VkRenderPass> m_rpasses;
 	Mutex m_rpassesMtx;
 	Mutex m_rpassesMtx;
 	Array<VkFramebuffer, MAX_FRAMES_IN_FLIGHT> m_fbs = {};
 	Array<VkFramebuffer, MAX_FRAMES_IN_FLIGHT> m_fbs = {};
 
 

+ 2 - 0
src/anki/gr/vulkan/TextureImpl.cpp

@@ -137,6 +137,8 @@ Error TextureImpl::init(const TextureInitInfo& init_, Texture* tex)
 	m_depthStencil = formatIsDepthStencil(m_format);
 	m_depthStencil = formatIsDepthStencil(m_format);
 	m_aspect = convertImageAspect(m_format);
 	m_aspect = convertImageAspect(m_format);
 	m_usage = init.m_usage;
 	m_usage = init.m_usage;
+	m_usageWhenEncountered = init.m_usageWhenEncountered;
+	ANKI_ASSERT((m_usageWhenEncountered | m_usage) == m_usage);
 
 
 	if(m_aspect & VK_IMAGE_ASPECT_DEPTH_BIT)
 	if(m_aspect & VK_IMAGE_ASPECT_DEPTH_BIT)
 	{
 	{

+ 1 - 0
src/anki/gr/vulkan/TextureImpl.h

@@ -44,6 +44,7 @@ public:
 	VkImageAspectFlags m_aspect = 0;
 	VkImageAspectFlags m_aspect = 0;
 	DepthStencilAspectBit m_akAspect = DepthStencilAspectBit::NONE;
 	DepthStencilAspectBit m_akAspect = DepthStencilAspectBit::NONE;
 	TextureUsageBit m_usage = TextureUsageBit::NONE;
 	TextureUsageBit m_usage = TextureUsageBit::NONE;
+	TextureUsageBit m_usageWhenEncountered = TextureUsageBit::NONE;
 	PixelFormat m_format;
 	PixelFormat m_format;
 	VkFormat m_vkFormat = VK_FORMAT_UNDEFINED;
 	VkFormat m_vkFormat = VK_FORMAT_UNDEFINED;
 
 

+ 10 - 2
src/anki/renderer/Fs.cpp

@@ -64,11 +64,18 @@ Error Fs::initInternal(const ConfigSet&)
 
 
 Error Fs::initVol()
 Error Fs::initVol()
 {
 {
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_vol.m_noiseTex));
+
 	ANKI_CHECK(m_r->createShaderf("shaders/VolumetricUpscale.frag.glsl",
 	ANKI_CHECK(m_r->createShaderf("shaders/VolumetricUpscale.frag.glsl",
 		m_vol.m_frag,
 		m_vol.m_frag,
-		"#define SRC_SIZE vec2(float(%u), float(%u))\n",
+		"#define SRC_SIZE vec2(float(%u), float(%u))\n"
+		"#define FB_SIZE vec2(float(%u), float(%u))\n"
+		"#define NOISE_TEX_SIZE %u\n",
 		m_r->getWidth() / VOLUMETRIC_FRACTION,
 		m_r->getWidth() / VOLUMETRIC_FRACTION,
-		m_r->getHeight() / VOLUMETRIC_FRACTION));
+		m_r->getHeight() / VOLUMETRIC_FRACTION,
+		m_width,
+		m_height,
+		m_vol.m_noiseTex->getWidth()));
 
 
 	m_r->createDrawQuadShaderProgram(m_vol.m_frag->getGrShader(), m_vol.m_prog);
 	m_r->createDrawQuadShaderProgram(m_vol.m_frag->getGrShader(), m_vol.m_prog);
 
 
@@ -95,6 +102,7 @@ void Fs::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb)
 	cmdb->bindTextureAndSampler(0, 0, m_r->getDepthDownscale().m_hd.m_depthRt, m_vol.m_nearestSampler);
 	cmdb->bindTextureAndSampler(0, 0, m_r->getDepthDownscale().m_hd.m_depthRt, m_vol.m_nearestSampler);
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_qd.m_depthRt, m_vol.m_nearestSampler);
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_qd.m_depthRt, m_vol.m_nearestSampler);
 	cmdb->bindTexture(0, 2, m_r->getVolumetric().m_rt);
 	cmdb->bindTexture(0, 2, m_r->getVolumetric().m_rt);
+	cmdb->bindTexture(0, 3, m_vol.m_noiseTex->getGrTexture());
 
 
 	m_r->drawQuad(cmdb);
 	m_r->drawQuad(cmdb);
 
 

+ 1 - 0
src/anki/renderer/Fs.h

@@ -68,6 +68,7 @@ private:
 		ShaderResourcePtr m_frag;
 		ShaderResourcePtr m_frag;
 		ShaderProgramPtr m_prog;
 		ShaderProgramPtr m_prog;
 		SamplerPtr m_nearestSampler;
 		SamplerPtr m_nearestSampler;
+		TextureResourcePtr m_noiseTex;
 	} m_vol;
 	} m_vol;
 
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);

+ 12 - 10
src/anki/renderer/FsUpscale.cpp

@@ -30,6 +30,8 @@ Error FsUpscale::initInternal(const ConfigSet& config)
 {
 {
 	ANKI_LOGI("Initializing forward shading upscale");
 	ANKI_LOGI("Initializing forward shading upscale");
 
 
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
+
 	GrManager& gr = getGrManager();
 	GrManager& gr = getGrManager();
 
 
 	SamplerInitInfo sinit;
 	SamplerInitInfo sinit;
@@ -38,19 +40,18 @@ Error FsUpscale::initInternal(const ConfigSet& config)
 	m_nearestSampler = gr.newInstance<Sampler>(sinit);
 	m_nearestSampler = gr.newInstance<Sampler>(sinit);
 
 
 	// Shader
 	// Shader
-	StringAuto pps(getFrameAllocator());
-	pps.sprintf("#define SRC_SIZE uvec2(%uu, %uu)\n"
-				"#define SSAO_ENABLED %u\n",
+	ANKI_CHECK(m_r->createShaderf("shaders/FsUpscale.frag.glsl",
+		m_frag,
+		"#define FB_SIZE uvec2(%uu, %uu)\n"
+		"#define SRC_SIZE uvec2(%uu, %uu)\n"
+		"#define NOISE_TEX_SIZE %u\n",
+		m_r->getWidth(),
+		m_r->getHeight(),
 		m_r->getWidth() / FS_FRACTION,
 		m_r->getWidth() / FS_FRACTION,
 		m_r->getHeight() / FS_FRACTION,
 		m_r->getHeight() / FS_FRACTION,
-		1);
-
-	ANKI_CHECK(m_r->createShader("shaders/FsUpscale.frag.glsl", m_frag, pps.toCString()));
-
-	ANKI_CHECK(m_r->createShader("shaders/Quad.vert.glsl", m_vert, pps.toCString()));
+		m_noiseTex->getWidth()));
 
 
-	// Prog
-	m_prog = gr.newInstance<ShaderProgram>(m_vert->getGrShader(), m_frag->getGrShader());
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
 
 	// Create FB
 	// Create FB
 	FramebufferInitInfo fbInit;
 	FramebufferInitInfo fbInit;
@@ -73,6 +74,7 @@ void FsUpscale::run(RenderingContext& ctx)
 	cmdb->bindTexture(0, 0, m_r->getMs().m_depthRt);
 	cmdb->bindTexture(0, 0, m_r->getMs().m_depthRt);
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_hd.m_depthRt, m_nearestSampler);
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_hd.m_depthRt, m_nearestSampler);
 	cmdb->bindTexture(0, 2, m_r->getFs().getRt());
 	cmdb->bindTexture(0, 2, m_r->getFs().getRt());
+	cmdb->bindTexture(0, 3, m_noiseTex->getGrTexture());
 
 
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::SRC_ALPHA);
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::SRC_ALPHA);
 
 

+ 2 - 1
src/anki/renderer/FsUpscale.h

@@ -29,10 +29,11 @@ public:
 private:
 private:
 	FramebufferPtr m_fb;
 	FramebufferPtr m_fb;
 	ShaderResourcePtr m_frag;
 	ShaderResourcePtr m_frag;
-	ShaderResourcePtr m_vert;
 	ShaderProgramPtr m_prog;
 	ShaderProgramPtr m_prog;
 	SamplerPtr m_nearestSampler;
 	SamplerPtr m_nearestSampler;
 
 
+	TextureResourcePtr m_noiseTex;
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
 };
 };
 /// @}
 /// @}

+ 0 - 18
src/anki/renderer/Ssao.cpp

@@ -19,24 +19,6 @@ const F32 HEMISPHERE_RADIUS = 3.0; // In game units
 const U SAMPLES = 8;
 const U SAMPLES = 8;
 const PixelFormat Ssao::RT_PIXEL_FORMAT(ComponentFormat::R8, TransformFormat::UNORM);
 const PixelFormat Ssao::RT_PIXEL_FORMAT(ComponentFormat::R8, TransformFormat::UNORM);
 
 
-template<typename TVec>
-static void genHemisphere(TVec* ANKI_RESTRICT arr, TVec* ANKI_RESTRICT arrEnd)
-{
-	ANKI_ASSERT(arr && arrEnd && arr != arrEnd);
-
-	do
-	{
-		// Calculate the normal
-		arr->x() = randRange(-1.0, 1.0);
-		arr->y() = randRange(-1.0, 1.0);
-		arr->z() = randRange(0.1, 1.0);
-		arr->normalize();
-
-		// Adjust the length
-		(*arr) *= randRange(HEMISPHERE_RADIUS / 2.0f, HEMISPHERE_RADIUS);
-	} while(++arr != arrEnd);
-}
-
 template<typename TVec>
 template<typename TVec>
 static void genDisk(TVec* ANKI_RESTRICT arr, TVec* ANKI_RESTRICT arrEnd)
 static void genDisk(TVec* ANKI_RESTRICT arr, TVec* ANKI_RESTRICT arrEnd)
 {
 {

+ 103 - 21
src/anki/renderer/Volumetric.cpp

@@ -20,8 +20,6 @@ Volumetric::~Volumetric()
 
 
 Error Volumetric::init(const ConfigSet& config)
 Error Volumetric::init(const ConfigSet& config)
 {
 {
-	ANKI_LOGI("Initializing volumetric pass");
-
 	Error err = initInternal(config);
 	Error err = initInternal(config);
 	if(err)
 	if(err)
 	{
 	{
@@ -33,15 +31,17 @@ Error Volumetric::init(const ConfigSet& config)
 
 
 Error Volumetric::initInternal(const ConfigSet& config)
 Error Volumetric::initInternal(const ConfigSet& config)
 {
 {
-	U width = m_r->getWidth() / VOLUMETRIC_FRACTION;
-	U height = m_r->getHeight() / VOLUMETRIC_FRACTION;
+	m_width = m_r->getWidth() / VOLUMETRIC_FRACTION;
+	m_height = m_r->getHeight() / VOLUMETRIC_FRACTION;
+
+	ANKI_LOGI("Initializing volumetric pass. Size %ux%u", m_width, m_height);
 
 
 	// Misc
 	// Misc
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
 
 
 	// Create RTs
 	// Create RTs
-	m_r->createRenderTarget(width,
-		height,
+	m_r->createRenderTarget(m_width,
+		m_height,
 		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE | TextureUsageBit::CLEAR,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE | TextureUsageBit::CLEAR,
 		SamplingFilter::LINEAR,
 		SamplingFilter::LINEAR,
@@ -50,28 +50,68 @@ Error Volumetric::initInternal(const ConfigSet& config)
 
 
 	m_r->clearRenderTarget(m_rt, ClearValue(), TextureUsageBit::SAMPLED_FRAGMENT);
 	m_r->clearRenderTarget(m_rt, ClearValue(), TextureUsageBit::SAMPLED_FRAGMENT);
 
 
-	// Create shaders
+	m_r->createRenderTarget(m_width,
+		m_height,
+		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		SamplingFilter::LINEAR,
+		1,
+		m_hRt);
+
+	// Create FBs
+	FramebufferInitInfo fbInit;
+	fbInit.m_colorAttachmentCount = 1;
+	fbInit.m_colorAttachments[0].m_texture = m_rt;
+	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::LOAD;
+	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+
+	fbInit.m_colorAttachments[0].m_texture = m_rt;
+	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
+	m_vFb = getGrManager().newInstance<Framebuffer>(fbInit);
+
+	fbInit.m_colorAttachments[0].m_texture = m_hRt;
+	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
+	m_hFb = getGrManager().newInstance<Framebuffer>(fbInit);
+
+	// Create shaders and progs
 	ANKI_CHECK(m_r->createShaderf("shaders/Volumetric.frag.glsl",
 	ANKI_CHECK(m_r->createShaderf("shaders/Volumetric.frag.glsl",
 		m_frag,
 		m_frag,
 		"#define FB_SIZE uvec2(%uu, %uu)\n"
 		"#define FB_SIZE uvec2(%uu, %uu)\n"
 		"#define CLUSTER_COUNT uvec3(%uu, %uu, %uu)\n"
 		"#define CLUSTER_COUNT uvec3(%uu, %uu, %uu)\n"
 		"#define NOISE_MAP_SIZE %u\n",
 		"#define NOISE_MAP_SIZE %u\n",
-		width,
-		height,
+		m_width,
+		m_height,
 		m_r->getIs().getLightBin().getClusterer().getClusterCountX(),
 		m_r->getIs().getLightBin().getClusterer().getClusterCountX(),
 		m_r->getIs().getLightBin().getClusterer().getClusterCountY(),
 		m_r->getIs().getLightBin().getClusterer().getClusterCountY(),
 		m_r->getIs().getLightBin().getClusterer().getClusterCountZ(),
 		m_r->getIs().getLightBin().getClusterer().getClusterCountZ(),
 		m_noiseTex->getWidth()));
 		m_noiseTex->getWidth()));
 
 
-	// Create prog
 	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
 
-	// Create FBs
-	FramebufferInitInfo fbInit;
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::LOAD;
-	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+	// const char* shader = "shaders/DepthAwareBlurGeneric.frag.glsl";
+	// const char* shader = "shaders/GaussianBlurGeneric.frag.glsl";
+	const char* shader = "shaders/LumaAwareBlurGeneric.frag.glsl";
+	ANKI_CHECK(m_r->createShaderf(shader,
+		m_hFrag,
+		"#define HPASS\n"
+		"#define COL_RGB\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 11\n",
+		F32(m_width),
+		F32(m_height)));
+
+	m_r->createDrawQuadShaderProgram(m_hFrag->getGrShader(), m_hProg);
+
+	ANKI_CHECK(m_r->createShaderf(shader,
+		m_vFrag,
+		"#define VPASS\n"
+		"#define COL_RGB\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 11\n",
+		F32(m_width),
+		F32(m_height)));
+
+	m_r->createDrawQuadShaderProgram(m_vFrag->getGrShader(), m_vProg);
 
 
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
@@ -79,7 +119,7 @@ Error Volumetric::initInternal(const ConfigSet& config)
 void Volumetric::setPreRunBarriers(RenderingContext& ctx)
 void Volumetric::setPreRunBarriers(RenderingContext& ctx)
 {
 {
 	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
 	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::NONE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 }
@@ -87,7 +127,7 @@ void Volumetric::setPreRunBarriers(RenderingContext& ctx)
 void Volumetric::setPostRunBarriers(RenderingContext& ctx)
 void Volumetric::setPostRunBarriers(RenderingContext& ctx)
 {
 {
 	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
 	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 }
@@ -97,8 +137,10 @@ void Volumetric::run(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	const Frustum& frc = ctx.m_frustumComponent->getFrustum();
 	const Frustum& frc = ctx.m_frustumComponent->getFrustum();
 
 
-	// pass
-	cmdb->setViewport(0, 0, m_r->getWidth() / VOLUMETRIC_FRACTION, m_r->getHeight() / VOLUMETRIC_FRACTION);
+	//
+	// Main pass
+	//
+	cmdb->setViewport(0, 0, m_width, m_height);
 	cmdb->setBlendFactors(0, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA);
 	cmdb->setBlendFactors(0, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA);
 
 
 	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
 	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
@@ -113,7 +155,7 @@ void Volumetric::run(RenderingContext& ctx)
 	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4) * 2, cmdb, 0, 3);
 	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4) * 2, cmdb, 0, 3);
 	computeLinearizeDepthOptimal(frc.getNear(), frc.getFar(), uniforms[0].x(), uniforms[0].y());
 	computeLinearizeDepthOptimal(frc.getNear(), frc.getFar(), uniforms[0].x(), uniforms[0].y());
 
 
-	F32 texelOffset = (1.0 / m_noiseTex->getWidth()) * 1.0;
+	F32 texelOffset = 1.0 / m_noiseTex->getWidth();
 	uniforms[0].z() = m_r->getFrameCount() * texelOffset;
 	uniforms[0].z() = m_r->getFrameCount() * texelOffset;
 	uniforms[0].w() = m_r->getFrameCount() & (m_noiseTex->getLayerCount() - 1);
 	uniforms[0].w() = m_r->getFrameCount() & (m_noiseTex->getLayerCount() - 1);
 
 
@@ -130,6 +172,46 @@ void Volumetric::run(RenderingContext& ctx)
 
 
 	// Restore state
 	// Restore state
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
+
+	cmdb->setTextureSurfaceBarrier(m_rt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSurfaceInfo(0, 0, 0, 0));
+	cmdb->setTextureSurfaceBarrier(
+		m_hRt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+
+	//
+	// H Blur pass
+	//
+	Vec4* buniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
+	computeLinearizeDepthOptimal(frc.getNear(), frc.getFar(), buniforms[0].x(), buniforms[0].y());
+
+	cmdb->bindTexture(0, 0, m_rt);
+	cmdb->bindTexture(0, 1, m_r->getDepthDownscale().m_qd.m_depthRt);
+
+	cmdb->bindShaderProgram(m_hProg);
+
+	cmdb->beginRenderPass(m_hFb);
+	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
+
+	cmdb->setTextureSurfaceBarrier(m_rt,
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureSurfaceInfo(0, 0, 0, 0));
+	cmdb->setTextureSurfaceBarrier(m_hRt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSurfaceInfo(0, 0, 0, 0));
+
+	//
+	// V Blur pass
+	//
+	cmdb->bindTexture(0, 0, m_hRt);
+	cmdb->bindShaderProgram(m_vProg);
+	cmdb->beginRenderPass(m_vFb);
+	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 14 - 3
src/anki/renderer/Volumetric.h

@@ -23,7 +23,7 @@ public:
 	}
 	}
 
 
 anki_internal:
 anki_internal:
-	TexturePtr m_rt;
+	TexturePtr m_rt; ///< vRT
 
 
 	Volumetric(Renderer* r)
 	Volumetric(Renderer* r)
 		: RenderingPass(r)
 		: RenderingPass(r)
@@ -39,12 +39,23 @@ anki_internal:
 	void setPostRunBarriers(RenderingContext& ctx);
 	void setPostRunBarriers(RenderingContext& ctx);
 
 
 private:
 private:
+	U32 m_width = 0, m_height = 0;
+	Vec3 m_fogParticleColor = Vec3(1.0);
+	Mat3x4 m_prevCameraRot = Mat3x4::getIdentity();
+
+	TexturePtr m_hRt;
+
 	ShaderResourcePtr m_frag;
 	ShaderResourcePtr m_frag;
 	ShaderProgramPtr m_prog;
 	ShaderProgramPtr m_prog;
 	FramebufferPtr m_fb;
 	FramebufferPtr m_fb;
 
 
-	Vec3 m_fogParticleColor = Vec3(1.0);
-	Mat3x4 m_prevCameraRot = Mat3x4::getIdentity();
+	ShaderResourcePtr m_vFrag;
+	ShaderProgramPtr m_vProg;
+	FramebufferPtr m_vFb;
+
+	ShaderResourcePtr m_hFrag;
+	ShaderProgramPtr m_hProg;
+	FramebufferPtr m_hFb;
 
 
 	TextureResourcePtr m_noiseTex;
 	TextureResourcePtr m_noiseTex;
 
 

+ 3 - 0
src/anki/util/HashMap.h

@@ -261,6 +261,9 @@ public:
 	/// Find item.
 	/// Find item.
 	Iterator find(const Key& key);
 	Iterator find(const Key& key);
 
 
+	/// Find item.
+	ConstIterator find(const Key& key) const;
+
 protected:
 protected:
 	/// @privatesection
 	/// @privatesection
 	TNode* m_root = nullptr;
 	TNode* m_root = nullptr;

+ 29 - 0
src/anki/util/HashMap.inl.h

@@ -86,6 +86,35 @@ HashMapBase<TKey, TValue, THasher, TCompare, TNode>::find(const Key& key)
 	return Iterator(node);
 	return Iterator(node);
 }
 }
 
 
+template<typename TKey, typename TValue, typename THasher, typename TCompare, typename TNode>
+typename HashMapBase<TKey, TValue, THasher, TCompare, TNode>::ConstIterator
+HashMapBase<TKey, TValue, THasher, TCompare, TNode>::find(const Key& key) const
+{
+	const U64 hash = THasher()(key);
+
+	const TNode* node = m_root;
+	while(node)
+	{
+		const U64 bhash = node->m_hash;
+
+		if(hash < bhash)
+		{
+			node = node->m_left;
+		}
+		else if(hash > bhash)
+		{
+			node = node->m_right;
+		}
+		else
+		{
+			// Found
+			break;
+		}
+	}
+
+	return ConstIterator(node);
+}
+
 template<typename TKey, typename TValue, typename THasher, typename TCompare, typename TNode>
 template<typename TKey, typename TValue, typename THasher, typename TCompare, typename TNode>
 void HashMapBase<TKey, TValue, THasher, TCompare, TNode>::removeNode(TNode* del)
 void HashMapBase<TKey, TValue, THasher, TCompare, TNode>::removeNode(TNode* del)
 {
 {