2
0
Эх сурвалжийг харах

Introduce the notion of volume for 3D textures

Panagiotis Christopoulos Charitos 9 жил өмнө
parent
commit
666f6e1cd6

+ 40 - 5
include/anki/gr/CommandBuffer.h

@@ -188,22 +188,38 @@ public:
 
 	void dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ);
 
-	void generateMipmaps(TexturePtr tex, U depth, U face, U layer);
+	/// Generate mipmaps for non-3D textures.
+	void generateMipmaps2d(TexturePtr tex, U face, U layer);
 
-	void copyTextureToTexture(TexturePtr src,
+	/// Generate mipmaps only for 3D textures.
+	void generateMipmaps3d(TexturePtr tex);
+
+	void copyTextureSurfaceToTextureSurface(TexturePtr src,
 		const TextureSurfaceInfo& srcSurf,
 		TexturePtr dest,
 		const TextureSurfaceInfo& destSurf);
 
-	void clearTexture(TexturePtr tex,
+	void copyTextureVolumeToTextureVolume(TexturePtr src,
+		const TextureVolumeInfo& srcVol,
+		TexturePtr dest,
+		const TextureVolumeInfo& destVol);
+
+	void clearTexture(TexturePtr tex, const ClearValue& clearValue);
+
+	void clearTextureSurface(TexturePtr tex,
 		const TextureSurfaceInfo& surf,
 		const ClearValue& clearValue);
+
+	void clearTextureVolume(TexturePtr tex,
+		const TextureVolumeInfo& vol,
+		const ClearValue& clearValue);
 	/// @}
 
 	/// @name Resource upload
 	/// @{
 
-	/// Upload data to a texture. It's the base of all texture upload methods.
+	/// Upload data to a texture surface. It's the base of all texture surface
+	/// upload methods.
 	void uploadTextureSurface(TexturePtr tex,
 		const TextureSurfaceInfo& surf,
 		const TransientMemoryToken& token);
@@ -231,6 +247,20 @@ public:
 		void* data,
 		PtrSize dataSize);
 
+	/// Upload data to a texture volume. It's the base of all texture volume
+	/// upload methods.
+	void uploadTextureVolume(TexturePtr tex,
+		const TextureVolumeInfo& vol,
+		const TransientMemoryToken& token);
+
+	/// Same as uploadTextureVolume but it will perform the transient
+	/// allocation and copy to it the @a data. If that allocation fails expect
+	/// the defaul OOM behaviour (crash).
+	void uploadTextureVolumeCopyData(TexturePtr tex,
+		const TextureVolumeInfo& surf,
+		void* data,
+		PtrSize dataSize);
+
 	/// Upload data to a buffer.
 	void uploadBuffer(
 		BufferPtr buff, PtrSize offset, const TransientMemoryToken& token);
@@ -238,11 +268,16 @@ public:
 
 	/// @name Sync
 	/// @{
-	void setTextureBarrier(TexturePtr tex,
+	void setTextureSurfaceBarrier(TexturePtr tex,
 		TextureUsageBit prevUsage,
 		TextureUsageBit nextUsage,
 		const TextureSurfaceInfo& surf);
 
+	void setTextureVolumeBarrier(TexturePtr tex,
+		TextureUsageBit prevUsage,
+		TextureUsageBit nextUsage,
+		const TextureVolumeInfo& vol);
+
 	void setBufferBarrier(
 		BufferPtr buff, BufferUsageBit prevUsage, BufferUsageBit nextUsage);
 	/// @}

+ 20 - 3
include/anki/gr/CommandBuffer.inl.h

@@ -17,7 +17,7 @@ inline void CommandBuffer::uploadTextureSurfaceData(TexturePtr tex,
 {
 	PtrSize allocationSize;
 	BufferUsageBit usage;
-	getManager().getTextureUploadInfo(tex, surf, allocationSize, usage);
+	getManager().getTextureSurfaceUploadInfo(tex, surf, allocationSize, usage);
 	ANKI_ASSERT(dataSize <= allocationSize);
 
 	TransientMemoryToken token;
@@ -35,7 +35,7 @@ inline void CommandBuffer::tryUploadTextureSurfaceData(TexturePtr tex,
 {
 	PtrSize allocationSize;
 	BufferUsageBit usage;
-	getManager().getTextureUploadInfo(tex, surf, allocationSize, usage);
+	getManager().getTextureSurfaceUploadInfo(tex, surf, allocationSize, usage);
 	ANKI_ASSERT(dataSize <= allocationSize);
 
 	TransientMemoryToken token;
@@ -56,7 +56,7 @@ inline void CommandBuffer::uploadTextureSurfaceCopyData(TexturePtr tex,
 {
 	PtrSize allocationSize;
 	BufferUsageBit usage;
-	getManager().getTextureUploadInfo(tex, surf, allocationSize, usage);
+	getManager().getTextureSurfaceUploadInfo(tex, surf, allocationSize, usage);
 	ANKI_ASSERT(dataSize <= allocationSize);
 
 	TransientMemoryToken token;
@@ -67,4 +67,21 @@ inline void CommandBuffer::uploadTextureSurfaceCopyData(TexturePtr tex,
 	uploadTextureSurface(tex, surf, token);
 }
 
+//==============================================================================
+inline void CommandBuffer::uploadTextureVolumeCopyData(
+	TexturePtr tex, const TextureVolumeInfo& vol, void* data, PtrSize dataSize)
+{
+	PtrSize allocationSize;
+	BufferUsageBit usage;
+	getManager().getTextureVolumeUploadInfo(tex, vol, allocationSize, usage);
+	ANKI_ASSERT(dataSize <= allocationSize);
+
+	TransientMemoryToken token;
+	void* ptr =
+		getManager().allocateFrameTransientMemory(allocationSize, usage, token);
+	memcpy(ptr, data, dataSize);
+
+	uploadTextureVolume(tex, vol, token);
+}
+
 } // end namespace anki

+ 16 - 0
include/anki/gr/Common.h

@@ -124,6 +124,22 @@ public:
 	U32 m_layer = 0;
 };
 
+/// A way to identify a volume in 3D textures.
+class TextureVolumeInfo
+{
+public:
+	TextureVolumeInfo() = default;
+
+	TextureVolumeInfo(const TextureVolumeInfo&) = default;
+
+	TextureVolumeInfo(U level)
+		: m_level(level)
+	{
+	}
+
+	U32 m_level = 0;
+};
+
 // Some constants
 const U MAX_VERTEX_ATTRIBUTES = 8;
 const U MAX_COLOR_ATTACHMENTS = 4;

+ 7 - 1
include/anki/gr/GrManager.h

@@ -86,11 +86,17 @@ public:
 	/// are required to allocate that amount and write your pixels to be
 	/// uploaded to the first part of the memory as before and leave the rest of
 	/// the memory for internal use.
-	void getTextureUploadInfo(TexturePtr tex,
+	void getTextureSurfaceUploadInfo(TexturePtr tex,
 		const TextureSurfaceInfo& surf,
 		PtrSize& expectedTransientAllocationSize,
 		BufferUsageBit& usage);
 
+	/// Same as getTextureSurfaceUploadInfo but for volumes.
+	void getTextureVolumeUploadInfo(TexturePtr tex,
+		const TextureVolumeInfo& vol,
+		PtrSize& expectedTransientAllocationSize,
+		BufferUsageBit& usage);
+
 anki_internal:
 	GrAllocator<U8>& getAllocator()
 	{

+ 3 - 0
include/anki/gr/common/Misc.h

@@ -95,4 +95,7 @@ void getFormatInfo(const PixelFormat& fmt,
 /// Compute the size of the surface.
 PtrSize computeSurfaceSize(U width, U height, const PixelFormat& fmt);
 
+/// Compute the size of the volume.
+PtrSize computeVolumeSize(U width, U height, U depth, const PixelFormat& fmt);
+
 } // end namespace anki

+ 13 - 2
include/anki/gr/gl/TextureImpl.h

@@ -48,6 +48,12 @@ public:
 			m_texType, m_depth, m_mipsCount, m_layerCount, surf);
 	}
 
+	void checkVolume(const TextureVolumeInfo& vol) const
+	{
+		ANKI_ASSERT(m_texType == TextureType::_3D);
+		ANKI_ASSERT(vol.m_level < m_mipsCount);
+	}
+
 	/// Init some stuff.
 	void preInit(const TextureInitInfo& init);
 
@@ -55,10 +61,15 @@ public:
 	void init(const TextureInitInfo& init);
 
 	/// Write texture data.
-	void write(const TextureSurfaceInfo& surf, void* data, PtrSize dataSize);
+	void writeSurface(
+		const TextureSurfaceInfo& surf, void* data, PtrSize dataSize);
+
+	/// Write texture data.
+	void writeVolume(
+		const TextureVolumeInfo& vol, void* data, PtrSize dataSize);
 
 	/// Generate mipmaps.
-	void generateMipmaps(U depth, U face, U layer);
+	void generateMipmaps2d(U face, U layer);
 
 	/// Copy a single surface from one texture to another.
 	static void copy(const TextureImpl& src,

+ 27 - 3
include/anki/gr/vulkan/CommandBufferImpl.h

@@ -85,12 +85,22 @@ public:
 		const TextureSurfaceInfo& surf,
 		const TransientMemoryToken& token);
 
-	void generateMipmaps(TexturePtr tex, U depth, U face, U layer);
+	void uploadTextureVolume(TexturePtr tex,
+		const TextureVolumeInfo& vol,
+		const TransientMemoryToken& token);
+
+	void generateMipmaps2d(TexturePtr tex, U face, U layer);
 
-	void clearTexture(TexturePtr tex,
+	void clearTexture(TexturePtr tex, const ClearValue& clearValue);
+
+	void clearTextureSurface(TexturePtr tex,
 		const TextureSurfaceInfo& surf,
 		const ClearValue& clearValue);
 
+	void clearTextureVolume(TexturePtr tex,
+		const TextureVolumeInfo& volume,
+		const ClearValue& clearValue);
+
 	void uploadBuffer(
 		BufferPtr buff, PtrSize offset, const TransientMemoryToken& token);
 
@@ -116,11 +126,16 @@ public:
 		TexturePtr tex,
 		const VkImageSubresourceRange& range);
 
-	void setImageBarrier(TexturePtr tex,
+	void setTextureSurfaceBarrier(TexturePtr tex,
 		TextureUsageBit prevUsage,
 		TextureUsageBit nextUsage,
 		const TextureSurfaceInfo& surf);
 
+	void setTextureVolumeBarrier(TexturePtr tex,
+		TextureUsageBit prevUsage,
+		TextureUsageBit nextUsage,
+		const TextureVolumeInfo& vol);
+
 	void setBufferBarrier(VkPipelineStageFlags srcStage,
 		VkAccessFlags srcAccess,
 		VkPipelineStageFlags dstStage,
@@ -182,6 +197,15 @@ private:
 	void flushBarriers()
 	{
 	}
+
+	void clearTextureInternal(TexturePtr tex,
+		const ClearValue& clearValue,
+		const VkImageSubresourceRange& range);
+
+	void setTextureBarrierInternal(TexturePtr tex,
+		TextureUsageBit prevUsage,
+		TextureUsageBit nextUsage,
+		const VkImageSubresourceRange& range);
 };
 /// @}
 

+ 91 - 25
include/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -95,24 +95,15 @@ inline void CommandBufferImpl::setImageBarrier(VkPipelineStageFlags srcStage,
 }
 
 //==============================================================================
-inline void CommandBufferImpl::setImageBarrier(TexturePtr tex,
+inline void CommandBufferImpl::setTextureBarrierInternal(TexturePtr tex,
 	TextureUsageBit prevUsage,
 	TextureUsageBit nextUsage,
-	const TextureSurfaceInfo& surf)
+	const VkImageSubresourceRange& range)
 {
-	if(surf.m_level > 0)
-	{
-		ANKI_ASSERT(!(nextUsage & TextureUsageBit::GENERATE_MIPMAPS)
-			&& "This transition happens inside "
-			   "CommandBufferImpl::generateMipmaps");
-	}
-
 	const TextureImpl& impl = tex->getImplementation();
 	ANKI_ASSERT(impl.usageValid(prevUsage));
 	ANKI_ASSERT(impl.usageValid(nextUsage));
 
-	impl.checkSurface(surf);
-
 	VkPipelineStageFlags srcStage;
 	VkAccessFlags srcAccess;
 	VkImageLayout oldLayout;
@@ -121,16 +112,13 @@ inline void CommandBufferImpl::setImageBarrier(TexturePtr tex,
 	VkImageLayout newLayout;
 	impl.computeBarrierInfo(prevUsage,
 		nextUsage,
-		surf.m_level,
+		range.baseMipLevel,
 		srcStage,
 		srcAccess,
 		dstStage,
 		dstAccess);
-	oldLayout = impl.computeLayout(prevUsage, surf.m_level);
-	newLayout = impl.computeLayout(nextUsage, surf.m_level);
-
-	VkImageSubresourceRange range;
-	impl.computeSubResourceRange(surf, range);
+	oldLayout = impl.computeLayout(prevUsage, range.baseMipLevel);
+	newLayout = impl.computeLayout(nextUsage, range.baseMipLevel);
 
 	setImageBarrier(srcStage,
 		srcAccess,
@@ -142,6 +130,48 @@ inline void CommandBufferImpl::setImageBarrier(TexturePtr tex,
 		range);
 }
 
+//==============================================================================
+inline void CommandBufferImpl::setTextureSurfaceBarrier(TexturePtr tex,
+	TextureUsageBit prevUsage,
+	TextureUsageBit nextUsage,
+	const TextureSurfaceInfo& surf)
+{
+	if(surf.m_level > 0)
+	{
+		ANKI_ASSERT(!(nextUsage & TextureUsageBit::GENERATE_MIPMAPS)
+			&& "This transition happens inside "
+			   "CommandBufferImpl::generateMipmapsX");
+	}
+
+	const TextureImpl& impl = tex->getImplementation();
+	impl.checkSurface(surf);
+
+	VkImageSubresourceRange range;
+	impl.computeSubResourceRange(surf, range);
+	setTextureBarrierInternal(tex, prevUsage, nextUsage, range);
+}
+
+//==============================================================================
+inline void CommandBufferImpl::setTextureVolumeBarrier(TexturePtr tex,
+	TextureUsageBit prevUsage,
+	TextureUsageBit nextUsage,
+	const TextureVolumeInfo& vol)
+{
+	if(vol.m_level > 0)
+	{
+		ANKI_ASSERT(!(nextUsage & TextureUsageBit::GENERATE_MIPMAPS)
+			&& "This transition happens inside "
+			   "CommandBufferImpl::generateMipmaps");
+	}
+
+	const TextureImpl& impl = tex->getImplementation();
+	impl.checkVolume(vol);
+
+	VkImageSubresourceRange range;
+	impl.computeSubResourceRange(vol, range);
+	setTextureBarrierInternal(tex, prevUsage, nextUsage, range);
+}
+
 //==============================================================================
 inline void CommandBufferImpl::setBufferBarrier(VkPipelineStageFlags srcStage,
 	VkAccessFlags srcAccess,
@@ -207,21 +237,18 @@ inline void CommandBufferImpl::endOcclusionQuery(OcclusionQueryPtr query)
 }
 
 //==============================================================================
-inline void CommandBufferImpl::clearTexture(TexturePtr tex,
-	const TextureSurfaceInfo& surf,
-	const ClearValue& clearValue)
+inline void CommandBufferImpl::clearTextureInternal(TexturePtr tex,
+	const ClearValue& clearValue,
+	const VkImageSubresourceRange& range)
 {
 	commandCommon();
 	flushBarriers();
-	const TextureImpl& impl = tex->getImplementation();
 
 	VkClearColorValue vclear;
 	static_assert(sizeof(vclear) == sizeof(clearValue), "See file");
 	memcpy(&vclear, &clearValue, sizeof(clearValue));
 
-	VkImageSubresourceRange range;
-	impl.computeSubResourceRange(surf, range);
-
+	const TextureImpl& impl = tex->getImplementation();
 	if(impl.m_aspect == VK_IMAGE_ASPECT_COLOR_BIT)
 	{
 		vkCmdClearColorImage(m_handle,
@@ -239,6 +266,45 @@ inline void CommandBufferImpl::clearTexture(TexturePtr tex,
 	m_texList.pushBack(m_alloc, tex);
 }
 
+//==============================================================================
+inline void CommandBufferImpl::clearTexture(
+	TexturePtr tex, const ClearValue& clearValue)
+{
+	VkImageSubresourceRange range;
+	range.aspectMask = tex->getImplementation().m_aspect;
+	range.baseMipLevel = 0;
+	range.levelCount = VK_REMAINING_MIP_LEVELS;
+	range.baseArrayLayer = 0;
+	range.layerCount = VK_REMAINING_ARRAY_LAYERS;
+
+	clearTextureInternal(tex, clearValue, range);
+}
+
+//==============================================================================
+inline void CommandBufferImpl::clearTextureSurface(TexturePtr tex,
+	const TextureSurfaceInfo& surf,
+	const ClearValue& clearValue)
+{
+	const TextureImpl& impl = tex->getImplementation();
+	ANKI_ASSERT(impl.m_type != TextureType::_3D && "Not for 3D");
+
+	VkImageSubresourceRange range;
+	impl.computeSubResourceRange(surf, range);
+	clearTextureInternal(tex, clearValue, range);
+}
+
+//==============================================================================
+inline void CommandBufferImpl::clearTextureVolume(
+	TexturePtr tex, const TextureVolumeInfo& vol, const ClearValue& clearValue)
+{
+	const TextureImpl& impl = tex->getImplementation();
+	ANKI_ASSERT(impl.m_type == TextureType::_3D && "Only for 3D");
+
+	VkImageSubresourceRange range;
+	impl.computeSubResourceRange(vol, range);
+	clearTextureInternal(tex, clearValue, range);
+}
+
 //==============================================================================
 inline void CommandBufferImpl::uploadBuffer(
 	BufferPtr buff, PtrSize offset, const TransientMemoryToken& token)
@@ -255,9 +321,9 @@ inline void CommandBufferImpl::uploadBuffer(
 	ANKI_ASSERT(offset + token.m_range <= impl.getSize());
 
 	vkCmdCopyBuffer(m_handle,
-		impl.getHandle(),
 		getGrManagerImpl().getTransientMemoryManager().getBufferHandle(
 			token.m_usage),
+		impl.getHandle(),
 		1,
 		&region);
 

+ 1 - 0
include/anki/gr/vulkan/GrManagerImpl.h

@@ -222,6 +222,7 @@ private:
 	Mutex m_queueSubmitMtx;
 
 	VkPhysicalDeviceProperties m_devProps = {};
+	VkPhysicalDeviceFeatures m_devFeatures = {};
 
 	/// @name Surface_related
 	/// @{

+ 71 - 4
include/anki/gr/vulkan/TextureImpl.h

@@ -32,9 +32,6 @@ public:
 	VkImage m_imageHandle = VK_NULL_HANDLE;
 	VkImageView m_viewHandle = VK_NULL_HANDLE;
 
-	/// A number of views, one for each level. Used in image load/store.
-	DynamicArray<VkImageView> m_viewsEveryLevel;
-
 	U32 m_memIdx = MAX_U32;
 	GpuMemoryAllocationHandle m_memHandle;
 
@@ -62,14 +59,35 @@ public:
 		checkTextureSurface(m_type, m_depth, m_mipCount, m_layerCount, surf);
 	}
 
+	void checkVolume(const TextureVolumeInfo& vol) const
+	{
+		ANKI_ASSERT(m_type == TextureType::_3D);
+		ANKI_ASSERT(vol.m_level < m_mipCount);
+	}
+
 	void computeSubResourceRange(
 		const TextureSurfaceInfo& surf, VkImageSubresourceRange& range) const;
 
+	void computeSubResourceRange(
+		const TextureVolumeInfo& vol, VkImageSubresourceRange& range) const;
+
+	/// Compute the layer as defined by Vulkan.
+	U computeVkArrayLayer(const TextureSurfaceInfo& surf) const;
+
+	U computeVkArrayLayer(const TextureVolumeInfo& vol) const
+	{
+		return 0;
+	}
+
 	Bool usageValid(TextureUsageBit usage) const
 	{
 		return (usage & m_usage) == usage;
 	}
 
+	VkImageView getOrCreateSingleSurfaceView(const TextureSurfaceInfo& surf);
+
+	VkImageView getOrCreateSingleLevelView(U level);
+
 	/// By knowing the previous and new texture usage calculate the relavant
 	/// info for a ppline barrier.
 	void computeBarrierInfo(TextureUsageBit before,
@@ -86,6 +104,14 @@ public:
 private:
 	class CreateContext;
 
+	/// A number of views, one for each surface. Used in image load/store.
+	DynamicArray<VkImageView> m_singleSurfaceViews;
+	Mutex m_singleSurfaceViewsMtx;
+
+	/// A number of views, one for each level. Used in image load/store.
+	DynamicArray<VkImageView> m_singleLevelViews;
+	Mutex m_singleLevelViewsMtx;
+
 	ANKI_USE_RESULT static VkFormatFeatureFlags calcFeatures(
 		const TextureInitInfo& init);
 
@@ -112,7 +138,7 @@ inline void TextureImpl::computeSubResourceRange(
 		range.baseArrayLayer = 0;
 		break;
 	case TextureType::_3D:
-		range.baseArrayLayer = surf.m_depth;
+		range.baseArrayLayer = 0;
 		break;
 	case TextureType::CUBE:
 		range.baseArrayLayer = surf.m_face;
@@ -128,6 +154,47 @@ inline void TextureImpl::computeSubResourceRange(
 	}
 	range.layerCount = 1;
 }
+
+//==============================================================================
+inline void TextureImpl::computeSubResourceRange(
+	const TextureVolumeInfo& vol, VkImageSubresourceRange& range) const
+{
+	checkVolume(vol);
+	range.aspectMask = m_aspect;
+	range.baseMipLevel = vol.m_level;
+	range.levelCount = 1;
+	range.baseArrayLayer = 0;
+	range.layerCount = 1;
+}
+
+//==============================================================================
+inline U TextureImpl::computeVkArrayLayer(const TextureSurfaceInfo& surf) const
+{
+	checkSurface(surf);
+	U layer = 0;
+	switch(m_type)
+	{
+	case TextureType::_2D:
+		layer = 0;
+		break;
+	case TextureType::_3D:
+		layer = 0;
+		break;
+	case TextureType::CUBE:
+		layer = surf.m_face;
+		break;
+	case TextureType::_2D_ARRAY:
+		layer = surf.m_layer;
+		break;
+	case TextureType::CUBE_ARRAY:
+		layer = surf.m_layer * 6 + surf.m_face;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	return layer;
+}
 /// @}
 
 } // end namespace anki

+ 27 - 1
include/anki/resource/ImageLoader.h

@@ -56,6 +56,16 @@ public:
 		DynamicArray<U8> m_data;
 	};
 
+	class Volume
+	{
+	public:
+		U32 m_width;
+		U32 m_height;
+		U32 m_depth;
+		U32 m_mipLevel;
+		DynamicArray<U8> m_data;
+	};
+
 	ImageLoader(GenericMemoryPoolAllocator<U8> alloc)
 		: m_alloc(alloc)
 	{
@@ -84,6 +94,16 @@ public:
 		return m_mipLevels;
 	}
 
+	U getWidth() const
+	{
+		return m_width;
+	}
+
+	U getHeight() const
+	{
+		return m_height;
+	}
+
 	U getDepth() const
 	{
 		ANKI_ASSERT(
@@ -104,7 +124,9 @@ public:
 		return m_textureType;
 	}
 
-	const Surface& getSurface(U level, U depth, U face, U layer) const;
+	const Surface& getSurface(U level, U face, U layer) const;
+
+	const Volume& getVolume(U level) const;
 
 	GenericMemoryPoolAllocator<U8> getAllocator() const
 	{
@@ -129,7 +151,11 @@ private:
 	/// so face and layer won't be used at the same time.
 	DynamicArray<Surface> m_surfaces;
 
+	DynamicArray<Volume> m_volumes;
+
 	U8 m_mipLevels = 0;
+	U32 m_width = 0;
+	U32 m_height = 0;
 	U8 m_depthOrLayerCount = 0;
 	DataCompression m_compression = DataCompression::NONE;
 	ColorFormat m_colorFormat = ColorFormat::NONE;

+ 23 - 0
src/gr/common/Misc.cpp

@@ -228,4 +228,27 @@ PtrSize computeSurfaceSize(U width, U height, const PixelFormat& fmt)
 	}
 }
 
+//==============================================================================
+PtrSize computeVolumeSize(U width, U height, U depth, const PixelFormat& fmt)
+{
+	ANKI_ASSERT(width > 0 && height > 0 && depth > 0);
+	U texelComponents;
+	U texelBytes;
+	U blockSize;
+	U blockBytes;
+	getFormatInfo(fmt, texelComponents, texelBytes, blockSize, blockBytes);
+
+	if(blockSize > 0)
+	{
+		// Compressed
+		ANKI_ASSERT((width % blockSize) == 0);
+		ANKI_ASSERT((height % blockSize) == 0);
+		return (width / blockSize) * (height / blockSize) * blockBytes * depth;
+	}
+	else
+	{
+		return width * height * depth * texelBytes;
+	}
+}
+
 } // end namespace anki

+ 69 - 13
src/gr/gl/CommandBuffer.cpp

@@ -333,14 +333,14 @@ void CommandBuffer::endOcclusionQuery(OcclusionQueryPtr query)
 }
 
 //==============================================================================
-class TexUploadCommand final : public GlCommand
+class TexSurfUploadCommand final : public GlCommand
 {
 public:
 	TexturePtr m_handle;
 	TextureSurfaceInfo m_surf;
 	TransientMemoryToken m_token;
 
-	TexUploadCommand(const TexturePtr& handle,
+	TexSurfUploadCommand(const TexturePtr& handle,
 		TextureSurfaceInfo surf,
 		const TransientMemoryToken& token)
 		: m_handle(handle)
@@ -356,7 +356,8 @@ public:
 						 .getBaseAddress(m_token);
 		data = static_cast<void*>(static_cast<U8*>(data) + m_token.m_offset);
 
-		m_handle->getImplementation().write(m_surf, data, m_token.m_range);
+		m_handle->getImplementation().writeSurface(
+			m_surf, data, m_token.m_range);
 
 		if(m_token.m_lifetime == TransientMemoryTokenLifetime::PERSISTENT)
 		{
@@ -377,7 +378,55 @@ void CommandBuffer::uploadTextureSurface(TexturePtr tex,
 	ANKI_ASSERT(token.m_range > 0);
 	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
 
-	m_impl->pushBackNewCommand<TexUploadCommand>(tex, surf, token);
+	m_impl->pushBackNewCommand<TexSurfUploadCommand>(tex, surf, token);
+}
+
+//==============================================================================
+class TexVolUploadCommand final : public GlCommand
+{
+public:
+	TexturePtr m_handle;
+	TextureVolumeInfo m_vol;
+	TransientMemoryToken m_token;
+
+	TexVolUploadCommand(const TexturePtr& handle,
+		TextureVolumeInfo vol,
+		const TransientMemoryToken& token)
+		: m_handle(handle)
+		, m_vol(vol)
+		, m_token(token)
+	{
+	}
+
+	Error operator()(GlState& state)
+	{
+		void* data = state.m_manager->getImplementation()
+						 .getTransientMemoryManager()
+						 .getBaseAddress(m_token);
+		data = static_cast<void*>(static_cast<U8*>(data) + m_token.m_offset);
+
+		m_handle->getImplementation().writeVolume(m_vol, data, m_token.m_range);
+
+		if(m_token.m_lifetime == TransientMemoryTokenLifetime::PERSISTENT)
+		{
+			state.m_manager->getImplementation()
+				.getTransientMemoryManager()
+				.free(m_token);
+		}
+
+		return ErrorCode::NONE;
+	}
+};
+
+void CommandBuffer::uploadTextureVolume(TexturePtr tex,
+	const TextureVolumeInfo& vol,
+	const TransientMemoryToken& token)
+{
+	ANKI_ASSERT(tex);
+	ANKI_ASSERT(token.m_range > 0);
+	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
+
+	m_impl->pushBackNewCommand<TexVolUploadCommand>(tex, vol, token);
 }
 
 //==============================================================================
@@ -432,13 +481,11 @@ class GenMipsCommand final : public GlCommand
 {
 public:
 	TexturePtr m_tex;
-	U32 m_depth;
 	U8 m_face;
 	U32 m_layer;
 
-	GenMipsCommand(const TexturePtr& tex, U depth, U face, U layer)
+	GenMipsCommand(const TexturePtr& tex, U face, U layer)
 		: m_tex(tex)
-		, m_depth(depth)
 		, m_face(face)
 		, m_layer(layer)
 	{
@@ -446,15 +493,15 @@ public:
 
 	Error operator()(GlState&)
 	{
-		m_tex->getImplementation().generateMipmaps(m_depth, m_face, m_layer);
+		m_tex->getImplementation().generateMipmaps2d(m_face, m_layer);
 		return ErrorCode::NONE;
 	}
 };
 
-void CommandBuffer::generateMipmaps(TexturePtr tex, U depth, U face, U layer)
+void CommandBuffer::generateMipmaps2d(TexturePtr tex, U face, U layer)
 {
 	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
-	m_impl->pushBackNewCommand<GenMipsCommand>(tex, depth, face, layer);
+	m_impl->pushBackNewCommand<GenMipsCommand>(tex, face, layer);
 }
 
 //==============================================================================
@@ -524,7 +571,7 @@ public:
 	}
 };
 
-void CommandBuffer::copyTextureToTexture(TexturePtr src,
+void CommandBuffer::copyTextureSurfaceToTextureSurface(TexturePtr src,
 	const TextureSurfaceInfo& srcSurf,
 	TexturePtr dest,
 	const TextureSurfaceInfo& destSurf)
@@ -600,7 +647,7 @@ void CommandBuffer::setBufferBarrier(
 }
 
 //==============================================================================
-void CommandBuffer::setTextureBarrier(TexturePtr tex,
+void CommandBuffer::setTextureSurfaceBarrier(TexturePtr tex,
 	TextureUsageBit prevUsage,
 	TextureUsageBit nextUsage,
 	const TextureSurfaceInfo& surf)
@@ -608,6 +655,15 @@ void CommandBuffer::setTextureBarrier(TexturePtr tex,
 	// Do nothing
 }
 
+//==============================================================================
+void CommandBuffer::setTextureVolumeBarrier(TexturePtr tex,
+	TextureUsageBit prevUsage,
+	TextureUsageBit nextUsage,
+	const TextureVolumeInfo& vol)
+{
+	// Do nothing
+}
+
 //==============================================================================
 class ClearTextCommand final : public GlCommand
 {
@@ -631,7 +687,7 @@ public:
 	}
 };
 
-void CommandBuffer::clearTexture(TexturePtr tex,
+void CommandBuffer::clearTextureSurface(TexturePtr tex,
 	const TextureSurfaceInfo& surf,
 	const ClearValue& clearValue)
 {

+ 18 - 1
src/gr/gl/GrManager.cpp

@@ -96,7 +96,7 @@ void* GrManager::tryAllocateFrameTransientMemory(
 }
 
 //==============================================================================
-void GrManager::getTextureUploadInfo(TexturePtr tex,
+void GrManager::getTextureSurfaceUploadInfo(TexturePtr tex,
 	const TextureSurfaceInfo& surf,
 	PtrSize& allocationSize,
 	BufferUsageBit& usage)
@@ -111,4 +111,21 @@ void GrManager::getTextureUploadInfo(TexturePtr tex,
 	usage = BufferUsageBit::TRANSFER_SOURCE;
 }
 
+//==============================================================================
+void GrManager::getTextureVolumeUploadInfo(TexturePtr tex,
+	const TextureVolumeInfo& vol,
+	PtrSize& allocationSize,
+	BufferUsageBit& usage)
+{
+	const TextureImpl& impl = tex->getImplementation();
+	impl.checkVolume(vol);
+
+	U width = impl.m_width >> vol.m_level;
+	U height = impl.m_height >> vol.m_level;
+	U depth = impl.m_depth >> vol.m_level;
+	allocationSize = computeVolumeSize(width, height, depth, impl.m_pformat);
+
+	usage = BufferUsageBit::TRANSFER_SOURCE;
+}
+
 } // end namespace anki

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

@@ -436,7 +436,7 @@ void TextureImpl::init(const TextureInitInfo& init)
 }
 
 //==============================================================================
-void TextureImpl::write(
+void TextureImpl::writeSurface(
 	const TextureSurfaceInfo& surf, void* data, PtrSize dataSize)
 {
 	checkSurface(surf);
@@ -531,9 +531,42 @@ void TextureImpl::write(
 }
 
 //==============================================================================
-void TextureImpl::generateMipmaps(U depth, U face, U layer)
+void TextureImpl::writeVolume(
+	const TextureVolumeInfo& vol, void* data, PtrSize dataSize)
 {
-	U surface = computeSurfaceIdx(TextureSurfaceInfo(0, depth, face, layer));
+	checkVolume(vol);
+	ANKI_ASSERT(data);
+	ANKI_ASSERT(dataSize > 0);
+	ANKI_ASSERT(m_texType == TextureType::_3D);
+
+	U mipmap = vol.m_level;
+	U w = m_width >> mipmap;
+	U h = m_height >> mipmap;
+	U d = m_depth >> mipmap;
+	ANKI_ASSERT(w > 0);
+	ANKI_ASSERT(h > 0);
+	ANKI_ASSERT(d > 0);
+
+	bind();
+
+	if(!m_compressed)
+	{
+		glTexSubImage3D(
+			m_target, mipmap, 0, 0, 0, w, h, d, m_format, m_type, data);
+	}
+	else
+	{
+		glCompressedTexSubImage3D(
+			m_target, mipmap, 0, 0, 0, w, h, d, m_format, dataSize, data);
+	}
+
+	ANKI_CHECK_GL_ERROR();
+}
+
+//==============================================================================
+void TextureImpl::generateMipmaps2d(U face, U layer)
+{
+	U surface = computeSurfaceIdx(TextureSurfaceInfo(0, 0, face, layer));
 	ANKI_ASSERT(!m_compressed);
 
 	if(m_surfaceCountPerLevel > 1)

+ 53 - 8
src/gr/vulkan/CommandBuffer.cpp

@@ -142,26 +142,54 @@ void CommandBuffer::dispatchCompute(
 }
 
 //==============================================================================
-void CommandBuffer::generateMipmaps(TexturePtr tex, U depth, U face, U layer)
+void CommandBuffer::generateMipmaps2d(TexturePtr tex, U face, U layer)
 {
-	m_impl->generateMipmaps(tex, depth, face, layer);
+	m_impl->generateMipmaps2d(tex, face, layer);
 }
 
 //==============================================================================
-void CommandBuffer::copyTextureToTexture(TexturePtr src,
+void CommandBuffer::generateMipmaps3d(TexturePtr tex)
+{
+	ANKI_ASSERT(0 && "TODO");
+}
+
+//==============================================================================
+void CommandBuffer::copyTextureSurfaceToTextureSurface(TexturePtr src,
 	const TextureSurfaceInfo& srcSurf,
 	TexturePtr dest,
 	const TextureSurfaceInfo& destSurf)
 {
-	ANKI_ASSERT(0);
+	ANKI_ASSERT(0 && "TODO");
 }
 
 //==============================================================================
-void CommandBuffer::clearTexture(TexturePtr tex,
+void CommandBuffer::copyTextureVolumeToTextureVolume(TexturePtr src,
+	const TextureVolumeInfo& srcVol,
+	TexturePtr dest,
+	const TextureVolumeInfo& destVol)
+{
+	ANKI_ASSERT(0 && "TODO");
+}
+
+//==============================================================================
+void CommandBuffer::clearTexture(TexturePtr tex, const ClearValue& clearValue)
+{
+	m_impl->clearTexture(tex, clearValue);
+}
+
+//==============================================================================
+void CommandBuffer::clearTextureSurface(TexturePtr tex,
 	const TextureSurfaceInfo& surf,
 	const ClearValue& clearValue)
 {
-	m_impl->clearTexture(tex, surf, clearValue);
+	m_impl->clearTextureSurface(tex, surf, clearValue);
+}
+
+//==============================================================================
+void CommandBuffer::clearTextureVolume(
+	TexturePtr tex, const TextureVolumeInfo& vol, const ClearValue& clearValue)
+{
+	m_impl->clearTextureVolume(tex, vol, clearValue);
 }
 
 //==============================================================================
@@ -172,6 +200,14 @@ void CommandBuffer::uploadTextureSurface(TexturePtr tex,
 	m_impl->uploadTextureSurface(tex, surf, token);
 }
 
+//==============================================================================
+void CommandBuffer::uploadTextureVolume(TexturePtr tex,
+	const TextureVolumeInfo& vol,
+	const TransientMemoryToken& token)
+{
+	m_impl->uploadTextureVolume(tex, vol, token);
+}
+
 //==============================================================================
 void CommandBuffer::uploadBuffer(
 	BufferPtr buff, PtrSize offset, const TransientMemoryToken& token)
@@ -180,12 +216,21 @@ void CommandBuffer::uploadBuffer(
 }
 
 //==============================================================================
-void CommandBuffer::setTextureBarrier(TexturePtr tex,
+void CommandBuffer::setTextureSurfaceBarrier(TexturePtr tex,
 	TextureUsageBit prevUsage,
 	TextureUsageBit nextUsage,
 	const TextureSurfaceInfo& surf)
 {
-	m_impl->setImageBarrier(tex, prevUsage, nextUsage, surf);
+	m_impl->setTextureSurfaceBarrier(tex, prevUsage, nextUsage, surf);
+}
+
+//==============================================================================
+void CommandBuffer::setTextureVolumeBarrier(TexturePtr tex,
+	TextureUsageBit prevUsage,
+	TextureUsageBit nextUsage,
+	const TextureVolumeInfo& vol)
+{
+	m_impl->setTextureVolumeBarrier(tex, prevUsage, nextUsage, vol);
 }
 
 //==============================================================================

+ 137 - 15
src/gr/vulkan/CommandBufferImpl.cpp

@@ -278,14 +278,13 @@ void CommandBufferImpl::bindResourceGroup(
 }
 
 //==============================================================================
-void CommandBufferImpl::generateMipmaps(
-	TexturePtr tex, U depth, U face, U layer)
+void CommandBufferImpl::generateMipmaps2d(TexturePtr tex, U face, U layer)
 {
 	commandCommon();
 	flushBarriers();
 
 	const TextureImpl& impl = tex->getImplementation();
-	ANKI_ASSERT(impl.m_type != TextureType::_3D && "Not design for that ATM");
+	ANKI_ASSERT(impl.m_type != TextureType::_3D && "Not for 3D");
 
 	for(U i = 0; i < impl.m_mipCount - 1u; ++i)
 	{
@@ -294,7 +293,7 @@ void CommandBufferImpl::generateMipmaps(
 		{
 			VkImageSubresourceRange range;
 			impl.computeSubResourceRange(
-				TextureSurfaceInfo(i, depth, face, layer), range);
+				TextureSurfaceInfo(i, 0, face, layer), range);
 
 			setImageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
 				VK_ACCESS_TRANSFER_WRITE_BIT,
@@ -310,7 +309,7 @@ void CommandBufferImpl::generateMipmaps(
 		{
 			VkImageSubresourceRange range;
 			impl.computeSubResourceRange(
-				TextureSurfaceInfo(i + 1, depth, face, layer), range);
+				TextureSurfaceInfo(i + 1, 0, face, layer), range);
 
 			setImageBarrier(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
 				0,
@@ -377,19 +376,18 @@ void CommandBufferImpl::uploadTextureSurface(TexturePtr tex,
 
 	if(!impl.m_workarounds)
 	{
-		VkImageSubresourceRange range;
-		impl.computeSubResourceRange(surf, range);
-
 		U width = impl.m_width >> surf.m_level;
 		U height = impl.m_height >> surf.m_level;
+		ANKI_ASSERT(
+			token.m_range == computeSurfaceSize(width, height, impl.m_format));
 
 		// Copy
 		VkBufferImageCopy region;
 		region.imageSubresource.aspectMask = impl.m_aspect;
-		region.imageSubresource.baseArrayLayer = range.baseArrayLayer;
+		region.imageSubresource.baseArrayLayer = impl.computeVkArrayLayer(surf);
 		region.imageSubresource.layerCount = 1;
 		region.imageSubresource.mipLevel = surf.m_level;
-		region.imageOffset = {0, 0, 0};
+		region.imageOffset = {0, 0, I32(surf.m_depth)};
 		region.imageExtent.width = width;
 		region.imageExtent.height = height;
 		region.imageExtent.depth = 1;
@@ -454,15 +452,12 @@ void CommandBufferImpl::uploadTextureSurface(TexturePtr tex,
 		flushBarriers();
 
 		// Do the copy to the image
-		VkImageSubresourceRange range;
-		impl.computeSubResourceRange(surf, range);
-
 		VkBufferImageCopy region;
 		region.imageSubresource.aspectMask = impl.m_aspect;
-		region.imageSubresource.baseArrayLayer = range.baseArrayLayer;
+		region.imageSubresource.baseArrayLayer = impl.computeVkArrayLayer(surf);
 		region.imageSubresource.layerCount = 1;
 		region.imageSubresource.mipLevel = surf.m_level;
-		region.imageOffset = {0, 0, 0};
+		region.imageOffset = {0, 0, I32(surf.m_depth)};
 		region.imageExtent.width = width;
 		region.imageExtent.height = height;
 		region.imageExtent.depth = 1;
@@ -485,4 +480,131 @@ void CommandBufferImpl::uploadTextureSurface(TexturePtr tex,
 	m_texList.pushBack(m_alloc, tex);
 }
 
+//==============================================================================
+void CommandBufferImpl::uploadTextureVolume(TexturePtr tex,
+	const TextureVolumeInfo& vol,
+	const TransientMemoryToken& token)
+{
+	commandCommon();
+	flushBarriers();
+
+	TextureImpl& impl = tex->getImplementation();
+	impl.checkVolume(vol);
+	ANKI_ASSERT(impl.usageValid(TextureUsageBit::UPLOAD));
+
+	if(!impl.m_workarounds)
+	{
+		U width = impl.m_width >> vol.m_level;
+		U height = impl.m_height >> vol.m_level;
+		U depth = impl.m_depth >> vol.m_level;
+		ANKI_ASSERT(token.m_range
+			== computeVolumeSize(width, height, depth, impl.m_format));
+
+		// Copy
+		VkBufferImageCopy region;
+		region.imageSubresource.aspectMask = impl.m_aspect;
+		region.imageSubresource.baseArrayLayer = impl.computeVkArrayLayer(vol);
+		region.imageSubresource.layerCount = 1;
+		region.imageSubresource.mipLevel = vol.m_level;
+		region.imageOffset = {0, 0, 0};
+		region.imageExtent.width = width;
+		region.imageExtent.height = height;
+		region.imageExtent.depth = impl.m_depth;
+		region.bufferOffset = token.m_offset;
+		region.bufferImageHeight = 0;
+		region.bufferRowLength = 0;
+
+		vkCmdCopyBufferToImage(m_handle,
+			getGrManagerImpl().getTransientMemoryManager().getBufferHandle(
+				token.m_usage),
+			impl.m_imageHandle,
+			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+			1,
+			&region);
+	}
+	else if(!!(impl.m_workarounds & TextureImplWorkaround::R8G8B8_TO_R8G8B8A8))
+	{
+		// Find the offset to the RGBA staging buff
+		U width = impl.m_width >> vol.m_level;
+		U height = impl.m_height >> vol.m_level;
+		U depth = impl.m_depth >> vol.m_level;
+		PtrSize dstOffset = computeVolumeSize(width,
+			height,
+			depth,
+			PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UNORM));
+		alignRoundUp(16, dstOffset);
+		ANKI_ASSERT(token.m_range
+			== dstOffset + computeVolumeSize(width,
+							   height,
+							   depth,
+							   PixelFormat(ComponentFormat::R8G8B8A8,
+												 TransformFormat::UNORM)));
+		dstOffset += token.m_offset;
+
+		// Create the copy regions
+		DynamicArrayAuto<VkBufferCopy> copies(m_alloc);
+		copies.create(width * height * depth);
+		U count = 0;
+		for(U x = 0; x < width; ++x)
+		{
+			for(U y = 0; y < height; ++y)
+			{
+				for(U d = 0; d < depth; ++d)
+				{
+					VkBufferCopy& c = copies[count++];
+					c.srcOffset = (d * height * width + y * width + x) * 3
+						+ token.m_offset;
+					c.dstOffset =
+						(d * height * width + y * width + x) * 4 + dstOffset;
+					c.size = 3;
+				}
+			}
+		}
+
+		// Copy buffer to buffer
+		VkBuffer buffHandle =
+			getGrManagerImpl().getTransientMemoryManager().getBufferHandle(
+				token.m_usage);
+		vkCmdCopyBuffer(
+			m_handle, buffHandle, buffHandle, copies.getSize(), &copies[0]);
+
+		// Set barrier
+		setBufferBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
+			VK_ACCESS_TRANSFER_WRITE_BIT,
+			VK_PIPELINE_STAGE_TRANSFER_BIT,
+			VK_ACCESS_TRANSFER_READ_BIT,
+			dstOffset,
+			token.m_range - (dstOffset - token.m_offset),
+			buffHandle);
+		flushBarriers();
+
+		// Do the copy to the image
+		VkBufferImageCopy region;
+		region.imageSubresource.aspectMask = impl.m_aspect;
+		region.imageSubresource.baseArrayLayer = impl.computeVkArrayLayer(vol);
+		region.imageSubresource.layerCount = 1;
+		region.imageSubresource.mipLevel = vol.m_level;
+		region.imageOffset = {0, 0, 0};
+		region.imageExtent.width = width;
+		region.imageExtent.height = height;
+		region.imageExtent.depth = depth;
+		region.bufferOffset = dstOffset;
+		region.bufferImageHeight = 0;
+		region.bufferRowLength = 0;
+
+		vkCmdCopyBufferToImage(m_handle,
+			buffHandle,
+			impl.m_imageHandle,
+			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+			1,
+			&region);
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+
+	m_texList.pushBack(m_alloc, tex);
+}
+
 } // end namespace anki

+ 12 - 7
src/gr/vulkan/FramebufferImpl.cpp

@@ -206,20 +206,25 @@ Error FramebufferImpl::initFramebuffer(const FramebufferInitInfo& init)
 
 		for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 		{
-			attachments[count] = init.m_colorAttachments[i]
-									 .m_texture->getImplementation()
-									 .m_viewHandle;
+			const FramebufferAttachmentInfo& att = init.m_colorAttachments[i];
+			TextureImpl& tex = att.m_texture->getImplementation();
 
-			m_refs[count++] = init.m_colorAttachments[i].m_texture;
+			attachments[count] =
+				tex.getOrCreateSingleSurfaceView(att.m_surface);
+
+			m_refs[count++] = att.m_texture;
 		}
 
 		if(hasDepthStencil)
 		{
+			const FramebufferAttachmentInfo& att =
+				init.m_depthStencilAttachment;
+			TextureImpl& tex = att.m_texture->getImplementation();
+
 			attachments[count] =
-				init.m_depthStencilAttachment.m_texture->getImplementation()
-					.m_viewHandle;
+				tex.getOrCreateSingleSurfaceView(att.m_surface);
 
-			m_refs[count++] = init.m_depthStencilAttachment.m_texture;
+			m_refs[count++] = att.m_texture;
 		}
 
 		m_width = m_refs[0]->getImplementation().m_width;

+ 39 - 1
src/gr/vulkan/GrManager.cpp

@@ -81,7 +81,7 @@ void* GrManager::tryAllocateFrameTransientMemory(
 }
 
 //==============================================================================
-void GrManager::getTextureUploadInfo(TexturePtr tex,
+void GrManager::getTextureSurfaceUploadInfo(TexturePtr tex,
 	const TextureSurfaceInfo& surf,
 	PtrSize& allocationSize,
 	BufferUsageBit& usage)
@@ -115,4 +115,42 @@ void GrManager::getTextureUploadInfo(TexturePtr tex,
 	usage = BufferUsageBit::TRANSFER_SOURCE;
 }
 
+//==============================================================================
+void GrManager::getTextureVolumeUploadInfo(TexturePtr tex,
+	const TextureVolumeInfo& vol,
+	PtrSize& allocationSize,
+	BufferUsageBit& usage)
+{
+	const TextureImpl& impl = tex->getImplementation();
+	impl.checkVolume(vol);
+
+	U width = impl.m_width >> vol.m_level;
+	U height = impl.m_height >> vol.m_level;
+	U depth = impl.m_depth >> vol.m_level;
+
+	if(!impl.m_workarounds)
+	{
+		allocationSize = computeVolumeSize(width, height, depth, impl.m_format);
+	}
+	else if(!!(impl.m_workarounds & TextureImplWorkaround::R8G8B8_TO_R8G8B8A8))
+	{
+		// Extra size for staging buffer
+		allocationSize = computeVolumeSize(width,
+			height,
+			depth,
+			PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UNORM));
+		alignRoundUp(16, allocationSize);
+		allocationSize += computeVolumeSize(width,
+			height,
+			depth,
+			PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM));
+	}
+	else
+	{
+		ANKI_ASSERT(0);
+	}
+
+	usage = BufferUsageBit::TRANSFER_SOURCE;
+}
+
 } // end namespace anki

+ 3 - 0
src/gr/vulkan/GrManagerImpl.cpp

@@ -307,6 +307,8 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 	}
 	ANKI_LOGI("GPU vendor is %s", &GPU_VENDOR_STR[m_vendor][0]);
 
+	vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_devFeatures);
+
 	return ErrorCode::NONE;
 }
 
@@ -367,6 +369,7 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 	ci.pQueueCreateInfos = &q;
 	ci.enabledExtensionCount = DEV_EXTENSIONS.getSize();
 	ci.ppEnabledExtensionNames = &DEV_EXTENSIONS[0];
+	ci.pEnabledFeatures = &m_devFeatures;
 
 	ANKI_VK_CHECK(vkCreateDevice(m_physicalDevice, &ci, nullptr, &m_device));
 

+ 2 - 9
src/gr/vulkan/ResourceGroupImpl.cpp

@@ -390,16 +390,9 @@ Error ResourceGroupImpl::init(const ResourceGroupInitInfo& init)
 		{
 			ANKI_ASSERT(!hole);
 			VkDescriptorImageInfo& inf = images[i];
-			const TextureImpl& tex = binding.m_texture->getImplementation();
+			TextureImpl& tex = binding.m_texture->getImplementation();
 
-			if(binding.m_level == 0)
-			{
-				inf.imageView = tex.m_viewHandle;
-			}
-			else
-			{
-				inf.imageView = tex.m_viewsEveryLevel[binding.m_level - 1];
-			}
+			inf.imageView = tex.getOrCreateSingleLevelView(binding.m_level);
 			inf.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
 
 			m_refs[refCount++] = binding.m_texture;

+ 114 - 18
src/gr/vulkan/TextureImpl.cpp

@@ -43,12 +43,15 @@ TextureImpl::~TextureImpl()
 		vkDestroyImageView(getDevice(), m_viewHandle, nullptr);
 	}
 
-	for(VkImageView view : m_viewsEveryLevel)
+	for(VkImageView view : m_singleSurfaceViews)
 	{
-		vkDestroyImageView(getDevice(), view, nullptr);
+		if(view)
+		{
+			vkDestroyImageView(getDevice(), view, nullptr);
+		}
 	}
 
-	m_viewsEveryLevel.destroy(getAllocator());
+	m_singleSurfaceViews.destroy(getAllocator());
 
 	if(m_imageHandle)
 	{
@@ -353,21 +356,6 @@ Error TextureImpl::initView(CreateContext& ctx)
 
 	ANKI_VK_CHECK(vkCreateImageView(getDevice(), &ci, nullptr, &m_viewHandle));
 
-	// Create the rest of the views
-	if(!!(m_usage & TextureUsageBit::IMAGE_COMPUTE_READ_WRITE))
-	{
-		ci.subresourceRange.levelCount = 1;
-
-		m_viewsEveryLevel.create(getAllocator(), m_mipCount - 1);
-
-		for(U i = 0; i < m_viewsEveryLevel.getSize(); ++i)
-		{
-			ci.subresourceRange.baseMipLevel = i + 1;
-			ANKI_VK_CHECK(vkCreateImageView(
-				getDevice(), &ci, nullptr, &m_viewsEveryLevel[i]));
-		}
-	}
-
 	return ErrorCode::NONE;
 }
 
@@ -667,4 +655,112 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 	return out;
 }
 
+//==============================================================================
+VkImageView TextureImpl::getOrCreateSingleSurfaceView(
+	const TextureSurfaceInfo& surf)
+{
+	checkSurface(surf);
+
+	// If it's single surface return the single view
+	if(m_mipCount == 1 && m_depth == 1 && m_layerCount == 1
+		&& m_type != TextureType::CUBE
+		&& m_type != TextureType::CUBE_ARRAY)
+	{
+		return m_viewHandle;
+	}
+
+	Bool isCube =
+		m_type == TextureType::CUBE || m_type == TextureType::CUBE_ARRAY;
+	U faceCount = (isCube) ? 6 : 1;
+
+	LockGuard<Mutex> lock(m_singleSurfaceViewsMtx);
+
+	// Create the array
+	if(m_singleSurfaceViews.isEmpty())
+	{
+		U surfCount = m_mipCount * m_depth * faceCount * m_layerCount;
+
+		m_singleSurfaceViews.create(getAllocator(), surfCount);
+		memset(&m_singleSurfaceViews[0],
+			0,
+			sizeof(m_singleSurfaceViews[0]) * surfCount);
+	}
+
+	// [level][depth][face][layer]
+	U surfIdx = surf.m_level * m_depth * faceCount * m_layerCount
+		+ surf.m_depth * faceCount * m_layerCount + surf.m_face * m_layerCount
+		+ surf.m_layer;
+
+	VkImageView& view = m_singleSurfaceViews[surfIdx];
+	if(ANKI_UNLIKELY(view == VK_NULL_HANDLE))
+	{
+		VkImageViewCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+		ci.image = m_imageHandle;
+		ci.viewType = convertTextureViewType(m_type);
+		ci.format = convertFormat(m_format);
+		ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+		ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+		ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+		ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+
+		computeSubResourceRange(surf, ci.subresourceRange);
+
+		ANKI_VK_CHECKF(vkCreateImageView(getDevice(), &ci, nullptr, &view));
+	}
+
+	ANKI_ASSERT(view);
+	return view;
+}
+
+//==============================================================================
+VkImageView TextureImpl::getOrCreateSingleLevelView(U level)
+{
+	ANKI_ASSERT(level < m_mipCount);
+
+	// If it's single level return the single view
+	if(m_mipCount == 1)
+	{
+		return m_viewHandle;
+	}
+
+	LockGuard<Mutex> lock(m_singleLevelViewsMtx);
+
+	// Create the array
+	if(m_singleLevelViews.isEmpty())
+	{
+		m_singleLevelViews.create(getAllocator(), m_mipCount);
+		memset(&m_singleLevelViews[0],
+			0,
+			sizeof(m_singleLevelViews[0]) * m_mipCount);
+	}
+
+	VkImageView& view = m_singleLevelViews[level];
+	if(ANKI_UNLIKELY(view == VK_NULL_HANDLE))
+	{
+		VkImageSubresourceRange range;
+		range.aspectMask = m_aspect;
+		range.baseMipLevel = level;
+		range.levelCount = 1;
+		range.baseArrayLayer = 0;
+		range.layerCount = VK_REMAINING_ARRAY_LAYERS;
+
+		VkImageViewCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+		ci.image = m_imageHandle;
+		ci.viewType = convertTextureViewType(m_type);
+		ci.format = convertFormat(m_format);
+		ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+		ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+		ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+		ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+		ci.subresourceRange = range;
+
+		ANKI_VK_CHECKF(vkCreateImageView(getDevice(), &ci, nullptr, &view));
+	}
+
+	ANKI_ASSERT(view);
+	return view;
+}
+
 } // end namespace anki

+ 4 - 4
src/renderer/Bloom.cpp

@@ -168,12 +168,12 @@ void Bloom::run(RenderingContext& ctx)
 	cmdb->endRenderPass();
 
 	// pass 1
-	cmdb->setTextureBarrier(m_extractExposure.m_rt,
+	cmdb->setTextureSurfaceBarrier(m_extractExposure.m_rt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
 
-	cmdb->setTextureBarrier(m_upscale.m_rt,
+	cmdb->setTextureSurfaceBarrier(m_upscale.m_rt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
@@ -188,7 +188,7 @@ void Bloom::run(RenderingContext& ctx)
 //==============================================================================
 void Bloom::setPreRunBarriers(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureBarrier(m_extractExposure.m_rt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_extractExposure.m_rt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
@@ -197,7 +197,7 @@ void Bloom::setPreRunBarriers(RenderingContext& ctx)
 //==============================================================================
 void Bloom::setPostRunBarriers(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureBarrier(m_upscale.m_rt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_upscale.m_rt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));

+ 2 - 2
src/renderer/DownscaleBlur.cpp

@@ -97,13 +97,13 @@ void DownscaleBlur::run(RenderingContext& ctx)
 
 		if(i > 0)
 		{
-			cmdb->setTextureBarrier(m_r->getIs().getRt(),
+			cmdb->setTextureSurfaceBarrier(m_r->getIs().getRt(),
 				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 				TextureUsageBit::SAMPLED_FRAGMENT,
 				TextureSurfaceInfo(i, 0, 0, 0));
 		}
 
-		cmdb->setTextureBarrier(m_r->getIs().getRt(),
+		cmdb->setTextureSurfaceBarrier(m_r->getIs().getRt(),
 			TextureUsageBit::NONE,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureSurfaceInfo(i + 1, 0, 0, 0));

+ 2 - 2
src/renderer/Fs.cpp

@@ -128,7 +128,7 @@ Error Fs::buildCommandBuffers(
 //==============================================================================
 void Fs::setPreRunBarriers(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureBarrier(m_rt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
@@ -137,7 +137,7 @@ void Fs::setPreRunBarriers(RenderingContext& ctx)
 //==============================================================================
 void Fs::setPostRunBarriers(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureBarrier(m_rt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));

+ 18 - 18
src/renderer/Ir.cpp

@@ -305,14 +305,14 @@ Error Ir::initIs()
 			{
 				TextureSurfaceInfo surf(l, 0, f, i);
 
-				cmdb->setTextureBarrier(m_is.m_lightRt,
+				cmdb->setTextureSurfaceBarrier(m_is.m_lightRt,
 					TextureUsageBit::NONE,
 					TextureUsageBit::CLEAR,
 					surf);
 
-				cmdb->clearTexture(m_is.m_lightRt, surf, clear);
+				cmdb->clearTextureSurface(m_is.m_lightRt, surf, clear);
 
-				cmdb->setTextureBarrier(m_is.m_lightRt,
+				cmdb->setTextureSurfaceBarrier(m_is.m_lightRt,
 					TextureUsageBit::CLEAR,
 					TextureUsageBit::SAMPLED_FRAGMENT,
 					surf);
@@ -450,14 +450,14 @@ Error Ir::initIrradiance()
 			{
 				TextureSurfaceInfo surf(l, 0, f, i);
 
-				cmdb->setTextureBarrier(m_is.m_lightRt,
+				cmdb->setTextureSurfaceBarrier(m_is.m_lightRt,
 					TextureUsageBit::NONE,
 					TextureUsageBit::CLEAR,
 					surf);
 
-				cmdb->clearTexture(m_irradiance.m_cubeArr, surf, clear);
+				cmdb->clearTextureSurface(m_irradiance.m_cubeArr, surf, clear);
 
-				cmdb->setTextureBarrier(m_is.m_lightRt,
+				cmdb->setTextureSurfaceBarrier(m_is.m_lightRt,
 					TextureUsageBit::CLEAR,
 					TextureUsageBit::SAMPLED_FRAGMENT,
 					surf);
@@ -486,13 +486,13 @@ Error Ir::runMs(
 	// Set barriers
 	for(U i = 0; i < MS_COLOR_ATTACHMENT_COUNT; ++i)
 	{
-		cmdb->setTextureBarrier(face.m_gbufferColorRts[i],
+		cmdb->setTextureSurfaceBarrier(face.m_gbufferColorRts[i],
 			TextureUsageBit::NONE,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureSurfaceInfo(0, 0, 0, 0));
 	}
 
-	cmdb->setTextureBarrier(face.m_gbufferDepthRt,
+	cmdb->setTextureSurfaceBarrier(face.m_gbufferDepthRt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
@@ -514,13 +514,13 @@ Error Ir::runMs(
 
 	for(U i = 0; i < MS_COLOR_ATTACHMENT_COUNT; ++i)
 	{
-		cmdb->setTextureBarrier(face.m_gbufferColorRts[i],
+		cmdb->setTextureSurfaceBarrier(face.m_gbufferColorRts[i],
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureUsageBit::SAMPLED_FRAGMENT,
 			TextureSurfaceInfo(0, 0, 0, 0));
 	}
 
-	cmdb->setTextureBarrier(face.m_gbufferDepthRt,
+	cmdb->setTextureSurfaceBarrier(face.m_gbufferDepthRt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT
 			| TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ,
@@ -538,7 +538,7 @@ void Ir::runIs(
 	FaceInfo& face = m_cacheEntries[layer].m_faces[faceIdx];
 
 	// Set barriers
-	cmdb->setTextureBarrier(m_is.m_lightRt,
+	cmdb->setTextureSurfaceBarrier(m_is.m_lightRt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureSurfaceInfo(0, 0, faceIdx, layer));
@@ -663,14 +663,14 @@ void Ir::runIs(
 	// Generate mips
 	cmdb->endRenderPass();
 
-	cmdb->setTextureBarrier(m_is.m_lightRt,
+	cmdb->setTextureSurfaceBarrier(m_is.m_lightRt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::GENERATE_MIPMAPS,
 		TextureSurfaceInfo(0, 0, faceIdx, layer));
 
-	cmdb->generateMipmaps(m_is.m_lightRt, 0, faceIdx, layer);
+	cmdb->generateMipmaps2d(m_is.m_lightRt, faceIdx, layer);
 
-	cmdb->setTextureBarrier(m_is.m_lightRt,
+	cmdb->setTextureSurfaceBarrier(m_is.m_lightRt,
 		TextureUsageBit::GENERATE_MIPMAPS,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, faceIdx, layer));
@@ -683,7 +683,7 @@ void Ir::computeIrradiance(RenderingContext& rctx, U layer, U faceIdx)
 	FaceInfo& face = m_cacheEntries[layer].m_faces[faceIdx];
 
 	// Set barrier
-	cmdb->setTextureBarrier(m_irradiance.m_cubeArr,
+	cmdb->setTextureSurfaceBarrier(m_irradiance.m_cubeArr,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureSurfaceInfo(0, 0, faceIdx, layer));
@@ -707,14 +707,14 @@ void Ir::computeIrradiance(RenderingContext& rctx, U layer, U faceIdx)
 	cmdb->endRenderPass();
 
 	// Gen mips
-	cmdb->setTextureBarrier(m_irradiance.m_cubeArr,
+	cmdb->setTextureSurfaceBarrier(m_irradiance.m_cubeArr,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::GENERATE_MIPMAPS,
 		TextureSurfaceInfo(0, 0, faceIdx, layer));
 
-	cmdb->generateMipmaps(m_irradiance.m_cubeArr, 0, faceIdx, layer);
+	cmdb->generateMipmaps2d(m_irradiance.m_cubeArr, faceIdx, layer);
 
-	cmdb->setTextureBarrier(m_irradiance.m_cubeArr,
+	cmdb->setTextureSurfaceBarrier(m_irradiance.m_cubeArr,
 		TextureUsageBit::GENERATE_MIPMAPS,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, faceIdx, layer));

+ 1 - 1
src/renderer/Is.cpp

@@ -259,7 +259,7 @@ void Is::updateCommonBlock(RenderingContext& ctx)
 //==============================================================================
 void Is::setPreRunBarriers(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureBarrier(m_rt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));

+ 8 - 8
src/renderer/Ms.cpp

@@ -193,22 +193,22 @@ void Ms::setPreRunBarriers(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	TextureSurfaceInfo surf(0, 0, 0, 0);
 
-	cmdb->setTextureBarrier(m_rt0,
+	cmdb->setTextureSurfaceBarrier(m_rt0,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		surf);
 
-	cmdb->setTextureBarrier(m_rt1,
+	cmdb->setTextureSurfaceBarrier(m_rt1,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		surf);
 
-	cmdb->setTextureBarrier(m_rt2,
+	cmdb->setTextureSurfaceBarrier(m_rt2,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		surf);
 
-	cmdb->setTextureBarrier(m_depthRt,
+	cmdb->setTextureSurfaceBarrier(m_depthRt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		surf);
@@ -224,22 +224,22 @@ void Ms::setPostRunBarriers(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	TextureSurfaceInfo surf(0, 0, 0, 0);
 
-	cmdb->setTextureBarrier(m_rt0,
+	cmdb->setTextureSurfaceBarrier(m_rt0,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		surf);
 
-	cmdb->setTextureBarrier(m_rt1,
+	cmdb->setTextureSurfaceBarrier(m_rt1,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		surf);
 
-	cmdb->setTextureBarrier(m_rt2,
+	cmdb->setTextureSurfaceBarrier(m_rt2,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		surf);
 
-	cmdb->setTextureBarrier(m_depthRt,
+	cmdb->setTextureSurfaceBarrier(m_depthRt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		surf);

+ 9 - 9
src/renderer/Renderer.cpp

@@ -260,28 +260,28 @@ Error Renderer::render(RenderingContext& ctx)
 
 	m_is->run(ctx);
 
-	cmdb->setTextureBarrier(m_ms->getDepthRt(),
+	cmdb->setTextureSurfaceBarrier(m_ms->getDepthRt(),
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureUsageBit::GENERATE_MIPMAPS,
 		TextureSurfaceInfo(0, 0, 0, 0));
 
-	cmdb->setTextureBarrier(m_ms->getRt2(),
+	cmdb->setTextureSurfaceBarrier(m_ms->getRt2(),
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureUsageBit::GENERATE_MIPMAPS,
 		TextureSurfaceInfo(0, 0, 0, 0));
 
-	cmdb->generateMipmaps(m_ms->getDepthRt(), 0, 0, 0);
-	cmdb->generateMipmaps(m_ms->getRt2(), 0, 0, 0);
+	cmdb->generateMipmaps2d(m_ms->getDepthRt(), 0, 0);
+	cmdb->generateMipmaps2d(m_ms->getRt2(), 0, 0);
 
 	for(U i = 0; i < m_ms->getDepthRtMipmapCount(); ++i)
 	{
-		cmdb->setTextureBarrier(m_ms->getDepthRt(),
+		cmdb->setTextureSurfaceBarrier(m_ms->getDepthRt(),
 			TextureUsageBit::GENERATE_MIPMAPS,
 			TextureUsageBit::SAMPLED_FRAGMENT
 				| TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ,
 			TextureSurfaceInfo(i, 0, 0, 0));
 
-		cmdb->setTextureBarrier(m_ms->getRt2(),
+		cmdb->setTextureSurfaceBarrier(m_ms->getRt2(),
 			TextureUsageBit::GENERATE_MIPMAPS,
 			TextureUsageBit::SAMPLED_FRAGMENT,
 			TextureSurfaceInfo(i, 0, 0, 0));
@@ -296,21 +296,21 @@ Error Renderer::render(RenderingContext& ctx)
 
 	m_upsample->run(ctx);
 
-	cmdb->setTextureBarrier(m_is->getRt(),
+	cmdb->setTextureSurfaceBarrier(m_is->getRt(),
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
 
 	m_downscale->run(ctx);
 
-	cmdb->setTextureBarrier(m_is->getRt(),
+	cmdb->setTextureSurfaceBarrier(m_is->getRt(),
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_COMPUTE,
 		TextureSurfaceInfo(m_is->getRtMipmapCount() - 1, 0, 0, 0));
 
 	m_tm->run(ctx);
 
-	cmdb->setTextureBarrier(m_is->getRt(),
+	cmdb->setTextureSurfaceBarrier(m_is->getRt(),
 		TextureUsageBit::SAMPLED_COMPUTE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(m_is->getRtMipmapCount() - 1, 0, 0, 0));

+ 4 - 4
src/renderer/Sm.cpp

@@ -464,7 +464,7 @@ void Sm::setPreRunBarriers(RenderingContext& ctx)
 	{
 		U layer = ctx.m_sm.m_spotCacheIndices[i];
 
-		cmdb->setTextureBarrier(m_spotTexArray,
+		cmdb->setTextureSurfaceBarrier(m_spotTexArray,
 			TextureUsageBit::NONE,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 			TextureSurfaceInfo(0, 0, 0, layer));
@@ -477,7 +477,7 @@ void Sm::setPreRunBarriers(RenderingContext& ctx)
 		{
 			U layer = ctx.m_sm.m_omniCacheIndices[i];
 
-			cmdb->setTextureBarrier(m_omniTexArray,
+			cmdb->setTextureSurfaceBarrier(m_omniTexArray,
 				TextureUsageBit::NONE,
 				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 				TextureSurfaceInfo(0, 0, j, layer));
@@ -498,7 +498,7 @@ void Sm::setPostRunBarriers(RenderingContext& ctx)
 	{
 		U layer = ctx.m_sm.m_spotCacheIndices[i];
 
-		cmdb->setTextureBarrier(m_spotTexArray,
+		cmdb->setTextureSurfaceBarrier(m_spotTexArray,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 			TextureUsageBit::SAMPLED_FRAGMENT,
 			TextureSurfaceInfo(0, 0, 0, layer));
@@ -511,7 +511,7 @@ void Sm::setPostRunBarriers(RenderingContext& ctx)
 		{
 			U layer = ctx.m_sm.m_omniCacheIndices[i];
 
-			cmdb->setTextureBarrier(m_omniTexArray,
+			cmdb->setTextureSurfaceBarrier(m_omniTexArray,
 				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 				TextureUsageBit::SAMPLED_FRAGMENT,
 				TextureSurfaceInfo(0, 0, j, layer));

+ 6 - 6
src/renderer/Ssao.cpp

@@ -279,7 +279,7 @@ Error Ssao::init(const ConfigSet& config)
 //==============================================================================
 void Ssao::setPreRunBarriers(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureBarrier(m_vblurRt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vblurRt,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
@@ -288,7 +288,7 @@ void Ssao::setPreRunBarriers(RenderingContext& ctx)
 //==============================================================================
 void Ssao::setPostRunBarriers(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureBarrier(m_vblurRt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vblurRt,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
@@ -318,11 +318,11 @@ void Ssao::run(RenderingContext& ctx)
 	for(U i = 0; i < m_blurringIterationsCount; i++)
 	{
 		// hpass
-		cmdb->setTextureBarrier(m_vblurRt,
+		cmdb->setTextureSurfaceBarrier(m_vblurRt,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureUsageBit::SAMPLED_FRAGMENT,
 			TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->setTextureBarrier(m_hblurRt,
+		cmdb->setTextureSurfaceBarrier(m_hblurRt,
 			TextureUsageBit::NONE,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureSurfaceInfo(0, 0, 0, 0));
@@ -333,11 +333,11 @@ void Ssao::run(RenderingContext& ctx)
 		cmdb->endRenderPass();
 
 		// vpass
-		cmdb->setTextureBarrier(m_hblurRt,
+		cmdb->setTextureSurfaceBarrier(m_hblurRt,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureUsageBit::SAMPLED_FRAGMENT,
 			TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->setTextureBarrier(m_vblurRt,
+		cmdb->setTextureSurfaceBarrier(m_vblurRt,
 			TextureUsageBit::NONE,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureSurfaceInfo(0, 0, 0, 0));

+ 165 - 50
src/resource/ImageLoader.cpp

@@ -247,6 +247,33 @@ static PtrSize calcSurfaceSize(const U width,
 	return out;
 }
 
+//==============================================================================
+/// Get the size in bytes of a single volume
+static PtrSize calcVolumeSize(const U width,
+	const U height,
+	const U depth,
+	const ImageLoader::DataCompression comp,
+	const ImageLoader::ColorFormat cf)
+{
+	PtrSize out = 0;
+
+	ANKI_ASSERT(width >= 4 || height >= 4 || depth >= 4);
+
+	switch(comp)
+	{
+	case ImageLoader::DataCompression::RAW:
+		out = width * height * depth
+			* ((cf == ImageLoader::ColorFormat::RGB8) ? 3 : 4);
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	ANKI_ASSERT(out > 0);
+
+	return out;
+}
+
 //==============================================================================
 /// Calculate the size of a compressed or uncomressed color data
 static PtrSize calcSizeOfSegment(
@@ -256,34 +283,50 @@ static PtrSize calcSizeOfSegment(
 	U width = header.m_width;
 	U height = header.m_height;
 	U mips = header.m_mipLevels;
-	U surfCountPerMip = 0;
-
 	ANKI_ASSERT(mips > 0);
 
-	switch(header.m_type)
+	if(header.m_type != ImageLoader::TextureType::_3D)
 	{
-	case ImageLoader::TextureType::_2D:
-		surfCountPerMip = 1;
-		break;
-	case ImageLoader::TextureType::CUBE:
-		surfCountPerMip = 6;
-		break;
-	case ImageLoader::TextureType::_2D_ARRAY:
-	case ImageLoader::TextureType::_3D:
-		surfCountPerMip = header.m_depthOrLayerCount;
-		break;
-	default:
-		ANKI_ASSERT(0);
-		break;
-	}
+		U surfCountPerMip = 0;
+
+		switch(header.m_type)
+		{
+		case ImageLoader::TextureType::_2D:
+			surfCountPerMip = 1;
+			break;
+		case ImageLoader::TextureType::CUBE:
+			surfCountPerMip = 6;
+			break;
+		case ImageLoader::TextureType::_2D_ARRAY:
+			surfCountPerMip = header.m_depthOrLayerCount;
+			break;
+		default:
+			ANKI_ASSERT(0);
+			break;
+		}
+
+		while(mips-- != 0)
+		{
+			out += calcSurfaceSize(width, height, comp, header.m_colorFormat)
+				* surfCountPerMip;
 
-	while(mips-- != 0)
+			width /= 2;
+			height /= 2;
+		}
+	}
+	else
 	{
-		out += calcSurfaceSize(width, height, comp, header.m_colorFormat)
-			* surfCountPerMip;
+		U depth = header.m_depthOrLayerCount;
+
+		while(mips-- != 0)
+		{
+			out += calcVolumeSize(
+				width, height, depth, comp, header.m_colorFormat);
 
-		width /= 2;
-		height /= 2;
+			width /= 2;
+			height /= 2;
+			depth /= 2;
+		}
 	}
 
 	return out;
@@ -294,9 +337,12 @@ static ANKI_USE_RESULT Error loadAnkiTexture(ResourceFilePtr file,
 	U32 maxTextureSize,
 	ImageLoader::DataCompression& preferredCompression,
 	DynamicArray<ImageLoader::Surface>& surfaces,
+	DynamicArray<ImageLoader::Volume>& volumes,
 	GenericMemoryPoolAllocator<U8>& alloc,
+	U32& width,
+	U32& height,
 	U8& depthOrLayerCount,
-	U8& mipLevels,
+	U8& loadedMips,
 	ImageLoader::TextureType& textureType,
 	ImageLoader::ColorFormat& colorFormat)
 {
@@ -322,7 +368,7 @@ static ANKI_USE_RESULT Error loadAnkiTexture(ResourceFilePtr file,
 		return ErrorCode::USER_DATA;
 	}
 
-	if(header.m_depthOrLayerCount < 1 || header.m_depthOrLayerCount > 128)
+	if(header.m_depthOrLayerCount < 1 || header.m_depthOrLayerCount > 4096)
 	{
 		ANKI_LOGE("Zero or too big depth or layerCount");
 		return ErrorCode::USER_DATA;
@@ -364,22 +410,30 @@ static ANKI_USE_RESULT Error loadAnkiTexture(ResourceFilePtr file,
 		return ErrorCode::USER_DATA;
 	}
 
+	width = header.m_width;
+	height = header.m_height;
+
 	// Check mip levels
 	U size = min(header.m_width, header.m_height);
-	U maxsize = max(header.m_width, header.m_height);
-	mipLevels = 0;
+	U maxSize = max(header.m_width, header.m_height);
+	if(header.m_type == ImageLoader::TextureType::_3D)
+	{
+		maxSize = max<U>(maxSize, header.m_depthOrLayerCount);
+		size = min<U>(size, header.m_depthOrLayerCount);
+	}
+	loadedMips = 0;
 	U tmpMipLevels = 0;
 	while(size >= 4) // The minimum size is 4x4
 	{
 		++tmpMipLevels;
 
-		if(maxsize <= maxTextureSize)
+		if(maxSize <= maxTextureSize)
 		{
-			++mipLevels;
+			++loadedMips;
 		}
 
 		size /= 2;
-		maxsize /= 2;
+		maxSize /= 2;
 	}
 
 	if(header.m_mipLevels > tmpMipLevels)
@@ -388,7 +442,7 @@ static ANKI_USE_RESULT Error loadAnkiTexture(ResourceFilePtr file,
 		return ErrorCode::USER_DATA;
 	}
 
-	mipLevels = min<U>(mipLevels, header.m_mipLevels);
+	loadedMips = min<U>(loadedMips, header.m_mipLevels);
 
 	colorFormat = header.m_colorFormat;
 
@@ -455,41 +509,82 @@ static ANKI_USE_RESULT Error loadAnkiTexture(ResourceFilePtr file,
 	//
 
 	// Allocate the surfaces
-	surfaces.create(alloc, mipLevels * depthOrLayerCount);
+	if(header.m_type != ImageLoader::TextureType::_3D)
+	{
+		surfaces.create(alloc, loadedMips * depthOrLayerCount);
+
+		// Read all surfaces
+		U mipWidth = header.m_width;
+		U mipHeight = header.m_height;
+		U index = 0;
+		for(U mip = 0; mip < header.m_mipLevels; mip++)
+		{
+			for(U d = 0; d < depthOrLayerCount; d++)
+			{
+				U dataSize = calcSurfaceSize(mipWidth,
+					mipHeight,
+					preferredCompression,
+					header.m_colorFormat);
+
+				// Check if this mipmap can be skipped because of size
+				if(maxSize <= maxTextureSize)
+				{
+					ImageLoader::Surface& surf = surfaces[index++];
+					surf.m_width = mipWidth;
+					surf.m_height = mipHeight;
 
-	// Read all surfaces
-	U mipWidth = header.m_width;
-	U mipHeight = header.m_height;
-	U index = 0;
-	for(U mip = 0; mip < header.m_mipLevels; mip++)
+					surf.m_data.create(alloc, dataSize);
+
+					ANKI_CHECK(file->read(&surf.m_data[0], dataSize));
+				}
+				else
+				{
+					ANKI_CHECK(file->seek(
+						dataSize, ResourceFile::SeekOrigin::CURRENT));
+				}
+			}
+
+			mipWidth /= 2;
+			mipHeight /= 2;
+		}
+	}
+	else
 	{
-		for(U d = 0; d < depthOrLayerCount; d++)
+		volumes.create(alloc, loadedMips);
+
+		U mipWidth = header.m_width;
+		U mipHeight = header.m_height;
+		U mipDepth = header.m_depthOrLayerCount;
+		for(U mip = 0; mip < header.m_mipLevels; mip++)
 		{
-			U dataSize = calcSurfaceSize(mipWidth,
+			U dataSize = calcVolumeSize(mipWidth,
 				mipHeight,
+				mipDepth,
 				preferredCompression,
 				header.m_colorFormat);
 
 			// Check if this mipmap can be skipped because of size
-			if(max(mipWidth, mipHeight) <= maxTextureSize)
+			if(maxSize <= maxTextureSize)
 			{
-				ImageLoader::Surface& surf = surfaces[index++];
-				surf.m_width = mipWidth;
-				surf.m_height = mipHeight;
+				ImageLoader::Volume& vol = volumes[mip];
+				vol.m_width = mipWidth;
+				vol.m_height = mipHeight;
+				vol.m_depth = mipDepth;
 
-				surf.m_data.create(alloc, dataSize);
+				vol.m_data.create(alloc, dataSize);
 
-				ANKI_CHECK(file->read(&surf.m_data[0], dataSize));
+				ANKI_CHECK(file->read(&vol.m_data[0], dataSize));
 			}
 			else
 			{
 				ANKI_CHECK(
 					file->seek(dataSize, ResourceFile::SeekOrigin::CURRENT));
 			}
-		}
 
-		mipWidth /= 2;
-		mipHeight /= 2;
+			mipWidth /= 2;
+			mipHeight /= 2;
+			mipDepth /= 2;
+		}
 	}
 
 	return ErrorCode::NONE;
@@ -531,6 +626,9 @@ Error ImageLoader::load(
 			m_surfaces[0].m_data,
 			m_alloc));
 
+		m_width = m_surfaces[0].m_width;
+		m_height = m_surfaces[0].m_height;
+
 		if(bpp == 32)
 		{
 			m_colorFormat = ColorFormat::RGBA8;
@@ -558,7 +656,10 @@ Error ImageLoader::load(
 			maxTextureSize,
 			m_compression,
 			m_surfaces,
+			m_volumes,
 			m_alloc,
+			m_width,
+			m_height,
 			m_depthOrLayerCount,
 			m_mipLevels,
 			m_textureType,
@@ -575,7 +676,7 @@ Error ImageLoader::load(
 
 //==============================================================================
 const ImageLoader::Surface& ImageLoader::getSurface(
-	U level, U depth, U face, U layer) const
+	U level, U face, U layer) const
 {
 	ANKI_ASSERT(level < m_mipLevels);
 
@@ -591,7 +692,7 @@ const ImageLoader::Surface& ImageLoader::getSurface(
 		idx = level * 6 + face;
 		break;
 	case TextureType::_3D:
-		idx = level * m_depthOrLayerCount + depth;
+		ANKI_ASSERT(0 && "Can't use that for 3D textures");
 		break;
 	case TextureType::_2D_ARRAY:
 		idx = level * m_depthOrLayerCount + layer;
@@ -603,15 +704,29 @@ const ImageLoader::Surface& ImageLoader::getSurface(
 	return m_surfaces[idx];
 }
 
+//==============================================================================
+const ImageLoader::Volume& ImageLoader::getVolume(U level) const
+{
+	ANKI_ASSERT(m_textureType == TextureType::_3D);
+	return m_volumes[level];
+}
+
 //==============================================================================
 void ImageLoader::destroy()
 {
-	for(ImageLoader::Surface& surf : m_surfaces)
+	for(Surface& surf : m_surfaces)
 	{
 		surf.m_data.destroy(m_alloc);
 	}
 
 	m_surfaces.destroy(m_alloc);
+
+	for(Volume& v : m_volumes)
+	{
+		v.m_data.destroy(m_alloc);
+	}
+
+	m_volumes.destroy(m_alloc);
 }
 
 } // end namespace anki

+ 92 - 44
src/resource/TextureResource.cpp

@@ -22,14 +22,13 @@ public:
 	ImageLoader m_loader;
 	TexturePtr m_tex;
 	GrManager* m_gr ANKI_DBG_NULLIFY_PTR;
-	U m_depth = 0;
 	U m_layers = 0;
 	U m_faces = 0;
+	TextureType m_texType;
 
 	class
 	{
 	public:
-		U m_depth = 0;
 		U m_face = 0;
 		U m_mip = 0;
 		U m_layer = 0;
@@ -51,64 +50,114 @@ Error TexUploadTask::operator()(AsyncLoaderTaskContext& ctx)
 	// Upload the data
 	for(U layer = m_ctx.m_layer; layer < m_layers; ++layer)
 	{
-		for(U depth = m_ctx.m_depth; depth < m_depth; ++depth)
+		for(U face = m_ctx.m_face; face < m_faces; ++face)
 		{
-			for(U face = m_ctx.m_face; face < m_faces; ++face)
+			for(U mip = m_ctx.m_mip; mip < m_loader.getMipLevelsCount(); ++mip)
 			{
-				for(U mip = m_ctx.m_mip; mip < m_loader.getMipLevelsCount();
-					++mip)
+				PtrSize surfOrVolSize;
+				const void* surfOrVolData;
+				PtrSize allocationSize;
+				BufferUsageBit uploadBuffUsage;
+
+				if(m_texType == TextureType::_3D)
+				{
+					const auto& vol = m_loader.getVolume(mip);
+					surfOrVolSize = vol.m_data.getSize();
+					surfOrVolData = &vol.m_data[0];
+
+					m_gr->getTextureVolumeUploadInfo(m_tex,
+						TextureVolumeInfo(mip),
+						allocationSize,
+						uploadBuffUsage);
+				}
+				else
 				{
-					const auto& surf =
-						m_loader.getSurface(mip, depth, face, layer);
+					const auto& surf = m_loader.getSurface(mip, face, layer);
+					surfOrVolSize = surf.m_data.getSize();
+					surfOrVolData = &surf.m_data[0];
 
-					PtrSize allocationSize;
-					BufferUsageBit uploadBuffUsage;
-					m_gr->getTextureUploadInfo(m_tex,
-						TextureSurfaceInfo(mip, depth, face, layer),
+					m_gr->getTextureSurfaceUploadInfo(m_tex,
+						TextureSurfaceInfo(mip, 0, face, layer),
 						allocationSize,
 						uploadBuffUsage);
-					ANKI_ASSERT(allocationSize >= surf.m_data.getSize());
+				}
+
+				ANKI_ASSERT(allocationSize >= surfOrVolSize);
+
+				TransientMemoryToken token;
+				void* data = m_gr->tryAllocateFrameTransientMemory(
+					allocationSize, uploadBuffUsage, token);
+
+				if(data)
+				{
+					// There is enough transfer memory
 
-					TransientMemoryToken token;
-					void* data = m_gr->tryAllocateFrameTransientMemory(
-						allocationSize, uploadBuffUsage, token);
+					memcpy(data, surfOrVolData, surfOrVolSize);
 
-					if(data)
+					if(!cmdb)
 					{
-						// There is enough transfer memory
+						CommandBufferInitInfo ci;
+						ci.m_flags = CommandBufferFlag::TRANSFER_WORK
+							| CommandBufferFlag::SMALL_BATCH;
 
-						memcpy(data, &surf.m_data[0], surf.m_data.getSize());
+						cmdb = m_gr->newInstance<CommandBuffer>(ci);
+					}
+
+					if(m_texType == TextureType::_3D)
+					{
+						TextureVolumeInfo vol(mip);
 
-						if(!cmdb)
-						{
-							cmdb = m_gr->newInstance<CommandBuffer>(
-								CommandBufferInitInfo());
-						}
+						cmdb->setTextureVolumeBarrier(m_tex,
+							TextureUsageBit::NONE,
+							TextureUsageBit::UPLOAD,
+							vol);
 
-						cmdb->uploadTextureSurface(m_tex,
-							TextureSurfaceInfo(mip, depth, face, layer),
-							token);
+						cmdb->uploadTextureVolume(m_tex, vol, token);
+
+						cmdb->setTextureVolumeBarrier(m_tex,
+							TextureUsageBit::UPLOAD,
+							TextureUsageBit::SAMPLED_FRAGMENT
+								| TextureUsageBit::
+									  SAMPLED_TESSELLATION_EVALUATION,
+							vol);
 					}
 					else
 					{
-						// Not enough transfer memory. Move the work to the
-						// future
+						TextureSurfaceInfo surf(mip, 0, face, layer);
 
-						if(cmdb)
-						{
-							cmdb->flush();
-						}
+						cmdb->setTextureSurfaceBarrier(m_tex,
+							TextureUsageBit::NONE,
+							TextureUsageBit::UPLOAD,
+							surf);
 
-						m_ctx.m_depth = depth;
-						m_ctx.m_mip = mip;
-						m_ctx.m_face = face;
-						m_ctx.m_layer = layer;
+						cmdb->uploadTextureSurface(m_tex, surf, token);
 
-						ctx.m_pause = true;
-						ctx.m_resubmitTask = true;
+						cmdb->setTextureSurfaceBarrier(m_tex,
+							TextureUsageBit::UPLOAD,
+							TextureUsageBit::SAMPLED_FRAGMENT
+								| TextureUsageBit::
+									  SAMPLED_TESSELLATION_EVALUATION,
+							surf);
+					}
+				}
+				else
+				{
+					// Not enough transfer memory. Move the work to the
+					// future
 
-						return ErrorCode::NONE;
+					if(cmdb)
+					{
+						cmdb->flush();
 					}
+
+					m_ctx.m_mip = mip;
+					m_ctx.m_face = face;
+					m_ctx.m_layer = layer;
+
+					ctx.m_pause = true;
+					ctx.m_resubmitTask = true;
+
+					return ErrorCode::NONE;
 				}
 			}
 		}
@@ -152,9 +201,8 @@ Error TextureResource::load(const ResourceFilename& filename)
 	ANKI_CHECK(loader.load(file, filename, getManager().getMaxTextureSize()));
 
 	// Various sizes
-	const auto& tmpSurf = loader.getSurface(0, 0, 0, 0);
-	init.m_width = tmpSurf.m_width;
-	init.m_height = tmpSurf.m_height;
+	init.m_width = loader.getWidth();
+	init.m_height = loader.getHeight();
 
 	switch(loader.getTextureType())
 	{
@@ -252,11 +300,11 @@ Error TextureResource::load(const ResourceFilename& filename)
 	m_tex = getManager().getGrManager().newInstance<Texture>(init);
 
 	// Upload the data asynchronously
-	task->m_depth = init.m_depth;
 	task->m_layers = init.m_layerCount;
 	task->m_faces = faces;
 	task->m_gr = &getManager().getGrManager();
 	task->m_tex = m_tex;
+	task->m_texType = init.m_type;
 
 	getManager().getAsyncLoader().submitTask(task);
 

+ 1 - 1
src/ui/UiInterfaceImpl.cpp

@@ -227,7 +227,7 @@ Error UiInterfaceImpl::createR8Image(
 	cmdb->uploadTextureSurface(tex, TextureSurfaceInfo(0, 0, 0, 0), token);
 
 	// Gen mips
-	cmdb->generateMipmaps(tex, 0, 0, 0);
+	cmdb->generateMipmaps2d(tex, 0, 0);
 	cmdb->flush();
 
 	// Create the UiImage

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 456 - 475
tests/gr/Gr.cpp


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно