Browse Source

Vulkan: Bug fixes and refactoring. Renderer: refactoring

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
c3ab40d70b

+ 1 - 1
src/anki/gr/gl/FramebufferImpl.cpp

@@ -114,7 +114,7 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 void FramebufferImpl::attachTextureInternal(
 	GLenum attachment, const TextureImpl& tex, const FramebufferAttachmentInfo& info)
 {
-	tex.checkSurface(info.m_surface);
+	tex.checkSurfaceOrVolume(info.m_surface);
 
 	const GLenum target = GL_FRAMEBUFFER;
 	switch(tex.m_target)

+ 2 - 2
src/anki/gr/gl/GrManager.cpp

@@ -55,7 +55,7 @@ void GrManager::finish()
 void GrManager::getTextureSurfaceUploadInfo(TexturePtr tex, const TextureSurfaceInfo& surf, PtrSize& allocationSize)
 {
 	const TextureImpl& impl = *tex->m_impl;
-	impl.checkSurface(surf);
+	impl.checkSurfaceOrVolume(surf);
 
 	U width = impl.m_width >> surf.m_level;
 	U height = impl.m_height >> surf.m_level;
@@ -65,7 +65,7 @@ void GrManager::getTextureSurfaceUploadInfo(TexturePtr tex, const TextureSurface
 void GrManager::getTextureVolumeUploadInfo(TexturePtr tex, const TextureVolumeInfo& vol, PtrSize& allocationSize)
 {
 	const TextureImpl& impl = *tex->m_impl;
-	impl.checkVolume(vol);
+	impl.checkSurfaceOrVolume(vol);
 
 	U width = impl.m_width >> vol.m_level;
 	U height = impl.m_height >> vol.m_level;

+ 3 - 3
src/anki/gr/gl/TextureImpl.cpp

@@ -250,7 +250,7 @@ void TextureImpl::init(const TextureInitInfo& init)
 
 void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, GLuint pbo, PtrSize offset, PtrSize dataSize)
 {
-	checkSurface(surf);
+	checkSurfaceOrVolume(surf);
 	ANKI_ASSERT(dataSize > 0);
 
 	U mipmap = surf.m_level;
@@ -308,7 +308,7 @@ void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, GLuint pbo, PtrSi
 
 void TextureImpl::writeVolume(const TextureVolumeInfo& vol, GLuint pbo, PtrSize offset, PtrSize dataSize)
 {
-	checkVolume(vol);
+	checkSurfaceOrVolume(vol);
 	ANKI_ASSERT(dataSize > 0);
 	ANKI_ASSERT(m_texType == TextureType::_3D);
 
@@ -453,7 +453,7 @@ void TextureImpl::clear(const TextureSurfaceInfo& surf, const ClearValue& clearV
 
 U TextureImpl::computeSurfaceIdx(const TextureSurfaceInfo& surf) const
 {
-	checkSurface(surf);
+	checkSurfaceOrVolume(surf);
 	U out;
 
 	if(m_target == GL_TEXTURE_3D)

+ 2 - 2
src/anki/gr/gl/TextureImpl.h

@@ -43,12 +43,12 @@ public:
 
 	~TextureImpl();
 
-	void checkSurface(const TextureSurfaceInfo& surf) const
+	void checkSurfaceOrVolume(const TextureSurfaceInfo& surf) const
 	{
 		checkTextureSurface(m_texType, m_depth, m_mipsCount, m_layerCount, surf);
 	}
 
-	void checkVolume(const TextureVolumeInfo& vol) const
+	void checkSurfaceOrVolume(const TextureVolumeInfo& vol) const
 	{
 		ANKI_ASSERT(m_texType == TextureType::_3D);
 		ANKI_ASSERT(vol.m_level < m_mipsCount);

+ 1 - 1
src/anki/gr/vulkan/CommandBuffer.cpp

@@ -297,7 +297,7 @@ void CommandBuffer::writeOcclusionQueryResultToBuffer(OcclusionQueryPtr query, P
 void CommandBuffer::copyBufferToBuffer(
 	BufferPtr src, PtrSize srcOffset, BufferPtr dst, PtrSize dstOffset, PtrSize range)
 {
-	ANKI_ASSERT(!"TODO");
+	m_impl->copyBufferToBuffer(src, srcOffset, dst, dstOffset, range);
 }
 
 void CommandBuffer::setTextureSurfaceBarrier(

+ 34 - 32
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -50,8 +50,29 @@ CommandBufferImpl::~CommandBufferImpl()
 	m_queryResetAtoms.destroy(m_alloc);
 	m_writeQueryAtoms.destroy(m_alloc);
 	m_secondLevelAtoms.destroy(m_alloc);
+}
+
+Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
+{
+	auto& pool = getGrManagerImpl().getAllocator().getMemoryPool();
+	m_alloc = StackAllocator<U8>(
+		pool.getAllocationCallback(), pool.getAllocationCallbackUserData(), init.m_hints.m_chunkSize, 1.0, 0, false);
+
+	m_texUsageTracker.init(m_alloc);
+
+	m_flags = init.m_flags;
+	m_tid = Thread::getCurrentThreadId();
 
-	m_texUsageTracker.destroy(m_alloc);
+	m_handle = getGrManagerImpl().newCommandBuffer(m_tid, m_flags);
+	ANKI_ASSERT(m_handle);
+
+	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
+	{
+		m_activeFb = init.m_framebuffer;
+		m_state.beginRenderPass(m_activeFb);
+	}
+
+	return ErrorCode::NONE;
 }
 
 void CommandBufferImpl::beginRecording()
@@ -72,14 +93,15 @@ void CommandBufferImpl::beginRecording()
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
 		for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
 		{
-			colAttLayouts[i] = m_texUsageTracker.findLayout(*impl.getColorAttachment(i), impl.getAttachedSurfaces()[i]);
+			colAttLayouts[i] = impl.getColorAttachment(i)->m_impl->findLayoutFromTracker(
+				impl.getAttachedSurfaces()[i], m_texUsageTracker);
 		}
 
 		VkImageLayout dsAttLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
 		if(impl.hasDepthStencil())
 		{
-			dsAttLayout = m_texUsageTracker.findLayout(
-				*impl.getDepthStencilAttachment(), impl.getAttachedSurfaces()[MAX_COLOR_ATTACHMENTS]);
+			dsAttLayout = impl.getDepthStencilAttachment()->m_impl->findLayoutFromTracker(
+				impl.getAttachedSurfaces()[MAX_COLOR_ATTACHMENTS], m_texUsageTracker);
 		}
 
 		inheritance.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
@@ -100,27 +122,6 @@ void CommandBufferImpl::beginRecording()
 	vkBeginCommandBuffer(m_handle, &begin);
 }
 
-Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
-{
-	auto& pool = getGrManagerImpl().getAllocator().getMemoryPool();
-	m_alloc = StackAllocator<U8>(
-		pool.getAllocationCallback(), pool.getAllocationCallbackUserData(), init.m_hints.m_chunkSize, 1.0, 0, false);
-
-	m_flags = init.m_flags;
-	m_tid = Thread::getCurrentThreadId();
-
-	m_handle = getGrManagerImpl().newCommandBuffer(m_tid, m_flags);
-	ANKI_ASSERT(m_handle);
-
-	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
-	{
-		m_activeFb = init.m_framebuffer;
-		m_state.beginRenderPass(m_activeFb);
-	}
-
-	return ErrorCode::NONE;
-}
-
 void CommandBufferImpl::beginRenderPass(FramebufferPtr fb)
 {
 	commandCommon();
@@ -156,14 +157,15 @@ void CommandBufferImpl::beginRenderPassInternal()
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
 		for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
 		{
-			colAttLayouts[i] = m_texUsageTracker.findLayout(*impl.getColorAttachment(i), impl.getAttachedSurfaces()[i]);
+			colAttLayouts[i] = impl.getColorAttachment(i)->m_impl->findLayoutFromTracker(
+				impl.getAttachedSurfaces()[i], m_texUsageTracker);
 		}
 
 		VkImageLayout dsAttLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
 		if(impl.hasDepthStencil())
 		{
-			dsAttLayout = m_texUsageTracker.findLayout(
-				*impl.getDepthStencilAttachment(), impl.getAttachedSurfaces()[MAX_COLOR_ATTACHMENTS]);
+			dsAttLayout = impl.getDepthStencilAttachment()->m_impl->findLayoutFromTracker(
+				impl.getAttachedSurfaces()[MAX_COLOR_ATTACHMENTS], m_texUsageTracker);
 		}
 
 		bi.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
@@ -595,9 +597,9 @@ void CommandBufferImpl::copyBufferToTextureSurface(
 	commandCommon();
 
 	TextureImpl& impl = *tex->m_impl;
-	impl.checkSurface(surf);
+	impl.checkSurfaceOrVolume(surf);
 	ANKI_ASSERT(impl.usageValid(TextureUsageBit::TRANSFER_DESTINATION));
-	const VkImageLayout layout = m_texUsageTracker.findLayout(*tex, surf);
+	const VkImageLayout layout = impl.findLayoutFromTracker(surf, m_texUsageTracker);
 
 	if(!impl.m_workarounds)
 	{
@@ -695,9 +697,9 @@ void CommandBufferImpl::copyBufferToTextureVolume(
 	commandCommon();
 
 	TextureImpl& impl = *tex->m_impl;
-	impl.checkVolume(vol);
+	impl.checkSurfaceOrVolume(vol);
 	ANKI_ASSERT(impl.usageValid(TextureUsageBit::TRANSFER_DESTINATION));
-	const VkImageLayout layout = m_texUsageTracker.findLayout(*tex, vol);
+	const VkImageLayout layout = impl.findLayoutFromTracker(vol, m_texUsageTracker);
 
 	if(!impl.m_workarounds)
 	{

+ 8 - 69
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -11,6 +11,7 @@
 #include <anki/gr/Buffer.h>
 #include <anki/gr/vulkan/BufferImpl.h>
 #include <anki/gr/vulkan/TextureImpl.h>
+#include <anki/gr/vulkan/TextureUsageTracker.h>
 #include <anki/gr/vulkan/Pipeline.h>
 #include <anki/util/List.h>
 
@@ -195,7 +196,7 @@ public:
 	{
 		const U realBinding = binding;
 		Texture& tex = *tex_;
-		VkImageLayout lay = m_texUsageTracker.findLayout(tex);
+		const VkImageLayout lay = tex.m_impl->findLayoutFromTracker(m_texUsageTracker);
 		m_dsetState[set].bindTexture(realBinding, &tex, aspect, lay);
 		m_texList.pushBack(m_alloc, tex_);
 	}
@@ -204,7 +205,7 @@ public:
 	{
 		const U realBinding = binding;
 		Texture& tex = *tex_;
-		VkImageLayout lay = m_texUsageTracker.findLayout(tex);
+		const VkImageLayout lay = tex.m_impl->findLayoutFromTracker(m_texUsageTracker);
 		m_dsetState[set].bindTextureAndSampler(realBinding, &tex, sampler.get(), aspect, lay);
 		m_texList.pushBack(m_alloc, tex_);
 		m_samplerList.pushBack(m_alloc, sampler);
@@ -300,14 +301,16 @@ public:
 
 	void informTextureSurfaceCurrentUsage(TexturePtr& tex, const TextureSurfaceInfo& surf, TextureUsageBit crntUsage)
 	{
-		m_texUsageTracker.setUsage(*tex, surf, crntUsage, m_alloc);
+		tex->m_impl->updateTracker(surf, crntUsage, m_texUsageTracker);
 	}
 
 	void informTextureVolumeCurrentUsage(TexturePtr& tex, const TextureVolumeInfo& vol, TextureUsageBit crntUsage)
 	{
-		m_texUsageTracker.setUsage(*tex, vol, crntUsage, m_alloc);
+		tex->m_impl->updateTracker(vol, crntUsage, m_texUsageTracker);
 	}
 
+	void copyBufferToBuffer(BufferPtr& src, PtrSize srcOffset, BufferPtr& dst, PtrSize dstOffset, PtrSize range);
+
 private:
 	StackAllocator<U8> m_alloc;
 
@@ -399,71 +402,7 @@ private:
 	/// @}
 
 	/// Track texture usage.
-	class TextureUsageTracker
-	{
-	public:
-		template<typename TextureInfo>
-		ANKI_USE_RESULT VkImageLayout findLayout(const Texture& tex, const TextureInfo& surf) const
-		{
-			auto it = m_map.find(tex.getUuid());
-			ANKI_ASSERT((it != m_map.getEnd() || tex.m_impl->m_usageWhenEncountered != TextureUsageBit::NONE)
-				&& "Cannot find the layout of the image");
-			if(it == m_map.getEnd())
-			{
-				return tex.m_impl->computeLayout(tex.m_impl->m_usageWhenEncountered, surf.m_level);
-			}
-			else
-			{
-				return tex.m_impl->getLayoutFromState(surf, *it);
-			}
-		}
-
-		ANKI_USE_RESULT VkImageLayout findLayout(const Texture& tex) const
-		{
-			auto it = m_map.find(tex.getUuid());
-			ANKI_ASSERT((it != m_map.getEnd() || tex.m_impl->m_usageWhenEncountered != TextureUsageBit::NONE)
-				&& "Cannot find the layout of the image");
-			if(it == m_map.getEnd())
-			{
-				return tex.m_impl->computeLayout(tex.m_impl->m_usageWhenEncountered, 0);
-			}
-			else
-			{
-				return tex.m_impl->getLayoutFromState(*it);
-			}
-		}
-
-		template<typename TextureInfo>
-		void setUsage(const Texture& tex, const TextureInfo& surf, TextureUsageBit usage, StackAllocator<U8>& alloc)
-		{
-			ANKI_ASSERT(usage != TextureUsageBit::NONE);
-
-			auto it = m_map.find(tex.getUuid());
-			if(it != m_map.getEnd())
-			{
-				tex.m_impl->updateUsageState(surf, usage, *it, alloc);
-			}
-			else
-			{
-				TextureUsageState state;
-				tex.m_impl->updateUsageState(surf, usage, state, alloc);
-				m_map.emplaceBack(alloc, tex.getUuid(), std::move(state));
-			}
-		}
-
-		void destroy(StackAllocator<U8>& alloc)
-		{
-			for(auto& it : m_map)
-			{
-				it.destroy(alloc);
-			}
-
-			m_map.destroy(alloc);
-		}
-
-	private:
-		HashMap<U64, TextureUsageState> m_map;
-	} m_texUsageTracker;
+	TextureUsageTracker m_texUsageTracker;
 
 	/// Some common operations per command.
 	void commandCommon();

+ 21 - 4
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -158,13 +158,13 @@ inline void CommandBufferImpl::setTextureSurfaceBarrier(
 	}
 
 	const TextureImpl& impl = *tex->m_impl;
-	impl.checkSurface(surf);
+	impl.checkSurfaceOrVolume(surf);
 
 	VkImageSubresourceRange range;
 	impl.computeSubResourceRange(surf, impl.m_akAspect, range);
 	setTextureBarrierRange(tex, prevUsage, nextUsage, range);
 
-	m_texUsageTracker.setUsage(*tex, surf, nextUsage, m_alloc);
+	impl.updateTracker(surf, nextUsage, m_texUsageTracker);
 }
 
 inline void CommandBufferImpl::setTextureVolumeBarrier(
@@ -177,13 +177,13 @@ inline void CommandBufferImpl::setTextureVolumeBarrier(
 	}
 
 	const TextureImpl& impl = *tex->m_impl;
-	impl.checkVolume(vol);
+	impl.checkSurfaceOrVolume(vol);
 
 	VkImageSubresourceRange range;
 	impl.computeSubResourceRange(vol, impl.m_akAspect, range);
 	setTextureBarrierRange(tex, prevUsage, nextUsage, range);
 
-	m_texUsageTracker.setUsage(*tex, vol, nextUsage, m_alloc);
+	impl.updateTracker(vol, nextUsage, m_texUsageTracker);
 }
 
 inline void CommandBufferImpl::setBufferBarrier(VkPipelineStageFlags srcStage,
@@ -691,4 +691,21 @@ inline void CommandBufferImpl::bindShaderProgram(ShaderProgramPtr& prog)
 	m_progs.pushBack(m_alloc, prog);
 }
 
+inline void CommandBufferImpl::copyBufferToBuffer(
+	BufferPtr& src, PtrSize srcOffset, BufferPtr& dst, PtrSize dstOffset, PtrSize range)
+{
+	commandCommon();
+
+	VkBufferCopy region = {};
+	region.srcOffset = srcOffset;
+	region.dstOffset = dstOffset;
+	region.size = range;
+
+	ANKI_CMD(
+		vkCmdCopyBuffer(m_handle, src->m_impl->getHandle(), dst->m_impl->getHandle(), 1, &region), ANY_OTHER_COMMAND);
+
+	m_bufferList.pushBack(m_alloc, src);
+	m_bufferList.pushBack(m_alloc, dst);
+}
+
 } // end namespace anki

+ 2 - 2
src/anki/gr/vulkan/GrManager.cpp

@@ -51,7 +51,7 @@ void GrManager::finish()
 void GrManager::getTextureSurfaceUploadInfo(TexturePtr tex, const TextureSurfaceInfo& surf, PtrSize& allocationSize)
 {
 	const TextureImpl& impl = *tex->m_impl;
-	impl.checkSurface(surf);
+	impl.checkSurfaceOrVolume(surf);
 
 	U width = impl.m_width >> surf.m_level;
 	U height = impl.m_height >> surf.m_level;
@@ -78,7 +78,7 @@ void GrManager::getTextureSurfaceUploadInfo(TexturePtr tex, const TextureSurface
 void GrManager::getTextureVolumeUploadInfo(TexturePtr tex, const TextureVolumeInfo& vol, PtrSize& allocationSize)
 {
 	const TextureImpl& impl = *tex->m_impl;
-	impl.checkVolume(vol);
+	impl.checkSurfaceOrVolume(vol);
 
 	U width = impl.m_width >> vol.m_level;
 	U height = impl.m_height >> vol.m_level;

+ 1 - 1
src/anki/gr/vulkan/ShaderProgramImpl.cpp

@@ -38,7 +38,7 @@ Error ShaderProgramImpl::init(const Array<ShaderPtr, U(ShaderType::COUNT)>& shad
 
 	// Merge bindings
 	//
-	Array2d<DescriptorBinding, MAX_BINDINGS_PER_DESCRIPTOR_SET, MAX_DESCRIPTOR_SETS> bindings;
+	Array2d<DescriptorBinding, MAX_DESCRIPTOR_SETS, MAX_BINDINGS_PER_DESCRIPTOR_SET> bindings;
 	Array<U, MAX_DESCRIPTOR_SETS> counts = {};
 	U descriptorSetCount = 0;
 	for(U set = 0; set < MAX_DESCRIPTOR_SETS; ++set)

+ 1 - 1
src/anki/gr/vulkan/Texture.cpp

@@ -20,7 +20,7 @@ Texture::~Texture()
 
 void Texture::init(const TextureInitInfo& init)
 {
-	m_impl.reset(getAllocator().newInstance<TextureImpl>(&getManager()));
+	m_impl.reset(getAllocator().newInstance<TextureImpl>(&getManager(), getUuid()));
 
 	if(m_impl->init(init, this))
 	{

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

@@ -15,8 +15,9 @@
 namespace anki
 {
 
-TextureImpl::TextureImpl(GrManager* manager)
+TextureImpl::TextureImpl(GrManager* manager, U64 uuid)
 	: VulkanObject(manager)
+	, m_uuid(uuid)
 {
 }
 
@@ -623,7 +624,7 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 
 VkImageView TextureImpl::getOrCreateSingleSurfaceView(const TextureSurfaceInfo& surf, DepthStencilAspectBit aspect)
 {
-	checkSurface(surf);
+	checkSurfaceOrVolume(surf);
 
 	VkImageViewCreateInfo ci = m_viewCreateInfoTemplate;
 	ci.viewType = VK_IMAGE_VIEW_TYPE_2D;

+ 23 - 70
src/anki/gr/vulkan/TextureImpl.h

@@ -13,6 +13,10 @@
 namespace anki
 {
 
+// Forward
+class TextureUsageTracker;
+class TextureUsageState;
+
 /// @addtogroup vulkan
 /// @{
 
@@ -25,17 +29,6 @@ enum class TextureImplWorkaround : U8
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(TextureImplWorkaround, inline)
 
-class TextureUsageState
-{
-public:
-	DynamicArray<VkImageLayout> m_subResources; ///< Volumes or surfaces.
-
-	void destroy(StackAllocator<U8>& alloc)
-	{
-		m_subResources.destroy(alloc);
-	}
-};
-
 /// Texture container.
 class TextureImpl : public VulkanObject
 {
@@ -63,18 +56,18 @@ public:
 	Bool m_depthStencil = false;
 	TextureImplWorkaround m_workarounds = TextureImplWorkaround::NONE;
 
-	TextureImpl(GrManager* manager);
+	TextureImpl(GrManager* manager, U64 uuid);
 
 	~TextureImpl();
 
 	ANKI_USE_RESULT Error init(const TextureInitInfo& init, Texture* tex);
 
-	void checkSurface(const TextureSurfaceInfo& surf) const
+	void checkSurfaceOrVolume(const TextureSurfaceInfo& surf) const
 	{
 		checkTextureSurface(m_type, m_depth, m_mipCount, m_layerCount, surf);
 	}
 
-	void checkVolume(const TextureVolumeInfo& vol) const
+	void checkSurfaceOrVolume(const TextureVolumeInfo& vol) const
 	{
 		ANKI_ASSERT(m_type == TextureType::_3D);
 		ANKI_ASSERT(vol.m_level < m_mipCount);
@@ -126,64 +119,13 @@ public:
 		ANKI_ASSERT(range.baseMipLevel + range.levelCount <= m_mipCount);
 	}
 
-	VkImageLayout getLayoutFromState(const TextureSurfaceInfo& surf, const TextureUsageState& state) const
-	{
-		checkSurface(surf);
-		const U surfIdx = computeSubresourceIdx(surf);
-		ANKI_ASSERT(state.m_subResources[surfIdx] != VK_IMAGE_LAYOUT_UNDEFINED);
-		return state.m_subResources[surfIdx];
-	}
-
-	VkImageLayout getLayoutFromState(const TextureVolumeInfo& vol, const TextureUsageState& state) const
-	{
-		checkVolume(vol);
-		ANKI_ASSERT(state.m_subResources[vol.m_level] != VK_IMAGE_LAYOUT_UNDEFINED);
-		return state.m_subResources[vol.m_level];
-	}
-
-	VkImageLayout getLayoutFromState(const TextureUsageState& state) const
-	{
-		for(VkImageLayout l : state.m_subResources)
-		{
-			ANKI_ASSERT(l == state.m_subResources[0] && "Not all image subresources are in the same layout");
-		}
-		ANKI_ASSERT(state.m_subResources[0] != VK_IMAGE_LAYOUT_UNDEFINED);
-		return state.m_subResources[0];
-	}
+	template<typename TextureInfo>
+	VkImageLayout findLayoutFromTracker(const TextureInfo& surfOrVol, const TextureUsageTracker& tracker) const;
 
-	void updateUsageState(const TextureSurfaceInfo& surf,
-		TextureUsageBit usage,
-		TextureUsageState& state,
-		StackAllocator<U8>& alloc) const
-	{
-		checkSurface(surf);
-		if(ANKI_UNLIKELY(state.m_subResources.getSize() == 0))
-		{
-			state.m_subResources.create(alloc, m_surfaceOrVolumeCount);
-			memorySet(reinterpret_cast<int*>(&state.m_subResources[0]),
-				int(VK_IMAGE_LAYOUT_UNDEFINED),
-				m_surfaceOrVolumeCount);
-		}
-		auto lay = computeLayout(usage, surf.m_level);
-		ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
-		state.m_subResources[computeSubresourceIdx(surf)] = lay;
-	}
+	VkImageLayout findLayoutFromTracker(const TextureUsageTracker& tracker) const;
 
-	void updateUsageState(
-		const TextureVolumeInfo& vol, TextureUsageBit usage, TextureUsageState& state, StackAllocator<U8>& alloc) const
-	{
-		checkVolume(vol);
-		if(ANKI_UNLIKELY(state.m_subResources.getSize() == 0))
-		{
-			state.m_subResources.create(alloc, m_surfaceOrVolumeCount);
-			memorySet(reinterpret_cast<int*>(&state.m_subResources[0]),
-				int(VK_IMAGE_LAYOUT_UNDEFINED),
-				m_surfaceOrVolumeCount);
-		}
-		auto lay = computeLayout(usage, vol.m_level);
-		ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
-		state.m_subResources[vol.m_level] = lay;
-	}
+	template<typename TextureInfo>
+	void updateTracker(const TextureInfo& surfOrVol, TextureUsageBit usage, TextureUsageTracker& tracker) const;
 
 private:
 	class ViewHasher
@@ -207,6 +149,7 @@ private:
 	HashMap<VkImageViewCreateInfo, VkImageView, ViewHasher, ViewCompare> m_viewsMap;
 	Mutex m_viewsMapMtx;
 	VkImageViewCreateInfo m_viewCreateInfoTemplate;
+	U64 m_uuid; ///< Steal the UUID from the Texture.
 
 	ANKI_USE_RESULT static VkFormatFeatureFlags calcFeatures(const TextureInitInfo& init);
 
@@ -219,6 +162,16 @@ private:
 	VkImageView getOrCreateView(const VkImageViewCreateInfo& ci);
 
 	U computeSubresourceIdx(const TextureSurfaceInfo& surf) const;
+
+	U computeSubresourceIdx(const TextureVolumeInfo& vol) const
+	{
+		checkSurfaceOrVolume(vol);
+		return vol.m_level;
+	}
+
+	template<typename TextureInfo>
+	void updateUsageState(
+		const TextureInfo& surfOrVol, TextureUsageBit usage, StackAllocator<U8>& alloc, TextureUsageState& state) const;
 };
 /// @}
 

+ 85 - 4
src/anki/gr/vulkan/TextureImpl.inl.h

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/gr/vulkan/TextureImpl.h>
+#include <anki/gr/vulkan/TextureUsageTracker.h>
 
 namespace anki
 {
@@ -29,7 +30,7 @@ inline VkImageAspectFlags TextureImpl::convertAspect(DepthStencilAspectBit ak) c
 inline void TextureImpl::computeSubResourceRange(
 	const TextureSurfaceInfo& surf, DepthStencilAspectBit aspect, VkImageSubresourceRange& range) const
 {
-	checkSurface(surf);
+	checkSurfaceOrVolume(surf);
 	range.aspectMask = convertAspect(aspect);
 	range.baseMipLevel = surf.m_level;
 	range.levelCount = 1;
@@ -60,7 +61,7 @@ inline void TextureImpl::computeSubResourceRange(
 inline void TextureImpl::computeSubResourceRange(
 	const TextureVolumeInfo& vol, DepthStencilAspectBit aspect, VkImageSubresourceRange& range) const
 {
-	checkVolume(vol);
+	checkSurfaceOrVolume(vol);
 	range.aspectMask = convertAspect(aspect);
 	range.baseMipLevel = vol.m_level;
 	range.levelCount = 1;
@@ -70,7 +71,7 @@ inline void TextureImpl::computeSubResourceRange(
 
 inline U TextureImpl::computeVkArrayLayer(const TextureSurfaceInfo& surf) const
 {
-	checkSurface(surf);
+	checkSurfaceOrVolume(surf);
 	U layer = 0;
 	switch(m_type)
 	{
@@ -98,7 +99,7 @@ inline U TextureImpl::computeVkArrayLayer(const TextureSurfaceInfo& surf) const
 
 inline U TextureImpl::computeSubresourceIdx(const TextureSurfaceInfo& surf) const
 {
-	checkSurface(surf);
+	checkSurfaceOrVolume(surf);
 
 	switch(m_type)
 	{
@@ -121,4 +122,84 @@ inline U TextureImpl::computeSubresourceIdx(const TextureSurfaceInfo& surf) cons
 	}
 }
 
+template<typename TextureInfo>
+inline VkImageLayout TextureImpl::findLayoutFromTracker(
+	const TextureInfo& surfOrVol, const TextureUsageTracker& tracker) const
+{
+	checkSurfaceOrVolume(surfOrVol);
+
+	auto it = tracker.m_map.find(m_uuid);
+	if(it == tracker.m_map.getEnd())
+	{
+		ANKI_ASSERT(m_usageWhenEncountered != TextureUsageBit::NONE);
+		return computeLayout(m_usageWhenEncountered, surfOrVol.m_level);
+	}
+	else
+	{
+		const U idx = computeSubresourceIdx(surfOrVol);
+		const VkImageLayout out = it->m_subResources[idx];
+		ANKI_ASSERT(out != VK_IMAGE_LAYOUT_UNDEFINED);
+		return out;
+	}
+}
+
+inline VkImageLayout TextureImpl::findLayoutFromTracker(const TextureUsageTracker& tracker) const
+{
+	auto it = tracker.m_map.find(m_uuid);
+	if(it == tracker.m_map.getEnd())
+	{
+		ANKI_ASSERT(m_usageWhenEncountered != TextureUsageBit::NONE);
+		return computeLayout(m_usageWhenEncountered, 0);
+	}
+	else
+	{
+		for(U i = 0; i < it->m_subResources.getSize(); ++i)
+		{
+			ANKI_ASSERT(
+				it->m_subResources[i] == it->m_subResources[0] && "Not all image subresources are in the same layout");
+		}
+		const VkImageLayout out = it->m_subResources[0];
+		ANKI_ASSERT(out != VK_IMAGE_LAYOUT_UNDEFINED);
+		return out;
+	}
+}
+
+template<typename TextureInfo>
+inline void TextureImpl::updateTracker(
+	const TextureInfo& surfOrVol, TextureUsageBit usage, TextureUsageTracker& tracker) const
+{
+	ANKI_ASSERT(usage != TextureUsageBit::NONE);
+	checkSurfaceOrVolume(surfOrVol);
+
+	auto it = tracker.m_map.find(m_uuid);
+	if(it != tracker.m_map.getEnd())
+	{
+		// Found
+		updateUsageState(surfOrVol, usage, tracker.m_alloc, *it);
+	}
+	else
+	{
+		// Not found
+		TextureUsageState state;
+		updateUsageState(surfOrVol, usage, tracker.m_alloc, state);
+		tracker.m_map.emplaceBack(tracker.m_alloc, m_uuid, std::move(state));
+	}
+}
+
+template<typename TextureInfo>
+inline void TextureImpl::updateUsageState(
+	const TextureInfo& surfOrVol, TextureUsageBit usage, StackAllocator<U8>& alloc, TextureUsageState& state) const
+{
+	checkSurfaceOrVolume(surfOrVol);
+	if(ANKI_UNLIKELY(state.m_subResources.getSize() == 0))
+	{
+		state.m_subResources.create(alloc, m_surfaceOrVolumeCount);
+		memorySet(
+			reinterpret_cast<int*>(&state.m_subResources[0]), int(VK_IMAGE_LAYOUT_UNDEFINED), m_surfaceOrVolumeCount);
+	}
+	const VkImageLayout lay = computeLayout(usage, surfOrVol.m_level);
+	ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
+	state.m_subResources[computeSubresourceIdx(surfOrVol)] = lay;
+}
+
 } // end namespace anki

+ 55 - 0
src/anki/gr/vulkan/TextureUsageTracker.h

@@ -0,0 +1,55 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/Texture.h>
+#include <anki/gr/vulkan/TextureImpl.h>
+#include <anki/util/HashMap.h>
+
+namespace anki
+{
+
+/// @addtogroup vulkan
+/// @{
+
+class TextureUsageState
+{
+public:
+	DynamicArray<VkImageLayout> m_subResources; ///< Volumes or surfaces.
+
+	void destroy(StackAllocator<U8>& alloc)
+	{
+		m_subResources.destroy(alloc);
+	}
+};
+
+class TextureUsageTracker
+{
+	friend class TextureImpl;
+
+public:
+	~TextureUsageTracker()
+	{
+		for(auto& it : m_map)
+		{
+			it.destroy(m_alloc);
+		}
+
+		m_map.destroy(m_alloc);
+	}
+
+	void init(StackAllocator<U8> alloc)
+	{
+		m_alloc = alloc;
+	}
+
+private:
+	StackAllocator<U8> m_alloc;
+	HashMap<U64, TextureUsageState> m_map;
+};
+/// @}
+
+} // end namespace anki

+ 0 - 2
src/anki/renderer/Bloom.h

@@ -14,8 +14,6 @@
 namespace anki
 {
 
-class ShaderProgram;
-
 /// @addtogroup renderer
 /// @{
 

+ 6 - 1
src/anki/renderer/Fs.cpp

@@ -99,9 +99,14 @@ void Fs::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb)
 	Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	computeLinearizeDepthOptimal(fr.getNear(), fr.getFar(), unis->x(), unis->y());
 
+	cmdb->informTextureSurfaceCurrentUsage(
+		m_r->getDepthDownscale().m_qd.m_depthRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
+	cmdb->informTextureSurfaceCurrentUsage(
+		m_r->getVolumetric().m_main.m_rt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
+
 	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->bindTexture(0, 2, m_r->getVolumetric().m_rt);
+	cmdb->bindTexture(0, 2, m_r->getVolumetric().m_main.m_rt);
 	cmdb->bindTexture(0, 3, m_vol.m_noiseTex->getGrTexture());
 
 	m_r->drawQuad(cmdb);

+ 1 - 1
src/anki/renderer/Is.cpp

@@ -189,7 +189,7 @@ void Is::run(RenderingContext& ctx)
 
 	cmdb->bindTexture(1, 0, (ctx.m_is.m_diffDecalTex) ? ctx.m_is.m_diffDecalTex : m_dummyTex);
 	cmdb->bindTexture(1, 1, (ctx.m_is.m_normRoughnessDecalTex) ? ctx.m_is.m_normRoughnessDecalTex : m_dummyTex);
-	cmdb->bindTexture(1, 2, m_r->getSsao().getRt());
+	cmdb->bindTexture(1, 2, m_r->getSsao().m_main.m_rt);
 
 	bindUniforms(cmdb, 0, 0, ctx.m_is.m_commonToken);
 	bindUniforms(cmdb, 0, 1, ctx.m_is.m_pointLightsToken);

+ 7 - 3
src/anki/renderer/Ms.cpp

@@ -120,6 +120,13 @@ Error Ms::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount)
 		CommandBufferPtr cmdb = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
 		ctx.m_ms.m_commandBuffers[threadId] = cmdb;
 
+		// Inform on RTs
+		TextureSurfaceInfo surf(0, 0, 0, 0);
+		cmdb->informTextureSurfaceCurrentUsage(m_rt0, surf, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE);
+		cmdb->informTextureSurfaceCurrentUsage(m_rt1, surf, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE);
+		cmdb->informTextureSurfaceCurrentUsage(m_rt2, surf, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE);
+		cmdb->informTextureSurfaceCurrentUsage(m_depthRt, surf, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
+
 		// Set some state, leave the rest to default
 		cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
@@ -166,11 +173,8 @@ void Ms::setPreRunBarriers(RenderingContext& ctx)
 	TextureSurfaceInfo surf(0, 0, 0, 0);
 
 	cmdb->setTextureSurfaceBarrier(m_rt0, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf);
-
 	cmdb->setTextureSurfaceBarrier(m_rt1, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf);
-
 	cmdb->setTextureSurfaceBarrier(m_rt2, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf);
-
 	cmdb->setTextureSurfaceBarrier(
 		m_depthRt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, surf);
 

+ 65 - 22
src/anki/renderer/Renderer.cpp

@@ -184,65 +184,94 @@ Error Renderer::render(RenderingContext& ctx)
 	m_lf->resetOcclusionQueries(ctx, cmdb);
 	ANKI_CHECK(buildCommandBuffers(ctx));
 
-	// Perform image transitions
+	// Barriers
 	m_sm->setPreRunBarriers(ctx);
 	m_ms->setPreRunBarriers(ctx);
-	m_is->setPreRunBarriers(ctx);
-	m_fs->setPreRunBarriers(ctx);
-	m_ssao->setPreRunBarriers(ctx);
-	m_bloom->m_extractExposure.setPreRunBarriers(ctx);
-	m_bloom->m_upscale.setPreRunBarriers(ctx);
-	m_depth->m_hd.setPreRunBarriers(ctx);
-	m_depth->m_qd.setPreRunBarriers(ctx);
-	m_smaa->m_edge.setPreRunBarriers(ctx);
-	m_smaa->m_weights.setPreRunBarriers(ctx);
-	m_vol->setPreRunBarriers(ctx);
 
-	// SM
+	// Passes
 	m_sm->run(ctx);
-
 	m_ms->run(ctx);
 
+	// Barriers
 	m_ms->setPostRunBarriers(ctx);
 	m_sm->setPostRunBarriers(ctx);
+	m_depth->m_hd.setPreRunBarriers(ctx);
+	m_is->setPreRunBarriers(ctx);
 
-	// Batch IS + HD
+	// Passes
 	m_is->run(ctx);
 	m_depth->m_hd.run(ctx);
 
+	// Barriers
 	m_depth->m_hd.setPostRunBarriers(ctx);
+	m_depth->m_qd.setPreRunBarriers(ctx);
 
+	// Passes
 	m_depth->m_qd.run(ctx);
 
+	// Barriers
 	m_depth->m_qd.setPostRunBarriers(ctx);
+	m_vol->m_main.setPreRunBarriers(ctx);
+	m_ssao->m_main.setPreRunBarriers(ctx);
 
-	m_vol->run(ctx);
-	m_vol->setPostRunBarriers(ctx);
+	// Passes
+	m_vol->m_main.run(ctx);
+	m_ssao->m_main.run(ctx);
+
+	// Barriers
+	m_vol->m_main.setPostRunBarriers(ctx);
+	m_vol->m_hblur.setPreRunBarriers(ctx);
+	m_ssao->m_main.setPostRunBarriers(ctx);
+	m_ssao->m_hblur.setPreRunBarriers(ctx);
 
+	// Misc
 	m_lf->updateIndirectInfo(ctx, cmdb);
 
-	// Batch FS & SSAO & VOL
+	// Passes
+	m_vol->m_hblur.run(ctx);
+	m_ssao->m_hblur.run(ctx);
+
+	// Barriers
+	m_vol->m_hblur.setPostRunBarriers(ctx);
+	m_vol->m_vblur.setPreRunBarriers(ctx);
+	m_ssao->m_hblur.setPostRunBarriers(ctx);
+	m_ssao->m_vblur.setPreRunBarriers(ctx);
+
+	// Passes
+	m_vol->m_vblur.run(ctx);
+	m_ssao->m_vblur.run(ctx);
+
+	// Barriers
+	m_vol->m_vblur.setPostRunBarriers(ctx);
+	m_ssao->m_vblur.setPostRunBarriers(ctx);
+	m_fs->setPreRunBarriers(ctx);
+
+	// Passes
 	m_fs->run(ctx);
-	m_ssao->run(ctx);
 
-	m_ssao->setPostRunBarriers(ctx);
+	// Barriers
 	m_fs->setPostRunBarriers(ctx);
 
+	// Passes
 	m_fsUpscale->run(ctx);
 
+	// Barriers
 	cmdb->setTextureSurfaceBarrier(m_is->getRt(),
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
 
+	// Passes
 	m_downscale->run(ctx);
 
+	// Barriers
 	cmdb->setTextureSurfaceBarrier(m_is->getRt(),
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_COMPUTE,
 		TextureSurfaceInfo(m_is->getRtMipmapCount() - 1, 0, 0, 0));
+	m_smaa->m_edge.setPreRunBarriers(ctx);
 
-	// Batch TM + SMAA_pass0
+	// Passes
 	m_tm->run(ctx);
 	m_smaa->m_edge.run(ctx);
 
@@ -252,17 +281,22 @@ Error Renderer::render(RenderingContext& ctx)
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(m_is->getRtMipmapCount() - 1, 0, 0, 0));
 	m_smaa->m_edge.setPostRunBarriers(ctx);
+	m_bloom->m_extractExposure.setPreRunBarriers(ctx);
+	m_smaa->m_weights.setPreRunBarriers(ctx);
 
-	// Batch bloom + SSLF + SMAA_pass1
+	// Passes
 	m_bloom->m_extractExposure.run(ctx);
 	m_smaa->m_weights.run(ctx);
 
 	// Barriers
 	m_bloom->m_extractExposure.setPostRunBarriers(ctx);
+	m_bloom->m_upscale.setPreRunBarriers(ctx);
 	m_smaa->m_weights.setPostRunBarriers(ctx);
 
-	// Bloom last pass
+	// Passes
 	m_bloom->m_upscale.run(ctx);
+
+	// Barriers
 	m_bloom->m_upscale.setPostRunBarriers(ctx);
 
 	if(m_dbg->getEnabled())
@@ -270,6 +304,7 @@ Error Renderer::render(RenderingContext& ctx)
 		ANKI_CHECK(m_dbg->run(ctx));
 	}
 
+	// Passes
 	ANKI_CHECK(m_pps->run(ctx));
 
 	++m_frameCount;
@@ -385,6 +420,14 @@ Error Renderer::buildCommandBuffersInternal(RenderingContext& ctx, U32 threadId,
 			CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::SMALL_BATCH;
 		init.m_framebuffer = m_fs->getFramebuffer();
 		CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(init);
+
+		// Inform on textures
+		cmdb->informTextureSurfaceCurrentUsage(
+			m_fs->getRt(), TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
+		cmdb->informTextureSurfaceCurrentUsage(m_depth->m_hd.m_depthRt,
+			TextureSurfaceInfo(0, 0, 0, 0),
+			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ | TextureUsageBit::SAMPLED_FRAGMENT);
+
 		cmdb->setViewport(0, 0, m_fs->getWidth(), m_fs->getHeight());
 
 		m_lf->run(ctx, cmdb);

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

@@ -305,6 +305,7 @@ anki_internal:
 		U mipsCount,
 		TexturePtr& rt);
 
+	/// Clear using graphics.
 	void clearRenderTarget(TexturePtr rt, const ClearValue& clear, TextureUsageBit transferTo);
 
 	ANKI_USE_RESULT Error createShader(CString fname, ShaderResourcePtr& shader, CString extra);

+ 14 - 2
src/anki/renderer/Sm.cpp

@@ -51,6 +51,8 @@ Error Sm::initInternal(const ConfigSet& config)
 	// Create shadowmaps array
 	TextureInitInfo sminit;
 	sminit.m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE;
+	sminit.m_usageWhenEncountered = TextureUsageBit::SAMPLED_FRAGMENT;
+	sminit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
 	sminit.m_type = TextureType::_2D_ARRAY;
 	sminit.m_width = m_resolution;
 	sminit.m_height = m_resolution;
@@ -271,6 +273,11 @@ Error Sm::doSpotLight(SceneNode& light, CommandBufferPtr& cmdb, FramebufferPtr&
 	cinf.m_framebuffer = fb;
 	cmdb = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
 
+	// Inform on Rts
+	cmdb->informTextureSurfaceCurrentUsage(m_spotTexArray,
+		TextureSurfaceInfo(0, 0, 0, light.getComponent<LightComponent>().getShadowMapIndex()),
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
+
 	// Set state
 	cmdb->setViewport(0, 0, m_resolution, m_resolution);
 	cmdb->setPolygonOffset(1.0, 2.0);
@@ -304,6 +311,11 @@ Error Sm::doOmniLight(
 			cinf.m_framebuffer = fbs[frCount];
 			cmdbs[frCount] = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
 
+			// Inform on Rts
+			cmdbs[frCount]->informTextureSurfaceCurrentUsage(m_omniTexArray,
+				TextureSurfaceInfo(0, 0, frCount, light.getComponent<LightComponent>().getShadowMapIndex()),
+				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
+
 			// Set state
 			cmdbs[frCount]->setViewport(0, 0, m_resolution, m_resolution);
 			cmdbs[frCount]->setPolygonOffset(1.0, 2.0);
@@ -400,7 +412,7 @@ void Sm::prepareBuildCommandBuffers(RenderingContext& ctx)
 		for(U i = 0; i < spotCastersCount; ++i)
 		{
 			const LightComponent& lightc = ctx.m_sm.m_spots[i]->getComponent<LightComponent>();
-			U idx = lightc.getShadowMapIndex();
+			const U idx = lightc.getShadowMapIndex();
 
 			ctx.m_sm.m_spotFramebuffers[i] = m_spots[idx].m_fb;
 			ctx.m_sm.m_spotCacheIndices[i] = idx;
@@ -423,7 +435,7 @@ void Sm::prepareBuildCommandBuffers(RenderingContext& ctx)
 		for(U i = 0; i < omniCastersCount; ++i)
 		{
 			const LightComponent& lightc = ctx.m_sm.m_omnis[i]->getComponent<LightComponent>();
-			U idx = lightc.getShadowMapIndex();
+			const U idx = lightc.getShadowMapIndex();
 
 			for(U j = 0; j < 6; ++j)
 			{

+ 152 - 128
src/anki/renderer/Ssao.cpp

@@ -15,14 +15,12 @@
 namespace anki
 {
 
-const F32 HEMISPHERE_RADIUS = 3.0; // In game units
-const U SAMPLES = 8;
 const PixelFormat Ssao::RT_PIXEL_FORMAT(ComponentFormat::R8, TransformFormat::UNORM);
 
 template<typename TVec>
 static void genDisk(TVec* ANKI_RESTRICT arr, TVec* ANKI_RESTRICT arrEnd)
 {
-	ANKI_ASSERT(arr && arrEnd && arr != arrEnd);
+	ANKI_ASSERT(arr && arrEnd && arr < arrEnd);
 
 	do
 	{
@@ -31,53 +29,31 @@ static void genDisk(TVec* ANKI_RESTRICT arr, TVec* ANKI_RESTRICT arrEnd)
 	} while(++arr != arrEnd);
 }
 
-Error Ssao::createFb(FramebufferPtr& fb, TexturePtr& rt)
+Error SsaoMain::init(const ConfigSet& config)
 {
-	// Set to bilinear because the blurring techniques take advantage of that
-	m_r->createRenderTarget(m_width,
-		m_height,
-		RT_PIXEL_FORMAT,
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+	// RT
+	m_r->createRenderTarget(m_ssao->m_width,
+		m_ssao->m_height,
+		Ssao::RT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::CLEAR,
 		SamplingFilter::LINEAR,
 		1,
-		rt);
+		m_rt);
+
+	ClearValue clear;
+	m_r->clearRenderTarget(m_rt, clear, TextureUsageBit::SAMPLED_FRAGMENT);
 
-	// Create FB
+	// FB
 	FramebufferInitInfo fbInit;
 	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = rt;
+	fbInit.m_colorAttachments[0].m_texture = m_rt;
 	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	fb = getGrManager().newInstance<Framebuffer>(fbInit);
-
-	return ErrorCode::NONE;
-}
-
-Error Ssao::initInternal(const ConfigSet& config)
-{
-	m_blurringIterationsCount = config.getNumber("ssao.blurringIterationsCount");
+	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
 
-	//
-	// Init the widths/heights
-	//
-	m_width = m_r->getWidth() / SSAO_FRACTION;
-	m_height = m_r->getHeight() / SSAO_FRACTION;
-
-	ANKI_R_LOGI("Initializing SSAO. Size %ux%u", m_width, m_height);
-
-	//
-	// create FBOs
-	//
-	ANKI_CHECK(createFb(m_hblurFb, m_hblurRt));
-	ANKI_CHECK(createFb(m_vblurFb, m_vblurRt));
-
-	//
-	// noise texture
-	//
+	// Noise
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
 
-	//
 	// Kernel
-	//
 	StringAuto kernelStr(getAllocator());
 	Array<Vec2, SAMPLES> kernel;
 
@@ -90,13 +66,9 @@ Error Ssao::initInternal(const ConfigSet& config)
 		kernelStr.append(tmp);
 	}
 
-	//
-	// Shaders
-	//
-
-	// main pass prog
+	// Shader
 	ANKI_CHECK(m_r->createShaderf("shaders/Ssao.frag.glsl",
-		m_ssaoFrag,
+		m_frag,
 		"#define NOISE_MAP_SIZE %u\n"
 		"#define WIDTH %u\n"
 		"#define HEIGHT %u\n"
@@ -104,130 +76,182 @@ Error Ssao::initInternal(const ConfigSet& config)
 		"#define KERNEL_ARRAY %s\n"
 		"#define RADIUS float(%f)\n",
 		m_noiseTex->getWidth(),
-		m_width,
-		m_height,
+		m_ssao->m_width,
+		m_ssao->m_height,
 		SAMPLES,
 		&kernelStr[0],
 		HEMISPHERE_RADIUS));
 
-	m_r->createDrawQuadShaderProgram(m_ssaoFrag->getGrShader(), m_ssaoProg);
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
+
+	return ErrorCode::NONE;
+}
+
+void SsaoMain::setPreRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(
+		m_rt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+}
+
+void SsaoMain::run(RenderingContext& ctx)
+{
+	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+
+	cmdb->beginRenderPass(m_fb);
+	cmdb->setViewport(0, 0, m_ssao->m_width, m_ssao->m_height);
+	cmdb->bindShaderProgram(m_prog);
+
+	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
+	cmdb->bindTexture(0, 1, m_r->getMs().m_rt2);
+	cmdb->bindTexture(0, 2, m_noiseTex->getGrTexture());
+
+	Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4) * 2, cmdb, 0, 0);
+	const FrustumComponent& frc = *ctx.m_frustumComponent;
+	const Mat4& pmat = frc.getProjectionMatrix();
+	*unis = frc.getProjectionParameters();
+	++unis;
+	*unis = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
 
-	// h blur
-	const char* SHADER_FILENAME = "shaders/GaussianBlurGeneric.frag.glsl";
+	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
+}
+
+void SsaoMain::setPostRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSurfaceInfo(0, 0, 0, 0));
+}
 
-	ANKI_CHECK(m_r->createShaderf(SHADER_FILENAME,
-		m_hblurFrag,
+Error SsaoHBlur::init(const ConfigSet& config)
+{
+	// RT
+	m_r->createRenderTarget(m_ssao->m_width,
+		m_ssao->m_height,
+		Ssao::RT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		SamplingFilter::LINEAR,
+		1,
+		m_rt);
+
+	// FB
+	FramebufferInitInfo fbInit;
+	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);
+
+	// shader
+	ANKI_CHECK(m_r->createShaderf("shaders/GaussianBlurGeneric.frag.glsl",
+		m_frag,
 		"#define HPASS\n"
 		"#define COL_R\n"
 		"#define TEXTURE_SIZE vec2(%f, %f)\n"
 		"#define KERNEL_SIZE 11\n",
-		F32(m_width),
-		F32(m_height)));
+		F32(m_ssao->m_width),
+		F32(m_ssao->m_height)));
 
-	m_r->createDrawQuadShaderProgram(m_hblurFrag->getGrShader(), m_hblurProg);
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
-	// v blur
-	ANKI_CHECK(m_r->createShaderf(SHADER_FILENAME,
-		m_vblurFrag,
-		"#define VPASS\n"
-		"#define COL_R\n"
-		"#define TEXTURE_SIZE vec2(%f, %f)\n"
-		"#define KERNEL_SIZE 9\n",
-		F32(m_width),
-		F32(m_height)));
+	return ErrorCode::NONE;
+}
 
-	m_r->createDrawQuadShaderProgram(m_vblurFrag->getGrShader(), m_vblurProg);
+void SsaoHBlur::setPreRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(
+		m_rt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+}
 
-	return ErrorCode::NONE;
+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_fb);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->bindTexture(0, 0, m_ssao->m_main.m_rt);
+	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
 }
 
-Error Ssao::init(const ConfigSet& config)
+void SsaoHBlur::setPostRunBarriers(RenderingContext& ctx)
 {
-	Error err = initInternal(config);
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSurfaceInfo(0, 0, 0, 0));
+}
 
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to init PPS SSAO");
-	}
+Error SsaoVBlur::init(const ConfigSet& config)
+{
+	ANKI_CHECK(m_r->createShaderf("shaders/GaussianBlurGeneric.frag.glsl",
+		m_frag,
+		"#define VPASS\n"
+		"#define COL_R\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 9\n",
+		F32(m_ssao->m_width),
+		F32(m_ssao->m_height)));
 
-	return err;
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
+
+	return ErrorCode::NONE;
 }
 
-void Ssao::setPreRunBarriers(RenderingContext& ctx)
+void SsaoVBlur::setPreRunBarriers(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vblurRt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_main.m_rt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 
-void Ssao::setPostRunBarriers(RenderingContext& ctx)
+void SsaoVBlur::run(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vblurRt,
+	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+
+	cmdb->setViewport(0, 0, m_ssao->m_width, m_ssao->m_height);
+	cmdb->beginRenderPass(m_ssao->m_main.m_fb);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->bindTexture(0, 0, m_ssao->m_hblur.m_rt);
+	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
+}
+
+void SsaoVBlur::setPostRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_main.m_rt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 
-void Ssao::run(RenderingContext& ctx)
+Error Ssao::init(const ConfigSet& config)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	m_width = m_r->getWidth() / SSAO_FRACTION;
+	m_height = m_r->getHeight() / SSAO_FRACTION;
 
-	// 1st pass
-	//
-	cmdb->beginRenderPass(m_vblurFb);
-	cmdb->setViewport(0, 0, m_width, m_height);
-	cmdb->bindShaderProgram(m_ssaoProg);
+	ANKI_R_LOGI("Initializing SSAO. Size %ux%u", m_width, m_height);
 
-	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
-	cmdb->bindTexture(0, 1, m_r->getMs().m_rt2);
-	cmdb->bindTexture(0, 2, m_noiseTex->getGrTexture());
+	Error err = m_main.init(config);
 
-	Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4) * 2, cmdb, 0, 0);
-	const FrustumComponent& frc = *ctx.m_frustumComponent;
-	const Mat4& pmat = frc.getProjectionMatrix();
-	*unis = frc.getProjectionParameters();
-	++unis;
-	*unis = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
+	if(!err)
+	{
+		err = m_hblur.init(config);
+	}
 
-	// Draw
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
+	if(!err)
+	{
+		err = m_vblur.init(config);
+	}
 
-	// Blurring passes
-	//
-	for(U i = 0; i < m_blurringIterationsCount; i++)
+	if(err)
 	{
-		// hpass
-		cmdb->setTextureSurfaceBarrier(m_vblurRt,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			TextureUsageBit::SAMPLED_FRAGMENT,
-			TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->setTextureSurfaceBarrier(m_hblurRt,
-			TextureUsageBit::NONE,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->beginRenderPass(m_hblurFb);
-		cmdb->bindShaderProgram(m_hblurProg);
-		cmdb->bindTexture(0, 0, m_vblurRt);
-		m_r->drawQuad(cmdb);
-		cmdb->endRenderPass();
-
-		// vpass
-		cmdb->setTextureSurfaceBarrier(m_hblurRt,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			TextureUsageBit::SAMPLED_FRAGMENT,
-			TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->setTextureSurfaceBarrier(m_vblurRt,
-			TextureUsageBit::NONE,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->beginRenderPass(m_vblurFb);
-		cmdb->bindShaderProgram(m_vblurProg);
-		cmdb->bindTexture(0, 0, m_hblurRt);
-		m_r->drawQuad(cmdb);
-		cmdb->endRenderPass();
+		ANKI_R_LOGE("Failed to init PPS SSAO");
 	}
+
+	return err;
 }
 
 } // end namespace anki

+ 96 - 21
src/anki/renderer/Ssao.h

@@ -14,51 +14,126 @@
 namespace anki
 {
 
+// Forward
+class Ssao;
+
 /// @addtogroup renderer
 /// @{
 
 /// Screen space ambient occlusion pass
-class Ssao : public RenderingPass
+class SsaoMain : public RenderingPass
 {
+	friend class SsaoVBlur;
+
 anki_internal:
-	static const PixelFormat RT_PIXEL_FORMAT;
+	static constexpr F32 HEMISPHERE_RADIUS = 3.0; // In game units
+	static const U SAMPLES = 8;
 
-	Ssao(Renderer* r)
+	TexturePtr m_rt;
+
+	SsaoMain(Renderer* r, Ssao* ssao)
+		: RenderingPass(r)
+		, m_ssao(ssao)
+	{
+	}
+
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
+
+	void setPreRunBarriers(RenderingContext& ctx);
+
+	void run(RenderingContext& ctx);
+
+	void setPostRunBarriers(RenderingContext& ctx);
+
+private:
+	Ssao* m_ssao;
+
+	FramebufferPtr m_fb;
+	ShaderResourcePtr m_frag;
+	ShaderProgramPtr m_prog;
+	TextureResourcePtr m_noiseTex;
+};
+
+/// Screen space ambient occlusion blur pass.
+class SsaoHBlur : public RenderingPass
+{
+	friend class SsaoVBlur;
+
+anki_internal:
+	SsaoHBlur(Renderer* r, Ssao* ssao)
 		: RenderingPass(r)
+		, m_ssao(ssao)
 	{
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
 
 	void setPreRunBarriers(RenderingContext& ctx);
+
 	void run(RenderingContext& ctx);
+
 	void setPostRunBarriers(RenderingContext& ctx);
 
-	TexturePtr& getRt()
+private:
+	Ssao* m_ssao;
+
+	TexturePtr m_rt;
+	FramebufferPtr m_fb;
+	ShaderResourcePtr m_frag;
+	ShaderProgramPtr m_prog;
+};
+
+/// Screen space ambient occlusion blur pass.
+class SsaoVBlur : public RenderingPass
+{
+anki_internal:
+	SsaoVBlur(Renderer* r, Ssao* ssao)
+		: RenderingPass(r)
+		, m_ssao(ssao)
 	{
-		return m_vblurRt;
 	}
 
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
+
+	void setPreRunBarriers(RenderingContext& ctx);
+
+	void run(RenderingContext& ctx);
+
+	void setPostRunBarriers(RenderingContext& ctx);
+
 private:
-	U32 m_width, m_height; ///< Blur passes size
-	U8 m_blurringIterationsCount;
+	Ssao* m_ssao;
+
+	ShaderResourcePtr m_frag;
+	ShaderProgramPtr m_prog;
+};
 
-	TexturePtr m_vblurRt;
-	TexturePtr m_hblurRt;
-	FramebufferPtr m_vblurFb;
-	FramebufferPtr m_hblurFb;
+/// Screen space ambient occlusion pass
+class Ssao : public RenderingPass
+{
+	friend class SsaoMain;
+	friend class SsaoHBlur;
+	friend class SsaoVBlur;
 
-	ShaderResourcePtr m_ssaoFrag;
-	ShaderResourcePtr m_hblurFrag;
-	ShaderResourcePtr m_vblurFrag;
-	ShaderProgramPtr m_ssaoProg;
-	ShaderProgramPtr m_hblurProg;
-	ShaderProgramPtr m_vblurProg;
+anki_internal:
+	static const PixelFormat RT_PIXEL_FORMAT;
 
-	TextureResourcePtr m_noiseTex;
+	SsaoMain m_main;
+	SsaoHBlur m_hblur;
+	SsaoVBlur m_vblur;
 
-	ANKI_USE_RESULT Error createFb(FramebufferPtr& fb, TexturePtr& rt);
-	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
+	Ssao(Renderer* r)
+		: RenderingPass(r)
+		, m_main(r, this)
+		, m_hblur(r, this)
+		, m_vblur(r, this)
+	{
+	}
+
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
+
+private:
+	U32 m_width, m_height;
 };
 /// @}
 

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

@@ -14,34 +14,14 @@
 namespace anki
 {
 
-Volumetric::~Volumetric()
+Error VolumetricMain::init(const ConfigSet& config)
 {
-}
-
-Error Volumetric::init(const ConfigSet& config)
-{
-	Error err = initInternal(config);
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to initialize volumetric pass");
-	}
-
-	return err;
-}
-
-Error Volumetric::initInternal(const ConfigSet& config)
-{
-	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);
-
 	// Misc
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
 
-	// Create RTs
-	m_r->createRenderTarget(m_width,
-		m_height,
+	// RT
+	m_r->createRenderTarget(m_vol->m_width,
+		m_vol->m_height,
 		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE | TextureUsageBit::CLEAR,
 		SamplingFilter::LINEAR,
@@ -50,37 +30,21 @@ Error Volumetric::initInternal(const ConfigSet& config)
 
 	m_r->clearRenderTarget(m_rt, ClearValue(), TextureUsageBit::SAMPLED_FRAGMENT);
 
-	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
+	// FB
 	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
+	// Shaders
 	ANKI_CHECK(m_r->createShaderf("shaders/Volumetric.frag.glsl",
 		m_frag,
 		"#define FB_SIZE uvec2(%uu, %uu)\n"
 		"#define CLUSTER_COUNT uvec3(%uu, %uu, %uu)\n"
 		"#define NOISE_MAP_SIZE %u\n",
-		m_width,
-		m_height,
+		m_vol->m_width,
+		m_vol->m_height,
 		m_r->getIs().getLightBin().getClusterer().getClusterCountX(),
 		m_r->getIs().getLightBin().getClusterer().getClusterCountY(),
 		m_r->getIs().getLightBin().getClusterer().getClusterCountZ(),
@@ -88,35 +52,10 @@ Error Volumetric::initInternal(const ConfigSet& config)
 
 	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
-	// 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;
 }
 
-void Volumetric::setPreRunBarriers(RenderingContext& ctx)
+void VolumetricMain::setPreRunBarriers(RenderingContext& ctx)
 {
 	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
 		TextureUsageBit::SAMPLED_FRAGMENT,
@@ -124,15 +63,7 @@ void Volumetric::setPreRunBarriers(RenderingContext& ctx)
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 
-void Volumetric::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void Volumetric::run(RenderingContext& ctx)
+void VolumetricMain::run(RenderingContext& ctx)
 {
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	const Frustum& frc = ctx.m_frustumComponent->getFrustum();
@@ -140,7 +71,7 @@ void Volumetric::run(RenderingContext& ctx)
 	//
 	// Main pass
 	//
-	cmdb->setViewport(0, 0, m_width, m_height);
+	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
 	cmdb->setBlendFactors(0, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA);
 
 	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
@@ -159,7 +90,7 @@ void Volumetric::run(RenderingContext& ctx)
 	uniforms[0].z() = m_r->getFrameCount() * texelOffset;
 	uniforms[0].w() = m_r->getFrameCount() & (m_noiseTex->getLayerCount() - 1);
 
-	// compute the blend factor. If the camera rotated or moved alot don't blend with previous frames
+	// Compute the blend factor. If the camera rotated or moved alot don't blend with previous frames
 	F32 dotZ = ctx.m_frustumComponent->getFrustum().getTransform().getRotation().getZAxis().xyz().dot(
 		ctx.m_prevCamTransform.getZAxis().xyz());
 	F32 dotY = ctx.m_frustumComponent->getFrustum().getTransform().getRotation().getYAxis().xyz().dot(
@@ -194,46 +125,152 @@ void Volumetric::run(RenderingContext& ctx)
 
 	// Restore state
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
+}
 
-	cmdb->setTextureSurfaceBarrier(m_rt,
+void VolumetricMain::setPostRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->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());
+Error VolumetricHBlur::init(const ConfigSet& config)
+{
+	// Create RTs
+	m_r->createRenderTarget(m_vol->m_width,
+		m_vol->m_height,
+		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		SamplingFilter::LINEAR,
+		1,
+		m_rt);
+
+	// Create FBs
+	FramebufferInitInfo fbInit;
+	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);
 
-	cmdb->bindTexture(0, 0, m_rt);
-	cmdb->bindTexture(0, 1, m_r->getDepthDownscale().m_qd.m_depthRt);
+	ANKI_CHECK(m_r->createShaderf("shaders/LumaAwareBlurGeneric.frag.glsl",
+		m_frag,
+		"#define HPASS\n"
+		"#define COL_RGB\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 11\n",
+		F32(m_vol->m_width),
+		F32(m_vol->m_height)));
 
-	cmdb->bindShaderProgram(m_hProg);
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
-	cmdb->beginRenderPass(m_hFb);
+	return ErrorCode::NONE;
+}
+
+void VolumetricHBlur::setPreRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(
+		m_rt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+}
+
+void VolumetricHBlur::run(RenderingContext& ctx)
+{
+	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+
+	cmdb->bindTexture(0, 0, m_vol->m_main.m_rt);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
+
+	cmdb->beginRenderPass(m_fb);
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
+}
 
-	cmdb->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::SAMPLED_FRAGMENT,
+void VolumetricHBlur::setPostRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
-	cmdb->setTextureSurfaceBarrier(m_hRt,
+}
+
+Error VolumetricVBlur::init(const ConfigSet& config)
+{
+	// Create FBs
+	FramebufferInitInfo fbInit;
+	fbInit.m_colorAttachmentCount = 1;
+	fbInit.m_colorAttachments[0].m_texture = m_vol->m_main.m_rt;
+	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+
+	ANKI_CHECK(m_r->createShaderf("shaders/LumaAwareBlurGeneric.frag.glsl",
+		m_frag,
+		"#define VPASS\n"
+		"#define COL_RGB\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 11\n",
+		F32(m_vol->m_width),
+		F32(m_vol->m_height)));
+
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
+
+	return ErrorCode::NONE;
+}
+
+void VolumetricVBlur::setPreRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vol->m_main.m_rt,
+		TextureUsageBit::NONE,
 		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);
+void VolumetricVBlur::run(RenderingContext& ctx)
+{
+	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+
+	cmdb->bindTexture(0, 0, m_vol->m_hblur.m_rt);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
+
+	cmdb->beginRenderPass(m_fb);
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
 }
 
+void VolumetricVBlur::setPostRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vol->m_main.m_rt,
+		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;
+
+	ANKI_R_LOGI("Initializing volumetric pass. Size %ux%u", m_width, m_height);
+
+	Error err = m_main.init(config);
+
+	if(!err)
+	{
+		err = m_hblur.init(config);
+	}
+
+	if(!err)
+	{
+		err = m_vblur.init(config);
+	}
+
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize volumetric pass");
+	}
+
+	return err;
+}
+
 } // end namespace anki

+ 110 - 19
src/anki/renderer/Volumetric.h

@@ -10,27 +10,31 @@
 namespace anki
 {
 
+// Forward
+class Volumetric;
+
 /// @addtogroup renderer
 /// @{
 
-/// Volumetric effects.
-class Volumetric : public RenderingPass
+/// Volumetic main pass.
+class VolumetricMain : public RenderingPass
 {
-public:
-	void setFogParticleColor(const Vec3& color)
-	{
-		m_fogParticleColor = color;
-	}
+	friend class Volumetric;
+	friend class VolumetricHBlur;
+	friend class VolumetricVBlur;
 
 anki_internal:
 	TexturePtr m_rt; ///< vRT
 
-	Volumetric(Renderer* r)
+	VolumetricMain(Renderer* r, Volumetric* vol)
 		: RenderingPass(r)
+		, m_vol(vol)
 	{
 	}
 
-	~Volumetric();
+	~VolumetricMain()
+	{
+	}
 
 	ANKI_USE_RESULT Error init(const ConfigSet& config);
 
@@ -39,27 +43,114 @@ anki_internal:
 	void setPostRunBarriers(RenderingContext& ctx);
 
 private:
-	U32 m_width = 0, m_height = 0;
+	Volumetric* m_vol;
+
 	Vec3 m_fogParticleColor = Vec3(1.0);
 	Mat3x4 m_prevCameraRot = Mat3x4::getIdentity();
 
-	TexturePtr m_hRt;
+	ShaderResourcePtr m_frag;
+	ShaderProgramPtr m_prog;
+	FramebufferPtr m_fb;
+
+	TextureResourcePtr m_noiseTex;
+};
+
+/// Volumetric blur pass.
+class VolumetricHBlur : public RenderingPass
+{
+	friend class Volumetric;
+	friend class VolumetricVBlur;
+
+anki_internal:
+	VolumetricHBlur(Renderer* r, Volumetric* vol)
+		: RenderingPass(r)
+		, m_vol(vol)
+	{
+	}
+
+	~VolumetricHBlur()
+	{
+	}
+
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
+
+	void setPreRunBarriers(RenderingContext& ctx);
+	void run(RenderingContext& ctx);
+	void setPostRunBarriers(RenderingContext& ctx);
+
+private:
+	Volumetric* m_vol;
 
+	TexturePtr m_rt;
 	ShaderResourcePtr m_frag;
 	ShaderProgramPtr m_prog;
 	FramebufferPtr m_fb;
+};
 
-	ShaderResourcePtr m_vFrag;
-	ShaderProgramPtr m_vProg;
-	FramebufferPtr m_vFb;
+/// Volumetric blur pass.
+class VolumetricVBlur : public RenderingPass
+{
+	friend class Volumetric;
 
-	ShaderResourcePtr m_hFrag;
-	ShaderProgramPtr m_hProg;
-	FramebufferPtr m_hFb;
+anki_internal:
+	VolumetricVBlur(Renderer* r, Volumetric* vol)
+		: RenderingPass(r)
+		, m_vol(vol)
+	{
+	}
 
-	TextureResourcePtr m_noiseTex;
+	~VolumetricVBlur()
+	{
+	}
+
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
+
+	void setPreRunBarriers(RenderingContext& ctx);
+	void run(RenderingContext& ctx);
+	void setPostRunBarriers(RenderingContext& ctx);
 
-	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
+private:
+	Volumetric* m_vol;
+
+	ShaderResourcePtr m_frag;
+	ShaderProgramPtr m_prog;
+	FramebufferPtr m_fb;
+};
+
+/// Volumetric effects.
+class Volumetric : public RenderingPass
+{
+	friend class VolumetricMain;
+	friend class VolumetricHBlur;
+	friend class VolumetricVBlur;
+
+public:
+	void setFogParticleColor(const Vec3& col)
+	{
+		m_main.m_fogParticleColor = col;
+	}
+
+anki_internal:
+	VolumetricMain m_main;
+	VolumetricHBlur m_hblur;
+	VolumetricVBlur m_vblur;
+
+	Volumetric(Renderer* r)
+		: RenderingPass(r)
+		, m_main(r, this)
+		, m_hblur(r, this)
+		, m_vblur(r, this)
+	{
+	}
+
+	~Volumetric()
+	{
+	}
+
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
+
+private:
+	U32 m_width = 0, m_height = 0;
 };
 /// @}
 

+ 1 - 0
src/anki/resource/TextureResource.cpp

@@ -160,6 +160,7 @@ Error TextureResource::load(const ResourceFilename& filename)
 	TextureInitInfo init;
 	init.m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION
 		| TextureUsageBit::TRANSFER_DESTINATION;
+	init.m_usageWhenEncountered = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION;
 	U faces = 0;
 
 	// Load image

+ 1 - 1
src/anki/util/ThreadHive.cpp

@@ -32,7 +32,7 @@ public:
 		, m_hive(hive)
 	{
 		ANKI_ASSERT(hive);
-		m_thread.start(this, threadCallback, id);
+		m_thread.start(this, threadCallback);
 	}
 
 private: