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

Merge pull request #26 from godlikepanos/texture_view

Add support for texture views
Panagiotis Christopoulos Charitos 8 жил өмнө
parent
commit
c6ff40dcc5
98 өөрчлөгдсөн 2385 нэмэгдсэн , 1979 устгасан
  1. 1 0
      src/anki/Gr.h
  2. 8 8
      src/anki/collision/Frustum.cpp
  3. 1 1
      src/anki/core/NativeWindowSdl.cpp
  4. 6 3
      src/anki/gr/Buffer.h
  5. 27 50
      src/anki/gr/CommandBuffer.h
  6. 153 1
      src/anki/gr/Common.cpp
  7. 71 5
      src/anki/gr/Common.h
  8. 28 1
      src/anki/gr/Enums.h
  9. 10 7
      src/anki/gr/Framebuffer.h
  10. 1 13
      src/anki/gr/GrManager.h
  11. 1 0
      src/anki/gr/GrObject.h
  12. 161 174
      src/anki/gr/RenderGraph.cpp
  13. 80 135
      src/anki/gr/RenderGraph.h
  14. 87 0
      src/anki/gr/RenderGraph.inl.h
  15. 8 0
      src/anki/gr/Shader.h
  16. 41 0
      src/anki/gr/ShaderProgram.h
  17. 139 2
      src/anki/gr/Texture.h
  18. 137 0
      src/anki/gr/TextureView.h
  19. 0 197
      src/anki/gr/common/Misc.cpp
  20. 0 39
      src/anki/gr/common/Misc.h
  21. 6 10
      src/anki/gr/gl/Buffer.cpp
  22. 6 14
      src/anki/gr/gl/BufferImpl.cpp
  23. 12 3
      src/anki/gr/gl/BufferImpl.h
  24. 61 140
      src/anki/gr/gl/CommandBuffer.cpp
  25. 17 2
      src/anki/gr/gl/Common.cpp
  26. 1 0
      src/anki/gr/gl/Framebuffer.cpp
  27. 45 53
      src/anki/gr/gl/FramebufferImpl.cpp
  28. 4 14
      src/anki/gr/gl/FramebufferImpl.h
  29. 22 27
      src/anki/gr/gl/GrManager.cpp
  30. 1 0
      src/anki/gr/gl/OcclusionQuery.cpp
  31. 3 7
      src/anki/gr/gl/RenderingThread.cpp
  32. 1 0
      src/anki/gr/gl/Sampler.cpp
  33. 7 5
      src/anki/gr/gl/Shader.cpp
  34. 3 4
      src/anki/gr/gl/ShaderImpl.cpp
  35. 7 7
      src/anki/gr/gl/ShaderImpl.h
  36. 4 1
      src/anki/gr/gl/ShaderProgram.cpp
  37. 9 16
      src/anki/gr/gl/StateTracker.h
  38. 1 0
      src/anki/gr/gl/Texture.cpp
  39. 112 130
      src/anki/gr/gl/TextureImpl.cpp
  40. 26 29
      src/anki/gr/gl/TextureImpl.h
  41. 53 0
      src/anki/gr/gl/TextureView.cpp
  42. 25 0
      src/anki/gr/gl/TextureViewImpl.cpp
  43. 45 0
      src/anki/gr/gl/TextureViewImpl.h
  44. 19 46
      src/anki/gr/vulkan/CommandBuffer.cpp
  45. 99 167
      src/anki/gr/vulkan/CommandBufferImpl.cpp
  46. 20 23
      src/anki/gr/vulkan/CommandBufferImpl.h
  47. 35 33
      src/anki/gr/vulkan/CommandBufferImpl.inl.h
  48. 1 17
      src/anki/gr/vulkan/Common.cpp
  49. 34 4
      src/anki/gr/vulkan/Common.h
  50. 3 5
      src/anki/gr/vulkan/DescriptorSet.cpp
  51. 17 17
      src/anki/gr/vulkan/DescriptorSet.h
  52. 19 31
      src/anki/gr/vulkan/FramebufferImpl.cpp
  53. 3 3
      src/anki/gr/vulkan/FramebufferImpl.h
  54. 8 56
      src/anki/gr/vulkan/GrManager.cpp
  55. 11 0
      src/anki/gr/vulkan/GrManagerImpl.cpp
  56. 2 5
      src/anki/gr/vulkan/GrManagerImpl.h
  57. 0 1
      src/anki/gr/vulkan/ShaderImpl.h
  58. 1 0
      src/anki/gr/vulkan/ShaderProgramImpl.cpp
  59. 37 69
      src/anki/gr/vulkan/TextureImpl.cpp
  60. 62 52
      src/anki/gr/vulkan/TextureImpl.h
  61. 0 124
      src/anki/gr/vulkan/TextureImpl.inl.h
  62. 18 0
      src/anki/gr/vulkan/TextureView.cpp
  63. 39 0
      src/anki/gr/vulkan/TextureViewImpl.cpp
  64. 47 0
      src/anki/gr/vulkan/TextureViewImpl.h
  65. 1 1
      src/anki/math/Mat3x4.h
  66. 3 3
      src/anki/renderer/Bloom.cpp
  67. 11 8
      src/anki/renderer/DepthDownscale.cpp
  68. 2 2
      src/anki/renderer/DownscaleBlur.cpp
  69. 5 5
      src/anki/renderer/FinalComposite.cpp
  70. 22 12
      src/anki/renderer/ForwardShading.cpp
  71. 4 2
      src/anki/renderer/GBuffer.cpp
  72. 32 31
      src/anki/renderer/Indirect.cpp
  73. 2 2
      src/anki/renderer/Indirect.h
  74. 7 4
      src/anki/renderer/LensFlare.cpp
  75. 6 6
      src/anki/renderer/LightBin.cpp
  76. 2 2
      src/anki/renderer/LightBin.h
  77. 18 12
      src/anki/renderer/LightShading.cpp
  78. 1 1
      src/anki/renderer/MainRenderer.cpp
  79. 5 3
      src/anki/renderer/RenderQueue.h
  80. 28 13
      src/anki/renderer/Renderer.cpp
  81. 3 3
      src/anki/renderer/Renderer.h
  82. 7 6
      src/anki/renderer/ShadowMapping.cpp
  83. 12 9
      src/anki/renderer/Ssao.cpp
  84. 10 4
      src/anki/renderer/TemporalAA.cpp
  85. 1 1
      src/anki/renderer/Tonemapping.cpp
  86. 10 7
      src/anki/renderer/Volumetric.cpp
  87. 1 1
      src/anki/resource/ShaderProgramResource.cpp
  88. 6 1
      src/anki/resource/TextureAtlas.h
  89. 21 11
      src/anki/resource/TextureResource.cpp
  90. 8 7
      src/anki/resource/TextureResource.h
  91. 2 2
      src/anki/scene/DecalComponent.h
  92. 1 1
      src/anki/scene/LensFlareComponent.h
  93. 1 1
      src/anki/scene/RenderComponent.cpp
  94. 2 2
      src/anki/ui/Canvas.cpp
  95. 17 9
      src/anki/ui/Font.cpp
  96. 4 3
      src/anki/ui/Font.h
  97. 223 52
      tests/gr/Gr.cpp
  98. 1 1
      tools/format_source.sh

+ 1 - 0
src/anki/Gr.h

@@ -7,6 +7,7 @@
 
 #include <anki/gr/Buffer.h>
 #include <anki/gr/Texture.h>
+#include <anki/gr/TextureView.h>
 #include <anki/gr/Sampler.h>
 #include <anki/gr/Shader.h>
 #include <anki/gr/ShaderProgram.h>

+ 8 - 8
src/anki/collision/Frustum.cpp

@@ -211,17 +211,17 @@ Mat4 OrthographicFrustum::calculateProjectionMatrix() const
 void OrthographicFrustum::recalculate()
 {
 	// Planes
-	m_planesL[FrustumPlaneType::LEFT] = Plane(Vec4(1.0, 0.0, 0.0, 0.0), m_left);
-	m_planesL[FrustumPlaneType::RIGHT] = Plane(Vec4(-1.0, 0.0, 0.0, 0.0), -m_right);
+	m_planesL[FrustumPlaneType::LEFT] = Plane(Vec4(1.0f, 0.0f, 0.0f, 0.0f), m_left);
+	m_planesL[FrustumPlaneType::RIGHT] = Plane(Vec4(-1.0f, 0.0f, 0.0f, 0.0f), -m_right);
 
-	m_planesL[FrustumPlaneType::NEAR] = Plane(Vec4(0.0, 0.0, -1.0, 0.0), m_near);
-	m_planesL[FrustumPlaneType::FAR] = Plane(Vec4(0.0, 0.0, 1.0, 0.0), -m_far);
-	m_planesL[FrustumPlaneType::TOP] = Plane(Vec4(0.0, -1.0, 0.0, 0.0), -m_top);
-	m_planesL[FrustumPlaneType::BOTTOM] = Plane(Vec4(0.0, 1.0, 0.0, 0.0), m_bottom);
+	m_planesL[FrustumPlaneType::NEAR] = Plane(Vec4(0.0f, 0.0f, -1.0f, 0.0f), m_near);
+	m_planesL[FrustumPlaneType::FAR] = Plane(Vec4(0.0f, 0.0f, 1.0f, 0.0f), -m_far);
+	m_planesL[FrustumPlaneType::TOP] = Plane(Vec4(0.0f, -1.0f, 0.0f, 0.0f), -m_top);
+	m_planesL[FrustumPlaneType::BOTTOM] = Plane(Vec4(0.0f, 1.0f, 0.0f, 0.0f), m_bottom);
 
 	// OBB
-	Vec4 c((m_right + m_left) * 0.5, (m_top + m_bottom) * 0.5, -(m_far + m_near) * 0.5, 0.0);
-	Vec4 e = Vec4(m_right, m_top, -m_far, 0.0) - c;
+	Vec4 c((m_right + m_left) * 0.5f, (m_top + m_bottom) * 0.5f, -(m_far + m_near) * 0.5f, 0.0f);
+	Vec4 e = Vec4(m_right, m_top, -m_far, 0.0f) - c;
 	m_obbL = Obb(c, Mat3x4::getIdentity(), e);
 }
 

+ 1 - 1
src/anki/core/NativeWindowSdl.cpp

@@ -28,7 +28,7 @@ Error NativeWindow::init(NativeWindowInitInfo& init, HeapAllocator<U8>& alloc)
 #if ANKI_GR_BACKEND == ANKI_GR_BACKEND_VULKAN
 	if(SDL_Vulkan_LoadLibrary(nullptr))
 	{
-		ANKI_CORE_LOGE("SDL_Vulkan_LoadLibrary() failed");
+		ANKI_CORE_LOGE("SDL_Vulkan_LoadLibrary() failed: %s", SDL_GetError());
 		return Error::FUNCTION_FAILED;
 	}
 #endif

+ 6 - 3
src/anki/gr/Buffer.h

@@ -21,9 +21,7 @@ public:
 	BufferUsageBit m_usage = BufferUsageBit::NONE;
 	BufferMapAccessBit m_access = BufferMapAccessBit::NONE;
 
-	BufferInitInfo() = default;
-
-	BufferInitInfo(CString name)
+	BufferInitInfo(CString name = {})
 		: GrBaseInitInfo(name)
 	{
 	}
@@ -35,6 +33,11 @@ public:
 		, m_access(access)
 	{
 	}
+
+	Bool isValid() const
+	{
+		return m_size && !!m_usage;
+	}
 };
 
 /// GPU buffer.

+ 27 - 50
src/anki/gr/CommandBuffer.h

@@ -140,9 +140,6 @@ public:
 	/// @param[out] fence Optionaly create fence.
 	void flush(FencePtr* fence = nullptr);
 
-	/// Flush and wait to finish.
-	void finish();
-
 	/// @name State manipulation
 	/// @{
 
@@ -226,16 +223,10 @@ public:
 	/// Bind texture and sample.
 	/// @param set The set to bind to.
 	/// @param binding The binding to bind to.
-	/// @param tex The texture to bind.
+	/// @param texView The texture view to bind.
 	/// @param sampler The sampler to override the default sampler of the tex.
 	/// @param usage The state the tex is in.
-	/// @param aspect The depth stencil aspect.
-	void bindTextureAndSampler(U32 set,
-		U32 binding,
-		TexturePtr tex,
-		SamplerPtr sampler,
-		TextureUsageBit usage,
-		DepthStencilAspectBit aspect = DepthStencilAspectBit::DEPTH);
+	void bindTextureAndSampler(U32 set, U32 binding, TextureViewPtr texView, SamplerPtr sampler, TextureUsageBit usage);
 
 	/// Bind uniform buffer.
 	/// @param set The set to bind to.
@@ -256,7 +247,7 @@ public:
 	void bindStorageBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range);
 
 	/// Bind load/store image.
-	void bindImage(U32 set, U32 binding, TexturePtr img, U32 level);
+	void bindImage(U32 set, U32 binding, TextureViewPtr img);
 
 	/// Bind texture buffer.
 	void bindTextureBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range, PixelFormat fmt);
@@ -298,49 +289,30 @@ public:
 
 	/// Generate mipmaps for non-3D textures. You have to transition all the mip levels of this face and layer to
 	/// TextureUsageBit::GENERATE_MIPMAPS before calling this method.
-	/// @param tex The texture to generate mips.
-	/// @param face The face of a cube texture or zero.
-	/// @param layer The layer of an array texture or zero.
-	void generateMipmaps2d(TexturePtr tex, U face, U layer);
+	/// @param texView The texture view to generate mips. It should point to a subresource that contains the whole
+	///                mip chain and only one face and one layer.
+	void generateMipmaps2d(TextureViewPtr texView);
 
 	/// Generate mipmaps only for 3D textures.
-	/// @param tex The texture to generate mips.
-	void generateMipmaps3d(TexturePtr tex);
-
-	// TODO Rename to blit
-	void copyTextureSurfaceToTextureSurface(
-		TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf);
+	/// @param texView The texture view to generate mips.
+	void generateMipmaps3d(TextureViewPtr tex);
 
-	void copyTextureVolumeToTextureVolume(
-		TexturePtr src, const TextureVolumeInfo& srcVol, TexturePtr dest, const TextureVolumeInfo& destVol);
+	/// Blit from surface to surface.
+	/// @param srcView The source view that points to a surface.
+	/// @param dstView The destination view that points to a surface.
+	void blitTextureViews(TextureViewPtr srcView, TextureViewPtr destView);
 
 	/// Clear a single texture surface. Can be used for all textures except 3D.
-	/// @param tex The texture to clear.
-	/// @param surf The surface to clear.
-	/// @param clearValue The value to clear it with.
-	/// @param aspect The aspect of the depth stencil texture. Relevant only for depth stencil textures.
-	void clearTextureSurface(TexturePtr tex,
-		const TextureSurfaceInfo& surf,
-		const ClearValue& clearValue,
-		DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE);
-
-	/// Clear a volume out of a 3D texture.
-	/// @param tex The texture to clear.
-	/// @param vol The volume to clear.
-	/// @param clearValue The value to clear it with.
-	/// @param aspect The aspect of the depth stencil texture. Relevant only for depth stencil textures.
-	void clearTextureVolume(TexturePtr tex,
-		const TextureVolumeInfo& vol,
-		const ClearValue& clearValue,
-		DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE);
-
-	/// Copy a buffer to a texture surface.
-	void copyBufferToTextureSurface(
-		BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureSurfaceInfo& surf);
-
-	/// Copy buffer to a texture volume.
-	void copyBufferToTextureVolume(
-		BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureVolumeInfo& vol);
+	/// @param[in,out] texView The texture view to clear.
+	/// @param[in] clearValue The value to clear it with.
+	void clearTextureView(TextureViewPtr texView, const ClearValue& clearValue);
+
+	/// Copy a buffer to a texture surface or volume.
+	/// @param buff The source buffer to copy from.
+	/// @param offset The offset in the buffer to start reading from.
+	/// @param range The size of the buffer to read.
+	/// @param texView The texture view that points to a surface or volume to write to.
+	void copyBufferToTextureView(BufferPtr buff, PtrSize offset, PtrSize range, TextureViewPtr texView);
 
 	/// Fill a buffer with some value.
 	/// @param[in,out] buff The buffer to fill.
@@ -366,6 +338,11 @@ public:
 
 	/// @name Sync
 	/// @{
+	void setTextureBarrier(TexturePtr tex,
+		TextureUsageBit prevUsage,
+		TextureUsageBit nextUsage,
+		const TextureSubresourceInfo& subresource);
+
 	void setTextureSurfaceBarrier(
 		TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage, const TextureSurfaceInfo& surf);
 

+ 153 - 1
src/anki/gr/Common.cpp

@@ -10,4 +10,156 @@ namespace anki
 
 Array<CString, U(GpuVendor::COUNT)> GPU_VENDOR_STR = {{"UNKNOWN", "ARM", "NVIDIA", "AMD", "INTEL"}};
 
-} // end namespace anki
+static void getFormatInfo(const PixelFormat& fmt, U& texelComponents, U& texelBytes, U& blockSize, U& blockBytes)
+{
+	blockSize = 0;
+	blockBytes = 0;
+	texelBytes = 0;
+
+	switch(fmt.m_components)
+	{
+	case ComponentFormat::R8:
+		texelComponents = 1;
+		texelBytes = texelComponents * 1;
+		break;
+	case ComponentFormat::R8G8:
+		texelComponents = 2;
+		texelBytes = texelComponents * 1;
+		break;
+	case ComponentFormat::R8G8B8:
+		texelComponents = 3;
+		texelBytes = texelComponents * 1;
+		break;
+	case ComponentFormat::R8G8B8A8:
+		texelComponents = 4;
+		texelBytes = texelComponents * 1;
+		break;
+	case ComponentFormat::R16:
+		texelComponents = 1;
+		texelBytes = texelComponents * 2;
+		break;
+	case ComponentFormat::R16G16:
+		texelComponents = 2;
+		texelBytes = texelComponents * 2;
+		break;
+	case ComponentFormat::R16G16B16:
+		texelComponents = 3;
+		texelBytes = texelComponents * 2;
+		break;
+	case ComponentFormat::R16G16B16A16:
+		texelComponents = 4;
+		texelBytes = texelComponents * 2;
+		break;
+	case ComponentFormat::R32:
+		texelComponents = 1;
+		texelBytes = texelComponents * 4;
+		break;
+	case ComponentFormat::R32G32:
+		texelComponents = 2;
+		texelBytes = texelComponents * 4;
+		break;
+	case ComponentFormat::R32G32B32:
+		texelComponents = 3;
+		texelBytes = texelComponents * 4;
+		break;
+	case ComponentFormat::R32G32B32A32:
+		texelComponents = 4;
+		texelBytes = texelComponents * 4;
+		break;
+	case ComponentFormat::R10G10B10A2:
+		texelComponents = 4;
+		texelBytes = 4;
+		break;
+	case ComponentFormat::R11G11B10:
+		texelComponents = 3;
+		texelBytes = 4;
+		break;
+	case ComponentFormat::R8G8B8_BC1:
+		texelComponents = 3;
+		blockSize = 4;
+		blockBytes = 8;
+		break;
+	case ComponentFormat::R8G8B8A8_BC3:
+		texelComponents = 4;
+		blockSize = 4;
+		blockBytes = 16;
+		break;
+	case ComponentFormat::R16G16B16_BC6:
+		texelComponents = 3;
+		blockSize = 4;
+		blockBytes = 16;
+		break;
+	case ComponentFormat::R8G8B8_ETC2:
+		texelComponents = 3;
+		blockSize = 4;
+		blockBytes = 8;
+		break;
+	case ComponentFormat::R8G8B8A8_ETC2:
+		texelComponents = 4;
+		blockSize = 4;
+		blockBytes = 16;
+		break;
+	case ComponentFormat::D16:
+		texelComponents = 1;
+		texelBytes = texelComponents * 2;
+		break;
+	case ComponentFormat::D24S8:
+		texelComponents = 1;
+		texelBytes = texelComponents * 4;
+		break;
+	case ComponentFormat::D32:
+		texelComponents = 1;
+		texelBytes = texelComponents * 4;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+}
+
+PtrSize computeSurfaceSize(U width, U height, const PixelFormat& fmt)
+{
+	ANKI_ASSERT(width > 0 && height > 0);
+	ANKI_ASSERT(fmt.isValid());
+	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;
+	}
+	else
+	{
+		return width * height * texelBytes;
+	}
+}
+
+PtrSize computeVolumeSize(U width, U height, U depth, const PixelFormat& fmt)
+{
+	ANKI_ASSERT(width > 0 && height > 0 && depth > 0);
+	ANKI_ASSERT(fmt.isValid());
+	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

+ 71 - 5
src/anki/gr/Common.h

@@ -19,6 +19,7 @@ class GrObject;
 class GrManager;
 class GrManagerImpl;
 class TextureInitInfo;
+class TextureViewInitInfo;
 class SamplerInitInfo;
 class GrManagerInitInfo;
 class FramebufferInitInfo;
@@ -64,6 +65,7 @@ using GrObjectPtr = IntrusivePtr<T, DefaultPtrDeleter<T>>;
 
 ANKI_GR_CLASS(Buffer)
 ANKI_GR_CLASS(Texture)
+ANKI_GR_CLASS(TextureView)
 ANKI_GR_CLASS(Sampler)
 ANKI_GR_CLASS(CommandBuffer)
 ANKI_GR_CLASS(Shader)
@@ -140,6 +142,11 @@ public:
 class TextureSurfaceInfo
 {
 public:
+	U32 m_level = 0;
+	U32 m_depth = 0;
+	U32 m_face = 0;
+	U32 m_layer = 0;
+
 	TextureSurfaceInfo() = default;
 
 	TextureSurfaceInfo(const TextureSurfaceInfo&) = default;
@@ -167,16 +174,18 @@ public:
 		return anki::computeHash(this, sizeof(*this), 0x1234567);
 	}
 
-	U32 m_level = 0;
-	U32 m_depth = 0;
-	U32 m_face = 0;
-	U32 m_layer = 0;
+	static TextureSurfaceInfo newZero()
+	{
+		return TextureSurfaceInfo();
+	}
 };
 
 /// A way to identify a volume in 3D textures.
 class TextureVolumeInfo
 {
 public:
+	U32 m_level = 0;
+
 	TextureVolumeInfo() = default;
 
 	TextureVolumeInfo(const TextureVolumeInfo&) = default;
@@ -185,8 +194,59 @@ public:
 		: m_level(level)
 	{
 	}
+};
 
-	U32 m_level = 0;
+/// Defines a subset of a texture.
+class TextureSubresourceInfo
+{
+public:
+	U32 m_firstMipmap = 0;
+	U32 m_mipmapCount = 1;
+
+	U32 m_firstLayer = 0;
+	U32 m_layerCount = 1;
+
+	U8 m_firstFace = 0;
+	U8 m_faceCount = 1;
+
+	DepthStencilAspectBit m_depthStencilAspect = DepthStencilAspectBit::NONE;
+
+	U8 _m_padding[1] = {0};
+
+	TextureSubresourceInfo(DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE)
+		: m_depthStencilAspect(aspect)
+	{
+	}
+
+	TextureSubresourceInfo(const TextureSubresourceInfo&) = default;
+
+	TextureSubresourceInfo(const TextureSurfaceInfo& surf, DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE)
+		: m_firstMipmap(surf.m_level)
+		, m_mipmapCount(1)
+		, m_firstLayer(surf.m_layer)
+		, m_layerCount(1)
+		, m_firstFace(surf.m_face)
+		, m_faceCount(1)
+		, m_depthStencilAspect(aspect)
+	{
+	}
+
+	TextureSubresourceInfo(const TextureVolumeInfo& vol, DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE)
+		: m_firstMipmap(vol.m_level)
+		, m_mipmapCount(1)
+		, m_firstLayer(0)
+		, m_layerCount(1)
+		, m_firstFace(0)
+		, m_faceCount(1)
+		, m_depthStencilAspect(aspect)
+	{
+	}
+
+	U64 computeHash() const
+	{
+		static_assert(sizeof(*this) == sizeof(U32) * 4 + sizeof(U8) * 4, "Should be hashable");
+		return anki::computeHash(this, sizeof(*this));
+	}
 };
 
 enum class DescriptorType : U8
@@ -275,6 +335,12 @@ inline U computeMaxMipmapCount3d(U w, U h, U d)
 
 	return count;
 }
+
+/// Compute the size in bytes of a texture surface surface.
+PtrSize computeSurfaceSize(U width, U height, const PixelFormat& fmt);
+
+/// Compute the size in bytes of the texture volume.
+PtrSize computeVolumeSize(U width, U height, U depth, const PixelFormat& fmt);
 /// @}
 
 } // end namespace anki

+ 28 - 1
src/anki/gr/Enums.h

@@ -119,6 +119,8 @@ enum class VertexStepRate : U8
 	COUNT
 };
 
+/// Pixel and vertex component format.
+/// @note WARNING Be careful when changing this. You need updates to multiple places.
 enum class ComponentFormat : U8
 {
 	NONE,
@@ -139,7 +141,6 @@ enum class ComponentFormat : U8
 	// Special
 	R10G10B10A2,
 	R11G11B10,
-	DEFAULT_FRAMEBUFFER, ///< Implicit format.
 
 	// Compressed
 	R8G8B8_BC1, ///< DXT1
@@ -162,6 +163,21 @@ enum class ComponentFormat : U8
 	LAST_DEPTH_STENCIL = S8
 };
 
+inline Bool componentFormatIsDepthStencil(const ComponentFormat fmt)
+{
+	return fmt >= ComponentFormat::FIRST_DEPTH_STENCIL && fmt <= ComponentFormat::LAST_DEPTH_STENCIL;
+}
+
+inline Bool componentFormatIsDepth(const ComponentFormat fmt)
+{
+	return fmt >= ComponentFormat::D16 && fmt <= ComponentFormat::D32S8;
+}
+
+inline Bool componentFormatIsStencil(const ComponentFormat fmt)
+{
+	return fmt == ComponentFormat::D24S8 || fmt == ComponentFormat::D32S8 || fmt == ComponentFormat::S8;
+}
+
 enum class TransformFormat : U8
 {
 	NONE,
@@ -172,6 +188,7 @@ enum class TransformFormat : U8
 	FLOAT
 };
 
+/// Texture type.
 enum class TextureType : U8
 {
 	_1D,
@@ -183,6 +200,11 @@ enum class TextureType : U8
 	COUNT
 };
 
+inline Bool textureTypeIsCube(const TextureType t)
+{
+	return t == TextureType::CUBE || t == TextureType::CUBE_ARRAY;
+}
+
 /// Texture usage hints. They are very important.
 enum class TextureUsageBit : U16
 {
@@ -327,6 +349,11 @@ public:
 	{
 		return !(*this == b);
 	}
+
+	Bool isValid() const
+	{
+		return m_components != ComponentFormat::NONE && m_transform != TransformFormat::NONE;
+	}
 };
 
 static_assert(sizeof(PixelFormat) == 2, "Need it to be packed");

+ 10 - 7
src/anki/gr/Framebuffer.h

@@ -6,8 +6,7 @@
 #pragma once
 
 #include <anki/gr/GrObject.h>
-#include <anki/gr/Texture.h>
-#include <cstring>
+#include <anki/gr/TextureView.h>
 
 namespace anki
 {
@@ -19,16 +18,15 @@ namespace anki
 class FramebufferAttachmentInfo
 {
 public:
-	TexturePtr m_texture;
-	TextureSurfaceInfo m_surface;
+	TextureViewPtr m_textureView;
+
 	AttachmentLoadOperation m_loadOperation = AttachmentLoadOperation::CLEAR;
 	AttachmentStoreOperation m_storeOperation = AttachmentStoreOperation::STORE;
-	ClearValue m_clearValue;
 
 	AttachmentLoadOperation m_stencilLoadOperation = AttachmentLoadOperation::CLEAR;
 	AttachmentStoreOperation m_stencilStoreOperation = AttachmentStoreOperation::STORE;
 
-	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE; ///< Relevant only for depth stencil textures.
+	ClearValue m_clearValue;
 };
 
 /// Framebuffer initializer. If you require the default framebuffer then set m_colorAttachmentCount to 1 and don't set a
@@ -74,7 +72,12 @@ public:
 
 	Bool refersToDefaultFramebuffer() const
 	{
-		return m_colorAttachmentCount == 1 && !m_colorAttachments[0].m_texture.isCreated();
+		return m_colorAttachmentCount == 1 && !m_colorAttachments[0].m_textureView.isCreated();
+	}
+
+	Bool isValid() const
+	{
+		return m_colorAttachmentCount != 0 || m_depthStencilAttachment.m_textureView.isCreated();
 	}
 };
 

+ 1 - 13
src/anki/gr/GrManager.h

@@ -55,6 +55,7 @@ public:
 	/// @{
 	ANKI_USE_RESULT BufferPtr newBuffer(const BufferInitInfo& init);
 	ANKI_USE_RESULT TexturePtr newTexture(const TextureInitInfo& init);
+	ANKI_USE_RESULT TextureViewPtr newTextureView(const TextureViewInitInfo& init);
 	ANKI_USE_RESULT SamplerPtr newSampler(const SamplerInitInfo& init);
 	ANKI_USE_RESULT ShaderPtr newShader(const ShaderInitInfo& init);
 	ANKI_USE_RESULT ShaderProgramPtr newShaderProgram(const ShaderProgramInitInfo& init);
@@ -64,19 +65,6 @@ public:
 	ANKI_USE_RESULT RenderGraphPtr newRenderGraph();
 	/// @}
 
-	/// Call this before calling allocateFrameTransientMemory or tryAllocateFrameTransientMemory to get the exact memory
-	/// that will be required for the CommandBuffer::uploadTextureSurface.
-	///
-	/// If the expectedTransientAllocationSize is greater than the expected you 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 getTextureSurfaceUploadInfo(
-		TexturePtr tex, const TextureSurfaceInfo& surf, PtrSize& expectedTransientAllocationSize);
-
-	/// Same as getTextureSurfaceUploadInfo but for volumes.
-	void getTextureVolumeUploadInfo(
-		TexturePtr tex, const TextureVolumeInfo& vol, PtrSize& expectedTransientAllocationSize);
-
 	/// Get some buffer alignment and size info.
 	void getUniformBufferInfo(U32& bindOffsetAlignment, PtrSize& maxUniformBlockSize) const;
 

+ 1 - 0
src/anki/gr/GrObject.h

@@ -25,6 +25,7 @@ enum GrObjectType : U16
 	SAMPLER,
 	SHADER,
 	TEXTURE,
+	TEXTURE_VIEW,
 	SHADER_PROGRAM,
 	FENCE,
 	RENDER_GRAPH,

+ 161 - 174
src/anki/gr/RenderGraph.cpp

@@ -23,14 +23,8 @@ namespace anki
 class RenderGraph::RT
 {
 public:
-	struct Usage
-	{
-		TextureUsageBit m_usage;
-		TextureSurfaceInfo m_surface;
-	};
-
-	DynamicArray<Usage> m_surfUsages;
-
+	DynamicArray<TextureUsageBit> m_surfOrVolUsages;
+	DynamicArray<U16> m_lastBatchThatTransitionedIt;
 	TexturePtr m_texture; ///< Hold a reference.
 };
 
@@ -106,9 +100,7 @@ public:
 	public:
 		RenderTargetHandle m_handle;
 		TextureUsageBit m_usage;
-		TextureSurfaceInfo m_surface;
-		Bool8 m_wholeTex;
-		DepthStencilAspectBit m_aspect;
+		TextureSubresourceInfo m_subresource;
 	};
 	DynamicArray<ConsumedTextureInfo> m_consumedTextures;
 
@@ -155,45 +147,15 @@ void FramebufferDescription::bake()
 	ANKI_ASSERT(m_hash == 0 && "Already baked");
 	if(m_defaultFb)
 	{
-		m_fbInitInfo.m_colorAttachmentCount = 1;
 		m_hash = 1;
 		return;
 	}
 
-	// Populate the FB init info
-	m_fbInitInfo.m_colorAttachmentCount = m_colorAttachmentCount;
-	for(U i = 0; i < m_colorAttachmentCount; ++i)
-	{
-		FramebufferAttachmentInfo& out = m_fbInitInfo.m_colorAttachments[i];
-		const FramebufferDescriptionAttachment& in = m_colorAttachments[i];
-
-		out.m_surface = in.m_surface;
-		out.m_clearValue = in.m_clearValue;
-		out.m_loadOperation = in.m_loadOperation;
-		out.m_storeOperation = in.m_storeOperation;
-	}
-
-	if(!!m_depthStencilAttachment.m_aspect)
-	{
-		FramebufferAttachmentInfo& out = m_fbInitInfo.m_depthStencilAttachment;
-		const FramebufferDescriptionAttachment& in = m_depthStencilAttachment;
-
-		out.m_surface = in.m_surface;
-		out.m_loadOperation = in.m_loadOperation;
-		out.m_storeOperation = in.m_storeOperation;
-		out.m_clearValue = in.m_clearValue;
-
-		out.m_stencilLoadOperation = in.m_stencilLoadOperation;
-		out.m_stencilStoreOperation = in.m_stencilStoreOperation;
-
-		out.m_aspect = in.m_aspect;
-	}
-
 	m_hash = 0;
-	ANKI_ASSERT(m_fbInitInfo.m_colorAttachmentCount > 0 || !!m_fbInitInfo.m_depthStencilAttachment.m_aspect);
+	ANKI_ASSERT(m_colorAttachmentCount > 0 || !!m_depthStencilAttachment.m_aspect);
 
 	// First the depth attachments
-	if(m_fbInitInfo.m_colorAttachmentCount)
+	if(m_colorAttachmentCount)
 	{
 		ANKI_BEGIN_PACKED_STRUCT
 		struct ColorAttachment
@@ -207,20 +169,20 @@ void FramebufferDescription::bake()
 		static_assert(sizeof(ColorAttachment) == 4 * (4 + 1 + 1 + 4), "Wrong size");
 
 		Array<ColorAttachment, MAX_COLOR_ATTACHMENTS> colorAttachments;
-		for(U i = 0; i < m_fbInitInfo.m_colorAttachmentCount; ++i)
+		for(U i = 0; i < m_colorAttachmentCount; ++i)
 		{
-			const FramebufferAttachmentInfo& inAtt = m_fbInitInfo.m_colorAttachments[i];
+			const FramebufferDescriptionAttachment& inAtt = m_colorAttachments[i];
 			colorAttachments[i].m_surf = inAtt.m_surface;
 			colorAttachments[i].m_loadOp = static_cast<U32>(inAtt.m_loadOperation);
 			colorAttachments[i].m_storeOp = static_cast<U32>(inAtt.m_storeOperation);
 			memcpy(&colorAttachments[i].m_clearColor[0], &inAtt.m_clearValue.m_coloru[0], sizeof(U32) * 4);
 		}
 
-		m_hash = computeHash(&colorAttachments[0], sizeof(ColorAttachment) * m_fbInitInfo.m_colorAttachmentCount);
+		m_hash = computeHash(&colorAttachments[0], sizeof(ColorAttachment) * m_colorAttachmentCount);
 	}
 
 	// DS attachment
-	if(!!m_fbInitInfo.m_depthStencilAttachment.m_aspect)
+	if(!!m_depthStencilAttachment.m_aspect)
 	{
 		ANKI_BEGIN_PACKED_STRUCT
 		struct DSAttachment
@@ -236,7 +198,7 @@ void FramebufferDescription::bake()
 		} outAtt;
 		ANKI_END_PACKED_STRUCT
 
-		const FramebufferAttachmentInfo& inAtt = m_fbInitInfo.m_depthStencilAttachment;
+		const FramebufferDescriptionAttachment& inAtt = m_depthStencilAttachment;
 		const Bool hasDepth = !!(inAtt.m_aspect & DepthStencilAspectBit::DEPTH);
 		const Bool hasStencil = !!(inAtt.m_aspect & DepthStencilAspectBit::STENCIL);
 
@@ -362,9 +324,10 @@ TexturePtr RenderGraph::getOrCreateRenderTarget(const TextureInitInfo& initInf,
 }
 
 FramebufferPtr RenderGraph::getOrCreateFramebuffer(
-	const FramebufferInitInfo& fbInit_, const RenderTargetHandle* rtHandles, U64 hash)
+	const FramebufferDescription& fbDescr, const RenderTargetHandle* rtHandles)
 {
 	ANKI_ASSERT(rtHandles);
+	U64 hash = fbDescr.m_hash;
 	ANKI_ASSERT(hash > 0);
 
 	const Bool defaultFb = hash == 1;
@@ -374,12 +337,12 @@ FramebufferPtr RenderGraph::getOrCreateFramebuffer(
 		// Create a hash that includes the render targets
 		Array<U64, MAX_COLOR_ATTACHMENTS + 1> uuids;
 		U count = 0;
-		for(U i = 0; i < fbInit_.m_colorAttachmentCount; ++i)
+		for(U i = 0; i < fbDescr.m_colorAttachmentCount; ++i)
 		{
 			uuids[count++] = m_ctx->m_rts[rtHandles[i].m_idx].m_texture->getUuid();
 		}
 
-		if(!!fbInit_.m_depthStencilAttachment.m_aspect)
+		if(!!fbDescr.m_depthStencilAttachment.m_aspect)
 		{
 			uuids[count++] = m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS].m_idx].m_texture->getUuid();
 		}
@@ -396,22 +359,51 @@ FramebufferPtr RenderGraph::getOrCreateFramebuffer(
 	else
 	{
 		// Create a complete fb init info
-		FramebufferInitInfo fbInit = fbInit_;
+		FramebufferInitInfo fbInit("RenderGraph");
 		if(!defaultFb)
 		{
+			fbInit.m_colorAttachmentCount = fbDescr.m_colorAttachmentCount;
 			for(U i = 0; i < fbInit.m_colorAttachmentCount; ++i)
 			{
-				fbInit.m_colorAttachments[i].m_texture = m_ctx->m_rts[rtHandles[i].m_idx].m_texture;
-				ANKI_ASSERT(fbInit.m_colorAttachments[i].m_texture.isCreated());
+				FramebufferAttachmentInfo& outAtt = fbInit.m_colorAttachments[i];
+				const FramebufferDescriptionAttachment& inAtt = fbDescr.m_colorAttachments[i];
+
+				outAtt.m_clearValue = inAtt.m_clearValue;
+				outAtt.m_loadOperation = inAtt.m_loadOperation;
+				outAtt.m_storeOperation = inAtt.m_storeOperation;
+
+				// Create texture view
+				TextureViewInitInfo viewInit(
+					m_ctx->m_rts[rtHandles[i].m_idx].m_texture, TextureSubresourceInfo(inAtt.m_surface), "RenderGraph");
+				TextureViewPtr view = getManager().newTextureView(viewInit);
+
+				outAtt.m_textureView = view;
 			}
 
-			if(!!fbInit.m_depthStencilAttachment.m_aspect)
+			if(!!fbDescr.m_depthStencilAttachment.m_aspect)
 			{
-				fbInit.m_depthStencilAttachment.m_texture =
-					m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS].m_idx].m_texture;
-				ANKI_ASSERT(fbInit.m_depthStencilAttachment.m_texture.isCreated());
+				FramebufferAttachmentInfo& outAtt = fbInit.m_depthStencilAttachment;
+				const FramebufferDescriptionAttachment& inAtt = fbDescr.m_depthStencilAttachment;
+
+				outAtt.m_clearValue = inAtt.m_clearValue;
+				outAtt.m_loadOperation = inAtt.m_loadOperation;
+				outAtt.m_storeOperation = inAtt.m_storeOperation;
+				outAtt.m_stencilLoadOperation = inAtt.m_stencilLoadOperation;
+				outAtt.m_stencilStoreOperation = inAtt.m_stencilStoreOperation;
+
+				// Create texture view
+				TextureViewInitInfo viewInit(m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS].m_idx].m_texture,
+					TextureSubresourceInfo(inAtt.m_surface, inAtt.m_aspect),
+					"RenderGraph");
+				TextureViewPtr view = getManager().newTextureView(viewInit);
+
+				outAtt.m_textureView = view;
 			}
 		}
+		else
+		{
+			fbInit.m_colorAttachmentCount = 1;
+		}
 
 		fb = getManager().newFramebuffer(fbInit);
 
@@ -422,15 +414,28 @@ FramebufferPtr RenderGraph::getOrCreateFramebuffer(
 	return fb;
 }
 
+Bool RenderGraph::overlappingTextureSubresource(const TextureSubresourceInfo& suba, const TextureSubresourceInfo& subb)
+{
+#define ANKI_OVERLAPPING(first, count) \
+	((suba.first < subb.first + subb.count) && (subb.first < suba.first + suba.count))
+
+	const Bool overlappingFaces = ANKI_OVERLAPPING(m_firstFace, m_faceCount);
+	const Bool overlappingMips = ANKI_OVERLAPPING(m_firstMipmap, m_mipmapCount);
+	const Bool overlappingLayers = ANKI_OVERLAPPING(m_firstLayer, m_layerCount);
+#undef ANKI_OVERLAPPING
+
+	return overlappingFaces && overlappingLayers && overlappingMips;
+}
+
 template<Bool isTexture>
-Bool RenderGraph::overlappingDependency(const RenderPassDependency& a, const RenderPassDependency& b)
+Bool RenderGraph::overlappingDependency(const RenderPassDependency& a, const RenderPassDependency& b) const
 {
 	ANKI_ASSERT(a.m_isTexture == isTexture && b.m_isTexture == isTexture);
 
 	if(isTexture)
 	{
 		return a.m_texture.m_handle == b.m_texture.m_handle
-			&& (a.m_texture.m_surface == b.m_texture.m_surface || a.m_texture.m_wholeTex || b.m_texture.m_wholeTex);
+			&& overlappingTextureSubresource(a.m_texture.m_subresource, b.m_texture.m_subresource);
 	}
 	else
 	{
@@ -438,7 +443,7 @@ Bool RenderGraph::overlappingDependency(const RenderPassDependency& a, const Ren
 	}
 }
 
-Bool RenderGraph::passADependsOnB(const RenderPassDescriptionBase& a, const RenderPassDescriptionBase& b)
+Bool RenderGraph::passADependsOnB(const RenderPassDescriptionBase& a, const RenderPassDescriptionBase& b) const
 {
 	// Render targets
 	{
@@ -538,17 +543,28 @@ RenderGraph::BakeContext* RenderGraph::newContext(const RenderGraphDescription&
 	ctx->m_rts.create(alloc, descr.m_renderTargets.getSize());
 	for(U rtIdx = 0; rtIdx < ctx->m_rts.getSize(); ++rtIdx)
 	{
-		if(descr.m_renderTargets[rtIdx].m_importedTex.isCreated())
+		RT& outRt = ctx->m_rts[rtIdx];
+
+		TexturePtr tex;
+		Bool imported = descr.m_renderTargets[rtIdx].m_importedTex.isCreated();
+		if(imported)
 		{
 			// It's imported
-			ctx->m_rts[rtIdx].m_texture = descr.m_renderTargets[rtIdx].m_importedTex;
+			tex = descr.m_renderTargets[rtIdx].m_importedTex;
 		}
 		else
 		{
-			TexturePtr rt =
-				getOrCreateRenderTarget(descr.m_renderTargets[rtIdx].m_initInfo, descr.m_renderTargets[rtIdx].m_hash);
-			ctx->m_rts[rtIdx].m_texture = rt;
+			tex = getOrCreateRenderTarget(descr.m_renderTargets[rtIdx].m_initInfo, descr.m_renderTargets[rtIdx].m_hash);
 		}
+
+		outRt.m_texture = tex;
+
+		// Init the surfs or volumes
+		const U surfOrVolumeCount =
+			tex->getMipmapCount() * tex->getLayerCount() * (textureTypeIsCube(tex->getTextureType()) ? 6 : 1);
+		outRt.m_surfOrVolUsages.create(
+			alloc, surfOrVolumeCount, (imported) ? descr.m_renderTargets[rtIdx].m_usage : TextureUsageBit::NONE);
+		outRt.m_lastBatchThatTransitionedIt.create(alloc, surfOrVolumeCount, MAX_U16);
 	}
 
 	// Buffers
@@ -600,39 +616,34 @@ void RenderGraph::initRenderPassesAndSetDeps(const RenderGraphDescription& descr
 
 			if(graphicsPass.hasFramebuffer())
 			{
-				outPass.fb() = getOrCreateFramebuffer(
-					graphicsPass.m_fbInitInfo, &graphicsPass.m_rtHandles[0], graphicsPass.m_fbHash);
+				outPass.fb() = getOrCreateFramebuffer(graphicsPass.m_fbDescr, &graphicsPass.m_rtHandles[0]);
 
 				outPass.m_fbRenderArea = graphicsPass.m_fbRenderArea;
 
 				// Init the usage bits
-				if(graphicsPass.m_fbHash != 1)
+				if(graphicsPass.m_fbDescr.m_hash != 1)
 				{
 					TextureUsageBit usage;
-					DepthStencilAspectBit aspect;
 
-					for(U i = 0; i < graphicsPass.m_fbInitInfo.m_colorAttachmentCount; ++i)
+					for(U i = 0; i < graphicsPass.m_fbDescr.m_colorAttachmentCount; ++i)
 					{
-						getCrntUsageAndAspect(graphicsPass.m_rtHandles[i],
+						getCrntUsage(graphicsPass.m_rtHandles[i],
 							passIdx,
-							graphicsPass.m_fbInitInfo.m_colorAttachments[i].m_surface,
-							usage,
-							aspect);
+							TextureSubresourceInfo(graphicsPass.m_fbDescr.m_colorAttachments[i].m_surface),
+							usage);
 
 						outPass.m_colorUsages[i] = usage;
-						ANKI_ASSERT(!aspect);
 					}
 
-					if(!!graphicsPass.m_fbInitInfo.m_depthStencilAttachment.m_aspect)
+					if(!!graphicsPass.m_fbDescr.m_depthStencilAttachment.m_aspect)
 					{
-						getCrntUsageAndAspect(graphicsPass.m_rtHandles[MAX_COLOR_ATTACHMENTS],
-							passIdx,
-							graphicsPass.m_fbInitInfo.m_depthStencilAttachment.m_surface,
-							usage,
-							aspect);
+						TextureSubresourceInfo subresource =
+							TextureSubresourceInfo(graphicsPass.m_fbDescr.m_depthStencilAttachment.m_surface,
+								graphicsPass.m_fbDescr.m_depthStencilAttachment.m_aspect);
+
+						getCrntUsage(graphicsPass.m_rtHandles[MAX_COLOR_ATTACHMENTS], passIdx, subresource, usage);
 
 						outPass.m_dsUsage = usage;
-						ANKI_ASSERT(!!aspect);
 					}
 				}
 
@@ -702,92 +713,88 @@ void RenderGraph::initBatches()
 	}
 }
 
-void RenderGraph::setTextureBarrier(Batch& batch,
-	const RenderPassDescriptionBase& pass,
-	const RenderPassDependency& consumer,
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64>& rtHasBarrierMask,
-	const RenderGraphDescription& descr,
-	BakeContext& ctx) const
+template<typename TFunc>
+void RenderGraph::iterateSurfsOrVolumes(const TexturePtr& tex, const TextureSubresourceInfo& subresource, TFunc func)
 {
-	ANKI_ASSERT(consumer.m_isTexture);
-
-	const StackAllocator<U8>& alloc = ctx.m_alloc;
-
-	const U32 rtIdx = consumer.m_texture.m_handle.m_idx;
-	const TextureUsageBit consumerUsage = consumer.m_texture.m_usage;
-	const Bool consumerWholeTex = consumer.m_texture.m_wholeTex;
-
-	Bool anySurfaceFound = false;
-	for(RT::Usage& u : ctx.m_rts[rtIdx].m_surfUsages)
+	for(U mip = subresource.m_firstMipmap; mip < subresource.m_firstMipmap + subresource.m_mipmapCount; ++mip)
 	{
-		if(!consumerWholeTex && u.m_surface != consumer.m_texture.m_surface)
+		for(U layer = subresource.m_firstLayer; layer < subresource.m_firstLayer + subresource.m_layerCount; ++layer)
 		{
-			// Not the right surface, continue
-			continue;
+			for(U face = subresource.m_firstFace; face < subresource.m_firstFace + subresource.m_faceCount; ++face)
+			{
+				// Compute surf or vol idx
+				const U faceCount = textureTypeIsCube(tex->getTextureType()) ? 6 : 1;
+				const U idx = (faceCount * tex->getLayerCount()) * mip + faceCount * layer + face;
+				const TextureSurfaceInfo surf(mip, 0, face, layer);
+
+				if(!func(idx, surf))
+				{
+					return;
+				}
+			}
 		}
+	}
+}
 
-		anySurfaceFound = true;
+void RenderGraph::setTextureBarrier(Batch& batch, const RenderPassDependency& consumer)
+{
+	ANKI_ASSERT(consumer.m_isTexture);
 
-		if(u.m_usage == consumerUsage)
-		{
-			// Surface in the correct usage, continue
-			continue;
-		}
+	BakeContext& ctx = *m_ctx;
+	const U batchIdx = &batch - &ctx.m_batches[0];
+	const U rtIdx = consumer.m_texture.m_handle.m_idx;
+	const TextureUsageBit consumerUsage = consumer.m_texture.m_usage;
+	RT& rt = ctx.m_rts[rtIdx];
 
-		// Check if we can merge barriers
-		const Bool rtHasBarrier = rtHasBarrierMask.get(rtIdx);
-		Barrier* barrierToMergeTo = nullptr;
-		if(rtHasBarrier)
-		{
-			for(Barrier& b : batch.m_barriersBefore)
+	iterateSurfsOrVolumes(
+		rt.m_texture, consumer.m_texture.m_subresource, [&](U surfOrVolIdx, const TextureSurfaceInfo& surf) {
+			TextureUsageBit& crntUsage = rt.m_surfOrVolUsages[surfOrVolIdx];
+			if(crntUsage != consumerUsage)
 			{
-				if(b.m_isTexture && b.m_texture.m_idx == rtIdx && b.m_texture.m_surface == u.m_surface)
+				// Check if we can merge barriers
+				if(rt.m_lastBatchThatTransitionedIt[surfOrVolIdx] == batchIdx)
 				{
-					barrierToMergeTo = &b;
-					break;
-				}
-			}
-		}
+					// Will merge the barriers
 
-		if(barrierToMergeTo == nullptr)
-		{
-			// RT hasn't had a barrier in this batch, add a new one
-			batch.m_barriersBefore.emplaceBack(
-				alloc, consumer.m_texture.m_handle.m_idx, u.m_usage, consumerUsage, u.m_surface);
+					crntUsage |= consumerUsage;
 
-			u.m_usage = consumer.m_texture.m_usage;
-			rtHasBarrierMask.set(rtIdx);
-		}
-		else
-		{
-			// RT already in a barrier, merge the 2 barriers
+					Bool found = false;
+					for(Barrier& b : batch.m_barriersBefore)
+					{
+						if(b.m_isTexture && b.m_texture.m_idx == rtIdx && b.m_texture.m_surface == surf)
+						{
+							b.m_texture.m_usageAfter |= consumerUsage;
+							found = true;
+							break;
+						}
+					}
 
-			ANKI_ASSERT(!!barrierToMergeTo->m_texture.m_usageAfter);
-			ANKI_ASSERT(!!u.m_usage);
-			barrierToMergeTo->m_texture.m_usageAfter |= consumerUsage;
-			u.m_usage |= consumerUsage;
-		}
-	} // end for
+					(void)found;
+					ANKI_ASSERT(found);
+				}
+				else
+				{
+					// Create a new barrier for this surface
 
-	// Create the transition from the initial state
-	if(!anySurfaceFound && descr.m_renderTargets[rtIdx].m_usage != consumerUsage)
-	{
-		batch.m_barriersBefore.emplaceBack(
-			alloc, rtIdx, descr.m_renderTargets[rtIdx].m_usage, consumerUsage, consumer.m_texture.m_surface);
+					batch.m_barriersBefore.emplaceBack(ctx.m_alloc, rtIdx, crntUsage, consumerUsage, surf);
 
-		RT::Usage usage{consumerUsage, consumer.m_texture.m_surface};
-		ctx.m_rts[rtIdx].m_surfUsages.emplaceBack(alloc, usage);
-	}
+					crntUsage = consumerUsage;
+					rt.m_lastBatchThatTransitionedIt[surfOrVolIdx] = batchIdx;
+				}
+			}
+
+			return true;
+		});
 }
 
-void RenderGraph::setBatchBarriers(const RenderGraphDescription& descr, BakeContext& ctx) const
+void RenderGraph::setBatchBarriers(const RenderGraphDescription& descr)
 {
+	BakeContext& ctx = *m_ctx;
 	const StackAllocator<U8>& alloc = ctx.m_alloc;
 
 	// For all batches
 	for(Batch& batch : ctx.m_batches)
 	{
-		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64> rtHasBarrierMask = {false};
 		BitSet<MAX_RENDER_GRAPH_BUFFERS, U64> buffHasBarrierMask = {false};
 
 		// For all passes of that batch
@@ -800,7 +807,7 @@ void RenderGraph::setBatchBarriers(const RenderGraphDescription& descr, BakeCont
 			{
 				if(consumer.m_isTexture)
 				{
-					setTextureBarrier(batch, pass, consumer, rtHasBarrierMask, descr, ctx);
+					setTextureBarrier(batch, consumer);
 				}
 				else
 				{
@@ -896,7 +903,7 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr, StackAllo
 	initBatches();
 
 	// Create barriers between batches
-	setBatchBarriers(descr, ctx);
+	setBatchBarriers(descr);
 
 	// Create main command buffer
 	CommandBufferInitInfo cmdbInit;
@@ -1032,39 +1039,19 @@ void RenderGraph::flush()
 	m_ctx->m_cmdb->flush();
 }
 
-void RenderGraph::getCrntUsageAndAspect(RenderTargetHandle handle,
-	U32 passIdx,
-	const TextureSurfaceInfo& surf,
-	TextureUsageBit& usage,
-	DepthStencilAspectBit& aspect) const
+void RenderGraph::getCrntUsage(
+	RenderTargetHandle handle, U32 passIdx, const TextureSubresourceInfo& subresource, TextureUsageBit& usage) const
 {
-	for(const Pass::ConsumedTextureInfo& consumer : m_ctx->m_passes[passIdx].m_consumedTextures)
-	{
-		if(consumer.m_handle == handle && (consumer.m_wholeTex || consumer.m_surface == surf))
-		{
-			usage = consumer.m_usage;
-			aspect = consumer.m_aspect;
-			return;
-		}
-	}
-
-	ANKI_ASSERT(!"Combination of handle and surface not found");
-}
+	usage = TextureUsageBit::NONE;
 
-void RenderGraph::getCrntUsageAndAspect(
-	RenderTargetHandle handle, U32 passIdx, TextureUsageBit& usage, DepthStencilAspectBit& aspect) const
-{
 	for(const Pass::ConsumedTextureInfo& consumer : m_ctx->m_passes[passIdx].m_consumedTextures)
 	{
-		if(consumer.m_handle == handle)
+		if(consumer.m_handle == handle && overlappingTextureSubresource(subresource, consumer.m_subresource))
 		{
 			usage = consumer.m_usage;
-			aspect = consumer.m_aspect;
-			return;
+			break;
 		}
 	}
-
-	ANKI_ASSERT(!"Handle not found");
 }
 
 #if ANKI_DBG_RENDER_GRAPH

+ 80 - 135
src/anki/gr/RenderGraph.h

@@ -7,8 +7,9 @@
 
 #include <anki/gr/GrObject.h>
 #include <anki/gr/Enums.h>
-#include <anki/gr/Texture.h>
+#include <anki/gr/TextureView.h>
 #include <anki/gr/Buffer.h>
+#include <anki/gr/GrManager.h>
 #include <anki/gr/Framebuffer.h>
 #include <anki/gr/CommandBuffer.h>
 #include <anki/util/HashMap.h>
@@ -19,6 +20,7 @@ namespace anki
 
 // Forward
 class RenderGraph;
+class RenderGraphDescription;
 
 /// @addtogroup graphics
 /// @{
@@ -44,6 +46,11 @@ public:
 		return m_idx == b.m_idx;
 	}
 
+	bool operator!=(const RenderTargetHandle& b) const
+	{
+		return m_idx != b.m_idx;
+	}
+
 	Bool isValid() const
 	{
 		return m_idx != MAX_U32;
@@ -121,32 +128,32 @@ public:
 
 	void getBufferState(RenderPassBufferHandle handle, BufferPtr& buff) const;
 
-	void getRenderTargetState(
-		RenderTargetHandle handle, TexturePtr& tex, TextureUsageBit& usage, DepthStencilAspectBit& aspect) const;
-	void getRenderTargetState(RenderTargetHandle handle,
-		const TextureSurfaceInfo& surf,
-		TexturePtr& tex,
-		TextureUsageBit& usage,
-		DepthStencilAspectBit& aspect) const;
-	void getRenderTargetState(RenderTargetHandle handle,
-		const TextureVolumeInfo& vol,
-		TexturePtr& tex,
-		TextureUsageBit& usage,
-		DepthStencilAspectBit& aspect) const;
 	void getRenderTargetState(RenderTargetHandle handle,
-		U32 level,
+		const TextureSubresourceInfo& subresource,
 		TexturePtr& tex,
-		TextureUsageBit& usage,
-		DepthStencilAspectBit& aspect) const;
+		TextureUsageBit& usage) const;
 
 	/// Convenience method.
-	void bindTextureAndSampler(U32 set, U32 binding, RenderTargetHandle handle, SamplerPtr sampler)
+	void bindTextureAndSampler(
+		U32 set, U32 binding, RenderTargetHandle handle, const TextureSubresourceInfo& subresource, SamplerPtr sampler)
 	{
 		TexturePtr tex;
 		TextureUsageBit usage;
-		DepthStencilAspectBit aspect;
-		getRenderTargetState(handle, tex, usage, aspect);
-		m_commandBuffer->bindTextureAndSampler(set, binding, tex, sampler, usage, aspect);
+		getRenderTargetState(handle, subresource, tex, usage);
+		TextureViewInitInfo viewInit(tex, subresource, "TmpRenderGraph");
+		TextureViewPtr view = m_commandBuffer->getManager().newTextureView(viewInit);
+		m_commandBuffer->bindTextureAndSampler(set, binding, view, sampler, usage);
+	}
+
+	/// Convenience method to bind the whole texture as color.
+	void bindColorTextureAndSampler(U32 set, U32 binding, RenderTargetHandle handle, SamplerPtr sampler)
+	{
+		TexturePtr tex = getTexture(handle);
+		TextureViewInitInfo viewInit(tex); // Use the whole texture
+		TextureUsageBit usage;
+		getRenderTargetState(handle, viewInit, tex, usage);
+		TextureViewPtr view = m_commandBuffer->getManager().newTextureView(viewInit);
+		m_commandBuffer->bindTextureAndSampler(set, binding, view, sampler, usage);
 	}
 
 	/// Convenience method.
@@ -168,6 +175,8 @@ public:
 private:
 	const RenderGraph* m_rgraph ANKI_DBG_NULLIFY;
 	U32 m_passIdx ANKI_DBG_NULLIFY;
+
+	TexturePtr getTexture(RenderTargetHandle handle) const;
 };
 
 /// Work callback for a RenderGraph pass.
@@ -180,12 +189,9 @@ class RenderPassDependency
 	friend class RenderPassDescriptionBase;
 
 public:
-	/// Dependency to an individual surface.
-	RenderPassDependency(RenderTargetHandle handle,
-		TextureUsageBit usage,
-		const TextureSurfaceInfo& surface,
-		DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE)
-		: m_texture({handle, usage, surface, false, aspect})
+	/// Dependency to a texture subresource.
+	RenderPassDependency(RenderTargetHandle handle, TextureUsageBit usage, const TextureSubresourceInfo& subresource)
+		: m_texture({handle, usage, subresource})
 		, m_isTexture(true)
 	{
 		ANKI_ASSERT(handle.valid());
@@ -194,10 +200,12 @@ public:
 	/// Dependency to the whole texture.
 	RenderPassDependency(
 		RenderTargetHandle handle, TextureUsageBit usage, DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE)
-		: m_texture({handle, usage, TextureSurfaceInfo(0, 0, 0, 0), true, aspect})
+		: m_texture({handle, usage, TextureSubresourceInfo()})
 		, m_isTexture(true)
 	{
 		ANKI_ASSERT(handle.valid());
+		m_texture.m_subresource.m_mipmapCount = MAX_U32; // Mark it as "whole texture"
+		m_texture.m_subresource.m_depthStencilAspect = aspect;
 	}
 
 	RenderPassDependency(RenderPassBufferHandle handle, BufferUsageBit usage)
@@ -213,9 +221,7 @@ private:
 	public:
 		RenderTargetHandle m_handle;
 		TextureUsageBit m_usage;
-		TextureSurfaceInfo m_surface;
-		Bool8 m_wholeTex;
-		DepthStencilAspectBit m_aspect;
+		TextureSubresourceInfo m_subresource;
 	};
 
 	struct BufferInfo
@@ -256,35 +262,10 @@ public:
 	}
 
 	/// Add new consumer dependency.
-	void newConsumer(const RenderPassDependency& dep)
-	{
-		m_consumers.emplaceBack(m_alloc, dep);
-
-		if(dep.m_isTexture && dep.m_texture.m_usage != TextureUsageBit::NONE)
-		{
-			m_consumerRtMask.set(dep.m_texture.m_handle.m_idx);
-		}
-		else if(dep.m_buffer.m_usage != BufferUsageBit::NONE)
-		{
-			m_consumerBufferMask.set(dep.m_buffer.m_handle.m_idx);
-			m_hasBufferDeps = true;
-		}
-	}
+	void newConsumer(const RenderPassDependency& dep);
 
 	/// Add new producer dependency.
-	void newProducer(const RenderPassDependency& dep)
-	{
-		m_producers.emplaceBack(m_alloc, dep);
-		if(dep.m_isTexture)
-		{
-			m_producerRtMask.set(dep.m_texture.m_handle.m_idx);
-		}
-		else
-		{
-			m_producerBufferMask.set(dep.m_buffer.m_handle.m_idx);
-			m_hasBufferDeps = true;
-		}
-	}
+	void newProducer(const RenderPassDependency& dep);
 
 protected:
 	enum class Type : U8
@@ -296,6 +277,7 @@ protected:
 	Type m_type;
 
 	StackAllocator<U8> m_alloc;
+	RenderGraphDescription* m_descr;
 
 	RenderPassWorkCallback m_callback = nullptr;
 	void* m_userData = nullptr;
@@ -312,15 +294,19 @@ protected:
 
 	String m_name;
 
-	RenderPassDescriptionBase(Type t)
+	RenderPassDescriptionBase(Type t, RenderGraphDescription* descr)
 		: m_type(t)
+		, m_descr(descr)
 	{
+		ANKI_ASSERT(descr);
 	}
 
 	void setName(CString name)
 	{
 		m_name.create(m_alloc, (name.isEmpty()) ? "N/A" : name);
 	}
+
+	void fixSubresource(RenderPassDependency& dep) const;
 };
 
 /// Framebuffer attachment info.
@@ -342,6 +328,7 @@ public:
 class FramebufferDescription
 {
 	friend class GraphicsRenderPassDescription;
+	friend class RenderGraph;
 
 public:
 	Array<FramebufferDescriptionAttachment, MAX_COLOR_ATTACHMENTS> m_colorAttachments;
@@ -362,7 +349,6 @@ public:
 	}
 
 private:
-	FramebufferInitInfo m_fbInitInfo;
 	Bool8 m_defaultFb = false;
 
 	U64 m_hash = 0;
@@ -373,14 +359,10 @@ class GraphicsRenderPassDescription : public RenderPassDescriptionBase
 {
 	friend class RenderGraphDescription;
 	friend class RenderGraph;
+	template<typename, typename>
+	friend class GenericPoolAllocator;
 
 public:
-	GraphicsRenderPassDescription()
-		: RenderPassDescriptionBase(Type::GRAPHICS)
-	{
-		memset(&m_rtHandles[0], 0xFF, sizeof(m_rtHandles));
-	}
-
 	void setFramebufferInfo(const FramebufferDescription& fbInfo,
 		const Array<RenderTargetHandle, MAX_COLOR_ATTACHMENTS>& colorRenderTargetHandles,
 		RenderTargetHandle depthStencilRenderTargetHandle,
@@ -413,30 +395,29 @@ public:
 		}
 #endif
 
-		if(fbInfo.m_defaultFb)
-		{
-			m_fbInitInfo.m_colorAttachmentCount = 1;
-		}
-		else
+		m_fbDescr = fbInfo;
+		if(!fbInfo.m_defaultFb)
 		{
-			m_fbInitInfo = fbInfo.m_fbInitInfo;
 			memcpy(&m_rtHandles[0], &colorRenderTargetHandles[0], sizeof(colorRenderTargetHandles));
 			m_rtHandles[MAX_COLOR_ATTACHMENTS] = depthStencilRenderTargetHandle;
 		}
-		m_fbInitInfo.setName(m_name.toCString());
-		m_fbHash = fbInfo.m_hash;
 		m_fbRenderArea = {{minx, miny, maxx, maxy}};
 	}
 
 private:
 	Array<RenderTargetHandle, MAX_COLOR_ATTACHMENTS + 1> m_rtHandles;
-	FramebufferInitInfo m_fbInitInfo;
+	FramebufferDescription m_fbDescr;
 	Array<U32, 4> m_fbRenderArea = {};
-	U64 m_fbHash = 0;
+
+	GraphicsRenderPassDescription(RenderGraphDescription* descr)
+		: RenderPassDescriptionBase(Type::GRAPHICS, descr)
+	{
+		memset(&m_rtHandles[0], 0xFF, sizeof(m_rtHandles));
+	}
 
 	Bool hasFramebuffer() const
 	{
-		return m_fbHash != 0;
+		return m_fbDescr.m_hash != 0;
 	}
 };
 
@@ -444,10 +425,12 @@ private:
 class ComputeRenderPassDescription : public RenderPassDescriptionBase
 {
 	friend class RenderGraphDescription;
+	template<typename, typename>
+	friend class GenericPoolAllocator;
 
-public:
-	ComputeRenderPassDescription()
-		: RenderPassDescriptionBase(Type::NO_GRAPHICS)
+private:
+	ComputeRenderPassDescription(RenderGraphDescription* descr)
+		: RenderPassDescriptionBase(Type::NO_GRAPHICS, descr)
 	{
 	}
 };
@@ -456,6 +439,7 @@ public:
 class RenderGraphDescription
 {
 	friend class RenderGraph;
+	friend class RenderPassDescriptionBase;
 
 public:
 	RenderGraphDescription(const StackAllocator<U8>& alloc)
@@ -477,7 +461,7 @@ public:
 	/// Create a new graphics render pass.
 	GraphicsRenderPassDescription& newGraphicsRenderPass(CString name)
 	{
-		GraphicsRenderPassDescription* pass = m_alloc.newInstance<GraphicsRenderPassDescription>();
+		GraphicsRenderPassDescription* pass = m_alloc.newInstance<GraphicsRenderPassDescription>(this);
 		pass->m_alloc = m_alloc;
 		pass->setName(name);
 		m_passes.emplaceBack(m_alloc, pass);
@@ -487,7 +471,7 @@ public:
 	/// Create a new compute render pass.
 	ComputeRenderPassDescription& newComputeRenderPass(CString name)
 	{
-		ComputeRenderPassDescription* pass = m_alloc.newInstance<ComputeRenderPassDescription>();
+		ComputeRenderPassDescription* pass = m_alloc.newInstance<ComputeRenderPassDescription>(this);
 		pass->m_alloc = m_alloc;
 		pass->setName(name);
 		m_passes.emplaceBack(m_alloc, pass);
@@ -654,33 +638,29 @@ private:
 	BakeContext* newContext(const RenderGraphDescription& descr, StackAllocator<U8>& alloc);
 	void initRenderPassesAndSetDeps(const RenderGraphDescription& descr, StackAllocator<U8>& alloc);
 	void initBatches();
-	void setBatchBarriers(const RenderGraphDescription& descr, BakeContext& ctx) const;
+	void setBatchBarriers(const RenderGraphDescription& descr);
 
 	TexturePtr getOrCreateRenderTarget(const TextureInitInfo& initInf, U64 hash);
-	FramebufferPtr getOrCreateFramebuffer(
-		const FramebufferInitInfo& fbInit, const RenderTargetHandle* rtHandles, U64 hash);
+	FramebufferPtr getOrCreateFramebuffer(const FramebufferDescription& fbDescr, const RenderTargetHandle* rtHandles);
 
-	static ANKI_HOT Bool passADependsOnB(const RenderPassDescriptionBase& a, const RenderPassDescriptionBase& b);
+	ANKI_HOT Bool passADependsOnB(const RenderPassDescriptionBase& a, const RenderPassDescriptionBase& b) const;
+
+	static Bool overlappingTextureSubresource(const TextureSubresourceInfo& suba, const TextureSubresourceInfo& subb);
 
 	template<Bool isTexture>
-	static ANKI_HOT Bool overlappingDependency(const RenderPassDependency& a, const RenderPassDependency& b);
+	ANKI_HOT Bool overlappingDependency(const RenderPassDependency& a, const RenderPassDependency& b) const;
 
 	static Bool passHasUnmetDependencies(const BakeContext& ctx, U32 passIdx);
 
-	void setTextureBarrier(Batch& batch,
-		const RenderPassDescriptionBase& pass,
-		const RenderPassDependency& consumer,
-		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64>& rtHasBarrierMask,
-		const RenderGraphDescription& descr,
-		BakeContext& ctx) const;
+	void setTextureBarrier(Batch& batch, const RenderPassDependency& consumer);
+
+	template<typename TFunc>
+	static void iterateSurfsOrVolumes(const TexturePtr& tex, const TextureSubresourceInfo& subresource, TFunc func);
 
-	void getCrntUsageAndAspect(
-		RenderTargetHandle handle, U32 passIdx, TextureUsageBit& usage, DepthStencilAspectBit& aspect) const;
-	void getCrntUsageAndAspect(RenderTargetHandle handle,
+	void getCrntUsage(RenderTargetHandle handle,
 		U32 passIdx,
-		const TextureSurfaceInfo& surf,
-		TextureUsageBit& usage,
-		DepthStencilAspectBit& aspect) const;
+		const TextureSubresourceInfo& subresource,
+		TextureUsageBit& usage) const;
 
 	/// @name Dump the dependency graph into a file.
 	/// @{
@@ -695,41 +675,6 @@ private:
 };
 /// @}
 
-inline void RenderPassWorkContext::getBufferState(RenderPassBufferHandle handle, BufferPtr& buff) const
-{
-	buff = m_rgraph->getBuffer(handle);
-}
-
-inline void RenderPassWorkContext::getRenderTargetState(
-	RenderTargetHandle handle, TexturePtr& tex, TextureUsageBit& usage, DepthStencilAspectBit& aspect) const
-{
-	m_rgraph->getCrntUsageAndAspect(handle, m_passIdx, usage, aspect);
-	tex = m_rgraph->getTexture(handle);
-}
-
-inline void RenderPassWorkContext::getRenderTargetState(RenderTargetHandle handle,
-	const TextureSurfaceInfo& surf,
-	TexturePtr& tex,
-	TextureUsageBit& usage,
-	DepthStencilAspectBit& aspect) const
-{
-	m_rgraph->getCrntUsageAndAspect(handle, m_passIdx, surf, usage, aspect);
-	tex = m_rgraph->getTexture(handle);
-}
-
-inline void RenderPassWorkContext::getRenderTargetState(RenderTargetHandle handle,
-	const TextureVolumeInfo& vol,
-	TexturePtr& tex,
-	TextureUsageBit& usage,
-	DepthStencilAspectBit& aspect) const
-{
-	ANKI_ASSERT(!"TODO");
-}
-
-inline void RenderPassWorkContext::getRenderTargetState(
-	RenderTargetHandle handle, U32 level, TexturePtr& tex, TextureUsageBit& usage, DepthStencilAspectBit& aspect) const
-{
-	ANKI_ASSERT(!"TODO");
-}
-
 } // end namespace anki
+
+#include <anki/gr/RenderGraph.inl.h>

+ 87 - 0
src/anki/gr/RenderGraph.inl.h

@@ -0,0 +1,87 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/RenderGraph.h>
+
+namespace anki
+{
+
+inline void RenderPassWorkContext::getBufferState(RenderPassBufferHandle handle, BufferPtr& buff) const
+{
+	buff = m_rgraph->getBuffer(handle);
+}
+
+inline void RenderPassWorkContext::getRenderTargetState(
+	RenderTargetHandle handle, const TextureSubresourceInfo& subresource, TexturePtr& tex, TextureUsageBit& usage) const
+{
+	m_rgraph->getCrntUsage(handle, m_passIdx, subresource, usage);
+	tex = m_rgraph->getTexture(handle);
+}
+
+inline TexturePtr RenderPassWorkContext::getTexture(RenderTargetHandle handle) const
+{
+	return m_rgraph->getTexture(handle);
+}
+
+inline void RenderPassDescriptionBase::fixSubresource(RenderPassDependency& dep) const
+{
+	ANKI_ASSERT(dep.m_isTexture);
+
+	TextureSubresourceInfo& subresource = dep.m_texture.m_subresource;
+	const Bool wholeTexture = subresource.m_mipmapCount == MAX_U32;
+	if(wholeTexture)
+	{
+		ANKI_ASSERT(subresource.m_firstFace == 0);
+		ANKI_ASSERT(subresource.m_firstMipmap == 0);
+		ANKI_ASSERT(subresource.m_firstLayer == 0);
+
+		const RenderGraphDescription::RT& rt = m_descr->m_renderTargets[dep.m_texture.m_handle.m_idx];
+		if(rt.m_importedTex)
+		{
+			subresource.m_faceCount = textureTypeIsCube(rt.m_importedTex->getTextureType()) ? 6 : 1;
+			subresource.m_mipmapCount = rt.m_importedTex->getMipmapCount();
+			subresource.m_layerCount = rt.m_importedTex->getLayerCount();
+		}
+		else
+		{
+			subresource.m_faceCount = textureTypeIsCube(rt.m_initInfo.m_type) ? 6 : 1;
+			subresource.m_mipmapCount = rt.m_initInfo.m_mipmapCount;
+			subresource.m_layerCount = rt.m_initInfo.m_layerCount;
+		}
+	}
+}
+
+inline void RenderPassDescriptionBase::newConsumer(const RenderPassDependency& dep)
+{
+	m_consumers.emplaceBack(m_alloc, dep);
+
+	if(dep.m_isTexture && dep.m_texture.m_usage != TextureUsageBit::NONE)
+	{
+		fixSubresource(m_consumers.getBack());
+		m_consumerRtMask.set(dep.m_texture.m_handle.m_idx);
+	}
+	else if(dep.m_buffer.m_usage != BufferUsageBit::NONE)
+	{
+		m_consumerBufferMask.set(dep.m_buffer.m_handle.m_idx);
+		m_hasBufferDeps = true;
+	}
+}
+
+inline void RenderPassDescriptionBase::newProducer(const RenderPassDependency& dep)
+{
+	m_producers.emplaceBack(m_alloc, dep);
+	if(dep.m_isTexture)
+	{
+		fixSubresource(m_producers.getBack());
+		m_producerRtMask.set(dep.m_texture.m_handle.m_idx);
+	}
+	else
+	{
+		m_producerBufferMask.set(dep.m_buffer.m_handle.m_idx);
+		m_hasBufferDeps = true;
+	}
+}
+
+} // end namespace anki

+ 8 - 0
src/anki/gr/Shader.h

@@ -97,7 +97,15 @@ class Shader : public GrObject
 public:
 	static const GrObjectType CLASS_TYPE = GrObjectType::SHADER;
 
+	ShaderType getShaderType() const
+	{
+		ANKI_ASSERT(m_shaderType != ShaderType::COUNT);
+		return m_shaderType;
+	}
+
 protected:
+	ShaderType m_shaderType = ShaderType::COUNT;
+
 	/// Construct.
 	Shader(GrManager* manager)
 		: GrObject(manager, CLASS_TYPE)

+ 41 - 0
src/anki/gr/ShaderProgram.h

@@ -19,6 +19,47 @@ class ShaderProgramInitInfo : GrBaseInitInfo
 {
 public:
 	Array<ShaderPtr, U(ShaderType::COUNT)> m_shaders = {};
+
+	ShaderProgramInitInfo(CString name = {})
+		: GrBaseInitInfo(name)
+	{
+	}
+
+	ShaderProgramInitInfo(const ShaderPtr& compute, CString name = {})
+		: GrBaseInitInfo(name)
+	{
+		m_shaders[compute->getShaderType()] = compute;
+	}
+
+	ShaderProgramInitInfo(const ShaderPtr& vert, const ShaderPtr& frag, CString name = {})
+		: GrBaseInitInfo(name)
+	{
+		m_shaders[vert->getShaderType()] = vert;
+		m_shaders[frag->getShaderType()] = frag;
+	}
+
+	Bool isValid() const
+	{
+		I32 invalid = 0;
+
+		if(m_shaders[ShaderType::COMPUTE])
+		{
+			invalid |= m_shaders[ShaderType::VERTEX] || m_shaders[ShaderType::TESSELLATION_CONTROL]
+				|| m_shaders[ShaderType::TESSELLATION_EVALUATION] || m_shaders[ShaderType::GEOMETRY]
+				|| m_shaders[ShaderType::FRAGMENT];
+		}
+		else
+		{
+			invalid |= !m_shaders[ShaderType::VERTEX] || !m_shaders[ShaderType::FRAGMENT];
+		}
+
+		for(ShaderType type = ShaderType::FIRST; type < ShaderType::COUNT; ++type)
+		{
+			invalid |= m_shaders[type] && m_shaders[type]->getShaderType() != type;
+		}
+
+		return invalid == 0;
+	}
 };
 
 /// GPU program.

+ 139 - 2
src/anki/gr/Texture.h

@@ -26,7 +26,7 @@ public:
 	TextureUsageBit m_initialUsage = TextureUsageBit::NONE; ///< Its initial usage.
 	TextureType m_type = TextureType::_2D;
 
-	U8 m_mipmapsCount = 1;
+	U8 m_mipmapCount = 1;
 
 	PixelFormat m_format;
 	U8 m_samples = 1;
@@ -50,6 +50,48 @@ public:
 				+ sizeof(U8));
 		return anki::computeHash(first, size);
 	}
+
+	Bool isValid() const
+	{
+#define ANKI_CHECK_VAL_VALIDITY(x) \
+	do                             \
+	{                              \
+		if(!(x))                   \
+		{                          \
+			return false;          \
+		}                          \
+	} while(0)
+
+		ANKI_CHECK_VAL_VALIDITY(m_usage != TextureUsageBit::NONE);
+		ANKI_CHECK_VAL_VALIDITY(m_mipmapCount > 0);
+		ANKI_CHECK_VAL_VALIDITY(m_width > 0);
+		ANKI_CHECK_VAL_VALIDITY(m_height > 0);
+		switch(m_type)
+		{
+		case TextureType::_2D:
+			ANKI_CHECK_VAL_VALIDITY(m_depth == 1);
+			ANKI_CHECK_VAL_VALIDITY(m_layerCount == 1);
+			break;
+		case TextureType::CUBE:
+			ANKI_CHECK_VAL_VALIDITY(m_depth == 1);
+			ANKI_CHECK_VAL_VALIDITY(m_layerCount == 1);
+			break;
+		case TextureType::_3D:
+			ANKI_CHECK_VAL_VALIDITY(m_depth > 0);
+			ANKI_CHECK_VAL_VALIDITY(m_layerCount == 1);
+			break;
+		case TextureType::_2D_ARRAY:
+		case TextureType::CUBE_ARRAY:
+			ANKI_CHECK_VAL_VALIDITY(m_depth == 1);
+			ANKI_CHECK_VAL_VALIDITY(m_layerCount > 0);
+			break;
+		default:
+			ANKI_CHECK_VAL_VALIDITY(0);
+		};
+
+		return true;
+#undef ANKI_CHECK_VAL_VALIDITY
+	}
 };
 
 /// GPU texture.
@@ -78,7 +120,7 @@ public:
 		return m_depth;
 	}
 
-	U32 getLayercount() const
+	U32 getLayerCount() const
 	{
 		ANKI_ASSERT(m_layerCount);
 		return m_layerCount;
@@ -104,9 +146,103 @@ public:
 
 	const PixelFormat& getPixelFormat() const
 	{
+		ANKI_ASSERT(m_format.isValid());
 		return m_format;
 	}
 
+	DepthStencilAspectBit getDepthStencilAspect() const
+	{
+		ANKI_ASSERT(m_format.isValid());
+		return m_aspect;
+	}
+
+	Bool isSubresourceValid(const TextureSubresourceInfo& subresource) const
+	{
+#define ANKI_TEX_SUBRESOURCE_ASSERT(x_) \
+	if(!(x_))                           \
+	{                                   \
+		return false;                   \
+	}
+		const TextureType type = m_texType;
+		const Bool cube = textureTypeIsCube(type);
+
+		// Mips
+		ANKI_TEX_SUBRESOURCE_ASSERT(subresource.m_mipmapCount > 0);
+		ANKI_TEX_SUBRESOURCE_ASSERT(subresource.m_firstMipmap + subresource.m_mipmapCount <= m_mipCount);
+
+		// Layers
+		ANKI_TEX_SUBRESOURCE_ASSERT(subresource.m_layerCount > 0);
+		ANKI_TEX_SUBRESOURCE_ASSERT(subresource.m_firstLayer + subresource.m_layerCount <= m_layerCount);
+
+		// Faces
+		const U8 faceCount = (cube) ? 6 : 1;
+		ANKI_TEX_SUBRESOURCE_ASSERT(subresource.m_faceCount == 1 || subresource.m_faceCount == 6);
+		ANKI_TEX_SUBRESOURCE_ASSERT(subresource.m_firstFace + subresource.m_faceCount <= faceCount);
+
+		// Aspect
+		ANKI_TEX_SUBRESOURCE_ASSERT((m_aspect & subresource.m_depthStencilAspect) == subresource.m_depthStencilAspect);
+
+		// Misc
+		if(type == TextureType::CUBE_ARRAY && subresource.m_layerCount > 1)
+		{
+			// Because of the way surfaces are arranged in cube arrays
+			ANKI_TEX_SUBRESOURCE_ASSERT(subresource.m_faceCount == 6);
+		}
+
+#undef ANKI_TEX_SUBRESOURCE_ASSERT
+		return true;
+	}
+
+	/// Mipmap generation requires a specific subresource range.
+	Bool isSubresourceGoodForMipmapGeneration(const TextureSubresourceInfo& subresource) const
+	{
+		ANKI_ASSERT(isSubresourceValid(subresource));
+		if(m_texType != TextureType::_3D)
+		{
+			return subresource.m_firstMipmap == 0 && subresource.m_mipmapCount == m_mipCount
+				&& subresource.m_faceCount == 1 && subresource.m_layerCount == 1
+				&& subresource.m_depthStencilAspect == m_aspect;
+		}
+		else
+		{
+			ANKI_ASSERT(!"TODO");
+			return false;
+		}
+	}
+
+	/// Return true if the subresource is good to be bound for image load store.
+	Bool isSubresourceGoodForImageLoadStore(const TextureSubresourceInfo& subresource) const
+	{
+		ANKI_ASSERT(isSubresourceValid(subresource));
+		// One mip and no depth stencil
+		return subresource.m_mipmapCount == 1 && !subresource.m_depthStencilAspect;
+	}
+
+	/// Return true if the subresource can be bound for sampling.
+	Bool isSubresourceGoodForSampling(const TextureSubresourceInfo& subresource) const
+	{
+		ANKI_ASSERT(isSubresourceValid(subresource));
+		/// Can bound only one aspect at a time.
+		return subresource.m_depthStencilAspect == DepthStencilAspectBit::DEPTH
+			|| subresource.m_depthStencilAspect == DepthStencilAspectBit::STENCIL
+			|| subresource.m_depthStencilAspect == DepthStencilAspectBit::NONE;
+	}
+
+	/// Return true if the subresource can be used in CommandBuffer::copyBufferToTextureView.
+	Bool isSubresourceGoodForCopyFromBuffer(const TextureSubresourceInfo& subresource) const
+	{
+		ANKI_ASSERT(isSubresourceValid(subresource));
+		return subresource.m_faceCount == 1 && subresource.m_mipmapCount == 1 && subresource.m_layerCount == 1
+			&& subresource.m_depthStencilAspect == DepthStencilAspectBit::NONE;
+	}
+
+	/// Return true if the subresource can be used as Framebuffer attachment.
+	Bool isSubresourceGoodForFramebufferAttachment(const TextureSubresourceInfo& subresource) const
+	{
+		ANKI_ASSERT(isSubresourceValid(subresource));
+		return subresource.m_faceCount == 1 && subresource.m_mipmapCount == 1 && subresource.m_layerCount == 1;
+	}
+
 protected:
 	U32 m_width = 0;
 	U32 m_height = 0;
@@ -116,6 +252,7 @@ protected:
 	TextureType m_texType = TextureType::COUNT;
 	TextureUsageBit m_usage = TextureUsageBit::NONE;
 	PixelFormat m_format;
+	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE;
 
 	/// Construct.
 	Texture(GrManager* manager)

+ 137 - 0
src/anki/gr/TextureView.h

@@ -0,0 +1,137 @@
+// 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/GrObject.h>
+#include <anki/gr/Texture.h>
+
+namespace anki
+{
+
+/// @addtogroup graphics
+/// @{
+
+/// TextureView init info.
+class TextureViewInitInfo : public GrBaseInitInfo, public TextureSubresourceInfo
+{
+public:
+	TexturePtr m_texture;
+
+	TextureViewInitInfo(TexturePtr tex, CString name = {})
+		: GrBaseInitInfo(name)
+		, m_texture(tex)
+	{
+		m_firstMipmap = 0;
+		m_mipmapCount = tex->getMipmapCount();
+		m_firstLayer = 0;
+		m_layerCount = tex->getLayerCount();
+		m_firstFace = 0;
+		m_faceCount =
+			(tex->getTextureType() == TextureType::CUBE_ARRAY || tex->getTextureType() == TextureType::CUBE) ? 6 : 1;
+
+		const PixelFormat fmt = tex->getPixelFormat();
+		m_depthStencilAspect =
+			(componentFormatIsDepth(fmt.m_components)) ? DepthStencilAspectBit::DEPTH : DepthStencilAspectBit::NONE;
+		m_depthStencilAspect |=
+			(componentFormatIsStencil(fmt.m_components)) ? DepthStencilAspectBit::STENCIL : DepthStencilAspectBit::NONE;
+	}
+
+	TextureViewInitInfo(CString name = {})
+		: GrBaseInitInfo(name)
+	{
+	}
+
+	TextureViewInitInfo(TexturePtr tex,
+		const TextureSurfaceInfo& surf,
+		DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE,
+		CString name = {})
+		: GrBaseInitInfo(name)
+		, m_texture(tex)
+	{
+		m_firstMipmap = surf.m_level;
+		m_mipmapCount = 1;
+		m_firstLayer = surf.m_layer;
+		m_layerCount = 1;
+		m_firstFace = surf.m_face;
+		m_faceCount = 1;
+		m_depthStencilAspect = aspect;
+		ANKI_ASSERT(isValid());
+	}
+
+	TextureViewInitInfo(TexturePtr tex, const TextureSubresourceInfo& subresource, CString name = {})
+		: GrBaseInitInfo(name)
+		, m_texture(tex)
+	{
+		static_cast<TextureSubresourceInfo&>(*this) = subresource;
+		ANKI_ASSERT(isValid());
+	}
+
+	Bool isValid() const
+	{
+		return m_texture.isCreated() && m_texture->isSubresourceValid(*this);
+	}
+};
+
+/// GPU texture view.
+class TextureView : public GrObject
+{
+	ANKI_GR_OBJECT
+
+public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::TEXTURE_VIEW;
+
+	TextureType getTextureType() const
+	{
+		ANKI_ASSERT(initialized());
+		return m_texType;
+	}
+
+	const TextureSubresourceInfo& getSubresource() const
+	{
+		ANKI_ASSERT(initialized());
+		return m_subresource;
+	}
+
+protected:
+	TextureType m_texType = TextureType::COUNT;
+	TextureSubresourceInfo m_subresource;
+
+	/// Construct.
+	TextureView(GrManager* manager)
+		: GrObject(manager, CLASS_TYPE)
+	{
+		m_subresource.m_depthStencilAspect = DepthStencilAspectBit::NONE;
+
+		m_subresource.m_firstMipmap = MAX_U32;
+		m_subresource.m_mipmapCount = MAX_U32;
+
+		m_subresource.m_firstLayer = MAX_U32;
+		m_subresource.m_layerCount = MAX_U32;
+
+		m_subresource.m_firstFace = MAX_U8;
+		m_subresource.m_faceCount = MAX_U8;
+	}
+
+	/// Destroy.
+	~TextureView()
+	{
+	}
+
+	Bool initialized() const
+	{
+		return m_texType != TextureType::COUNT && m_subresource.m_firstMipmap < MAX_U32
+			&& m_subresource.m_mipmapCount < MAX_U32 && m_subresource.m_firstLayer < MAX_U32
+			&& m_subresource.m_layerCount < MAX_U32 && m_subresource.m_firstFace < MAX_U8
+			&& m_subresource.m_faceCount < MAX_U8;
+	}
+
+private:
+	/// Allocate and initialize new instance.
+	static ANKI_USE_RESULT TextureView* newInstance(GrManager* manager, const TextureViewInitInfo& init);
+};
+/// @}
+
+} // end namespace anki

+ 0 - 197
src/anki/gr/common/Misc.cpp

@@ -47,201 +47,4 @@ void logShaderErrorCode(const CString& error, const CString& source, GenericMemo
 		&error[0]);
 }
 
-Bool textureInitInfoValid(const TextureInitInfo& inf)
-{
-#define ANKI_CHECK_VAL_VALIDITY(x) \
-	do                             \
-	{                              \
-		if(!(x))                   \
-		{                          \
-			return false;          \
-		}                          \
-	} while(0)
-
-	ANKI_CHECK_VAL_VALIDITY(inf.m_usage != TextureUsageBit::NONE);
-	ANKI_CHECK_VAL_VALIDITY(inf.m_mipmapsCount > 0);
-	ANKI_CHECK_VAL_VALIDITY(inf.m_width > 0);
-	ANKI_CHECK_VAL_VALIDITY(inf.m_height > 0);
-	switch(inf.m_type)
-	{
-	case TextureType::_2D:
-		ANKI_CHECK_VAL_VALIDITY(inf.m_depth == 1);
-		ANKI_CHECK_VAL_VALIDITY(inf.m_layerCount == 1);
-		break;
-	case TextureType::CUBE:
-		ANKI_CHECK_VAL_VALIDITY(inf.m_depth == 1);
-		ANKI_CHECK_VAL_VALIDITY(inf.m_layerCount == 1);
-		break;
-	case TextureType::_3D:
-		ANKI_CHECK_VAL_VALIDITY(inf.m_depth > 0);
-		ANKI_CHECK_VAL_VALIDITY(inf.m_layerCount == 1);
-		break;
-	case TextureType::_2D_ARRAY:
-	case TextureType::CUBE_ARRAY:
-		ANKI_CHECK_VAL_VALIDITY(inf.m_depth == 1);
-		ANKI_CHECK_VAL_VALIDITY(inf.m_layerCount > 0);
-		break;
-	default:
-		ANKI_CHECK_VAL_VALIDITY(0);
-	};
-
-	return true;
-#undef ANKI_CHECK_VAL_VALIDITY
-}
-
-Bool framebufferInitInfoValid(const FramebufferInitInfo& inf)
-{
-	return inf.m_colorAttachmentCount != 0 || inf.m_depthStencilAttachment.m_texture.isCreated();
-}
-
-void getFormatInfo(const PixelFormat& fmt, U& texelComponents, U& texelBytes, U& blockSize, U& blockBytes)
-{
-	blockSize = 0;
-	blockBytes = 0;
-	texelBytes = 0;
-
-	switch(fmt.m_components)
-	{
-	case ComponentFormat::R8:
-		texelComponents = 1;
-		texelBytes = texelComponents * 1;
-		break;
-	case ComponentFormat::R8G8:
-		texelComponents = 2;
-		texelBytes = texelComponents * 1;
-		break;
-	case ComponentFormat::R8G8B8:
-		texelComponents = 3;
-		texelBytes = texelComponents * 1;
-		break;
-	case ComponentFormat::R8G8B8A8:
-		texelComponents = 4;
-		texelBytes = texelComponents * 1;
-		break;
-	case ComponentFormat::R16:
-		texelComponents = 1;
-		texelBytes = texelComponents * 2;
-		break;
-	case ComponentFormat::R16G16:
-		texelComponents = 2;
-		texelBytes = texelComponents * 2;
-		break;
-	case ComponentFormat::R16G16B16:
-		texelComponents = 3;
-		texelBytes = texelComponents * 2;
-		break;
-	case ComponentFormat::R16G16B16A16:
-		texelComponents = 4;
-		texelBytes = texelComponents * 2;
-		break;
-	case ComponentFormat::R32:
-		texelComponents = 1;
-		texelBytes = texelComponents * 4;
-		break;
-	case ComponentFormat::R32G32:
-		texelComponents = 2;
-		texelBytes = texelComponents * 4;
-		break;
-	case ComponentFormat::R32G32B32:
-		texelComponents = 3;
-		texelBytes = texelComponents * 4;
-		break;
-	case ComponentFormat::R32G32B32A32:
-		texelComponents = 4;
-		texelBytes = texelComponents * 4;
-		break;
-	case ComponentFormat::R10G10B10A2:
-		texelComponents = 4;
-		texelBytes = 4;
-		break;
-	case ComponentFormat::R11G11B10:
-		texelComponents = 3;
-		texelBytes = 4;
-		break;
-	case ComponentFormat::R8G8B8_BC1:
-		texelComponents = 3;
-		blockSize = 4;
-		blockBytes = 8;
-		break;
-	case ComponentFormat::R8G8B8A8_BC3:
-		texelComponents = 4;
-		blockSize = 4;
-		blockBytes = 16;
-		break;
-	case ComponentFormat::R16G16B16_BC6:
-		texelComponents = 3;
-		blockSize = 4;
-		blockBytes = 16;
-		break;
-	case ComponentFormat::R8G8B8_ETC2:
-		texelComponents = 3;
-		blockSize = 4;
-		blockBytes = 8;
-		break;
-	case ComponentFormat::R8G8B8A8_ETC2:
-		texelComponents = 4;
-		blockSize = 4;
-		blockBytes = 16;
-		break;
-	case ComponentFormat::D16:
-		texelComponents = 1;
-		texelBytes = texelComponents * 2;
-		break;
-	case ComponentFormat::D24S8:
-		texelComponents = 1;
-		texelBytes = texelComponents * 4;
-		break;
-	case ComponentFormat::D32:
-		texelComponents = 1;
-		texelBytes = texelComponents * 4;
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-}
-
-PtrSize computeSurfaceSize(U width, U height, const PixelFormat& fmt)
-{
-	ANKI_ASSERT(width > 0 && height > 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;
-	}
-	else
-	{
-		return width * height * texelBytes;
-	}
-}
-
-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

+ 0 - 39
src/anki/gr/common/Misc.h

@@ -13,45 +13,6 @@ namespace anki
 /// Internal function that logs a shader error.
 void logShaderErrorCode(const CString& error, const CString& source, GenericMemoryPoolAllocator<U8> alloc);
 
-inline void checkTextureSurface(TextureType type, U depth, U mipCount, U layerCount, const TextureSurfaceInfo& surf)
-{
-	ANKI_ASSERT(surf.m_level < mipCount);
-	switch(type)
-	{
-	case TextureType::_2D:
-		ANKI_ASSERT(surf.m_depth == 0 && surf.m_face == 0 && surf.m_layer == 0);
-		break;
-	case TextureType::CUBE:
-		ANKI_ASSERT(surf.m_depth == 0 && surf.m_face < 6 && surf.m_layer == 0);
-		break;
-	case TextureType::_3D:
-		ANKI_ASSERT(surf.m_depth < depth && surf.m_face == 0 && surf.m_layer == 0);
-		break;
-	case TextureType::_2D_ARRAY:
-		ANKI_ASSERT(surf.m_depth == 0 && surf.m_face == 0 && surf.m_layer < layerCount);
-		break;
-	case TextureType::CUBE_ARRAY:
-		ANKI_ASSERT(surf.m_depth == 0 && surf.m_face < 6 && surf.m_layer < layerCount);
-		break;
-	default:
-		ANKI_ASSERT(0);
-	};
-}
-
-/// Check the validity of the structure.
-Bool textureInitInfoValid(const TextureInitInfo& inf);
-
-Bool framebufferInitInfoValid(const FramebufferInitInfo& inf);
-
-/// Compute the size of a single surface.
-void getFormatInfo(const PixelFormat& fmt, U& texelComponents, U& texelBytes, U& blockSize, U& blockBytes);
-
-/// 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);
-
 inline Bool stencilTestDisabled(StencilOperation stencilFail,
 	StencilOperation stencilPassDepthFail,
 	StencilOperation stencilPassDepthPass,

+ 6 - 10
src/anki/gr/gl/Buffer.cpp

@@ -17,15 +17,9 @@ Buffer* Buffer::newInstance(GrManager* manager, const BufferInitInfo& inf)
 	{
 	public:
 		BufferPtr m_buff;
-		PtrSize m_size;
-		BufferUsageBit m_usage;
-		BufferMapAccessBit m_access;
 
-		BufferCreateCommand(Buffer* buff, PtrSize size, BufferUsageBit usage, BufferMapAccessBit access)
+		BufferCreateCommand(Buffer* buff)
 			: m_buff(buff)
-			, m_size(size)
-			, m_usage(usage)
-			, m_access(access)
 		{
 		}
 
@@ -33,7 +27,7 @@ Buffer* Buffer::newInstance(GrManager* manager, const BufferInitInfo& inf)
 		{
 			BufferImpl& impl = static_cast<BufferImpl&>(*m_buff);
 
-			impl.init(m_size, m_usage, m_access);
+			impl.init();
 
 			GlObject::State oldState = impl.setStateAtomically(GlObject::State::CREATED);
 			(void)oldState;
@@ -44,10 +38,12 @@ Buffer* Buffer::newInstance(GrManager* manager, const BufferInitInfo& inf)
 	};
 
 	BufferImpl* impl = manager->getAllocator().newInstance<BufferImpl>(manager);
+	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
+
+	impl->preInit(inf);
 
 	CommandBufferPtr cmdb = manager->newCommandBuffer(CommandBufferInitInfo());
-	static_cast<CommandBufferImpl&>(*cmdb).pushBackNewCommand<BufferCreateCommand>(
-		impl, inf.m_size, inf.m_usage, inf.m_access);
+	static_cast<CommandBufferImpl&>(*cmdb).pushBackNewCommand<BufferCreateCommand>(impl);
 	static_cast<CommandBufferImpl&>(*cmdb).flush();
 
 	return impl;

+ 6 - 14
src/anki/gr/gl/BufferImpl.cpp

@@ -10,20 +10,15 @@
 namespace anki
 {
 
-void BufferImpl::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access)
+void BufferImpl::init()
 {
 	ANKI_ASSERT(!isCreated());
-	m_usage = usage;
-	m_access = access;
 
-	///
-	// Check size
-	//
-
-	ANKI_ASSERT(size > 0 && "Unacceptable size");
+	const BufferUsageBit usage = getBufferUsage();
+	const BufferMapAccessBit access = getMapAccess();
 
 	// Align size to satisfy BufferImpl::fill
-	alignRoundUp(4, size);
+	m_realSize = getAlignedRoundUp(4, getSize());
 
 	// This is a guess, not very important since DSA doesn't care about it on creation
 	m_target = GL_ARRAY_BUFFER;
@@ -38,8 +33,6 @@ void BufferImpl::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit acc
 		m_target = GL_SHADER_STORAGE_BUFFER;
 	}
 
-	m_size = size;
-
 	//
 	// Determine the creation flags
 	//
@@ -73,8 +66,7 @@ void BufferImpl::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit acc
 	//
 	glGenBuffers(1, &m_glName);
 	glBindBuffer(m_target, m_glName);
-	// Align the size to satisfy BufferImpl::fill
-	glBufferStorage(m_target, size, nullptr, flags);
+	glBufferStorage(m_target, m_realSize, nullptr, flags);
 
 	//
 	// Map
@@ -83,7 +75,7 @@ void BufferImpl::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit acc
 	{
 		const GLbitfield MAP_BITS = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
 
-		m_persistentMapping = glMapBufferRange(m_target, 0, size, flags & MAP_BITS);
+		m_persistentMapping = glMapBufferRange(m_target, 0, m_size, flags & MAP_BITS);
 		ANKI_ASSERT(m_persistentMapping != nullptr);
 	}
 }

+ 12 - 3
src/anki/gr/gl/BufferImpl.h

@@ -28,7 +28,15 @@ public:
 		destroyDeferred(getManager(), glDeleteBuffers);
 	}
 
-	void init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access);
+	void preInit(const BufferInitInfo& init)
+	{
+		ANKI_ASSERT(init.isValid());
+		m_size = init.m_size;
+		m_usage = init.m_usage;
+		m_access = init.m_access;
+	}
+
+	void init();
 
 	void bind(GLenum target, U32 binding, PtrSize offset, PtrSize range) const
 	{
@@ -60,8 +68,8 @@ public:
 		ANKI_ASSERT(offset < m_size);
 		ANKI_ASSERT((offset % 4) == 0 && "Should be multiple of 4");
 
-		size = (size == MAX_PTR_SIZE) ? (m_size - offset) : size;
-		ANKI_ASSERT(offset + size <= m_size);
+		size = (size == MAX_PTR_SIZE) ? (m_realSize - offset) : size;
+		ANKI_ASSERT(offset + size <= m_realSize);
 		ANKI_ASSERT((size % 4) == 0 && "Should be multiple of 4");
 
 		glClearNamedBufferSubData(m_glName, GL_R32UI, offset, size, GL_RED_INTEGER, GL_UNSIGNED_INT, &value);
@@ -103,6 +111,7 @@ private:
 #if ANKI_EXTRA_CHECKS
 	Bool m_mapped = false;
 #endif
+	PtrSize m_realSize = 0;
 };
 /// @}
 

+ 61 - 140
src/anki/gr/gl/CommandBuffer.cpp

@@ -10,18 +10,13 @@
 #include <anki/gr/gl/RenderingThread.h>
 #include <anki/gr/gl/GlState.h>
 
-#include <anki/gr/Framebuffer.h>
 #include <anki/gr/gl/FramebufferImpl.h>
-#include <anki/gr/OcclusionQuery.h>
 #include <anki/gr/gl/OcclusionQueryImpl.h>
-#include <anki/gr/Texture.h>
 #include <anki/gr/gl/TextureImpl.h>
-#include <anki/gr/Buffer.h>
 #include <anki/gr/gl/BufferImpl.h>
-#include <anki/gr/Sampler.h>
 #include <anki/gr/gl/SamplerImpl.h>
-#include <anki/gr/ShaderProgram.h>
 #include <anki/gr/gl/ShaderProgramImpl.h>
+#include <anki/gr/gl/TextureViewImpl.h>
 
 #include <anki/core/Trace.h>
 
@@ -56,18 +51,6 @@ void CommandBuffer::flush(FencePtr* fence)
 	}
 }
 
-void CommandBuffer::finish()
-{
-	ANKI_GL_SELF(CommandBufferImpl);
-
-	if(!self.isSecondLevel())
-	{
-		ANKI_ASSERT(!self.m_state.insideRenderPass());
-	}
-
-	static_cast<GrManagerImpl&>(getManager()).getRenderingThread().finishCommandBuffer(CommandBufferPtr(this));
-}
-
 void CommandBuffer::bindVertexBuffer(
 	U32 binding, BufferPtr buff, PtrSize offset, PtrSize stride, VertexStepRate stepRate)
 {
@@ -631,35 +614,38 @@ void CommandBuffer::setBlendOperation(U32 attachment, BlendOperation funcRgb, Bl
 }
 
 void CommandBuffer::bindTextureAndSampler(
-	U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, TextureUsageBit usage, DepthStencilAspectBit aspect)
+	U32 set, U32 binding, TextureViewPtr texView, SamplerPtr sampler, TextureUsageBit usage)
 {
 	class Cmd final : public GlCommand
 	{
 	public:
 		U32 m_unit;
-		TexturePtr m_tex;
+		TextureViewPtr m_texView;
 		SamplerPtr m_sampler;
 
-		Cmd(U32 unit, TexturePtr tex, SamplerPtr sampler)
+		Cmd(U32 unit, TextureViewPtr texView, SamplerPtr sampler)
 			: m_unit(unit)
-			, m_tex(tex)
+			, m_texView(texView)
 			, m_sampler(sampler)
 		{
 		}
 
 		Error operator()(GlState&)
 		{
-			glBindTextureUnit(m_unit, static_cast<const TextureImpl&>(*m_tex).getGlName());
+			glBindTextureUnit(m_unit, static_cast<const TextureViewImpl&>(*m_texView).m_view.m_glName);
 			glBindSampler(m_unit, static_cast<const SamplerImpl&>(*m_sampler).getGlName());
 			return Error::NONE;
 		}
 	};
 
 	ANKI_GL_SELF(CommandBufferImpl);
-	if(self.m_state.bindTextureAndSampler(set, binding, tex, sampler, aspect))
+	ANKI_ASSERT(static_cast<const TextureViewImpl&>(*texView).m_tex->isSubresourceGoodForSampling(
+		static_cast<const TextureViewImpl&>(*texView).getSubresource()));
+
+	if(self.m_state.bindTextureViewAndSampler(set, binding, texView, sampler))
 	{
 		U unit = binding + MAX_TEXTURE_BINDINGS * set;
-		self.pushBackNewCommand<Cmd>(unit, tex, sampler);
+		self.pushBackNewCommand<Cmd>(unit, texView, sampler);
 	}
 }
 
@@ -735,42 +721,44 @@ void CommandBuffer::bindStorageBuffer(U32 set, U32 binding, BufferPtr buff, PtrS
 	}
 }
 
-void CommandBuffer::bindImage(U32 set, U32 binding, TexturePtr img, U32 level)
+void CommandBuffer::bindImage(U32 set, U32 binding, TextureViewPtr img)
 {
 	class Cmd final : public GlCommand
 	{
 	public:
-		TexturePtr m_img;
+		TextureViewPtr m_img;
 		U16 m_unit;
-		U8 m_level;
 
-		Cmd(U32 unit, TexturePtr img, U32 level)
+		Cmd(U32 unit, TextureViewPtr img)
 			: m_img(img)
 			, m_unit(unit)
-			, m_level(level)
 		{
 		}
 
 		Error operator()(GlState&)
 		{
+			const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*m_img);
+
 			glBindImageTexture(m_unit,
-				static_cast<const TextureImpl&>(*m_img).getGlName(),
-				m_level,
+				view.m_view.m_glName,
+				m_img->getSubresource().m_firstMipmap,
 				GL_TRUE,
 				0,
 				GL_READ_WRITE,
-				static_cast<const TextureImpl&>(*m_img).m_internalFormat);
+				static_cast<const TextureImpl&>(*view.m_tex).m_internalFormat);
 			return Error::NONE;
 		}
 	};
 
 	ANKI_ASSERT(img);
 	ANKI_GL_SELF(CommandBufferImpl);
+	ANKI_ASSERT(static_cast<const TextureViewImpl&>(*img).m_tex->isSubresourceGoodForImageLoadStore(
+		static_cast<const TextureViewImpl&>(*img).getSubresource()));
 
-	if(self.m_state.bindImage(set, binding, img, level))
+	if(self.m_state.bindImage(set, binding, img))
 	{
 		binding = binding + set * MAX_IMAGE_BINDINGS;
-		self.pushBackNewCommand<Cmd>(binding, img, level);
+		self.pushBackNewCommand<Cmd>(binding, img);
 	}
 }
 
@@ -1172,8 +1160,7 @@ void CommandBuffer::endOcclusionQuery(OcclusionQueryPtr query)
 	self.pushBackNewCommand<OqEndCommand>(query);
 }
 
-void CommandBuffer::copyBufferToTextureSurface(
-	BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureSurfaceInfo& surf)
+void CommandBuffer::copyBufferToTextureView(BufferPtr buff, PtrSize offset, PtrSize range, TextureViewPtr texView)
 {
 	class TexSurfUploadCommand final : public GlCommand
 	{
@@ -1181,72 +1168,34 @@ void CommandBuffer::copyBufferToTextureSurface(
 		BufferPtr m_buff;
 		PtrSize m_offset;
 		PtrSize m_range;
-		TexturePtr m_tex;
-		TextureSurfaceInfo m_surf;
+		TextureViewPtr m_texView;
 
-		TexSurfUploadCommand(
-			BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureSurfaceInfo& surf)
+		TexSurfUploadCommand(BufferPtr buff, PtrSize offset, PtrSize range, TextureViewPtr texView)
 			: m_buff(buff)
 			, m_offset(offset)
 			, m_range(range)
-			, m_tex(tex)
-			, m_surf(surf)
-		{
-		}
-
-		Error operator()(GlState& state)
+			, m_texView(texView)
 		{
-			static_cast<TextureImpl&>(*m_tex).writeSurface(
-				m_surf, static_cast<const BufferImpl&>(*m_buff).getGlName(), m_offset, m_range);
-			return Error::NONE;
 		}
-	};
-
-	ANKI_ASSERT(tex);
-	ANKI_ASSERT(buff);
-	ANKI_ASSERT(range > 0);
-	ANKI_GL_SELF(CommandBufferImpl);
-	ANKI_ASSERT(!self.m_state.insideRenderPass());
-
-	self.pushBackNewCommand<TexSurfUploadCommand>(buff, offset, range, tex, surf);
-}
-
-void CommandBuffer::copyBufferToTextureVolume(
-	BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureVolumeInfo& vol)
-{
-	class TexVolUploadCommand final : public GlCommand
-	{
-	public:
-		BufferPtr m_buff;
-		PtrSize m_offset;
-		PtrSize m_range;
-		TexturePtr m_tex;
-		TextureVolumeInfo m_vol;
 
-		TexVolUploadCommand(BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureVolumeInfo& vol)
-			: m_buff(buff)
-			, m_offset(offset)
-			, m_range(range)
-			, m_tex(tex)
-			, m_vol(vol)
+		Error operator()(GlState&)
 		{
-		}
+			const TextureViewImpl& viewImpl = static_cast<TextureViewImpl&>(*m_texView);
+			const TextureImpl& texImpl = static_cast<TextureImpl&>(*viewImpl.m_tex);
 
-		Error operator()(GlState& state)
-		{
-			static_cast<const TextureImpl&>(*m_tex).writeVolume(
-				m_vol, static_cast<const BufferImpl&>(*m_buff).getGlName(), m_offset, m_range);
+			texImpl.copyFromBuffer(
+				viewImpl.getSubresource(), static_cast<const BufferImpl&>(*m_buff).getGlName(), m_offset, m_range);
 			return Error::NONE;
 		}
 	};
 
-	ANKI_ASSERT(tex);
+	ANKI_ASSERT(texView);
 	ANKI_ASSERT(buff);
 	ANKI_ASSERT(range > 0);
 	ANKI_GL_SELF(CommandBufferImpl);
 	ANKI_ASSERT(!self.m_state.insideRenderPass());
 
-	self.pushBackNewCommand<TexVolUploadCommand>(buff, offset, range, tex, vol);
+	self.pushBackNewCommand<TexSurfUploadCommand>(buff, offset, range, texView);
 }
 
 void CommandBuffer::copyBufferToBuffer(
@@ -1287,35 +1236,34 @@ void CommandBuffer::copyBufferToBuffer(
 	self.pushBackNewCommand<Cmd>(src, srcOffset, dst, dstOffset, range);
 }
 
-void CommandBuffer::generateMipmaps2d(TexturePtr tex, U face, U layer)
+void CommandBuffer::generateMipmaps2d(TextureViewPtr texView)
 {
 	class GenMipsCommand final : public GlCommand
 	{
 	public:
-		TexturePtr m_tex;
-		U8 m_face;
-		U32 m_layer;
+		TextureViewPtr m_texView;
 
-		GenMipsCommand(const TexturePtr& tex, U face, U layer)
-			: m_tex(tex)
-			, m_face(face)
-			, m_layer(layer)
+		GenMipsCommand(const TextureViewPtr& view)
+			: m_texView(view)
 		{
 		}
 
 		Error operator()(GlState&)
 		{
-			static_cast<TextureImpl&>(*m_tex).generateMipmaps2d(m_face, m_layer);
+			const TextureViewImpl& viewImpl = static_cast<TextureViewImpl&>(*m_texView);
+			const TextureImpl& texImpl = static_cast<TextureImpl&>(*viewImpl.m_tex);
+
+			texImpl.generateMipmaps2d(viewImpl);
 			return Error::NONE;
 		}
 	};
 
 	ANKI_GL_SELF(CommandBufferImpl);
 	ANKI_ASSERT(!self.m_state.insideRenderPass());
-	self.pushBackNewCommand<GenMipsCommand>(tex, face, layer);
+	self.pushBackNewCommand<GenMipsCommand>(texView);
 }
 
-void CommandBuffer::generateMipmaps3d(TexturePtr tex)
+void CommandBuffer::generateMipmaps3d(TextureViewPtr tex)
 {
 	ANKI_ASSERT(!!"TODO");
 }
@@ -1356,39 +1304,9 @@ Bool CommandBuffer::isEmpty() const
 	return self.isEmpty();
 }
 
-void CommandBuffer::copyTextureSurfaceToTextureSurface(
-	TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf)
+void CommandBuffer::blitTextureViews(TextureViewPtr srcView, TextureViewPtr destView)
 {
-	class CopyTexCommand final : public GlCommand
-	{
-	public:
-		TexturePtr m_src;
-		TextureSurfaceInfo m_srcSurf;
-		TexturePtr m_dest;
-		TextureSurfaceInfo m_destSurf;
-
-		CopyTexCommand(
-			TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf)
-			: m_src(src)
-			, m_srcSurf(srcSurf)
-			, m_dest(dest)
-			, m_destSurf(destSurf)
-		{
-		}
-
-		Error operator()(GlState&)
-		{
-			TextureImpl::copy(static_cast<const TextureImpl&>(*m_src),
-				m_srcSurf,
-				static_cast<const TextureImpl&>(*m_dest),
-				m_destSurf);
-			return Error::NONE;
-		}
-	};
-
-	ANKI_GL_SELF(CommandBufferImpl);
-	ANKI_ASSERT(!self.m_state.insideRenderPass());
-	self.pushBackNewCommand<CopyTexCommand>(src, srcSurf, dest, destSurf);
+	ANKI_ASSERT(!"TODO");
 }
 
 void CommandBuffer::setBufferBarrier(
@@ -1467,36 +1385,39 @@ void CommandBuffer::setTextureVolumeBarrier(
 	// Do nothing
 }
 
-void CommandBuffer::clearTextureSurface(
-	TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectBit aspect)
+void CommandBuffer::setTextureBarrier(
+	TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage, const TextureSubresourceInfo& subresource)
+{
+	// Do nothing for GL
+}
+
+void CommandBuffer::clearTextureView(TextureViewPtr texView, const ClearValue& clearValue)
 {
 	class ClearTextCommand final : public GlCommand
 	{
 	public:
-		TexturePtr m_tex;
+		TextureViewPtr m_texView;
 		ClearValue m_val;
-		TextureSurfaceInfo m_surf;
-		DepthStencilAspectBit m_aspect;
 
-		ClearTextCommand(
-			TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& val, DepthStencilAspectBit aspect)
-			: m_tex(tex)
+		ClearTextCommand(TextureViewPtr texView, const ClearValue& val)
+			: m_texView(texView)
 			, m_val(val)
-			, m_surf(surf)
-			, m_aspect(aspect)
 		{
 		}
 
 		Error operator()(GlState&)
 		{
-			static_cast<TextureImpl&>(*m_tex).clear(m_surf, m_val, m_aspect);
+			const TextureViewImpl& viewImpl = static_cast<TextureViewImpl&>(*m_texView);
+			const TextureImpl& texImpl = static_cast<TextureImpl&>(*viewImpl.m_tex);
+
+			texImpl.clear(viewImpl.getSubresource(), m_val);
 			return Error::NONE;
 		}
 	};
 
 	ANKI_GL_SELF(CommandBufferImpl);
 	ANKI_ASSERT(!self.m_state.insideRenderPass());
-	self.pushBackNewCommand<ClearTextCommand>(tex, surf, clearValue, aspect);
+	self.pushBackNewCommand<ClearTextCommand>(texView, clearValue);
 }
 
 void CommandBuffer::fillBuffer(BufferPtr buff, PtrSize offset, PtrSize size, U32 value)

+ 17 - 2
src/anki/gr/gl/Common.cpp

@@ -343,12 +343,27 @@ void convertTextureInformation(const PixelFormat& pf,
 			internalFormat = GL_RGB8;
 			type = GL_UNSIGNED_BYTE;
 		}
-		else
+		else if(pf.m_transform == TransformFormat::SNORM)
 		{
-			ANKI_ASSERT(pf.m_transform == TransformFormat::SNORM);
 			internalFormat = GL_RGB8_SNORM;
 			type = GL_BYTE;
 		}
+		else if(pf.m_transform == TransformFormat::UINT)
+		{
+			internalFormat = GL_RGB8UI;
+			type = GL_UNSIGNED_BYTE;
+			format = GL_RGB_INTEGER;
+		}
+		else if(pf.m_transform == TransformFormat::SINT)
+		{
+			internalFormat = GL_RGB8I;
+			type = GL_BYTE;
+			format = GL_RGB_INTEGER;
+		}
+		else
+		{
+			ANKI_ASSERT(!"TODO");
+		}
 		break;
 	case ComponentFormat::R8G8B8A8:
 		format = GL_RGBA;

+ 1 - 0
src/anki/gr/gl/Framebuffer.cpp

@@ -40,6 +40,7 @@ Framebuffer* Framebuffer::newInstance(GrManager* manager, const FramebufferInitI
 	};
 
 	FramebufferImpl* impl = manager->getAllocator().newInstance<FramebufferImpl>(manager);
+	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
 
 	CommandBufferPtr cmdb = manager->newCommandBuffer(CommandBufferInitInfo());
 	static_cast<CommandBufferImpl&>(*cmdb).pushBackNewCommand<CreateFramebufferCommand>(impl, init);

+ 45 - 53
src/anki/gr/gl/FramebufferImpl.cpp

@@ -5,7 +5,7 @@
 
 #include <anki/gr/gl/FramebufferImpl.h>
 #include <anki/gr/Texture.h>
-#include <anki/gr/gl/TextureImpl.h>
+#include <anki/gr/gl/TextureViewImpl.h>
 #include <anki/gr/gl/GlState.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/gl/GrManagerImpl.h>
@@ -20,7 +20,7 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 	ANKI_ASSERT(!isCreated());
 	m_in = init;
 
-	if(m_in.m_colorAttachmentCount == 1 && !m_in.m_colorAttachments[0].m_texture.isCreated())
+	if(init.refersToDefaultFramebuffer())
 	{
 		m_fbSize[0] = m_fbSize[1] = MAX_U16;
 		m_bindDefault = true;
@@ -38,9 +38,12 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 	for(U i = 0; i < m_in.m_colorAttachmentCount; i++)
 	{
 		const FramebufferAttachmentInfo& att = m_in.m_colorAttachments[i];
+		const TextureViewImpl& viewImpl = static_cast<const TextureViewImpl&>(*att.m_textureView);
+		ANKI_ASSERT(viewImpl.m_tex->isSubresourceGoodForFramebufferAttachment(viewImpl.getSubresource()));
+
 		const GLenum binding = GL_COLOR_ATTACHMENT0 + i;
 
-		attachTextureInternal(binding, static_cast<const TextureImpl&>(*att.m_texture), att);
+		attachTextureInternal(binding, viewImpl, att);
 
 		m_drawBuffers[i] = binding;
 
@@ -51,59 +54,39 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 
 		if(m_fbSize[0] == 0)
 		{
-			m_fbSize[0] = att.m_texture->getWidth();
-			m_fbSize[1] = att.m_texture->getHeight();
+			m_fbSize[0] = viewImpl.m_tex->getWidth() >> viewImpl.getSubresource().m_firstMipmap;
+			m_fbSize[1] = viewImpl.m_tex->getHeight() >> viewImpl.getSubresource().m_firstMipmap;
 		}
 		else
 		{
-			ANKI_ASSERT(m_fbSize[0] == att.m_texture->getWidth());
-			ANKI_ASSERT(m_fbSize[1] == att.m_texture->getHeight());
+			ANKI_ASSERT(m_fbSize[0] == (viewImpl.m_tex->getWidth() >> viewImpl.getSubresource().m_firstMipmap));
+			ANKI_ASSERT(m_fbSize[1] == (viewImpl.m_tex->getHeight() >> viewImpl.getSubresource().m_firstMipmap));
 		}
 	}
 
 	// Attach depth/stencil
-	if(m_in.m_depthStencilAttachment.m_texture.isCreated())
+	if(m_in.m_depthStencilAttachment.m_textureView.isCreated())
 	{
 		const FramebufferAttachmentInfo& att = m_in.m_depthStencilAttachment;
-		const TextureImpl& tex = static_cast<const TextureImpl&>(*att.m_texture);
+		const TextureViewImpl& viewImpl = static_cast<const TextureViewImpl&>(*att.m_textureView);
+		ANKI_ASSERT(viewImpl.m_tex->isSubresourceGoodForFramebufferAttachment(viewImpl.getSubresource()));
 
 		GLenum binding;
-		if(tex.m_glFormat == GL_DEPTH_COMPONENT)
+		if(viewImpl.getSubresource().m_depthStencilAspect == DepthStencilAspectBit::DEPTH)
 		{
 			binding = GL_DEPTH_ATTACHMENT;
-			m_dsAspect = DepthStencilAspectBit::DEPTH;
 		}
-		else if(tex.m_glFormat == GL_STENCIL_INDEX)
+		else if(viewImpl.getSubresource().m_depthStencilAspect == DepthStencilAspectBit::STENCIL)
 		{
 			binding = GL_STENCIL_ATTACHMENT;
-			m_dsAspect = DepthStencilAspectBit::STENCIL;
 		}
 		else
 		{
-			ANKI_ASSERT(tex.m_glFormat == GL_DEPTH_STENCIL);
-
-			if(att.m_aspect == DepthStencilAspectBit::DEPTH)
-			{
-				binding = GL_DEPTH_ATTACHMENT;
-			}
-			else if(att.m_aspect == DepthStencilAspectBit::STENCIL)
-			{
-				binding = GL_STENCIL_ATTACHMENT;
-			}
-			else if(att.m_aspect == DepthStencilAspectBit::DEPTH_STENCIL)
-			{
-				binding = GL_DEPTH_STENCIL_ATTACHMENT;
-			}
-			else
-			{
-				ANKI_ASSERT(!"Need to set FramebufferAttachmentInfo::m_aspect");
-				binding = 0;
-			}
-
-			m_dsAspect = att.m_aspect;
+			ANKI_ASSERT(viewImpl.getSubresource().m_depthStencilAspect == DepthStencilAspectBit::DEPTH_STENCIL);
+			binding = GL_DEPTH_STENCIL_ATTACHMENT;
 		}
 
-		attachTextureInternal(binding, tex, att);
+		attachTextureInternal(binding, viewImpl, att);
 
 		if(att.m_loadOperation == AttachmentLoadOperation::DONT_CARE)
 		{
@@ -112,14 +95,21 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 
 		if(m_fbSize[0] == 0)
 		{
-			m_fbSize[0] = att.m_texture->getWidth();
-			m_fbSize[1] = att.m_texture->getHeight();
+			m_fbSize[0] = viewImpl.m_tex->getWidth() >> viewImpl.getSubresource().m_firstMipmap;
+			m_fbSize[1] = viewImpl.m_tex->getHeight() >> viewImpl.getSubresource().m_firstMipmap;
 		}
 		else
 		{
-			ANKI_ASSERT(m_fbSize[0] == att.m_texture->getWidth());
-			ANKI_ASSERT(m_fbSize[1] == att.m_texture->getHeight());
+			ANKI_ASSERT(m_fbSize[0] == (viewImpl.m_tex->getWidth() >> viewImpl.getSubresource().m_firstMipmap));
+			ANKI_ASSERT(m_fbSize[1] == (viewImpl.m_tex->getHeight() >> viewImpl.getSubresource().m_firstMipmap));
 		}
+
+		// Misc
+		m_clearDepth = !!(viewImpl.getSubresource().m_depthStencilAspect & DepthStencilAspectBit::DEPTH)
+			&& att.m_loadOperation == AttachmentLoadOperation::CLEAR;
+
+		m_clearStencil = !!(viewImpl.getSubresource().m_depthStencilAspect & DepthStencilAspectBit::STENCIL)
+			&& att.m_stencilLoadOperation == AttachmentLoadOperation::CLEAR;
 	}
 
 	// Check completeness
@@ -135,38 +125,42 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 }
 
 void FramebufferImpl::attachTextureInternal(
-	GLenum attachment, const TextureImpl& tex, const FramebufferAttachmentInfo& info)
+	GLenum attachment, const TextureViewImpl& view, const FramebufferAttachmentInfo& info)
 {
-	tex.checkSurfaceOrVolume(info.m_surface);
-
 	const GLenum target = GL_FRAMEBUFFER;
+	const TextureImpl& tex = static_cast<const TextureImpl&>(*view.m_tex);
+
 	switch(tex.m_target)
 	{
 	case GL_TEXTURE_2D:
 #if ANKI_GL == ANKI_GL_DESKTOP
 	case GL_TEXTURE_2D_MULTISAMPLE:
 #endif
-		glFramebufferTexture2D(target, attachment, tex.m_target, tex.getGlName(), info.m_surface.m_level);
+		glFramebufferTexture2D(target, attachment, tex.m_target, tex.getGlName(), view.getSubresource().m_firstMipmap);
 		break;
 	case GL_TEXTURE_CUBE_MAP:
 		glFramebufferTexture2D(target,
 			attachment,
-			GL_TEXTURE_CUBE_MAP_POSITIVE_X + info.m_surface.m_face,
+			GL_TEXTURE_CUBE_MAP_POSITIVE_X + view.getSubresource().m_firstFace,
 			tex.getGlName(),
-			info.m_surface.m_level);
+			view.getSubresource().m_firstMipmap);
 		break;
 	case GL_TEXTURE_2D_ARRAY:
-		glFramebufferTextureLayer(target, attachment, tex.getGlName(), info.m_surface.m_level, info.m_surface.m_layer);
+		glFramebufferTextureLayer(target,
+			attachment,
+			tex.getGlName(),
+			view.getSubresource().m_firstMipmap,
+			view.getSubresource().m_firstLayer);
 		break;
 	case GL_TEXTURE_3D:
-		glFramebufferTextureLayer(target, attachment, tex.getGlName(), info.m_surface.m_level, info.m_surface.m_depth);
+		ANKI_ASSERT(!"TODO");
 		break;
 	case GL_TEXTURE_CUBE_MAP_ARRAY:
 		glFramebufferTextureLayer(target,
 			attachment,
 			tex.getGlName(),
-			info.m_surface.m_level,
-			info.m_surface.m_layer * 6 + info.m_surface.m_face);
+			view.getSubresource().m_firstMipmap,
+			view.getSubresource().m_firstLayer * 6 + view.getSubresource().m_firstFace);
 		break;
 	default:
 		ANKI_ASSERT(0);
@@ -258,8 +252,7 @@ void FramebufferImpl::bind(const GlState& state, U32 minx, U32 miny, U32 width,
 		}
 
 		// Clear depth
-		if(!!(m_dsAspect & DepthStencilAspectBit::DEPTH) && m_in.m_depthStencilAttachment.m_texture.isCreated()
-			&& m_in.m_depthStencilAttachment.m_loadOperation == AttachmentLoadOperation::CLEAR)
+		if(m_clearDepth)
 		{
 			// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
 			if(state.m_depthWriteMask == false)
@@ -276,8 +269,7 @@ void FramebufferImpl::bind(const GlState& state, U32 minx, U32 miny, U32 width,
 		}
 
 		// Clear stencil
-		if(!!(m_dsAspect & DepthStencilAspectBit::STENCIL) && m_in.m_depthStencilAttachment.m_texture.isCreated()
-			&& m_in.m_depthStencilAttachment.m_stencilLoadOperation == AttachmentLoadOperation::CLEAR)
+		if(m_clearStencil)
 		{
 			// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
 			// From the spec: The clear operation always uses the front stencil write mask when clearing the stencil

+ 4 - 14
src/anki/gr/gl/FramebufferImpl.h

@@ -42,18 +42,6 @@ public:
 		return m_in.m_colorAttachmentCount;
 	}
 
-	Bool hasDepthBuffer() const
-	{
-		return m_in.m_depthStencilAttachment.m_texture.isCreated()
-			&& !!(m_in.m_depthStencilAttachment.m_aspect & DepthStencilAspectBit::DEPTH);
-	}
-
-	Bool hasStencilBuffer() const
-	{
-		return m_in.m_depthStencilAttachment.m_texture.isCreated()
-			&& !!(m_in.m_depthStencilAttachment.m_aspect & DepthStencilAspectBit::STENCIL);
-	}
-
 private:
 	FramebufferInitInfo m_in;
 
@@ -62,10 +50,12 @@ private:
 	Array<GLenum, MAX_COLOR_ATTACHMENTS + 1> m_invalidateBuffers;
 	U8 m_invalidateBuffersCount = 0;
 	Bool8 m_bindDefault = false;
-	DepthStencilAspectBit m_dsAspect = DepthStencilAspectBit::NONE;
+	Bool8 m_clearDepth = false;
+	Bool8 m_clearStencil = false;
 
 	/// Attach a texture
-	static void attachTextureInternal(GLenum attachment, const TextureImpl& tex, const FramebufferAttachmentInfo& info);
+	static void attachTextureInternal(
+		GLenum attachment, const TextureViewImpl& view, const FramebufferAttachmentInfo& info);
 
 	/// Create the FBO
 	ANKI_USE_RESULT Error createFbo(const Array<U, MAX_COLOR_ATTACHMENTS + 1>& layers, GLenum depthStencilBindingPoint);

+ 22 - 27
src/anki/gr/gl/GrManager.cpp

@@ -81,29 +81,40 @@ void GrManager::finish()
 	self.getRenderingThread().syncClientServer();
 }
 
+#define ANKI_SAFE_CONSTRUCT(class_)                \
+	class_* out = class_::newInstance(this, init); \
+	class_##Ptr ptr(out);                          \
+	out->getRefcount().fetchSub(1);                \
+	return ptr
+
 BufferPtr GrManager::newBuffer(const BufferInitInfo& init)
 {
-	return BufferPtr(Buffer::newInstance(this, init));
+	ANKI_SAFE_CONSTRUCT(Buffer);
 }
 
 TexturePtr GrManager::newTexture(const TextureInitInfo& init)
 {
-	return TexturePtr(Texture::newInstance(this, init));
+	ANKI_SAFE_CONSTRUCT(Texture);
+}
+
+TextureViewPtr GrManager::newTextureView(const TextureViewInitInfo& init)
+{
+	ANKI_SAFE_CONSTRUCT(TextureView);
 }
 
 SamplerPtr GrManager::newSampler(const SamplerInitInfo& init)
 {
-	return SamplerPtr(Sampler::newInstance(this, init));
+	ANKI_SAFE_CONSTRUCT(Sampler);
 }
 
 ShaderPtr GrManager::newShader(const ShaderInitInfo& init)
 {
-	return ShaderPtr(Shader::newInstance(this, init));
+	ANKI_SAFE_CONSTRUCT(Shader);
 }
 
 ShaderProgramPtr GrManager::newShaderProgram(const ShaderProgramInitInfo& init)
 {
-	return ShaderProgramPtr(ShaderProgram::newInstance(this, init));
+	ANKI_SAFE_CONSTRUCT(ShaderProgram);
 }
 
 CommandBufferPtr GrManager::newCommandBuffer(const CommandBufferInitInfo& init)
@@ -113,12 +124,15 @@ CommandBufferPtr GrManager::newCommandBuffer(const CommandBufferInitInfo& init)
 
 FramebufferPtr GrManager::newFramebuffer(const FramebufferInitInfo& init)
 {
-	return FramebufferPtr(Framebuffer::newInstance(this, init));
+	ANKI_SAFE_CONSTRUCT(Framebuffer);
 }
 
 OcclusionQueryPtr GrManager::newOcclusionQuery()
 {
-	return OcclusionQueryPtr(OcclusionQuery::newInstance(this));
+	OcclusionQuery* out = OcclusionQuery::newInstance(this);
+	OcclusionQueryPtr ptr(out);
+	out->getRefcount().fetchSub(1);
+	return ptr;
 }
 
 RenderGraphPtr GrManager::newRenderGraph()
@@ -126,26 +140,7 @@ RenderGraphPtr GrManager::newRenderGraph()
 	return RenderGraphPtr(RenderGraph::newInstance(this));
 }
 
-void GrManager::getTextureSurfaceUploadInfo(TexturePtr tex, const TextureSurfaceInfo& surf, PtrSize& allocationSize)
-{
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	impl.checkSurfaceOrVolume(surf);
-
-	U width = impl.m_width >> surf.m_level;
-	U height = impl.m_height >> surf.m_level;
-	allocationSize = computeSurfaceSize(width, height, impl.m_format);
-}
-
-void GrManager::getTextureVolumeUploadInfo(TexturePtr tex, const TextureVolumeInfo& vol, PtrSize& allocationSize)
-{
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	impl.checkSurfaceOrVolume(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_format);
-}
+#undef ANKI_SAFE_CONSTRUCT
 
 void GrManager::getUniformBufferInfo(U32& bindOffsetAlignment, PtrSize& maxUniformBlockSize) const
 {

+ 1 - 0
src/anki/gr/gl/OcclusionQuery.cpp

@@ -39,6 +39,7 @@ OcclusionQuery* OcclusionQuery::newInstance(GrManager* manager)
 	};
 
 	OcclusionQueryImpl* impl = manager->getAllocator().newInstance<OcclusionQueryImpl>(manager);
+	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
 
 	CommandBufferPtr cmdb = manager->newCommandBuffer(CommandBufferInitInfo());
 	static_cast<CommandBufferImpl&>(*cmdb).pushBackNewCommand<CreateOqCommand>(impl);

+ 3 - 7
src/anki/gr/gl/RenderingThread.cpp

@@ -30,6 +30,9 @@ public:
 
 	ANKI_USE_RESULT Error operator()(GlState&)
 	{
+		// Make sure that all GPU and CPU work is done
+		glFlush();
+		glFinish();
 		m_renderingThread->m_syncBarrier.wait();
 		return Error::NONE;
 	}
@@ -131,13 +134,6 @@ void RenderingThread::flushCommandBuffer(CommandBufferPtr cmdb, FencePtr* fence)
 	}
 }
 
-void RenderingThread::finishCommandBuffer(CommandBufferPtr commands)
-{
-	flushCommandBuffer(commands, nullptr);
-
-	syncClientServer();
-}
-
 void RenderingThread::start()
 {
 	ANKI_ASSERT(m_tail == 0 && m_head == 0);

+ 1 - 0
src/anki/gr/gl/Sampler.cpp

@@ -40,6 +40,7 @@ Sampler* Sampler::newInstance(GrManager* manager, const SamplerInitInfo& init)
 	};
 
 	SamplerImpl* impl = manager->getAllocator().newInstance<SamplerImpl>(manager);
+	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
 
 	CommandBufferPtr cmdb = manager->newCommandBuffer(CommandBufferInitInfo());
 	static_cast<CommandBufferImpl&>(*cmdb).pushBackNewCommand<CreateSamplerCommand>(impl, init);

+ 7 - 5
src/anki/gr/gl/Shader.cpp

@@ -17,12 +17,10 @@ Shader* Shader::newInstance(GrManager* manager, const ShaderInitInfo& init)
 	{
 	public:
 		ShaderPtr m_shader;
-		ShaderType m_type;
 		StringAuto m_source;
 
-		ShaderCreateCommand(Shader* shader, ShaderType type, CString source, const CommandBufferAllocator<U8>& alloc)
+		ShaderCreateCommand(Shader* shader, CString source, const CommandBufferAllocator<U8>& alloc)
 			: m_shader(shader)
-			, m_type(type)
 			, m_source(alloc)
 		{
 			m_source.create(source);
@@ -32,7 +30,7 @@ Shader* Shader::newInstance(GrManager* manager, const ShaderInitInfo& init)
 		{
 			ShaderImpl& impl = static_cast<ShaderImpl&>(*m_shader);
 
-			Error err = impl.init(m_type, m_source.toCString());
+			Error err = impl.init(m_source.toCString());
 
 			GlObject::State oldState =
 				impl.setStateAtomically((err) ? GlObject::State::ERROR : GlObject::State::CREATED);
@@ -46,13 +44,17 @@ Shader* Shader::newInstance(GrManager* manager, const ShaderInitInfo& init)
 	ANKI_ASSERT(!init.m_source.isEmpty() && init.m_source.getLength() > 0);
 
 	ShaderImpl* impl = manager->getAllocator().newInstance<ShaderImpl>(manager);
+	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
+
+	// Need to pre-init because some funcs ask for members and we don't want to serialize
+	impl->preInit(init);
 
 	// Copy source to the command buffer
 	CommandBufferPtr cmdb = manager->newCommandBuffer(CommandBufferInitInfo());
 	CommandBufferImpl& cmdbimpl = static_cast<CommandBufferImpl&>(*cmdb);
 	CommandBufferAllocator<U8> alloc = cmdbimpl.getInternalAllocator();
 
-	cmdbimpl.pushBackNewCommand<ShaderCreateCommand>(impl, init.m_shaderType, init.m_source, alloc);
+	cmdbimpl.pushBackNewCommand<ShaderCreateCommand>(impl, init.m_source, alloc);
 	cmdbimpl.flush();
 
 	return impl;

+ 3 - 4
src/anki/gr/gl/ShaderImpl.cpp

@@ -54,7 +54,7 @@ ShaderImpl::~ShaderImpl()
 	destroyDeferred(getManager(), deleteShaders);
 }
 
-Error ShaderImpl::init(ShaderType type, const CString& source)
+Error ShaderImpl::init(const CString& source)
 {
 	ANKI_ASSERT(source);
 	ANKI_ASSERT(!isCreated());
@@ -73,8 +73,7 @@ Error ShaderImpl::init(ShaderType type, const CString& source)
 		"ANKI_FRAGMENT_SHADER",
 		"ANKI_COMPUTE_SHADER"}};
 
-	m_type = type;
-	m_glType = gltype[U(type)];
+	m_glType = gltype[U(m_shaderType)];
 
 	// 1) Append some things in the source string
 	//
@@ -98,7 +97,7 @@ Error ShaderImpl::init(ShaderType type, const CString& source)
 		version,
 		versionType,
 		&GPU_VENDOR_STR[static_cast<const GrManagerImpl&>(getManager()).getState().m_gpu][0],
-		shaderName[U(type)],
+		shaderName[U(m_shaderType)],
 		MAX_UNIFORM_BUFFER_BINDINGS,
 		MAX_STORAGE_BUFFER_BINDINGS,
 		MAX_TEXTURE_BINDINGS,

+ 7 - 7
src/anki/gr/gl/ShaderImpl.h

@@ -13,17 +13,15 @@ namespace anki
 
 // Forward
 class CString;
-class String;
 
 /// @addtogroup opengl
 /// @{
 
-/// Shader program. It only contains a single shader and it can be combined with other programs in a program pipiline.
+/// Shader program implementation.
 class ShaderImpl final : public Shader, public GlObject
 {
 public:
 	GLenum m_glType = 0;
-	ShaderType m_type;
 
 	ShaderImpl(GrManager* manager)
 		: Shader(manager)
@@ -32,10 +30,12 @@ public:
 
 	~ShaderImpl();
 
-	/// Create the shader.
-	/// @param shaderType The type of the shader in the program
-	/// @param source The shader's source
-	ANKI_USE_RESULT Error init(ShaderType shaderType, const CString& source);
+	void preInit(const ShaderInitInfo& init)
+	{
+		m_shaderType = init.m_shaderType;
+	}
+
+	ANKI_USE_RESULT Error init(const CString& source);
 };
 /// @}
 

+ 4 - 1
src/anki/gr/gl/ShaderProgram.cpp

@@ -34,7 +34,7 @@ ShaderProgram* ShaderProgram::newInstance(GrManager* manager, const ShaderProgra
 			ShaderPtr comp)
 			: m_prog(prog)
 			, m_vert(vert)
-			, m_tessc(tesse)
+			, m_tessc(tessc)
 			, m_tesse(tesse)
 			, m_geom(geom)
 			, m_frag(frag)
@@ -55,7 +55,10 @@ ShaderProgram* ShaderProgram::newInstance(GrManager* manager, const ShaderProgra
 		}
 	};
 
+	ANKI_ASSERT(init.isValid());
+
 	ShaderProgramImpl* impl = manager->getAllocator().newInstance<ShaderProgramImpl>(manager);
+	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
 
 	CommandBufferPtr cmdb = manager->newCommandBuffer(CommandBufferInitInfo());
 	static_cast<CommandBufferImpl&>(*cmdb).pushBackNewCommand<CreateCommand>(impl,

+ 9 - 16
src/anki/gr/gl/StateTracker.h

@@ -447,26 +447,21 @@ public:
 	class TextureBinding
 	{
 	public:
-		TextureImpl* m_tex = nullptr;
-		SamplerImpl* m_sampler = reinterpret_cast<SamplerImpl*>(0x1);
-		DepthStencilAspectBit m_aspect;
+		U64 m_texViewUuid = 0;
+		U64 m_samplerUuid = 0;
 	};
 
 	Array2d<TextureBinding, MAX_DESCRIPTOR_SETS, MAX_TEXTURE_BINDINGS> m_textures;
 
-	Bool bindTextureAndSampler(U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, DepthStencilAspectBit aspect)
+	Bool bindTextureViewAndSampler(U32 set, U32 binding, const TextureViewPtr& texView, const SamplerPtr& sampler)
 	{
 		TextureBinding& b = m_textures[set][binding];
 
-		TextureImpl* teximpl = static_cast<TextureImpl*>(tex.get());
-		SamplerImpl* samplerimpl = static_cast<SamplerImpl*>(sampler.get());
-
 		Bool dirty;
-		if(b.m_tex != teximpl || b.m_sampler != samplerimpl || b.m_aspect != aspect)
+		if(b.m_texViewUuid != texView->getUuid() || b.m_samplerUuid != sampler->getUuid())
 		{
-			b.m_tex = teximpl;
-			b.m_sampler = samplerimpl;
-			b.m_aspect = aspect;
+			b.m_texViewUuid = texView->getUuid();
+			b.m_samplerUuid = sampler->getUuid();
 			dirty = true;
 		}
 		else
@@ -510,17 +505,15 @@ public:
 	class ImageBinding
 	{
 	public:
-		TextureImpl* m_tex = nullptr;
-		U8 m_level;
+		U64 m_texViewUuid = 0;
 	};
 
 	Array2d<ImageBinding, MAX_DESCRIPTOR_SETS, MAX_IMAGE_BINDINGS> m_images;
 
-	Bool bindImage(U32 set, U32 binding, TexturePtr img, U32 level)
+	Bool bindImage(U32 set, U32 binding, const TextureViewPtr& img)
 	{
 		ImageBinding& b = m_images[set][binding];
-		b.m_tex = static_cast<TextureImpl*>(img.get());
-		b.m_level = level;
+		b.m_texViewUuid = img->getUuid();
 		return true;
 	}
 

+ 1 - 0
src/anki/gr/gl/Texture.cpp

@@ -40,6 +40,7 @@ Texture* Texture::newInstance(GrManager* manager, const TextureInitInfo& init)
 	};
 
 	TextureImpl* impl = manager->getAllocator().newInstance<TextureImpl>(manager);
+	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
 
 	// Need to pre-init because some funcs ask for members and we don't want to serialize
 	impl->preInit(init);

+ 112 - 130
src/anki/gr/gl/TextureImpl.cpp

@@ -9,8 +9,8 @@
 #include <anki/util/Functions.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/gl/GrManagerImpl.h>
+#include <anki/gr/gl/TextureViewImpl.h>
 #include <anki/gr/gl/RenderingThread.h>
-#include <anki/gr/CommandBuffer.h>
 #include <anki/gr/gl/CommandBufferImpl.h>
 
 namespace anki
@@ -50,10 +50,10 @@ class DeleteTextureCommand final : public GlCommand
 {
 public:
 	GLuint m_tex;
-	DynamicArray<GLuint> m_views;
+	HashMap<TextureSubresourceInfo, MicroTextureView> m_views;
 	GrAllocator<U8> m_alloc;
 
-	DeleteTextureCommand(GLuint tex, DynamicArray<GLuint>& views, GrAllocator<U8> alloc)
+	DeleteTextureCommand(GLuint tex, HashMap<TextureSubresourceInfo, MicroTextureView>& views, GrAllocator<U8> alloc)
 		: m_tex(tex)
 		, m_views(std::move(views))
 		, m_alloc(alloc)
@@ -63,16 +63,15 @@ public:
 	Error operator()(GlState& state)
 	{
 		// Delete views
-		if(m_views.getSize() > 0)
+		auto it = m_views.getBegin();
+		auto end = m_views.getEnd();
+		while(it != end)
 		{
-			for(GLuint name : m_views)
-			{
-				if(name != 0)
-				{
-					glDeleteTextures(1, &name);
-				}
-			}
+			const MicroTextureView& view = *it;
+			glDeleteTextures(1, &view.m_glName);
+			++it;
 		}
+
 		m_views.destroy(m_alloc);
 
 		// Delete texture
@@ -96,12 +95,12 @@ TextureImpl::~TextureImpl()
 
 		commands = manager.newCommandBuffer(CommandBufferInitInfo());
 		static_cast<CommandBufferImpl&>(*commands).pushBackNewCommand<DeleteTextureCommand>(
-			m_glName, m_texViews, getAllocator());
+			m_glName, m_viewsMap, getAllocator());
 		static_cast<CommandBufferImpl&>(*commands).flush();
 	}
 	else
 	{
-		DeleteTextureCommand cmd(m_glName, m_texViews, getAllocator());
+		DeleteTextureCommand cmd(m_glName, m_viewsMap, getAllocator());
 		cmd(static_cast<GrManagerImpl&>(manager).getState());
 	}
 
@@ -116,7 +115,7 @@ void TextureImpl::bind() const
 
 void TextureImpl::preInit(const TextureInitInfo& init)
 {
-	ANKI_ASSERT(textureInitInfoValid(init));
+	ANKI_ASSERT(init.isValid());
 
 	m_width = init.m_width;
 	m_height = init.m_height;
@@ -126,15 +125,15 @@ void TextureImpl::preInit(const TextureInitInfo& init)
 	m_texType = init.m_type;
 	m_format = init.m_format;
 
-	convertTextureInformation(init.m_format, m_compressed, m_glFormat, m_internalFormat, m_glType, m_dsAspect);
+	convertTextureInformation(init.m_format, m_compressed, m_glFormat, m_internalFormat, m_glType, m_aspect);
 
 	if(m_target != GL_TEXTURE_3D)
 	{
-		m_mipCount = min<U>(init.m_mipmapsCount, computeMaxMipmapCount2d(m_width, m_height));
+		m_mipCount = min<U>(init.m_mipmapCount, computeMaxMipmapCount2d(m_width, m_height));
 	}
 	else
 	{
-		m_mipCount = min<U>(init.m_mipmapsCount, computeMaxMipmapCount3d(m_width, m_height, m_depth));
+		m_mipCount = min<U>(init.m_mipmapCount, computeMaxMipmapCount3d(m_width, m_height, m_depth));
 	}
 
 	// Surface count
@@ -209,15 +208,16 @@ void TextureImpl::init(const TextureInitInfo& init)
 	ANKI_CHECK_GL_ERROR();
 }
 
-void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, GLuint pbo, PtrSize offset, PtrSize dataSize)
+void TextureImpl::copyFromBuffer(
+	const TextureSubresourceInfo& subresource, GLuint pbo, PtrSize offset, PtrSize dataSize) const
 {
-	checkSurfaceOrVolume(surf);
+	ANKI_ASSERT(isSubresourceGoodForCopyFromBuffer(subresource));
 	ANKI_ASSERT(dataSize > 0);
 
-	U mipmap = surf.m_level;
-	U surfIdx = computeSurfaceIdx(surf);
-	U w = m_width >> mipmap;
-	U h = m_height >> mipmap;
+	const U mipmap = subresource.m_firstMipmap;
+	const U w = m_width >> mipmap;
+	const U h = m_height >> mipmap;
+	const U d = m_depth >> mipmap;
 	ANKI_ASSERT(w > 0);
 	ANKI_ASSERT(h > 0);
 
@@ -238,6 +238,8 @@ void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, GLuint pbo, PtrSi
 		}
 		break;
 	case GL_TEXTURE_CUBE_MAP:
+	{
+		const U surfIdx = computeSurfaceIdx(TextureSurfaceInfo(mipmap, 0, subresource.m_firstFace, 0));
 		if(!m_compressed)
 		{
 			glTexSubImage2D(
@@ -249,8 +251,10 @@ void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, GLuint pbo, PtrSi
 				GL_TEXTURE_CUBE_MAP_POSITIVE_X + surfIdx, mipmap, 0, 0, w, h, m_glFormat, dataSize, ptrOffset);
 		}
 		break;
+	}
 	case GL_TEXTURE_2D_ARRAY:
-	case GL_TEXTURE_3D:
+	{
+		const U surfIdx = computeSurfaceIdx(TextureSurfaceInfo(mipmap, 0, 0, subresource.m_firstLayer));
 		if(!m_compressed)
 		{
 			glTexSubImage3D(m_target, mipmap, 0, 0, surfIdx, w, h, 1, m_glFormat, m_glType, ptrOffset);
@@ -260,6 +264,18 @@ void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, GLuint pbo, PtrSi
 			glCompressedTexSubImage3D(m_target, mipmap, 0, 0, surfIdx, w, h, 1, m_glFormat, dataSize, ptrOffset);
 		}
 		break;
+	}
+	case GL_TEXTURE_3D:
+		ANKI_ASSERT(d > 0);
+		if(!m_compressed)
+		{
+			glTexSubImage3D(m_target, mipmap, 0, 0, 0, w, h, d, m_glFormat, m_glType, ptrOffset);
+		}
+		else
+		{
+			glCompressedTexSubImage3D(m_target, mipmap, 0, 0, 0, w, h, d, m_glFormat, dataSize, ptrOffset);
+		}
+		break;
 	default:
 		ANKI_ASSERT(0);
 	}
@@ -268,73 +284,15 @@ void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, GLuint pbo, PtrSi
 	ANKI_CHECK_GL_ERROR();
 }
 
-void TextureImpl::writeVolume(const TextureVolumeInfo& vol, GLuint pbo, PtrSize offset, PtrSize dataSize) const
-{
-	checkSurfaceOrVolume(vol);
-	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();
-	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
-	const void* ptrOffset = numberToPtr<const void*>(offset);
-
-	if(!m_compressed)
-	{
-		glTexSubImage3D(m_target, mipmap, 0, 0, 0, w, h, d, m_glFormat, m_glType, ptrOffset);
-	}
-	else
-	{
-		glCompressedTexSubImage3D(m_target, mipmap, 0, 0, 0, w, h, d, m_glFormat, dataSize, ptrOffset);
-	}
-
-	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
-	ANKI_CHECK_GL_ERROR();
-}
-
-void TextureImpl::generateMipmaps2d(U face, U layer)
+void TextureImpl::generateMipmaps2d(const TextureViewImpl& view) const
 {
-	U surface = computeSurfaceIdx(TextureSurfaceInfo(0, 0, face, layer));
+	ANKI_ASSERT(view.m_tex.get() == this);
+	ANKI_ASSERT(isSubresourceGoodForMipmapGeneration(view.getSubresource()));
 	ANKI_ASSERT(!m_compressed);
 
 	if(m_surfaceCountPerLevel > 1)
 	{
-		// Lazily create the view array
-		if(m_texViews.getSize() == 0)
-		{
-			m_texViews.create(getAllocator(), m_surfaceCountPerLevel);
-			memset(&m_texViews[0], 0, m_texViews.getSize() * sizeof(GLuint));
-		}
-
-		// Lazily create the view
-		if(m_texViews[surface] == 0)
-		{
-			glGenTextures(1, &m_texViews[surface]);
-
-			// Get the target
-			GLenum target = GL_NONE;
-			switch(m_target)
-			{
-			case GL_TEXTURE_CUBE_MAP:
-			case GL_TEXTURE_2D_ARRAY:
-			case GL_TEXTURE_CUBE_MAP_ARRAY:
-				target = GL_TEXTURE_2D;
-				break;
-			default:
-				ANKI_ASSERT(0);
-			}
-
-			glTextureView(m_texViews[surface], target, m_glName, m_internalFormat, 0, m_mipCount, surface, 1);
-		}
-
-		glGenerateTextureMipmap(m_texViews[surface]);
+		glGenerateTextureMipmap(view.m_view.m_glName);
 	}
 	else
 	{
@@ -342,48 +300,14 @@ void TextureImpl::generateMipmaps2d(U face, U layer)
 	}
 }
 
-void TextureImpl::copy(const TextureImpl& src,
-	const TextureSurfaceInfo& srcSurf,
-	const TextureImpl& dest,
-	const TextureSurfaceInfo& destSurf)
-{
-	ANKI_ASSERT(src.m_internalFormat == dest.m_internalFormat);
-	ANKI_ASSERT(src.m_glFormat == dest.m_glFormat);
-	ANKI_ASSERT(src.m_glType == dest.m_glType);
-
-	U width = src.m_width >> srcSurf.m_level;
-	U height = src.m_height >> srcSurf.m_level;
-
-	ANKI_ASSERT(width > 0 && height > 0);
-	ANKI_ASSERT(width == (dest.m_width >> destSurf.m_level) && height == (dest.m_height >> destSurf.m_level));
-
-	U srcSurfaceIdx = src.computeSurfaceIdx(srcSurf);
-	U destSurfaceIdx = dest.computeSurfaceIdx(destSurf);
-
-	glCopyImageSubData(src.getGlName(),
-		src.m_target,
-		srcSurf.m_level,
-		0,
-		0,
-		srcSurfaceIdx,
-		dest.getGlName(),
-		dest.m_target,
-		destSurf.m_level,
-		0,
-		0,
-		destSurfaceIdx,
-		width,
-		height,
-		1);
-}
-
-void TextureImpl::clear(const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectBit aspect)
+void TextureImpl::clear(const TextureSubresourceInfo& subresource, const ClearValue& clearValue) const
 {
 	ANKI_ASSERT(isCreated());
-	ANKI_ASSERT(surf.m_level < m_mipCount);
-	ANKI_ASSERT((aspect & m_dsAspect) == aspect);
+	ANKI_ASSERT(isSubresourceValid(subresource));
+	ANKI_ASSERT(m_texType != TextureType::_3D && "TODO");
 
 	// Find the aspect to clear
+	const DepthStencilAspectBit aspect = subresource.m_depthStencilAspect;
 	GLenum format;
 	if(aspect == DepthStencilAspectBit::DEPTH)
 	{
@@ -405,17 +329,26 @@ void TextureImpl::clear(const TextureSurfaceInfo& surf, const ClearValue& clearV
 		format = m_glFormat;
 	}
 
-	U surfaceIdx = computeSurfaceIdx(surf);
-	U width = m_width >> surf.m_level;
-	U height = m_height >> surf.m_level;
+	for(U mip = subresource.m_firstMipmap; mip < subresource.m_firstMipmap + subresource.m_mipmapCount; ++mip)
+	{
+		for(U face = subresource.m_firstFace; face < subresource.m_firstFace + subresource.m_faceCount; ++face)
+		{
+			for(U layer = subresource.m_firstLayer; layer < subresource.m_firstLayer + subresource.m_layerCount;
+				++layer)
+			{
+				const U surfaceIdx = computeSurfaceIdx(TextureSurfaceInfo(mip, 0, face, layer));
+				const U width = m_width >> mip;
+				const U height = m_height >> mip;
 
-	glClearTexSubImage(
-		m_glName, surf.m_level, 0, 0, surfaceIdx, width, height, 1, format, GL_FLOAT, &clearValue.m_colorf[0]);
+				glClearTexSubImage(
+					m_glName, mip, 0, 0, surfaceIdx, width, height, 1, format, GL_FLOAT, &clearValue.m_colorf[0]);
+			}
+		}
+	}
 }
 
 U TextureImpl::computeSurfaceIdx(const TextureSurfaceInfo& surf) const
 {
-	checkSurfaceOrVolume(surf);
 	U out;
 
 	if(m_target == GL_TEXTURE_3D)
@@ -433,4 +366,53 @@ U TextureImpl::computeSurfaceIdx(const TextureSurfaceInfo& surf) const
 	return out;
 }
 
+MicroTextureView TextureImpl::getOrCreateView(const TextureSubresourceInfo& subresource) const
+{
+	LockGuard<Mutex> lock(m_viewsMapMtx);
+	auto it = m_viewsMap.find(subresource);
+
+	if(it != m_viewsMap.getEnd())
+	{
+		return *it;
+	}
+	else
+	{
+		// Create a new view
+
+		// Compute the new target if needed
+		const TextureType newTexType = computeNewTexTypeOfSubresource(subresource);
+		GLenum glTarget = m_target;
+		if(newTexType == TextureType::_2D)
+		{
+			// Change that anyway
+			glTarget = GL_TEXTURE_2D;
+		}
+
+		const U firstSurf = computeSurfaceIdx(
+			TextureSurfaceInfo(subresource.m_firstMipmap, 0, subresource.m_firstFace, subresource.m_firstLayer));
+		const U lastSurf = computeSurfaceIdx(TextureSurfaceInfo(subresource.m_firstMipmap,
+			0,
+			subresource.m_firstFace + subresource.m_faceCount - 1,
+			subresource.m_firstLayer + subresource.m_layerCount - 1));
+		ANKI_ASSERT(firstSurf <= lastSurf);
+
+		MicroTextureView view;
+		view.m_aspect = subresource.m_depthStencilAspect;
+
+		glGenTextures(1, &view.m_glName);
+		glTextureView(view.m_glName,
+			glTarget,
+			m_glName,
+			m_internalFormat,
+			subresource.m_firstMipmap,
+			subresource.m_mipmapCount,
+			firstSurf,
+			lastSurf - firstSurf + 1);
+
+		m_viewsMap.emplace(getAllocator(), subresource, view);
+
+		return view;
+	}
+}
+
 } // end namespace anki

+ 26 - 29
src/anki/gr/gl/TextureImpl.h

@@ -8,7 +8,7 @@
 #include <anki/gr/Texture.h>
 #include <anki/gr/gl/GlObject.h>
 #include <anki/gr/common/Misc.h>
-#include <anki/util/DynamicArray.h>
+#include <anki/util/HashMap.h>
 
 namespace anki
 {
@@ -16,19 +16,24 @@ namespace anki
 /// @addtogroup opengl
 /// @{
 
+/// Small wrapper on top of a texture view and its aspect.
+struct MicroTextureView
+{
+	GLuint m_glName;
+	DepthStencilAspectBit m_aspect;
+};
+
 /// Texture container.
 class TextureImpl final : public Texture, public GlObject
 {
 public:
-	GLenum m_target = GL_NONE; ///< GL_TEXTURE_2D, GL_TEXTURE_3D... etc
-	GLenum m_internalFormat = GL_NONE; ///< GL_COMPRESSED_RED, GL_RGB16 etc
+	GLenum m_target = GL_NONE; ///< GL_TEXTURE_2D, GL_TEXTURE_3D... etc.
+	GLenum m_internalFormat = GL_NONE; ///< GL_COMPRESSED_RED, GL_RGB16 etc.
 	GLenum m_glFormat = GL_NONE;
 	GLenum m_glType = GL_NONE;
 	U32 m_surfaceCountPerLevel = 0;
-	U8 m_faceCount = 0; ///< 6 for cubes and 1 for the rest
+	U8 m_faceCount = 0; ///< 6 for cubes and 1 for the rest.
 	Bool8 m_compressed = false;
-	DynamicArray<GLuint> m_texViews; ///< Temp views for gen mips.
-	DepthStencilAspectBit m_dsAspect = DepthStencilAspectBit::NONE;
 
 	TextureImpl(GrManager* manager)
 		: Texture(manager)
@@ -37,17 +42,6 @@ public:
 
 	~TextureImpl();
 
-	void checkSurfaceOrVolume(const TextureSurfaceInfo& surf) const
-	{
-		checkTextureSurface(m_texType, m_depth, m_mipCount, m_layerCount, surf);
-	}
-
-	void checkSurfaceOrVolume(const TextureVolumeInfo& vol) const
-	{
-		ANKI_ASSERT(m_texType == TextureType::_3D);
-		ANKI_ASSERT(vol.m_level < m_mipCount);
-	}
-
 	/// Init some stuff.
 	void preInit(const TextureInitInfo& init);
 
@@ -55,25 +49,28 @@ public:
 	void init(const TextureInitInfo& init);
 
 	/// Write texture data.
-	void writeSurface(const TextureSurfaceInfo& surf, GLuint pbo, PtrSize offset, PtrSize dataSize);
-
-	/// Write texture data.
-	void writeVolume(const TextureVolumeInfo& vol, GLuint pbo, PtrSize offset, PtrSize dataSize) const;
+	void copyFromBuffer(const TextureSubresourceInfo& subresource, GLuint pbo, PtrSize offset, PtrSize dataSize) const;
 
 	/// Generate mipmaps.
-	void generateMipmaps2d(U face, U layer);
-
-	/// Copy a single surface from one texture to another.
-	static void copy(const TextureImpl& src,
-		const TextureSurfaceInfo& srcSurf,
-		const TextureImpl& dest,
-		const TextureSurfaceInfo& destSurf);
+	void generateMipmaps2d(const TextureViewImpl& view) const;
 
 	void bind() const;
 
-	void clear(const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectBit aspect);
+	void clear(const TextureSubresourceInfo& subresource, const ClearValue& clearValue) const;
 
 	U computeSurfaceIdx(const TextureSurfaceInfo& surf) const;
+
+	MicroTextureView getOrCreateView(const TextureSubresourceInfo& subresource) const;
+
+	TextureType computeNewTexTypeOfSubresource(const TextureSubresourceInfo& subresource) const
+	{
+		ANKI_ASSERT(isSubresourceValid(subresource));
+		return (textureTypeIsCube(m_texType) && subresource.m_faceCount != 6) ? TextureType::_2D : m_texType;
+	}
+
+private:
+	mutable HashMap<TextureSubresourceInfo, MicroTextureView> m_viewsMap;
+	mutable Mutex m_viewsMapMtx;
 };
 /// @}
 

+ 53 - 0
src/anki/gr/gl/TextureView.cpp

@@ -0,0 +1,53 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/TextureView.h>
+#include <anki/gr/gl/TextureViewImpl.h>
+#include <anki/gr/gl/CommandBufferImpl.h>
+#include <anki/gr/GrManager.h>
+
+namespace anki
+{
+
+TextureView* TextureView::newInstance(GrManager* manager, const TextureViewInitInfo& init)
+{
+	class CreateTextureViewCommand final : public GlCommand
+	{
+	public:
+		TextureViewPtr m_view;
+
+		CreateTextureViewCommand(TextureView* view)
+			: m_view(view)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			TextureViewImpl& impl = static_cast<TextureViewImpl&>(*m_view);
+
+			impl.init();
+
+			GlObject::State oldState = impl.setStateAtomically(GlObject::State::CREATED);
+			ANKI_ASSERT(oldState == GlObject::State::TO_BE_CREATED);
+			(void)oldState;
+
+			return Error::NONE;
+		}
+	};
+
+	TextureViewImpl* impl = manager->getAllocator().newInstance<TextureViewImpl>(manager);
+	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
+
+	// Need to pre-init because some funcs ask for members and we don't want to serialize
+	impl->preInit(init);
+
+	CommandBufferPtr cmdb = manager->newCommandBuffer(CommandBufferInitInfo());
+	static_cast<CommandBufferImpl&>(*cmdb).pushBackNewCommand<CreateTextureViewCommand>(impl);
+	static_cast<CommandBufferImpl&>(*cmdb).flush();
+
+	return impl;
+}
+
+} // end namespace anki

+ 25 - 0
src/anki/gr/gl/TextureViewImpl.cpp

@@ -0,0 +1,25 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/gl/TextureViewImpl.h>
+
+namespace anki
+{
+
+void TextureViewImpl::preInit(const TextureViewInitInfo& inf)
+{
+	ANKI_ASSERT(inf.isValid());
+
+	// Store some stuff
+	m_subresource = inf;
+
+	m_tex = inf.m_texture;
+	const TextureImpl& tex = static_cast<const TextureImpl&>(*m_tex);
+	ANKI_ASSERT(tex.isSubresourceValid(inf));
+
+	m_texType = tex.computeNewTexTypeOfSubresource(inf);
+}
+
+} // end namespace anki

+ 45 - 0
src/anki/gr/gl/TextureViewImpl.h

@@ -0,0 +1,45 @@
+// 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/TextureView.h>
+#include <anki/gr/gl/GlObject.h>
+#include <anki/gr/gl/TextureImpl.h>
+
+namespace anki
+{
+
+/// @addtogroup opengl
+/// @{
+
+/// Texture view implementation.
+class TextureViewImpl final : public TextureView, public GlObject
+{
+public:
+	MicroTextureView m_view = {};
+	TexturePtr m_tex; ///< Hold a reference.
+
+	TextureViewImpl(GrManager* manager)
+		: TextureView(manager)
+	{
+	}
+
+	~TextureViewImpl()
+	{
+		m_glName = 0;
+	}
+
+	void preInit(const TextureViewInitInfo& inf);
+
+	void init()
+	{
+		m_view = static_cast<const TextureImpl&>(*m_tex).getOrCreateView(getSubresource());
+		m_glName = m_view.m_glName;
+	}
+};
+/// @}
+
+} // end namespace anki

+ 19 - 46
src/anki/gr/vulkan/CommandBuffer.cpp

@@ -37,17 +37,6 @@ void CommandBuffer::flush(FencePtr* fence)
 	}
 }
 
-void CommandBuffer::finish()
-{
-	ANKI_VK_SELF(CommandBufferImpl);
-	self.endRecording();
-
-	if(!self.isSecondLevel())
-	{
-		self.getGrManagerImpl().finishCommandBuffer(CommandBufferPtr(this));
-	}
-}
-
 void CommandBuffer::bindVertexBuffer(
 	U32 binding, BufferPtr buff, PtrSize offset, PtrSize stride, VertexStepRate stepRate)
 {
@@ -174,10 +163,10 @@ void CommandBuffer::setBlendOperation(U32 attachment, BlendOperation funcRgb, Bl
 }
 
 void CommandBuffer::bindTextureAndSampler(
-	U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, TextureUsageBit usage, DepthStencilAspectBit aspect)
+	U32 set, U32 binding, TextureViewPtr texView, SamplerPtr sampler, TextureUsageBit usage)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.bindTextureAndSampler(set, binding, tex, sampler, usage, aspect);
+	self.bindTextureAndSamplerInternal(set, binding, texView, sampler, usage);
 }
 
 void CommandBuffer::bindUniformBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range)
@@ -192,10 +181,10 @@ void CommandBuffer::bindStorageBuffer(U32 set, U32 binding, BufferPtr buff, PtrS
 	self.bindStorageBuffer(set, binding, buff, offset, range);
 }
 
-void CommandBuffer::bindImage(U32 set, U32 binding, TexturePtr img, U32 level)
+void CommandBuffer::bindImage(U32 set, U32 binding, TextureViewPtr img)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.bindImage(set, binding, img, level);
+	self.bindImage(set, binding, img);
 }
 
 void CommandBuffer::bindTextureBuffer(
@@ -259,55 +248,32 @@ void CommandBuffer::dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupC
 	self.dispatchCompute(groupCountX, groupCountY, groupCountZ);
 }
 
-void CommandBuffer::generateMipmaps2d(TexturePtr tex, U face, U layer)
+void CommandBuffer::generateMipmaps2d(TextureViewPtr texView)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.generateMipmaps2d(tex, face, layer);
-}
-
-void CommandBuffer::generateMipmaps3d(TexturePtr tex)
-{
-	ANKI_ASSERT(!"TODO");
+	self.generateMipmaps2d(texView);
 }
 
-void CommandBuffer::copyTextureSurfaceToTextureSurface(
-	TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf)
+void CommandBuffer::generateMipmaps3d(TextureViewPtr texView)
 {
 	ANKI_ASSERT(!"TODO");
 }
 
-void CommandBuffer::copyTextureVolumeToTextureVolume(
-	TexturePtr src, const TextureVolumeInfo& srcVol, TexturePtr dest, const TextureVolumeInfo& destVol)
+void CommandBuffer::blitTextureViews(TextureViewPtr srcView, TextureViewPtr destView)
 {
 	ANKI_ASSERT(!"TODO");
 }
 
-void CommandBuffer::clearTextureSurface(
-	TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectBit aspect)
+void CommandBuffer::clearTextureView(TextureViewPtr texView, const ClearValue& clearValue)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.clearTextureSurface(tex, surf, clearValue, aspect);
+	self.clearTextureView(texView, clearValue);
 }
 
-void CommandBuffer::clearTextureVolume(
-	TexturePtr tex, const TextureVolumeInfo& vol, const ClearValue& clearValue, DepthStencilAspectBit aspect)
+void CommandBuffer::copyBufferToTextureView(BufferPtr buff, PtrSize offset, PtrSize range, TextureViewPtr texView)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.clearTextureVolume(tex, vol, clearValue, aspect);
-}
-
-void CommandBuffer::copyBufferToTextureSurface(
-	BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureSurfaceInfo& surf)
-{
-	ANKI_VK_SELF(CommandBufferImpl);
-	self.copyBufferToTextureSurface(buff, offset, range, tex, surf);
-}
-
-void CommandBuffer::copyBufferToTextureVolume(
-	BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureVolumeInfo& vol)
-{
-	ANKI_VK_SELF(CommandBufferImpl);
-	self.copyBufferToTextureVolume(buff, offset, range, tex, vol);
+	self.copyBufferToTextureViewInternal(buff, offset, range, texView);
 }
 
 void CommandBuffer::fillBuffer(BufferPtr buff, PtrSize offset, PtrSize size, U32 value)
@@ -329,6 +295,13 @@ void CommandBuffer::copyBufferToBuffer(
 	self.copyBufferToBuffer(src, srcOffset, dst, dstOffset, range);
 }
 
+void CommandBuffer::setTextureBarrier(
+	TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage, const TextureSubresourceInfo& subresource)
+{
+	ANKI_VK_SELF(CommandBufferImpl);
+	self.setTextureBarrier(tex, prevUsage, nextUsage, subresource);
+}
+
 void CommandBuffer::setTextureSurfaceBarrier(
 	TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage, const TextureSurfaceInfo& surf)
 {

+ 99 - 167
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -76,16 +76,16 @@ void CommandBufferImpl::beginRecording()
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
 		for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
 		{
-			colAttLayouts[i] = static_cast<const TextureImpl&>(
-				*impl.getColorAttachment(
-					i)).computeLayout(m_colorAttachmentUsages[i], 0);
+			const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*impl.getColorAttachment(i));
+			colAttLayouts[i] =
+				static_cast<const TextureImpl&>(*view.m_tex).computeLayout(m_colorAttachmentUsages[i], 0);
 		}
 
 		VkImageLayout dsAttLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
 		if(impl.hasDepthStencil())
 		{
-			dsAttLayout = static_cast<const TextureImpl&>(*impl.getDepthStencilAttachment())
-							  .computeLayout(m_depthStencilAttachmentUsage, 0);
+			const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*impl.getDepthStencilAttachment());
+			dsAttLayout = static_cast<const TextureImpl&>(*view.m_tex).computeLayout(m_depthStencilAttachmentUsage, 0);
 		}
 
 		inheritance.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
@@ -178,16 +178,16 @@ void CommandBufferImpl::beginRenderPassInternal()
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
 		for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
 		{
-			colAttLayouts[i] = static_cast<const TextureImpl&>(
-				*impl.getColorAttachment(
-					i)).computeLayout(m_colorAttachmentUsages[i], 0);
+			const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*impl.getColorAttachment(i));
+			colAttLayouts[i] =
+				static_cast<const TextureImpl&>(*view.m_tex).computeLayout(m_colorAttachmentUsages[i], 0);
 		}
 
 		VkImageLayout dsAttLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
 		if(impl.hasDepthStencil())
 		{
-			dsAttLayout = static_cast<const TextureImpl&>(*impl.getDepthStencilAttachment())
-							  .computeLayout(m_depthStencilAttachmentUsage, 0);
+			const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*impl.getDepthStencilAttachment());
+			dsAttLayout = static_cast<const TextureImpl&>(*view.m_tex).computeLayout(m_depthStencilAttachmentUsage, 0);
 		}
 
 		bi.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
@@ -295,20 +295,27 @@ void CommandBufferImpl::endRecording()
 #endif
 }
 
-void CommandBufferImpl::generateMipmaps2d(TexturePtr tex, U face, U layer)
+void CommandBufferImpl::generateMipmaps2d(TextureViewPtr texView)
 {
 	commandCommon();
 
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	ANKI_ASSERT(impl.getTextureType() != TextureType::_3D && "Not for 3D");
+	const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*texView);
+	const TextureImpl& tex = static_cast<const TextureImpl&>(*view.m_tex);
+	ANKI_ASSERT(tex.getTextureType() != TextureType::_3D && "Not for 3D");
+	ANKI_ASSERT(tex.isSubresourceGoodForMipmapGeneration(view.getSubresource()));
 
-	for(U i = 0; i < impl.getMipmapCount() - 1u; ++i)
+	const DepthStencilAspectBit aspect = view.getSubresource().m_depthStencilAspect;
+	const U face = view.getSubresource().m_firstFace;
+	const U layer = view.getSubresource().m_firstLayer;
+
+	for(U i = 0; i < tex.getMipmapCount() - 1u; ++i)
 	{
 		// Transition source
 		if(i > 0)
 		{
 			VkImageSubresourceRange range;
-			impl.computeSubResourceRange(TextureSurfaceInfo(i, 0, face, layer), impl.m_akAspect, range);
+			tex.computeVkImageSubresourceRange(
+				TextureSubresourceInfo(TextureSurfaceInfo(i, 0, face, layer), aspect), range);
 
 			setImageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
 				VK_ACCESS_TRANSFER_WRITE_BIT,
@@ -316,14 +323,15 @@ void CommandBufferImpl::generateMipmaps2d(TexturePtr tex, U face, U layer)
 				VK_PIPELINE_STAGE_TRANSFER_BIT,
 				VK_ACCESS_TRANSFER_READ_BIT,
 				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-				impl.m_imageHandle,
+				tex.m_imageHandle,
 				range);
 		}
 
 		// Transition destination
 		{
 			VkImageSubresourceRange range;
-			impl.computeSubResourceRange(TextureSurfaceInfo(i + 1, 0, face, layer), impl.m_akAspect, range);
+			tex.computeVkImageSubresourceRange(
+				TextureSubresourceInfo(TextureSurfaceInfo(i + 1, 0, face, layer), aspect), range);
 
 			setImageBarrier(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
 				0,
@@ -331,21 +339,21 @@ void CommandBufferImpl::generateMipmaps2d(TexturePtr tex, U face, U layer)
 				VK_PIPELINE_STAGE_TRANSFER_BIT,
 				VK_ACCESS_TRANSFER_WRITE_BIT,
 				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-				impl.m_imageHandle,
+				tex.m_imageHandle,
 				range);
 		}
 
 		// Setup the blit struct
-		I32 srcWidth = impl.getWidth() >> i;
-		I32 srcHeight = impl.getHeight() >> i;
+		I32 srcWidth = tex.getWidth() >> i;
+		I32 srcHeight = tex.getHeight() >> i;
 
-		I32 dstWidth = impl.getWidth() >> (i + 1);
-		I32 dstHeight = impl.getHeight() >> (i + 1);
+		I32 dstWidth = tex.getWidth() >> (i + 1);
+		I32 dstHeight = tex.getHeight() >> (i + 1);
 
 		ANKI_ASSERT(srcWidth > 0 && srcHeight > 0 && dstWidth > 0 && dstHeight > 0);
 
 		U vkLayer = 0;
-		switch(impl.getTextureType())
+		switch(tex.getTextureType())
 		{
 		case TextureType::_2D:
 		case TextureType::_2D_ARRAY:
@@ -362,14 +370,14 @@ void CommandBufferImpl::generateMipmaps2d(TexturePtr tex, U face, U layer)
 		}
 
 		VkImageBlit blit;
-		blit.srcSubresource.aspectMask = impl.m_aspect;
+		blit.srcSubresource.aspectMask = convertImageAspect(aspect);
 		blit.srcSubresource.baseArrayLayer = vkLayer;
 		blit.srcSubresource.layerCount = 1;
 		blit.srcSubresource.mipLevel = i;
 		blit.srcOffsets[0] = {0, 0, 0};
 		blit.srcOffsets[1] = {srcWidth, srcHeight, 1};
 
-		blit.dstSubresource.aspectMask = impl.m_aspect;
+		blit.dstSubresource.aspectMask = convertImageAspect(aspect);
 		blit.dstSubresource.baseArrayLayer = vkLayer;
 		blit.dstSubresource.layerCount = 1;
 		blit.dstSubresource.mipLevel = i + 1;
@@ -377,18 +385,18 @@ void CommandBufferImpl::generateMipmaps2d(TexturePtr tex, U face, U layer)
 		blit.dstOffsets[1] = {dstWidth, dstHeight, 1};
 
 		ANKI_CMD(vkCmdBlitImage(m_handle,
-					 impl.m_imageHandle,
+					 tex.m_imageHandle,
 					 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-					 impl.m_imageHandle,
+					 tex.m_imageHandle,
 					 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
 					 1,
 					 &blit,
-					 (impl.m_depthStencil) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR),
+					 (!!aspect) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR),
 			ANY_OTHER_COMMAND);
 	}
 
 	// Hold the reference
-	m_microCmdb->pushObjectRef(tex);
+	m_microCmdb->pushObjectRef(texView);
 }
 
 void CommandBufferImpl::flushBarriers()
@@ -650,135 +658,46 @@ void CommandBufferImpl::flushWriteQueryResults()
 	m_writeQueryAtomCount = 0;
 }
 
-void CommandBufferImpl::copyBufferToTextureSurface(
-	BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureSurfaceInfo& surf)
+void CommandBufferImpl::copyBufferToTextureViewInternal(
+	BufferPtr buff, PtrSize offset, PtrSize range, TextureViewPtr texView)
 {
 	commandCommon();
 
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	impl.checkSurfaceOrVolume(surf);
-	ANKI_ASSERT(impl.usageValid(TextureUsageBit::TRANSFER_DESTINATION));
+	const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*texView);
+	const TextureImpl& tex = static_cast<const TextureImpl&>(*view.m_tex);
+	ANKI_ASSERT(tex.usageValid(TextureUsageBit::TRANSFER_DESTINATION));
+	ANKI_ASSERT(tex.isSubresourceGoodForCopyFromBuffer(view.getSubresource()));
 	const VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+	const Bool is3D = tex.getTextureType() == TextureType::_3D;
+	const VkImageAspectFlags aspect = convertImageAspect(view.getSubresource().m_depthStencilAspect);
 
-	if(!impl.m_workarounds)
-	{
-		U width = impl.getWidth() >> surf.m_level;
-		U height = impl.getHeight() >> surf.m_level;
-		ANKI_ASSERT(range == computeSurfaceSize(width, height, impl.getPixelFormat()));
+	const TextureSurfaceInfo surf(
+		view.getSubresource().m_firstMipmap, view.getSubresource().m_firstFace, 0, view.getSubresource().m_firstLayer);
+	const TextureVolumeInfo vol(view.getSubresource().m_firstMipmap);
 
-		// Copy
-		VkBufferImageCopy region;
-		region.imageSubresource.aspectMask = impl.m_aspect;
-		region.imageSubresource.baseArrayLayer = impl.computeVkArrayLayer(surf);
-		region.imageSubresource.layerCount = 1;
-		region.imageSubresource.mipLevel = surf.m_level;
-		region.imageOffset = {0, 0, I32(surf.m_depth)};
-		region.imageExtent.width = width;
-		region.imageExtent.height = height;
-		region.imageExtent.depth = 1;
-		region.bufferOffset = offset;
-		region.bufferImageHeight = 0;
-		region.bufferRowLength = 0;
+	// Compute the sizes of the mip
+	const U width = tex.getWidth() >> surf.m_level;
+	const U height = tex.getHeight() >> surf.m_level;
+	ANKI_ASSERT(width && height);
+	const U depth = (is3D) ? (tex.getDepth() >> surf.m_level) : 1u;
 
-		ANKI_CMD(
-			vkCmdCopyBufferToImage(
-				m_handle, static_cast<const BufferImpl&>(*buff).getHandle(), impl.m_imageHandle, layout, 1, &region),
-			ANY_OTHER_COMMAND);
-	}
-	else if(!!(impl.m_workarounds & TextureImplWorkaround::R8G8B8_TO_R8G8B8A8))
+	if(!tex.m_workarounds)
 	{
-		U width = impl.getWidth() >> surf.m_level;
-		U height = impl.getHeight() >> surf.m_level;
-
-		// Create a new shadow buffer
-		const PtrSize shadowSize =
-			computeSurfaceSize(width, height, PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM));
-		BufferPtr shadow = getManager().newBuffer(
-			BufferInitInfo(shadowSize, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::NONE, "Workaround"));
-		const VkBuffer shadowHandle = static_cast<const BufferImpl&>(*shadow).getHandle();
-		m_microCmdb->pushObjectRef(shadow);
-
-		// Create the copy regions
-		DynamicArrayAuto<VkBufferCopy> copies(m_alloc);
-		copies.create(width * height);
-		U count = 0;
-		for(U x = 0; x < width; ++x)
+		if(!is3D)
 		{
-			for(U y = 0; y < height; ++y)
-			{
-				VkBufferCopy& c = copies[count++];
-				c.srcOffset = (y * width + x) * 3 + offset;
-				c.dstOffset = (y * width + x) * 4 + 0;
-				c.size = 3;
-			}
+			ANKI_ASSERT(range == computeSurfaceSize(width, height, tex.getPixelFormat()));
+		}
+		else
+		{
+			ANKI_ASSERT(range == computeVolumeSize(width, height, depth, tex.getPixelFormat()));
 		}
-
-		// Copy buffer to buffer
-		ANKI_CMD(vkCmdCopyBuffer(m_handle,
-					 static_cast<const BufferImpl&>(*buff).getHandle(),
-					 shadowHandle,
-					 copies.getSize(),
-					 &copies[0]),
-			ANY_OTHER_COMMAND);
-
-		// Set barrier
-		setBufferBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
-			VK_ACCESS_TRANSFER_WRITE_BIT,
-			VK_PIPELINE_STAGE_TRANSFER_BIT,
-			VK_ACCESS_TRANSFER_READ_BIT,
-			0,
-			shadowSize,
-			static_cast<const BufferImpl&>(*shadow).getHandle());
-
-		// Do the copy to the image
-		VkBufferImageCopy region;
-		region.imageSubresource.aspectMask = impl.m_aspect;
-		region.imageSubresource.baseArrayLayer = impl.computeVkArrayLayer(surf);
-		region.imageSubresource.layerCount = 1;
-		region.imageSubresource.mipLevel = surf.m_level;
-		region.imageOffset = {0, 0, I32(surf.m_depth)};
-		region.imageExtent.width = width;
-		region.imageExtent.height = height;
-		region.imageExtent.depth = 1;
-		region.bufferOffset = 0;
-		region.bufferImageHeight = 0;
-		region.bufferRowLength = 0;
-
-		ANKI_CMD(
-			vkCmdCopyBufferToImage(m_handle, shadowHandle, impl.m_imageHandle, layout, 1, &region), ANY_OTHER_COMMAND);
-	}
-	else
-	{
-		ANKI_ASSERT(0);
-	}
-
-	m_microCmdb->pushObjectRef(tex);
-	m_microCmdb->pushObjectRef(buff);
-}
-
-void CommandBufferImpl::copyBufferToTextureVolume(
-	BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureVolumeInfo& vol)
-{
-	commandCommon();
-
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	impl.checkSurfaceOrVolume(vol);
-	ANKI_ASSERT(impl.usageValid(TextureUsageBit::TRANSFER_DESTINATION));
-	const VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
-
-	if(!impl.m_workarounds)
-	{
-		U width = impl.getWidth() >> vol.m_level;
-		U height = impl.getHeight() >> vol.m_level;
-		U depth = impl.getDepth() >> vol.m_level;
-		ANKI_ASSERT(range == computeVolumeSize(width, height, depth, impl.getPixelFormat()));
 
 		// Copy
 		VkBufferImageCopy region;
-		region.imageSubresource.aspectMask = impl.m_aspect;
-		region.imageSubresource.baseArrayLayer = impl.computeVkArrayLayer(vol);
+		region.imageSubresource.aspectMask = aspect;
+		region.imageSubresource.baseArrayLayer = (is3D) ? tex.computeVkArrayLayer(vol) : tex.computeVkArrayLayer(surf);
 		region.imageSubresource.layerCount = 1;
-		region.imageSubresource.mipLevel = vol.m_level;
+		region.imageSubresource.mipLevel = surf.m_level;
 		region.imageOffset = {0, 0, 0};
 		region.imageExtent.width = width;
 		region.imageExtent.height = height;
@@ -789,19 +708,15 @@ void CommandBufferImpl::copyBufferToTextureVolume(
 
 		ANKI_CMD(
 			vkCmdCopyBufferToImage(
-				m_handle, static_cast<const BufferImpl&>(*buff).getHandle(), impl.m_imageHandle, layout, 1, &region),
+				m_handle, static_cast<const BufferImpl&>(*buff).getHandle(), tex.m_imageHandle, layout, 1, &region),
 			ANY_OTHER_COMMAND);
 	}
-	else if(!!(impl.m_workarounds & TextureImplWorkaround::R8G8B8_TO_R8G8B8A8))
+	else if(!!(tex.m_workarounds & TextureImplWorkaround::R8G8B8_TO_R8G8B8A8))
 	{
-		// Find the offset to the RGBA staging buff
-		U width = impl.getWidth() >> vol.m_level;
-		U height = impl.getHeight() >> vol.m_level;
-		U depth = impl.getDepth() >> vol.m_level;
-
 		// Create a new shadow buffer
-		const PtrSize shadowSize =
-			computeVolumeSize(width, height, depth, PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM));
+		const PtrSize shadowSize = (is3D)
+			? computeVolumeSize(width, height, depth, PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM))
+			: computeSurfaceSize(width, height, PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM));
 		BufferPtr shadow = getManager().newBuffer(
 			BufferInitInfo(shadowSize, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::NONE, "Workaround"));
 		const VkBuffer shadowHandle = static_cast<const BufferImpl&>(*shadow).getHandle();
@@ -809,17 +724,35 @@ void CommandBufferImpl::copyBufferToTextureVolume(
 
 		// Create the copy regions
 		DynamicArrayAuto<VkBufferCopy> copies(m_alloc);
-		copies.create(width * height * depth);
-		U count = 0;
-		for(U x = 0; x < width; ++x)
+		if(is3D)
 		{
-			for(U y = 0; y < height; ++y)
+			copies.create(width * height * depth);
+			U count = 0;
+			for(U x = 0; x < width; ++x)
 			{
-				for(U d = 0; d < depth; ++d)
+				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 + offset;
+						c.dstOffset = (d * height * width + y * width + x) * 4 + 0;
+						c.size = 3;
+					}
+				}
+			}
+		}
+		else
+		{
+			copies.create(width * height);
+			U count = 0;
+			for(U x = 0; x < width; ++x)
+			{
+				for(U y = 0; y < height; ++y)
 				{
 					VkBufferCopy& c = copies[count++];
-					c.srcOffset = (d * height * width + y * width + x) * 3 + offset;
-					c.dstOffset = (d * height * width + y * width + x) * 4 + 0;
+					c.srcOffset = (y * width + x) * 3 + offset;
+					c.dstOffset = (y * width + x) * 4 + 0;
 					c.size = 3;
 				}
 			}
@@ -844,10 +777,10 @@ void CommandBufferImpl::copyBufferToTextureVolume(
 
 		// Do the copy to the image
 		VkBufferImageCopy region;
-		region.imageSubresource.aspectMask = impl.m_aspect;
-		region.imageSubresource.baseArrayLayer = impl.computeVkArrayLayer(vol);
+		region.imageSubresource.aspectMask = aspect;
+		region.imageSubresource.baseArrayLayer = (is3D) ? tex.computeVkArrayLayer(vol) : tex.computeVkArrayLayer(surf);
 		region.imageSubresource.layerCount = 1;
-		region.imageSubresource.mipLevel = vol.m_level;
+		region.imageSubresource.mipLevel = surf.m_level;
 		region.imageOffset = {0, 0, 0};
 		region.imageExtent.width = width;
 		region.imageExtent.height = height;
@@ -856,16 +789,15 @@ void CommandBufferImpl::copyBufferToTextureVolume(
 		region.bufferImageHeight = 0;
 		region.bufferRowLength = 0;
 
-		ANKI_CMD(vkCmdCopyBufferToImage(
-					 m_handle, shadowHandle, impl.m_imageHandle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region),
-			ANY_OTHER_COMMAND);
+		ANKI_CMD(
+			vkCmdCopyBufferToImage(m_handle, shadowHandle, tex.m_imageHandle, layout, 1, &region), ANY_OTHER_COMMAND);
 	}
 	else
 	{
 		ANKI_ASSERT(0);
 	}
 
-	m_microCmdb->pushObjectRef(tex);
+	m_microCmdb->pushObjectRef(texView);
 	m_microCmdb->pushObjectRef(buff);
 }
 

+ 20 - 23
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -219,26 +219,28 @@ public:
 		m_state.setBlendOperation(attachment, funcRgb, funcA);
 	}
 
-	void bindTextureAndSampler(
-		U32 set, U32 binding, TexturePtr& tex_, SamplerPtr sampler, TextureUsageBit usage, DepthStencilAspectBit aspect)
+	void bindTextureAndSamplerInternal(
+		U32 set, U32 binding, TextureViewPtr& texView, SamplerPtr sampler, TextureUsageBit usage)
 	{
 		commandCommon();
 		const U realBinding = binding;
-		const Texture& tex = *tex_;
-		const TextureImpl& teximpl = static_cast<const TextureImpl&>(tex);
-		ANKI_ASSERT((!teximpl.m_depthStencil || !!aspect) && "Need to set aspect for DS textures");
-		const VkImageLayout lay = teximpl.computeLayout(usage, 0);
-		m_dsetState[set].bindTextureAndSampler(realBinding, &tex, sampler.get(), aspect, lay);
-		m_microCmdb->pushObjectRef(tex_);
+		const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*texView);
+		const TextureImpl& tex = static_cast<const TextureImpl&>(*view.m_tex);
+		ANKI_ASSERT(tex.isSubresourceGoodForSampling(view.getSubresource()));
+		const VkImageLayout lay = tex.computeLayout(usage, 0);
+
+		m_dsetState[set].bindTextureAndSampler(realBinding, &view, sampler.get(), lay);
+
+		m_microCmdb->pushObjectRef(texView);
 		m_microCmdb->pushObjectRef(sampler);
 	}
 
-	void bindImage(U32 set, U32 binding, TexturePtr& img, U32 level)
+	void bindImage(U32 set, U32 binding, TextureViewPtr& img)
 	{
 		commandCommon();
 		const U realBinding =
 			binding + MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS;
-		m_dsetState[set].bindImage(realBinding, img.get(), level);
+		m_dsetState[set].bindImage(realBinding, img.get());
 		m_microCmdb->pushObjectRef(img);
 	}
 
@@ -269,18 +271,19 @@ public:
 
 	void endOcclusionQuery(OcclusionQueryPtr query);
 
-	void generateMipmaps2d(TexturePtr tex, U face, U layer);
-
-	void clearTextureSurface(
-		TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectBit aspect);
+	void generateMipmaps2d(TextureViewPtr texView);
 
-	void clearTextureVolume(
-		TexturePtr tex, const TextureVolumeInfo& volume, const ClearValue& clearValue, DepthStencilAspectBit aspect);
+	void clearTextureView(TextureViewPtr texView, const ClearValue& clearValue);
 
 	void pushSecondLevelCommandBuffer(CommandBufferPtr cmdb);
 
 	void endRecording();
 
+	void setTextureBarrier(TexturePtr tex,
+		TextureUsageBit prevUsage,
+		TextureUsageBit nextUsage,
+		const TextureSubresourceInfo& subresource);
+
 	void setTextureSurfaceBarrier(
 		TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage, const TextureSurfaceInfo& surf);
 
@@ -322,11 +325,7 @@ public:
 		m_microCmdb->pushObjectRef(buff);
 	}
 
-	void copyBufferToTextureSurface(
-		BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureSurfaceInfo& surf);
-
-	void copyBufferToTextureVolume(
-		BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureVolumeInfo& vol);
+	void copyBufferToTextureViewInternal(BufferPtr buff, PtrSize offset, PtrSize range, TextureViewPtr texView);
 
 	void copyBufferToBuffer(BufferPtr& src, PtrSize srcOffset, BufferPtr& dst, PtrSize dstOffset, PtrSize range);
 
@@ -449,8 +448,6 @@ private:
 
 	void flushWriteQueryResults();
 
-	void clearTextureInternal(TexturePtr tex, const ClearValue& clearValue, const VkImageSubresourceRange& range);
-
 	void setImageBarrier(VkPipelineStageFlags srcStage,
 		VkAccessFlags srcAccess,
 		VkImageLayout prevLayout,

+ 35 - 33
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -148,6 +148,29 @@ inline void CommandBufferImpl::setTextureBarrierRange(
 	m_microCmdb->pushObjectRef(tex);
 }
 
+inline void CommandBufferImpl::setTextureBarrier(
+	TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage, const TextureSubresourceInfo& subresource_)
+{
+	TextureSubresourceInfo subresource = subresource_;
+	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
+
+	// The transition of the non zero mip levels happens inside CommandBufferImpl::generateMipmapsX so limit the
+	// subresource
+	if(nextUsage == TextureUsageBit::GENERATE_MIPMAPS)
+	{
+		ANKI_ASSERT(impl.isSubresourceGoodForMipmapGeneration(subresource));
+
+		subresource.m_firstMipmap = 0;
+		subresource.m_mipmapCount = 1;
+	}
+
+	ANKI_ASSERT(tex->isSubresourceValid(subresource));
+
+	VkImageSubresourceRange range;
+	impl.computeVkImageSubresourceRange(subresource, range);
+	setTextureBarrierRange(tex, prevUsage, nextUsage, range);
+}
+
 inline void CommandBufferImpl::setTextureSurfaceBarrier(
 	TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage, const TextureSurfaceInfo& surf)
 {
@@ -158,10 +181,9 @@ inline void CommandBufferImpl::setTextureSurfaceBarrier(
 	}
 
 	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	impl.checkSurfaceOrVolume(surf);
 
 	VkImageSubresourceRange range;
-	impl.computeSubResourceRange(surf, impl.m_akAspect, range);
+	impl.computeVkImageSubresourceRange(TextureSubresourceInfo(surf, impl.getDepthStencilAspect()), range);
 	setTextureBarrierRange(tex, prevUsage, nextUsage, range);
 }
 
@@ -175,10 +197,9 @@ inline void CommandBufferImpl::setTextureVolumeBarrier(
 	}
 
 	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	impl.checkSurfaceOrVolume(vol);
 
 	VkImageSubresourceRange range;
-	impl.computeSubResourceRange(vol, impl.m_akAspect, range);
+	impl.computeVkImageSubresourceRange(TextureSubresourceInfo(vol, impl.getDepthStencilAspect()), range);
 	setTextureBarrierRange(tex, prevUsage, nextUsage, range);
 }
 
@@ -284,6 +305,7 @@ inline void CommandBufferImpl::drawElementsIndirect(
 inline void CommandBufferImpl::dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ)
 {
 	ANKI_ASSERT(m_computeProg);
+	ANKI_ASSERT(!!(m_flags & CommandBufferFlag::COMPUTE_WORK));
 	commandCommon();
 
 	// Bind descriptors
@@ -375,50 +397,30 @@ inline void CommandBufferImpl::endOcclusionQuery(OcclusionQueryPtr query)
 	m_microCmdb->pushObjectRef(query);
 }
 
-inline void CommandBufferImpl::clearTextureInternal(
-	TexturePtr tex, const ClearValue& clearValue, const VkImageSubresourceRange& range)
+inline void CommandBufferImpl::clearTextureView(TextureViewPtr texView, const ClearValue& clearValue)
 {
 	commandCommon();
 
+	const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*texView);
+	const TextureImpl& tex = static_cast<const TextureImpl&>(*view.m_tex);
+
 	VkClearColorValue vclear;
 	static_assert(sizeof(vclear) == sizeof(clearValue), "See file");
 	memcpy(&vclear, &clearValue, sizeof(clearValue));
 
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	if(impl.m_aspect == VK_IMAGE_ASPECT_COLOR_BIT)
+	if(!view.getSubresource().m_depthStencilAspect)
 	{
+		VkImageSubresourceRange vkRange = view.getVkImageSubresourceRange();
 		ANKI_CMD(vkCmdClearColorImage(
-					 m_handle, impl.m_imageHandle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &vclear, 1, &range),
+					 m_handle, tex.m_imageHandle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &vclear, 1, &vkRange),
 			ANY_OTHER_COMMAND);
 	}
 	else
 	{
-		ANKI_ASSERT(0 && "TODO");
+		ANKI_ASSERT(!"TODO");
 	}
 
-	m_microCmdb->pushObjectRef(tex);
-}
-
-inline void CommandBufferImpl::clearTextureSurface(
-	TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectBit aspect)
-{
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	ANKI_ASSERT(impl.getTextureType() != TextureType::_3D && "Not for 3D");
-
-	VkImageSubresourceRange range;
-	impl.computeSubResourceRange(surf, aspect, range);
-	clearTextureInternal(tex, clearValue, range);
-}
-
-inline void CommandBufferImpl::clearTextureVolume(
-	TexturePtr tex, const TextureVolumeInfo& vol, const ClearValue& clearValue, DepthStencilAspectBit aspect)
-{
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	ANKI_ASSERT(impl.getTextureType() == TextureType::_3D && "Only for 3D");
-
-	VkImageSubresourceRange range;
-	impl.computeSubResourceRange(vol, aspect, range);
-	clearTextureInternal(tex, clearValue, range);
+	m_microCmdb->pushObjectRef(texView);
 }
 
 inline void CommandBufferImpl::pushSecondLevelCommandBuffer(CommandBufferPtr cmdb)

+ 1 - 17
src/anki/gr/vulkan/Common.cpp

@@ -279,22 +279,6 @@ VkFormat convertFormat(PixelFormat ak)
 	return out;
 }
 
-VkImageAspectFlags convertImageAspect(PixelFormat ak)
-{
-	VkImageAspectFlags out = 0;
-	for(U i = 0; i < CONVERT_FORMAT_TABLE_SIZE; ++i)
-	{
-		const ConvertFormat& entry = CONVERT_FORMAT_TABLE[i];
-		if(ak == entry.m_ak)
-		{
-			out = static_cast<VkImageAspectFlags>(entry.m_aspect);
-		}
-	}
-
-	ANKI_ASSERT(out);
-	return out;
-}
-
 VkPrimitiveTopology convertTopology(PrimitiveTopology ak)
 {
 	VkPrimitiveTopology out = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
@@ -625,7 +609,7 @@ VkImageUsageFlags convertTextureUsage(TextureUsageBit ak, const PixelFormat& for
 
 	if(!!(ak & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE))
 	{
-		if(formatIsDepthStencil(format))
+		if(componentFormatIsDepthStencil(format.m_components))
 		{
 			out |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
 		}

+ 34 - 4
src/anki/gr/vulkan/Common.h

@@ -87,12 +87,42 @@ ANKI_USE_RESULT VkCompareOp convertCompareOp(CompareOperation ak);
 ANKI_USE_RESULT VkFormat convertFormat(PixelFormat ak);
 
 /// Get format aspect mask.
-ANKI_USE_RESULT VkImageAspectFlags convertImageAspect(PixelFormat ak);
+ANKI_USE_RESULT inline DepthStencilAspectBit getImageAspectFromFormat(const PixelFormat& ak)
+{
+	DepthStencilAspectBit out = DepthStencilAspectBit::NONE;
+	if(componentFormatIsStencil(ak.m_components))
+	{
+		out = DepthStencilAspectBit::STENCIL;
+	}
+
+	if(componentFormatIsDepth(ak.m_components))
+	{
+		out |= DepthStencilAspectBit::DEPTH;
+	}
 
-ANKI_USE_RESULT inline Bool formatIsDepthStencil(PixelFormat fmt)
+	return out;
+}
+
+/// Convert image aspect.
+ANKI_USE_RESULT inline VkImageAspectFlags convertImageAspect(const DepthStencilAspectBit ak)
 {
-	VkImageAspectFlags aspect = convertImageAspect(fmt);
-	return !!(aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
+	VkImageAspectFlags out = 0;
+	if(!!(ak & DepthStencilAspectBit::DEPTH))
+	{
+		out |= VK_IMAGE_ASPECT_DEPTH_BIT;
+	}
+
+	if(!!(ak & DepthStencilAspectBit::STENCIL))
+	{
+		out |= VK_IMAGE_ASPECT_STENCIL_BIT;
+	}
+
+	if(!out)
+	{
+		out = VK_IMAGE_ASPECT_COLOR_BIT;
+	}
+
+	return out;
 }
 
 /// Convert topology.

+ 3 - 5
src/anki/gr/vulkan/DescriptorSet.cpp

@@ -286,7 +286,7 @@ void DSThreadAllocator::writeSet(const Array<AnyBinding, MAX_BINDINGS_PER_DESCRI
 			{
 			case DescriptorType::TEXTURE:
 				tex[texCount].sampler = b.m_tex.m_sampler->getHandle();
-				tex[texCount].imageView = b.m_tex.m_tex->getOrCreateResourceGroupView(b.m_tex.m_aspect);
+				tex[texCount].imageView = b.m_tex.m_texView->m_handle;
 				tex[texCount].imageLayout = b.m_tex.m_layout;
 
 				w.pImageInfo = &tex[texCount];
@@ -305,8 +305,7 @@ void DSThreadAllocator::writeSet(const Array<AnyBinding, MAX_BINDINGS_PER_DESCRI
 				break;
 			case DescriptorType::IMAGE:
 				tex[texCount].sampler = VK_NULL_HANDLE;
-				tex[texCount].imageView =
-					b.m_image.m_tex->getOrCreateSingleLevelView(b.m_image.m_level, b.m_tex.m_aspect);
+				tex[texCount].imageView = b.m_image.m_texView->m_handle;
 				tex[texCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
 
 				w.pImageInfo = &tex[texCount];
@@ -507,7 +506,6 @@ void DescriptorSetState::flush(Bool& stateDirty,
 			{
 			case DescriptorType::TEXTURE:
 				toHash[toHashCount++] = m_bindings[i].m_uuids[1];
-				toHash[toHashCount++] = U64(m_bindings[i].m_tex.m_aspect);
 				toHash[toHashCount++] = U64(m_bindings[i].m_tex.m_layout);
 				break;
 			case DescriptorType::UNIFORM_BUFFER:
@@ -518,7 +516,7 @@ void DescriptorSetState::flush(Bool& stateDirty,
 				dynamicOffsetsDirty = dynamicOffsetsDirty || m_dynamicOffsetDirty.get(i);
 				break;
 			case DescriptorType::IMAGE:
-				toHash[toHashCount++] = m_bindings[i].m_image.m_level;
+				// Nothing
 				break;
 			default:
 				ANKI_ASSERT(0);

+ 17 - 17
src/anki/gr/vulkan/DescriptorSet.h

@@ -6,11 +6,9 @@
 #pragma once
 
 #include <anki/gr/vulkan/Common.h>
-#include <anki/gr/Buffer.h>
 #include <anki/gr/vulkan/BufferImpl.h>
-#include <anki/gr/Texture.h>
 #include <anki/gr/vulkan/TextureImpl.h>
-#include <anki/gr/Sampler.h>
+#include <anki/gr/vulkan/TextureViewImpl.h>
 #include <anki/gr/vulkan/SamplerImpl.h>
 #include <anki/util/BitSet.h>
 
@@ -66,9 +64,8 @@ private:
 class TextureBinding
 {
 public:
-	const TextureImpl* m_tex = nullptr;
+	const TextureViewImpl* m_texView = nullptr;
 	const MicroSampler* m_sampler = nullptr;
-	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE;
 	VkImageLayout m_layout = VK_IMAGE_LAYOUT_MAX_ENUM;
 };
 
@@ -83,8 +80,7 @@ public:
 class ImageBinding
 {
 public:
-	const TextureImpl* m_tex = nullptr;
-	U16 m_level = 0;
+	const TextureViewImpl* m_texView = nullptr;
 };
 
 class AnyBinding
@@ -110,18 +106,19 @@ public:
 		m_layoutDirty = true;
 	}
 
-	void bindTextureAndSampler(
-		U binding, const Texture* tex, const Sampler* sampler, DepthStencilAspectBit aspect, VkImageLayout layout)
+	void bindTextureAndSampler(U binding, const TextureView* texView, const Sampler* sampler, VkImageLayout layout)
 	{
+		const TextureViewImpl& viewImpl = static_cast<const TextureViewImpl&>(*texView);
+		ANKI_ASSERT(viewImpl.m_tex->isSubresourceGoodForSampling(viewImpl.getSubresource()));
+
 		AnyBinding& b = m_bindings[binding];
 		b = {};
 		b.m_type = DescriptorType::TEXTURE;
-		b.m_uuids[0] = tex->getUuid();
+		b.m_uuids[0] = viewImpl.m_hash;
 		b.m_uuids[1] = sampler->getUuid();
 
-		b.m_tex.m_tex = static_cast<const TextureImpl*>(tex);
+		b.m_tex.m_texView = &viewImpl;
 		b.m_tex.m_sampler = static_cast<const SamplerImpl*>(sampler)->m_sampler.get();
-		b.m_tex.m_aspect = aspect;
 		b.m_tex.m_layout = layout;
 
 		m_anyBindingDirty = true;
@@ -157,15 +154,18 @@ public:
 		m_dynamicOffsetDirty.set(binding);
 	}
 
-	void bindImage(U binding, const Texture* tex, U32 level)
+	void bindImage(U binding, const TextureView* texView)
 	{
+		ANKI_ASSERT(texView);
+		const TextureViewImpl* impl = static_cast<const TextureViewImpl*>(texView);
+		ANKI_ASSERT(impl->m_tex->isSubresourceGoodForImageLoadStore(impl->getSubresource()));
+
 		AnyBinding& b = m_bindings[binding];
 		b = {};
 		b.m_type = DescriptorType::IMAGE;
-		b.m_uuids[0] = b.m_uuids[1] = tex->getUuid();
-
-		b.m_image.m_tex = static_cast<const TextureImpl*>(tex);
-		b.m_image.m_level = level;
+		ANKI_ASSERT(impl->m_hash);
+		b.m_uuids[0] = b.m_uuids[1] = impl->m_hash;
+		b.m_image.m_texView = impl;
 
 		m_anyBindingDirty = true;
 	}

+ 19 - 31
src/anki/gr/vulkan/FramebufferImpl.cpp

@@ -35,6 +35,8 @@ FramebufferImpl::~FramebufferImpl()
 
 Error FramebufferImpl::init(const FramebufferInitInfo& init)
 {
+	ANKI_ASSERT(init.isValid());
+
 	// Init common
 	m_defaultFb = init.refersToDefaultFramebuffer();
 	strcpy(&m_name[0], (init.getName()) ? init.getName().cstr() : "");
@@ -45,27 +47,9 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 		m_colorAttCount = i + 1;
 	}
 
-	if(!m_defaultFb && init.m_depthStencilAttachment.m_texture)
+	if(!m_defaultFb && init.m_depthStencilAttachment.m_textureView)
 	{
-		const TextureImpl& tex = static_cast<const TextureImpl&>(*init.m_depthStencilAttachment.m_texture);
-
-		if(!!(tex.m_workarounds & TextureImplWorkaround::S8_TO_D24S8))
-		{
-			m_aspect = DepthStencilAspectBit::STENCIL;
-		}
-		else if(tex.m_akAspect == DepthStencilAspectBit::DEPTH)
-		{
-			m_aspect = DepthStencilAspectBit::DEPTH;
-		}
-		else if(tex.m_akAspect == DepthStencilAspectBit::STENCIL)
-		{
-			m_aspect = DepthStencilAspectBit::STENCIL;
-		}
-		else
-		{
-			ANKI_ASSERT(!!init.m_depthStencilAttachment.m_aspect);
-			m_aspect = init.m_depthStencilAttachment.m_aspect;
-		}
+		m_aspect = init.m_depthStencilAttachment.m_textureView->getSubresource().m_depthStencilAspect;
 	}
 
 	initClearValues(init);
@@ -140,33 +124,37 @@ Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	{
 		const FramebufferAttachmentInfo& att = init.m_colorAttachments[i];
-		const TextureImpl& tex = static_cast<const TextureImpl&>(*att.m_texture);
+		const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*att.m_textureView);
+		const TextureImpl& tex = static_cast<const TextureImpl&>(*view.m_tex);
+		ANKI_ASSERT(tex.isSubresourceGoodForFramebufferAttachment(view.getSubresource()));
 
-		imgViews[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, att.m_aspect);
+		imgViews[count++] = view.m_handle;
 
 		if(m_noDflt.m_width == 0)
 		{
-			m_noDflt.m_width = tex.getWidth() >> att.m_surface.m_level;
-			m_noDflt.m_height = tex.getHeight() >> att.m_surface.m_level;
+			m_noDflt.m_width = tex.getWidth() >> view.getSubresource().m_firstMipmap;
+			m_noDflt.m_height = tex.getHeight() >> view.getSubresource().m_firstMipmap;
 		}
 
-		m_noDflt.m_refs[i] = att.m_texture;
+		m_noDflt.m_refs[i] = att.m_textureView;
 	}
 
 	if(hasDepthStencil())
 	{
 		const FramebufferAttachmentInfo& att = init.m_depthStencilAttachment;
-		const TextureImpl& tex = static_cast<const TextureImpl&>(*att.m_texture);
+		const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*att.m_textureView);
+		const TextureImpl& tex = static_cast<const TextureImpl&>(*view.m_tex);
+		ANKI_ASSERT(tex.isSubresourceGoodForFramebufferAttachment(view.getSubresource()));
 
-		imgViews[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, m_aspect);
+		imgViews[count++] = view.m_handle;
 
 		if(m_noDflt.m_width == 0)
 		{
-			m_noDflt.m_width = tex.getWidth() >> att.m_surface.m_level;
-			m_noDflt.m_height = tex.getHeight() >> att.m_surface.m_level;
+			m_noDflt.m_width = tex.getWidth() >> view.getSubresource().m_firstMipmap;
+			m_noDflt.m_height = tex.getHeight() >> view.getSubresource().m_firstMipmap;
 		}
 
-		m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS] = att.m_texture;
+		m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS] = att.m_textureView;
 	}
 
 	ci.width = m_noDflt.m_width;
@@ -186,7 +174,7 @@ void FramebufferImpl::setupAttachmentDescriptor(
 	const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc, VkImageLayout layout) const
 {
 	desc = {};
-	desc.format = convertFormat(att.m_texture->getPixelFormat());
+	desc.format = convertFormat(static_cast<const TextureViewImpl&>(*att.m_textureView).m_tex->getPixelFormat());
 	desc.samples = VK_SAMPLE_COUNT_1_BIT;
 	desc.loadOp = convertLoadOp(att.m_loadOperation);
 	desc.storeOp = convertStoreOp(att.m_storeOperation);

+ 3 - 3
src/anki/gr/vulkan/FramebufferImpl.h

@@ -89,13 +89,13 @@ public:
 		return m_colorAttCount + (hasDepthStencil() ? 1 : 0);
 	}
 
-	TexturePtr getColorAttachment(U att) const
+	TextureViewPtr getColorAttachment(U att) const
 	{
 		ANKI_ASSERT(m_noDflt.m_refs[att].get());
 		return m_noDflt.m_refs[att];
 	}
 
-	TexturePtr getDepthStencilAttachment() const
+	TextureViewPtr getDepthStencilAttachment() const
 	{
 		ANKI_ASSERT(m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS].get());
 		return m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS];
@@ -150,7 +150,7 @@ private:
 		U32 m_width = 0;
 		U32 m_height = 0;
 
-		Array<TexturePtr, MAX_COLOR_ATTACHMENTS + 1> m_refs; ///< @note The pos of every attachment is fixed.
+		Array<TextureViewPtr, MAX_COLOR_ATTACHMENTS + 1> m_refs; ///< @note The pos of every attachment is fixed.
 
 		// RenderPass create info
 		VkRenderPassCreateInfo m_rpassCi = {};

+ 8 - 56
src/anki/gr/vulkan/GrManager.cpp

@@ -8,6 +8,7 @@
 
 #include <anki/gr/Buffer.h>
 #include <anki/gr/Texture.h>
+#include <anki/gr/TextureView.h>
 #include <anki/gr/Sampler.h>
 #include <anki/gr/Shader.h>
 #include <anki/gr/ShaderProgram.h>
@@ -75,7 +76,8 @@ void GrManager::swapBuffers()
 
 void GrManager::finish()
 {
-	// TODO
+	ANKI_VK_SELF(GrManagerImpl);
+	self.finish();
 }
 
 BufferPtr GrManager::newBuffer(const BufferInitInfo& init)
@@ -88,6 +90,11 @@ TexturePtr GrManager::newTexture(const TextureInitInfo& init)
 	return TexturePtr(Texture::newInstance(this, init));
 }
 
+TextureViewPtr GrManager::newTextureView(const TextureViewInitInfo& init)
+{
+	return TextureViewPtr(TextureView::newInstance(this, init));
+}
+
 SamplerPtr GrManager::newSampler(const SamplerInitInfo& init)
 {
 	return SamplerPtr(Sampler::newInstance(this, init));
@@ -123,61 +130,6 @@ RenderGraphPtr GrManager::newRenderGraph()
 	return RenderGraphPtr(RenderGraph::newInstance(this));
 }
 
-void GrManager::getTextureSurfaceUploadInfo(TexturePtr tex, const TextureSurfaceInfo& surf, PtrSize& allocationSize)
-{
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	impl.checkSurfaceOrVolume(surf);
-
-	U width = impl.m_width >> surf.m_level;
-	U height = impl.m_height >> surf.m_level;
-
-	if(!impl.m_workarounds)
-	{
-		allocationSize = computeSurfaceSize(width, height, impl.m_format);
-	}
-	else if(!!(impl.m_workarounds & TextureImplWorkaround::R8G8B8_TO_R8G8B8A8))
-	{
-		// Extra size for staging buffer
-		allocationSize =
-			computeSurfaceSize(width, height, PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UNORM));
-		alignRoundUp(16, allocationSize);
-		allocationSize +=
-			computeSurfaceSize(width, height, PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM));
-	}
-	else
-	{
-		ANKI_ASSERT(0);
-	}
-}
-
-void GrManager::getTextureVolumeUploadInfo(TexturePtr tex, const TextureVolumeInfo& vol, PtrSize& allocationSize)
-{
-	const TextureImpl& impl = static_cast<const TextureImpl&>(*tex);
-	impl.checkSurfaceOrVolume(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);
-	}
-}
-
 void GrManager::getUniformBufferInfo(U32& bindOffsetAlignment, PtrSize& maxUniformBlockSize) const
 {
 	ANKI_VK_SELF_CONST(GrManagerImpl);

+ 11 - 0
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -564,6 +564,11 @@ Error GrManagerImpl::initMemory(const ConfigSet& cfg)
 void* GrManagerImpl::allocateCallback(
 	void* userData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
 {
+	if(ANKI_UNLIKELY(size == 0))
+	{
+		return nullptr;
+	}
+
 	ANKI_ASSERT(userData);
 	ANKI_ASSERT(size);
 	ANKI_ASSERT(isPowerOfTwo(alignment));
@@ -758,6 +763,12 @@ void GrManagerImpl::flushCommandBuffer(CommandBufferPtr cmdb, FencePtr* outFence
 	}
 }
 
+void GrManagerImpl::finish()
+{
+	LockGuard<Mutex> lock(m_globalMtx);
+	vkQueueWaitIdle(m_queue);
+}
+
 void GrManagerImpl::trySetVulkanHandleName(CString name, VkDebugReportObjectTypeEXT type, U64 handle) const
 {
 	if(name && name.getLength())

+ 2 - 5
src/anki/gr/vulkan/GrManagerImpl.h

@@ -58,6 +58,8 @@ public:
 
 	void endFrame();
 
+	void finish();
+
 	VkDevice getDevice() const
 	{
 		ANKI_ASSERT(m_device);
@@ -91,11 +93,6 @@ public:
 
 	void flushCommandBuffer(CommandBufferPtr ptr, FencePtr* fence, Bool wait = false);
 
-	void finishCommandBuffer(CommandBufferPtr ptr)
-	{
-		flushCommandBuffer(ptr, nullptr, true);
-	}
-
 	/// @name Memory
 	/// @{
 	GpuMemoryManager& getGpuMemoryManager()

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

@@ -22,7 +22,6 @@ class ShaderImpl final : public Shader, public VulkanObject<Shader, ShaderImpl>
 {
 public:
 	VkShaderModule m_handle = VK_NULL_HANDLE;
-	ShaderType m_shaderType = ShaderType::COUNT;
 
 	Array<DynamicArray<DescriptorBinding>, MAX_DESCRIPTOR_SETS> m_bindings;
 	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_colorAttachmentWritemask = {false};

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

@@ -28,6 +28,7 @@ ShaderProgramImpl::~ShaderProgramImpl()
 
 Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 {
+	ANKI_ASSERT(inf.isValid());
 	ShaderTypeBit shaderMask = ShaderTypeBit::NONE;
 	m_shaders = inf.m_shaders;
 

+ 37 - 69
src/anki/gr/vulkan/TextureImpl.cpp

@@ -45,7 +45,7 @@ TextureImpl::~TextureImpl()
 Error TextureImpl::init(const TextureInitInfo& init_)
 {
 	TextureInitInfo init = init_;
-	ANKI_ASSERT(textureInitInfoValid(init));
+	ANKI_ASSERT(init.isValid());
 
 	// Set some stuff
 	m_width = init.m_width;
@@ -63,31 +63,21 @@ Error TextureImpl::init(const TextureInitInfo& init_)
 
 	if(m_texType == TextureType::_3D)
 	{
-		m_mipCount = min<U>(init.m_mipmapsCount, computeMaxMipmapCount3d(m_width, m_height, m_depth));
+		m_mipCount = min<U>(init.m_mipmapCount, computeMaxMipmapCount3d(m_width, m_height, m_depth));
 	}
 	else
 	{
-		m_mipCount = min<U>(init.m_mipmapsCount, computeMaxMipmapCount2d(m_width, m_height));
+		m_mipCount = min<U>(init.m_mipmapCount, computeMaxMipmapCount2d(m_width, m_height));
 	}
-	init.m_mipmapsCount = m_mipCount;
+	init.m_mipmapCount = m_mipCount;
 
 	m_layerCount = init.m_layerCount;
 
 	m_format = init.m_format;
 	m_vkFormat = convertFormat(m_format);
-	m_depthStencil = formatIsDepthStencil(m_format);
-	m_aspect = convertImageAspect(m_format);
+	m_aspect = getImageAspectFromFormat(m_format);
 	m_usage = init.m_usage;
 
-	if(m_aspect & VK_IMAGE_ASPECT_DEPTH_BIT)
-	{
-		m_akAspect |= DepthStencilAspectBit::DEPTH;
-	}
-	if(m_aspect & VK_IMAGE_ASPECT_STENCIL_BIT)
-	{
-		m_akAspect |= DepthStencilAspectBit::STENCIL;
-	}
-
 	ANKI_CHECK(initImage(init));
 
 	// Init the template
@@ -100,7 +90,7 @@ Error TextureImpl::init(const TextureInitInfo& init_)
 	m_viewCreateInfoTemplate.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
 	m_viewCreateInfoTemplate.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
 	m_viewCreateInfoTemplate.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
-	m_viewCreateInfoTemplate.subresourceRange.aspectMask = m_aspect;
+	m_viewCreateInfoTemplate.subresourceRange.aspectMask = convertImageAspect(m_aspect);
 	m_viewCreateInfoTemplate.subresourceRange.baseArrayLayer = 0;
 	m_viewCreateInfoTemplate.subresourceRange.baseMipLevel = 0;
 	m_viewCreateInfoTemplate.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
@@ -117,7 +107,7 @@ Error TextureImpl::init(const TextureInitInfo& init_)
 		CommandBufferPtr cmdb = getManager().newCommandBuffer(cmdbinit);
 
 		VkImageSubresourceRange range;
-		range.aspectMask = m_aspect;
+		range.aspectMask = convertImageAspect(m_aspect);
 		range.baseArrayLayer = 0;
 		range.baseMipLevel = 0;
 		range.layerCount = m_layerCount;
@@ -137,7 +127,7 @@ VkFormatFeatureFlags TextureImpl::calcFeatures(const TextureInitInfo& init)
 {
 	VkFormatFeatureFlags flags = 0;
 
-	if(init.m_mipmapsCount > 1 && !!(init.m_usage & TextureUsageBit::GENERATE_MIPMAPS))
+	if(init.m_mipmapCount > 1 && !!(init.m_usage & TextureUsageBit::GENERATE_MIPMAPS))
 	{
 		// May be used for mip gen.
 		flags |= VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT;
@@ -145,7 +135,7 @@ VkFormatFeatureFlags TextureImpl::calcFeatures(const TextureInitInfo& init)
 
 	if(!!(init.m_usage & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE))
 	{
-		if(formatIsDepthStencil(init.m_format))
+		if(componentFormatIsDepthStencil(init.m_format.m_components))
 		{
 			flags |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
 		}
@@ -225,8 +215,6 @@ Error TextureImpl::initImage(const TextureInitInfo& init_)
 			m_format = init.m_format;
 			m_vkFormat = convertFormat(m_format);
 			m_workarounds = TextureImplWorkaround::S8_TO_D24S8;
-			m_aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
-			m_akAspect = DepthStencilAspectBit::DEPTH | DepthStencilAspectBit::STENCIL;
 		}
 		else if(init.m_format.m_components == ComponentFormat::D24S8)
 		{
@@ -236,8 +224,6 @@ Error TextureImpl::initImage(const TextureInitInfo& init_)
 			m_format = init.m_format;
 			m_vkFormat = convertFormat(m_format);
 			m_workarounds = TextureImplWorkaround::D24S8_TO_D32S8;
-			m_aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
-			m_akAspect = DepthStencilAspectBit::DEPTH | DepthStencilAspectBit::STENCIL;
 		}
 		else
 		{
@@ -318,6 +304,11 @@ Error TextureImpl::initImage(const TextureInitInfo& init_)
 
 	ANKI_VK_CHECK(vkCreateImage(getDevice(), &ci, nullptr, &m_imageHandle));
 	getGrManagerImpl().trySetVulkanHandleName(init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_imageHandle);
+#if 0
+	printf("Creating texture %p %s\n",
+		static_cast<void*>(m_imageHandle),
+		init.getName() ? init.getName().cstr() : "Unnamed");
+#endif
 
 	// Allocate memory
 	//
@@ -384,7 +375,8 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit before,
 	srcAccesses = 0;
 	dstStages = 0;
 	dstAccesses = 0;
-	Bool lastLevel = level == m_mipCount - 1u;
+	const Bool lastLevel = level == m_mipCount - 1u;
+	const Bool depthStencil = !!m_aspect;
 
 	//
 	// Before
@@ -433,7 +425,7 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit before,
 
 	if(!!(before & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ))
 	{
-		if(m_depthStencil)
+		if(depthStencil)
 		{
 			srcStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
 			srcAccesses |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
@@ -447,7 +439,7 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit before,
 
 	if(!!(before & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE))
 	{
-		if(m_depthStencil)
+		if(depthStencil)
 		{
 			srcStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
 			srcAccesses |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
@@ -537,7 +529,7 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit before,
 
 	if(!!(after & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ))
 	{
-		if(m_depthStencil)
+		if(depthStencil)
 		{
 			dstStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
 			dstAccesses |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
@@ -551,7 +543,7 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit before,
 
 	if(!!(after & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE))
 	{
-		if(m_depthStencil)
+		if(depthStencil)
 		{
 			dstStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
 			dstAccesses |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
@@ -598,7 +590,8 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 	ANKI_ASSERT(usageValid(usage));
 
 	VkImageLayout out = VK_IMAGE_LAYOUT_MAX_ENUM;
-	Bool lastLevel = level == m_mipCount - 1u;
+	const Bool lastLevel = level == m_mipCount - 1u;
+	const Bool depthStencil = !!m_aspect;
 
 	if(usage == TextureUsageBit::NONE)
 	{
@@ -617,7 +610,7 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 	else if(!(usage & ~TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE))
 	{
 		// Only FB access
-		if(m_depthStencil)
+		if(depthStencil)
 		{
 			out = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 		}
@@ -626,13 +619,13 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 			out = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 		}
 	}
-	else if(m_depthStencil
+	else if(depthStencil
 		&& !(usage & ~(TextureUsageBit::SAMPLED_ALL_GRAPHICS | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ)))
 	{
 		// FB read & shader read
 		out = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
 	}
-	else if(m_depthStencil
+	else if(depthStencil
 		&& !(usage & ~(TextureUsageBit::SAMPLED_ALL_GRAPHICS | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE)))
 	{
 		// Wild guess: One aspect is shader read and the other is read write
@@ -653,7 +646,7 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 	{
 		out = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
 	}
-	else if(!m_depthStencil && usage == TextureUsageBit::TRANSFER_DESTINATION)
+	else if(!depthStencil && usage == TextureUsageBit::TRANSFER_DESTINATION)
 	{
 		out = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
 	}
@@ -662,55 +655,30 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 	return out;
 }
 
-VkImageView TextureImpl::getOrCreateSingleLevelView(U32 mip, DepthStencilAspectBit aspect) const
-{
-	ANKI_ASSERT(mip < m_mipCount);
-
-	VkImageViewCreateInfo ci = m_viewCreateInfoTemplate;
-	ci.subresourceRange.baseMipLevel = mip;
-	ci.subresourceRange.levelCount = 1;
-	ci.subresourceRange.aspectMask = convertAspect(aspect);
-
-	return getOrCreateView(ci);
-}
-
-VkImageView TextureImpl::getOrCreateSingleSurfaceView(
-	const TextureSurfaceInfo& surf, DepthStencilAspectBit aspect) const
-{
-	checkSurfaceOrVolume(surf);
-
-	VkImageViewCreateInfo ci = m_viewCreateInfoTemplate;
-	ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
-	computeSubResourceRange(surf, aspect, ci.subresourceRange);
-
-	return getOrCreateView(ci);
-}
-
-VkImageView TextureImpl::getOrCreateResourceGroupView(DepthStencilAspectBit aspect) const
-{
-	VkImageViewCreateInfo ci = m_viewCreateInfoTemplate;
-	ci.subresourceRange.aspectMask = convertAspect(aspect);
-
-	return getOrCreateView(ci);
-}
-
-VkImageView TextureImpl::getOrCreateView(const VkImageViewCreateInfo& ci) const
+VkImageView TextureImpl::getOrCreateView(const TextureSubresourceInfo& subresource, TextureType& newTexType) const
 {
 	LockGuard<Mutex> lock(m_viewsMapMtx);
-	auto it = m_viewsMap.find(ci);
+	auto it = m_viewsMap.find(subresource);
 
 	if(it != m_viewsMap.getEnd())
 	{
+		// Fixup the image view type
+		newTexType = computeNewTexTypeOfSubresource(subresource);
+
 		return *it;
 	}
 	else
 	{
+		// Compute the VkImageViewCreateInfo
+		VkImageViewCreateInfo viewCi;
+		computeVkImageViewCreateInfo(subresource, viewCi, newTexType);
+
 		VkImageView view = VK_NULL_HANDLE;
-		ANKI_VK_CHECKF(vkCreateImageView(getDevice(), &ci, nullptr, &view));
+		ANKI_VK_CHECKF(vkCreateImageView(getDevice(), &viewCi, nullptr, &view));
 		getGrManagerImpl().trySetVulkanHandleName(
 			(m_name[0]) ? &m_name[0] : CString(), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT, ptrToNumber(view));
 
-		m_viewsMap.emplace(getAllocator(), ci, view);
+		m_viewsMap.emplace(getAllocator(), subresource, view);
 
 		return view;
 	}

+ 62 - 52
src/anki/gr/vulkan/TextureImpl.h

@@ -39,13 +39,13 @@ public:
 	GpuMemoryHandle m_memHandle;
 
 	U32 m_surfaceOrVolumeCount = 0;
-	VkImageAspectFlags m_aspect = 0;
-	DepthStencilAspectBit m_akAspect = DepthStencilAspectBit::NONE;
+
 	VkFormat m_vkFormat = VK_FORMAT_UNDEFINED;
 
-	Bool m_depthStencil = false;
 	TextureImplWorkaround m_workarounds = TextureImplWorkaround::NONE;
 
+	VkImageViewCreateInfo m_viewCreateInfoTemplate;
+
 	TextureImpl(GrManager* manager)
 		: Texture(manager)
 	{
@@ -55,28 +55,39 @@ public:
 
 	ANKI_USE_RESULT Error init(const TextureInitInfo& init);
 
-	void checkSurfaceOrVolume(const TextureSurfaceInfo& surf) const
+	Bool aspectValid(DepthStencilAspectBit aspect) const
 	{
-		checkTextureSurface(m_texType, m_depth, m_mipCount, m_layerCount, surf);
+		return m_aspect == aspect || !!(aspect & m_aspect);
 	}
 
-	void checkSurfaceOrVolume(const TextureVolumeInfo& vol) const
+	/// Compute the layer as defined by Vulkan.
+	U computeVkArrayLayer(const TextureSurfaceInfo& surf) const
 	{
-		ANKI_ASSERT(m_texType == TextureType::_3D);
-		ANKI_ASSERT(vol.m_level < m_mipCount);
-	}
-
-	void computeSubResourceRange(
-		const TextureSurfaceInfo& surf, DepthStencilAspectBit aspect, VkImageSubresourceRange& range) const;
-
-	void computeSubResourceRange(
-		const TextureVolumeInfo& vol, DepthStencilAspectBit aspect, VkImageSubresourceRange& range) const;
+		U layer = 0;
+		switch(m_texType)
+		{
+		case TextureType::_2D:
+			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);
+		}
 
-	/// Compute the layer as defined by Vulkan.
-	U computeVkArrayLayer(const TextureSurfaceInfo& surf) const;
+		return layer;
+	}
 
 	U computeVkArrayLayer(const TextureVolumeInfo& vol) const
 	{
+		ANKI_ASSERT(m_texType == TextureType::_3D);
 		return 0;
 	}
 
@@ -85,14 +96,6 @@ public:
 		return (usage & m_usage) == usage;
 	}
 
-	/// For image load/store.
-	VkImageView getOrCreateSingleLevelView(U32 mip, DepthStencilAspectBit aspect) const;
-
-	VkImageView getOrCreateSingleSurfaceView(const TextureSurfaceInfo& surf, DepthStencilAspectBit aspect) const;
-
-	/// That view will be used in descriptor sets.
-	VkImageView getOrCreateResourceGroupView(DepthStencilAspectBit aspect) const;
-
 	/// By knowing the previous and new texture usage calculate the relavant info for a ppline barrier.
 	void computeBarrierInfo(TextureUsageBit before,
 		TextureUsageBit after,
@@ -105,29 +108,41 @@ public:
 	/// Predict the image layout.
 	VkImageLayout computeLayout(TextureUsageBit usage, U level) const;
 
-	VkImageAspectFlags convertAspect(DepthStencilAspectBit ak) const;
-
-	void checkSubresourceRange(const VkImageSubresourceRange& range) const
+	void computeVkImageSubresourceRange(const TextureSubresourceInfo& in, VkImageSubresourceRange& range) const
 	{
-		ANKI_ASSERT(range.baseArrayLayer < m_layerCount);
-		ANKI_ASSERT(range.baseArrayLayer + range.layerCount <= m_layerCount);
-		ANKI_ASSERT(range.levelCount < m_mipCount);
-		ANKI_ASSERT(range.baseMipLevel + range.levelCount <= m_mipCount);
+		ANKI_ASSERT(isSubresourceValid(in));
+
+		range.aspectMask = convertImageAspect(in.m_depthStencilAspect);
+		range.baseMipLevel = in.m_firstMipmap;
+		range.levelCount = in.m_mipmapCount;
+
+		const U32 faceCount = textureTypeIsCube(m_texType) ? 6 : 1;
+		range.baseArrayLayer = in.m_firstLayer * faceCount + in.m_firstFace;
+		range.layerCount = in.m_layerCount * in.m_faceCount;
 	}
 
-private:
-	class ViewHasher
+	void computeVkImageViewCreateInfo(
+		const TextureSubresourceInfo& subresource, VkImageViewCreateInfo& viewCi, TextureType& newTextureType) const
 	{
-	public:
-		U64 operator()(const VkImageViewCreateInfo& b) const
+		ANKI_ASSERT(isSubresourceValid(subresource));
+
+		viewCi = m_viewCreateInfoTemplate;
+		computeVkImageSubresourceRange(subresource, viewCi.subresourceRange);
+
+		// Fixup the image view type
+		newTextureType = computeNewTexTypeOfSubresource(subresource);
+		if(newTextureType == TextureType::_2D)
 		{
-			return computeHash(&b, sizeof(b));
+			// Change that anyway
+			viewCi.viewType = VK_IMAGE_VIEW_TYPE_2D;
 		}
-	};
+	}
 
-	mutable HashMap<VkImageViewCreateInfo, VkImageView, ViewHasher> m_viewsMap;
+	VkImageView getOrCreateView(const TextureSubresourceInfo& subresource, TextureType& newTexType) const;
+
+private:
+	mutable HashMap<TextureSubresourceInfo, VkImageView> m_viewsMap;
 	mutable Mutex m_viewsMapMtx;
-	VkImageViewCreateInfo m_viewCreateInfoTemplate;
 
 	VkDeviceMemory m_dedicatedMem = VK_NULL_HANDLE;
 
@@ -141,24 +156,19 @@ private:
 
 	ANKI_USE_RESULT Error initImage(const TextureInitInfo& init);
 
-	VkImageView getOrCreateView(const VkImageViewCreateInfo& ci) const;
-
-	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;
 
 	void updateUsageState(TextureUsageBit usage, StackAllocator<U8>& alloc, TextureUsageState& state) const;
+
+	/// Compute the new type of a texture view.
+	TextureType computeNewTexTypeOfSubresource(const TextureSubresourceInfo& subresource) const
+	{
+		ANKI_ASSERT(isSubresourceValid(subresource));
+		return (textureTypeIsCube(m_texType) && subresource.m_faceCount != 6) ? TextureType::_2D : m_texType;
+	}
 };
 /// @}
 
 } // end namespace anki
-
-#include <anki/gr/vulkan/TextureImpl.inl.h>

+ 0 - 124
src/anki/gr/vulkan/TextureImpl.inl.h

@@ -1,124 +0,0 @@
-// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/gr/vulkan/TextureImpl.h>
-
-namespace anki
-{
-
-inline VkImageAspectFlags TextureImpl::convertAspect(DepthStencilAspectBit ak) const
-{
-	VkImageAspectFlags out = 0;
-	if(m_aspect == (VK_IMAGE_ASPECT_STENCIL_BIT | VK_IMAGE_ASPECT_DEPTH_BIT))
-	{
-		out = !!(ak & DepthStencilAspectBit::DEPTH) ? VK_IMAGE_ASPECT_DEPTH_BIT : 0;
-		out |= !!(ak & DepthStencilAspectBit::STENCIL) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0;
-	}
-	else
-	{
-		out = m_aspect;
-	}
-
-	ANKI_ASSERT(out != 0);
-	ANKI_ASSERT((out & m_aspect) == out);
-	return out;
-}
-
-inline void TextureImpl::computeSubResourceRange(
-	const TextureSurfaceInfo& surf, DepthStencilAspectBit aspect, VkImageSubresourceRange& range) const
-{
-	checkSurfaceOrVolume(surf);
-	range.aspectMask = convertAspect(aspect);
-	range.baseMipLevel = surf.m_level;
-	range.levelCount = 1;
-	switch(m_texType)
-	{
-	case TextureType::_2D:
-		range.baseArrayLayer = 0;
-		break;
-	case TextureType::_3D:
-		range.baseArrayLayer = 0;
-		break;
-	case TextureType::CUBE:
-		range.baseArrayLayer = surf.m_face;
-		break;
-	case TextureType::_2D_ARRAY:
-		range.baseArrayLayer = surf.m_layer;
-		break;
-	case TextureType::CUBE_ARRAY:
-		range.baseArrayLayer = surf.m_layer * 6 + surf.m_face;
-		break;
-	default:
-		ANKI_ASSERT(0);
-		range.baseArrayLayer = 0;
-	}
-	range.layerCount = 1;
-}
-
-inline void TextureImpl::computeSubResourceRange(
-	const TextureVolumeInfo& vol, DepthStencilAspectBit aspect, VkImageSubresourceRange& range) const
-{
-	checkSurfaceOrVolume(vol);
-	range.aspectMask = convertAspect(aspect);
-	range.baseMipLevel = vol.m_level;
-	range.levelCount = 1;
-	range.baseArrayLayer = 0;
-	range.layerCount = 1;
-}
-
-inline U TextureImpl::computeVkArrayLayer(const TextureSurfaceInfo& surf) const
-{
-	checkSurfaceOrVolume(surf);
-	U layer = 0;
-	switch(m_texType)
-	{
-	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;
-}
-
-inline U TextureImpl::computeSubresourceIdx(const TextureSurfaceInfo& surf) const
-{
-	checkSurfaceOrVolume(surf);
-
-	switch(m_texType)
-	{
-	case TextureType::_1D:
-	case TextureType::_2D:
-		return surf.m_level;
-		break;
-	case TextureType::_2D_ARRAY:
-		return surf.m_layer * m_mipCount + surf.m_level;
-		break;
-	case TextureType::CUBE:
-		return surf.m_face * m_mipCount + surf.m_level;
-		break;
-	case TextureType::CUBE_ARRAY:
-		return surf.m_layer * m_mipCount * 6 + surf.m_face * m_mipCount + surf.m_level;
-		break;
-	default:
-		ANKI_ASSERT(0);
-		return 0;
-	}
-}
-
-} // end namespace anki

+ 18 - 0
src/anki/gr/vulkan/TextureView.cpp

@@ -0,0 +1,18 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/TextureView.h>
+#include <anki/gr/vulkan/TextureViewImpl.h>
+#include <anki/gr/GrManager.h>
+
+namespace anki
+{
+
+TextureView* TextureView::newInstance(GrManager* manager, const TextureViewInitInfo& init)
+{
+	return TextureViewImpl::newInstanceHelper(manager, init);
+}
+
+} // end namespace anki

+ 39 - 0
src/anki/gr/vulkan/TextureViewImpl.cpp

@@ -0,0 +1,39 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/vulkan/TextureViewImpl.h>
+#include <anki/gr/vulkan/TextureImpl.h>
+
+namespace anki
+{
+
+TextureViewImpl::~TextureViewImpl()
+{
+}
+
+Error TextureViewImpl::init(const TextureViewInitInfo& inf)
+{
+	ANKI_ASSERT(inf.isValid());
+
+	// Store some stuff
+	m_subresource = inf;
+
+	m_tex = inf.m_texture;
+	const TextureImpl& tex = static_cast<const TextureImpl&>(*m_tex);
+	ANKI_ASSERT(tex.isSubresourceValid(inf));
+
+	m_texType = tex.getTextureType();
+
+	// Ask the texture for a view
+	m_handle = tex.getOrCreateView(inf, m_texType);
+
+	// Create the hash
+	Array<U64, 2> toHash = {{tex.getUuid(), ptrToNumber(m_handle)}};
+	m_hash = computeHash(&toHash[0], sizeof(toHash));
+
+	return Error::NONE;
+}
+
+} // end namespace anki

+ 47 - 0
src/anki/gr/vulkan/TextureViewImpl.h

@@ -0,0 +1,47 @@
+// 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/TextureView.h>
+#include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/TextureImpl.h>
+
+namespace anki
+{
+
+/// @addtogroup vulkan
+/// @{
+
+/// Texture view implementation.
+class TextureViewImpl final : public TextureView, public VulkanObject<TextureView, TextureViewImpl>
+{
+public:
+	VkImageView m_handle = {};
+	TexturePtr m_tex; ///< Hold a reference.
+
+	/// This is a hash that depends on the Texture and the VkImageView. It's used as a replacement of
+	/// TextureView::m_uuid since it creates less unique IDs.
+	U64 m_hash = 0;
+
+	TextureViewImpl(GrManager* manager)
+		: TextureView(manager)
+	{
+	}
+
+	~TextureViewImpl();
+
+	ANKI_USE_RESULT Error init(const TextureViewInitInfo& inf);
+
+	VkImageSubresourceRange getVkImageSubresourceRange() const
+	{
+		VkImageSubresourceRange out;
+		static_cast<const TextureImpl&>(*m_tex).computeVkImageSubresourceRange(getSubresource(), out);
+		return out;
+	}
+};
+/// @}
+
+} // end namespace anki

+ 1 - 1
src/anki/math/Mat3x4.h

@@ -206,7 +206,7 @@ public:
 
 	static const TMat3x4& getIdentity()
 	{
-		static const TMat3x4 ident(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
+		static const TMat3x4 ident(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
 		return ident;
 	}
 	/// @}

+ 3 - 3
src/anki/renderer/Bloom.cpp

@@ -145,7 +145,7 @@ void Bloom::runExposure(RenderPassWorkContext& rgraphCtx)
 
 	cmdb->setViewport(0, 0, m_exposure.m_width, m_exposure.m_height);
 	cmdb->bindShaderProgram(m_exposure.m_grProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDownscaleBlur().getPassRt(MAX_U), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getDownscaleBlur().getPassRt(MAX_U), m_r->getLinearSampler());
 
 	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	*uniforms = Vec4(m_exposure.m_threshold, m_exposure.m_scale, 0.0, 0.0);
@@ -162,7 +162,7 @@ void Bloom::runUpscaleAndSslf(RenderPassWorkContext& rgraphCtx)
 	// Upscale
 	cmdb->setViewport(0, 0, m_upscale.m_width, m_upscale.m_height);
 	cmdb->bindShaderProgram(m_upscale.m_grProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_runCtx.m_exposureRt, m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_exposureRt, m_r->getLinearSampler());
 	drawQuad(cmdb);
 
 	// SSLF
@@ -170,7 +170,7 @@ void Bloom::runUpscaleAndSslf(RenderPassWorkContext& rgraphCtx)
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
 	cmdb->bindTextureAndSampler(0,
 		1,
-		m_sslf.m_lensDirtTex->getGrTexture(),
+		m_sslf.m_lensDirtTex->getGrTextureView(),
 		m_sslf.m_lensDirtTex->getSampler(),
 		TextureUsageBit::SAMPLED_FRAGMENT);
 	drawQuad(cmdb);

+ 11 - 8
src/anki/renderer/DepthDownscale.cpp

@@ -111,7 +111,11 @@ void DepthDownscale::runHalf(RenderPassWorkContext& rgraphCtx)
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 	cmdb->bindShaderProgram(m_half.m_grProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getGBuffer().getDepthRt(), m_r->getLinearSampler());
+	rgraphCtx.bindTextureAndSampler(0,
+		0,
+		m_r->getGBuffer().getDepthRt(),
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+		m_r->getLinearSampler());
 
 	cmdb->setViewport(0, 0, m_r->getWidth() / 2, m_r->getHeight() / 2);
 	cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
@@ -127,7 +131,7 @@ void DepthDownscale::runQuarter(RenderPassWorkContext& rgraphCtx)
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 	cmdb->bindShaderProgram(m_quarter.m_grProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_runCtx.m_halfColorRt, m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_halfColorRt, m_r->getLinearSampler());
 	cmdb->setViewport(0, 0, m_r->getWidth() / 4, m_r->getHeight() / 4);
 
 	drawQuad(cmdb);
@@ -149,14 +153,13 @@ void DepthDownscale::populateRenderGraph(RenderingContext& ctx)
 		pass.setFramebufferInfo(m_half.m_fbDescr, {{m_runCtx.m_halfColorRt}}, m_runCtx.m_halfDepthRt);
 		pass.setWork(runHalfCallback, this, 0);
 
-		pass.newConsumer(
-			{m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_FRAGMENT, DepthStencilAspectBit::DEPTH});
+		TextureSubresourceInfo subresource = TextureSubresourceInfo(DepthStencilAspectBit::DEPTH);
+
+		pass.newConsumer({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_FRAGMENT, subresource});
 		pass.newConsumer({m_runCtx.m_halfColorRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-		pass.newConsumer(
-			{m_runCtx.m_halfDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, DepthStencilAspectBit::DEPTH});
+		pass.newConsumer({m_runCtx.m_halfDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
 		pass.newProducer({m_runCtx.m_halfColorRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-		pass.newProducer(
-			{m_runCtx.m_halfDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, DepthStencilAspectBit::DEPTH});
+		pass.newProducer({m_runCtx.m_halfDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
 	}
 
 	// Create quarter depth render pass

+ 2 - 2
src/anki/renderer/DownscaleBlur.cpp

@@ -88,11 +88,11 @@ void DownscaleBlur::run(RenderPassWorkContext& rgraphCtx)
 	{
 		// Bind the previous pass' Rt
 
-		rgraphCtx.bindTextureAndSampler(0, 0, m_runCtx.m_rts[passIdx - 1], m_r->getLinearSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_rts[passIdx - 1], m_r->getLinearSampler());
 	}
 	else
 	{
-		rgraphCtx.bindTextureAndSampler(0, 0, m_r->getTemporalAA().getRt(), m_r->getLinearSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getTemporalAA().getRt(), m_r->getLinearSampler());
 	}
 
 	const Subpass& pass = m_passes[passIdx];

+ 5 - 5
src/anki/renderer/FinalComposite.cpp

@@ -111,17 +111,17 @@ void FinalComposite::run(const RenderingContext& ctx, RenderPassWorkContext& rgr
 	const Bool drawToDefaultFb = m_r->getDrawToDefaultFramebuffer();
 
 	// Bind stuff
-	rgraphCtx.bindTextureAndSampler(
+	rgraphCtx.bindColorTextureAndSampler(
 		0, 0, m_r->getTemporalAA().getRt(), (drawToDefaultFb) ? m_r->getNearestSampler() : m_r->getLinearSampler());
 
-	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getBloom().getRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getBloom().getRt(), m_r->getLinearSampler());
 	cmdb->bindTextureAndSampler(
-		0, 2, m_lut->getGrTexture(), m_r->getLinearSampler(), TextureUsageBit::SAMPLED_FRAGMENT);
+		0, 2, m_lut->getGrTextureView(), m_r->getLinearSampler(), TextureUsageBit::SAMPLED_FRAGMENT);
 	cmdb->bindTextureAndSampler(
-		0, 3, m_blueNoise->getGrTexture(), m_blueNoise->getSampler(), TextureUsageBit::SAMPLED_FRAGMENT);
+		0, 3, m_blueNoise->getGrTextureView(), m_blueNoise->getSampler(), TextureUsageBit::SAMPLED_FRAGMENT);
 	if(dbgEnabled)
 	{
-		rgraphCtx.bindTextureAndSampler(0, 5, m_r->getDbg().getRt(), m_r->getLinearSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, 5, m_r->getDbg().getRt(), m_r->getLinearSampler());
 	}
 
 	rgraphCtx.bindUniformBuffer(0, 1, m_r->getTonemapping().getAverageLuminanceBuffer());

+ 22 - 12
src/anki/renderer/ForwardShading.cpp

@@ -110,11 +110,15 @@ void ForwardShading::drawVolumetric(RenderingContext& ctx, RenderPassWorkContext
 	Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	computeLinearizeDepthOptimal(ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, unis->x(), unis->y());
 
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDepthDownscale().getHalfDepthColorRt(), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(0, 2, m_r->getVolumetric().getRt(), m_r->getLinearSampler());
-	cmdb->bindTextureAndSampler(
-		0, 3, m_vol.m_noiseTex->getGrTexture(), m_r->getTrilinearRepeatSampler(), TextureUsageBit::SAMPLED_FRAGMENT);
+	rgraphCtx.bindColorTextureAndSampler(
+		0, 0, m_r->getDepthDownscale().getHalfDepthColorRt(), m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 2, m_r->getVolumetric().getRt(), m_r->getLinearSampler());
+	cmdb->bindTextureAndSampler(0,
+		3,
+		m_vol.m_noiseTex->getGrTextureView(),
+		m_r->getTrilinearRepeatSampler(),
+		TextureUsageBit::SAMPLED_FRAGMENT);
 
 	drawQuad(cmdb);
 
@@ -133,12 +137,17 @@ void ForwardShading::drawUpscale(const RenderingContext& ctx, RenderPassWorkCont
 	computeLinearizeDepthOptimal(
 		ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, linearDepth->x(), linearDepth->y());
 
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getGBuffer().getDepthRt(), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getDepthDownscale().getHalfDepthColorRt(), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(0, 2, m_runCtx.m_rt, m_r->getLinearSampler());
+	rgraphCtx.bindTextureAndSampler(0,
+		0,
+		m_r->getGBuffer().getDepthRt(),
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+		m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(
+		0, 1, m_r->getDepthDownscale().getHalfDepthColorRt(), m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 2, m_runCtx.m_rt, m_r->getLinearSampler());
 	cmdb->bindTextureAndSampler(0,
 		3,
-		m_upscale.m_noiseTex->getGrTexture(),
+		m_upscale.m_noiseTex->getGrTextureView(),
 		m_r->getTrilinearRepeatSampler(),
 		TextureUsageBit::SAMPLED_FRAGMENT);
 
@@ -165,8 +174,9 @@ void ForwardShading::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx
 	if(start != end)
 	{
 		const LightShadingResources& rsrc = m_r->getLightShading().getResources();
-		rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
-		rgraphCtx.bindTextureAndSampler(0, 1, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
+		rgraphCtx.bindColorTextureAndSampler(
+			0, 0, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
 		bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
 		bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
 		bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
@@ -218,7 +228,7 @@ void ForwardShading::populateRenderGraph(RenderingContext& ctx)
 	pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE});
 	pass.newConsumer({m_r->getDepthDownscale().getHalfDepthDepthRt(),
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ,
-		DepthStencilAspectBit::DEPTH});
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
 	pass.newConsumer({m_r->getDepthDownscale().getHalfDepthColorRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newConsumer({m_r->getDepthDownscale().getQuarterColorRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newConsumer({m_r->getVolumetric().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});

+ 4 - 2
src/anki/renderer/GBuffer.cpp

@@ -168,8 +168,10 @@ void GBuffer::populateRenderGraph(RenderingContext& ctx)
 		pass.newConsumer({m_colorRts[i], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 		pass.newProducer({m_colorRts[i], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 	}
-	pass.newConsumer({m_depthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, DepthStencilAspectBit::DEPTH});
-	pass.newProducer({m_depthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, DepthStencilAspectBit::DEPTH});
+
+	TextureSubresourceInfo subresource(DepthStencilAspectBit::DEPTH);
+	pass.newConsumer({m_depthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
+	pass.newProducer({m_depthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
 }
 
 } // end namespace anki

+ 32 - 31
src/anki/renderer/Indirect.cpp

@@ -200,7 +200,7 @@ Error Indirect::initLightShading(const ConfigSet& config)
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
 				| TextureUsageBit::GENERATE_MIPMAPS,
 			"GI refl");
-		texinit.m_mipmapsCount = m_lightShading.m_mipCount;
+		texinit.m_mipmapCount = m_lightShading.m_mipCount;
 		texinit.m_type = TextureType::CUBE_ARRAY;
 		texinit.m_layerCount = m_cacheEntries.getSize();
 		texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
@@ -451,10 +451,13 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 	// NOTE: Use nearest sampler because we don't want the result to sample the near tiles
 	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
 	{
-		rgraphCtx.bindTextureAndSampler(0, i, m_ctx.m_gbufferColorRts[i], m_r->getNearestSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, i, m_ctx.m_gbufferColorRts[i], m_r->getNearestSampler());
 	}
-	rgraphCtx.bindTextureAndSampler(
-		0, GBUFFER_COLOR_ATTACHMENT_COUNT, m_ctx.m_gbufferDepthRt, m_r->getNearestSampler());
+	rgraphCtx.bindTextureAndSampler(0,
+		GBUFFER_COLOR_ATTACHMENT_COUNT,
+		m_ctx.m_gbufferDepthRt,
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+		m_r->getNearestSampler());
 	cmdb->setVertexAttribute(0, 0, PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT), 0);
 	cmdb->setViewport(0, 0, m_lightShading.m_tileSize, m_lightShading.m_tileSize);
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
@@ -565,12 +568,15 @@ void Indirect::runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& r
 
 	ANKI_TRACE_SCOPED_EVENT(RENDER_IR);
 
+	TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, m_ctx.m_cacheEntryIdx));
+	subresource.m_mipmapCount = m_lightShading.m_mipCount;
+
 	TexturePtr texToBind;
 	TextureUsageBit usage;
-	DepthStencilAspectBit aspect;
-	rgraphCtx.getRenderTargetState(m_ctx.m_lightShadingRt, texToBind, usage, aspect);
+	rgraphCtx.getRenderTargetState(m_ctx.m_lightShadingRt, subresource, texToBind, usage);
 
-	rgraphCtx.m_commandBuffer->generateMipmaps2d(texToBind, faceIdx, m_ctx.m_cacheEntryIdx);
+	TextureViewInitInfo viewInit(texToBind, subresource);
+	rgraphCtx.m_commandBuffer->generateMipmaps2d(getGrManager().newTextureView(viewInit));
 }
 
 void Indirect::runIrradiance(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
@@ -584,7 +590,7 @@ void Indirect::runIrradiance(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 
 	// Set state
 	cmdb->bindShaderProgram(m_irradiance.m_grProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_ctx.m_lightShadingRt, m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_ctx.m_lightShadingRt, m_r->getLinearSampler());
 	cmdb->setViewport(0, 0, m_irradiance.m_tileSize, m_irradiance.m_tileSize);
 
 	// Set uniforms
@@ -642,12 +648,10 @@ void Indirect::populateRenderGraph(RenderingContext& rctx)
 				pass.newConsumer({m_ctx.m_gbufferColorRts[i], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 				pass.newProducer({m_ctx.m_gbufferColorRts[i], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 			}
-			pass.newConsumer({m_ctx.m_gbufferDepthRt,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-				DepthStencilAspectBit::DEPTH});
-			pass.newProducer({m_ctx.m_gbufferDepthRt,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-				DepthStencilAspectBit::DEPTH});
+
+			TextureSubresourceInfo subresource(DepthStencilAspectBit::DEPTH);
+			pass.newConsumer({m_ctx.m_gbufferDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
+			pass.newProducer({m_ctx.m_gbufferDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
 		}
 
 		// Light shading passes
@@ -678,16 +682,17 @@ void Indirect::populateRenderGraph(RenderingContext& rctx)
 					{});
 				pass.setWork(callbacks[faceIdx], this, 0);
 
-				TextureSurfaceInfo surf(0, 0, faceIdx, probeToUpdateCacheEntryIdx);
-				pass.newConsumer({m_ctx.m_lightShadingRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf});
-				pass.newProducer({m_ctx.m_lightShadingRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf});
+				TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
+				pass.newConsumer({m_ctx.m_lightShadingRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, subresource});
+				pass.newProducer({m_ctx.m_lightShadingRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, subresource});
 
 				for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
 				{
 					pass.newConsumer({m_ctx.m_gbufferColorRts[i], TextureUsageBit::SAMPLED_FRAGMENT});
 				}
-				pass.newConsumer(
-					{m_ctx.m_gbufferDepthRt, TextureUsageBit::SAMPLED_FRAGMENT, DepthStencilAspectBit::DEPTH});
+				pass.newConsumer({m_ctx.m_gbufferDepthRt,
+					TextureUsageBit::SAMPLED_FRAGMENT,
+					TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
 			}
 		}
 
@@ -707,12 +712,11 @@ void Indirect::populateRenderGraph(RenderingContext& rctx)
 				GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass(passNames[faceIdx]);
 				pass.setWork(callbacks[faceIdx], this, 0);
 
-				for(U mip = 0; mip < m_lightShading.m_mipCount; ++mip)
-				{
-					TextureSurfaceInfo surf(mip, 0, faceIdx, probeToUpdateCacheEntryIdx);
-					pass.newConsumer({m_ctx.m_lightShadingRt, TextureUsageBit::GENERATE_MIPMAPS, surf});
-					pass.newProducer({m_ctx.m_lightShadingRt, TextureUsageBit::GENERATE_MIPMAPS, surf});
-				}
+				TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
+				subresource.m_mipmapCount = m_lightShading.m_mipCount;
+
+				pass.newConsumer({m_ctx.m_lightShadingRt, TextureUsageBit::GENERATE_MIPMAPS, subresource});
+				pass.newProducer({m_ctx.m_lightShadingRt, TextureUsageBit::GENERATE_MIPMAPS, subresource});
 			}
 		}
 
@@ -743,12 +747,9 @@ void Indirect::populateRenderGraph(RenderingContext& rctx)
 
 				pass.newConsumer({m_ctx.m_lightShadingRt, TextureUsageBit::SAMPLED_FRAGMENT});
 
-				pass.newConsumer({m_ctx.m_irradianceRt,
-					TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-					TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx)});
-				pass.newProducer({m_ctx.m_irradianceRt,
-					TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-					TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx)});
+				TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
+				pass.newConsumer({m_ctx.m_irradianceRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, subresource});
+				pass.newProducer({m_ctx.m_irradianceRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, subresource});
 			}
 		}
 	}

+ 2 - 2
src/anki/renderer/Indirect.h

@@ -36,9 +36,9 @@ anki_internal:
 		return m_lightShading.m_mipCount;
 	}
 
-	TexturePtr getIntegrationLut() const
+	TextureViewPtr getIntegrationLut() const
 	{
-		return m_integrationLut->getGrTexture();
+		return m_integrationLut->getGrTextureView();
 	}
 
 	SamplerPtr getIntegrationLutSampler() const

+ 7 - 4
src/anki/renderer/LensFlare.cpp

@@ -115,7 +115,7 @@ void LensFlare::updateIndirectInfo(const RenderingContext& ctx, RenderPassWorkCo
 
 	rgraphCtx.bindStorageBuffer(0, 1, m_runCtx.m_indirectBuffHandle);
 	// Bind neareset because you don't need high quality
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getNearestSampler());
 	cmdb->dispatchCompute(count, 1, 1);
 }
 
@@ -196,9 +196,12 @@ void LensFlare::runDrawFlares(const RenderingContext& ctx, CommandBufferPtr& cmd
 		++c;
 
 		// Render
-		ANKI_ASSERT(flareEl.m_texture);
-		cmdb->bindTextureAndSampler(
-			0, 0, TexturePtr(flareEl.m_texture), m_r->getTrilinearRepeatSampler(), TextureUsageBit::SAMPLED_FRAGMENT);
+		ANKI_ASSERT(flareEl.m_textureView);
+		cmdb->bindTextureAndSampler(0,
+			0,
+			TextureViewPtr(const_cast<TextureView*>(flareEl.m_textureView)),
+			m_r->getTrilinearRepeatSampler(),
+			TextureUsageBit::SAMPLED_FRAGMENT);
 
 		cmdb->drawArraysIndirect(
 			PrimitiveTopology::TRIANGLE_STRIP, 1, i * sizeof(DrawArraysIndirectInfo), m_indirectBuff);

+ 6 - 6
src/anki/renderer/LightBin.cpp

@@ -318,9 +318,9 @@ public:
 	Atomic<U32> m_count = {0};
 	Atomic<U32> m_count2 = {0};
 
-	TexturePtr m_diffDecalTexAtlas;
+	TextureViewPtr m_diffDecalTexAtlas;
 	SpinLock m_diffDecalTexAtlasMtx;
-	TexturePtr m_normalRoughnessDecalTexAtlas;
+	TextureViewPtr m_normalRoughnessDecalTexAtlas;
 	SpinLock m_normalRoughnessDecalTexAtlasMtx;
 
 	LightBin* m_bin = nullptr;
@@ -490,8 +490,8 @@ Error LightBin::bin(const Mat4& viewMat,
 	// Sync
 	ANKI_CHECK(m_threadPool->waitForAllThreadsToFinish());
 
-	out.m_diffDecalTex = ctx.m_diffDecalTexAtlas;
-	out.m_normRoughnessDecalTex = ctx.m_normalRoughnessDecalTexAtlas;
+	out.m_diffDecalTexView = ctx.m_diffDecalTexAtlas;
+	out.m_normRoughnessDecalTexView = ctx.m_normalRoughnessDecalTexAtlas;
 
 	return Error::NONE;
 }
@@ -792,7 +792,7 @@ void LightBin::writeAndBinDecal(const DecalQueueElement& decalEl, LightBinContex
 	I idx = ctx.m_decalCount.fetchAdd(1);
 	ShaderDecal& decal = ctx.m_decals[idx];
 
-	TexturePtr atlas(decalEl.m_diffuseAtlas);
+	TextureViewPtr atlas(const_cast<TextureView*>(decalEl.m_diffuseAtlas));
 	Vec4 uv = decalEl.m_diffuseAtlasUv;
 	decal.m_diffUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
 	decal.m_blendFactors[0] = decalEl.m_diffuseAtlasBlendFactor;
@@ -807,7 +807,7 @@ void LightBin::writeAndBinDecal(const DecalQueueElement& decalEl, LightBinContex
 		ctx.m_diffDecalTexAtlas = atlas;
 	}
 
-	atlas.reset(decalEl.m_normalRoughnessAtlas);
+	atlas.reset(const_cast<TextureView*>(decalEl.m_normalRoughnessAtlas));
 	uv = decalEl.m_normalRoughnessAtlasUv;
 	decal.m_normRoughnessUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
 	decal.m_blendFactors[1] = decalEl.m_normalRoughnessAtlasBlendFactor;

+ 2 - 2
src/anki/renderer/LightBin.h

@@ -27,8 +27,8 @@ public:
 	StagingGpuMemoryToken m_clustersToken;
 	StagingGpuMemoryToken m_lightIndicesToken;
 
-	TexturePtr m_diffDecalTex;
-	TexturePtr m_normRoughnessDecalTex;
+	TextureViewPtr m_diffDecalTexView;
+	TextureViewPtr m_normRoughnessDecalTexView;
 };
 
 /// Bins lights and probes to clusters.

+ 18 - 12
src/anki/renderer/LightShading.cpp

@@ -130,15 +130,19 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 	cmdb->bindShaderProgram(m_progVariant->getProgram());
 
-	rgraphCtx.bindTextureAndSampler(1, 0, m_r->getGBuffer().getColorRt(0), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(1, 1, m_r->getGBuffer().getColorRt(1), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(1, 2, m_r->getGBuffer().getColorRt(2), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(1, 3, m_r->getGBuffer().getDepthRt(), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(1, 4, m_r->getSsao().getRt(), m_r->getLinearSampler());
-
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
-	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getIndirect().getReflectionRt(), m_r->getLinearSampler());
-	rgraphCtx.bindTextureAndSampler(0, 2, m_r->getIndirect().getIrradianceRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(1, 0, m_r->getGBuffer().getColorRt(0), m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(1, 1, m_r->getGBuffer().getColorRt(1), m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(1, 2, m_r->getGBuffer().getColorRt(2), m_r->getNearestSampler());
+	rgraphCtx.bindTextureAndSampler(1,
+		3,
+		m_r->getGBuffer().getDepthRt(),
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+		m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(1, 4, m_r->getSsao().getRt(), m_r->getLinearSampler());
+
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getIndirect().getReflectionRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 2, m_r->getIndirect().getIrradianceRt(), m_r->getLinearSampler());
 	cmdb->bindTextureAndSampler(0,
 		3,
 		m_r->getIndirect().getIntegrationLut(),
@@ -146,12 +150,12 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 		TextureUsageBit::SAMPLED_FRAGMENT);
 	cmdb->bindTextureAndSampler(0,
 		4,
-		(rsrc.m_diffDecalTex) ? rsrc.m_diffDecalTex : m_r->getDummyTexture(),
+		(rsrc.m_diffDecalTexView) ? rsrc.m_diffDecalTexView : m_r->getDummyTextureView(),
 		m_r->getTrilinearRepeatSampler(),
 		TextureUsageBit::SAMPLED_FRAGMENT);
 	cmdb->bindTextureAndSampler(0,
 		5,
-		(rsrc.m_normRoughnessDecalTex) ? rsrc.m_normRoughnessDecalTex : m_r->getDummyTexture(),
+		(rsrc.m_normRoughnessDecalTexView) ? rsrc.m_normRoughnessDecalTexView : m_r->getDummyTextureView(),
 		m_r->getTrilinearRepeatSampler(),
 		TextureUsageBit::SAMPLED_FRAGMENT);
 
@@ -211,7 +215,9 @@ void LightShading::populateRenderGraph(RenderingContext& ctx)
 	pass.newConsumer({m_r->getGBuffer().getColorRt(0), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newConsumer({m_r->getGBuffer().getColorRt(1), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_FRAGMENT});
-	pass.newConsumer({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_FRAGMENT, DepthStencilAspectBit::DEPTH});
+	pass.newConsumer({m_r->getGBuffer().getDepthRt(),
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
 	pass.newConsumer({m_r->getSsao().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newConsumer({m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newConsumer({m_r->getIndirect().getReflectionRt(), TextureUsageBit::SAMPLED_FRAGMENT});

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

@@ -156,7 +156,7 @@ void MainRenderer::runBlit(RenderPassWorkContext& rgraphCtx)
 	cmdb->setViewport(0, 0, m_width, m_height);
 
 	cmdb->bindShaderProgram(m_blitGrProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getFinalComposite().getRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getFinalComposite().getRt(), m_r->getLinearSampler());
 
 	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3, 1);
 }

+ 5 - 3
src/anki/renderer/RenderQueue.h

@@ -128,7 +128,8 @@ public:
 	Vec3 m_worldPosition;
 	Vec2 m_firstFlareSize;
 	Vec4 m_colorMultiplier;
-	Texture* m_texture; ///< Totaly unsafe but we can't have a smart ptr in here since there will be no deletion.
+	/// Totaly unsafe but we can't have a smart ptr in here since there will be no deletion.
+	const TextureView* m_textureView;
 	const void* m_userData;
 	RenderQueueDrawCallback m_drawCallback;
 };
@@ -141,9 +142,10 @@ class DecalQueueElement final
 public:
 	const void* m_userData;
 	RenderQueueDrawCallback m_drawCallback;
-	Texture* m_diffuseAtlas; ///< Totaly unsafe but we can't have a smart ptr in here since there will be no deletion.
 	/// Totaly unsafe but we can't have a smart ptr in here since there will be no deletion.
-	Texture* m_normalRoughnessAtlas;
+	const TextureView* m_diffuseAtlas;
+	/// Totaly unsafe but we can't have a smart ptr in here since there will be no deletion.
+	const TextureView* m_normalRoughnessAtlas;
 	Vec4 m_diffuseAtlasUv;
 	Vec4 m_normalRoughnessAtlasUv;
 	F32 m_diffuseAtlasBlendFactor;

+ 28 - 13
src/anki/renderer/Renderer.cpp

@@ -93,7 +93,10 @@ Error Renderer::initInternal(const ConfigSet& config)
 		texinit.m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
 		texinit.m_format = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
 		texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
-		m_dummyTex = getGrManager().newTexture(texinit);
+		TexturePtr tex = getGrManager().newTexture(texinit);
+
+		TextureViewInitInfo viewinit(tex);
+		m_dummyTexView = getGrManager().newTextureView(viewinit);
 	}
 
 	m_dummyBuff = getGrManager().newBuffer(BufferInitInfo(getDummyBufferSize(),
@@ -308,7 +311,7 @@ TextureInitInfo Renderer::create2DRenderTargetInitInfo(
 	init.m_layerCount = 1;
 	init.m_type = TextureType::_2D;
 	init.m_format = format;
-	init.m_mipmapsCount = 1;
+	init.m_mipmapCount = 1;
 	init.m_samples = 1;
 	init.m_usage = usage;
 
@@ -327,7 +330,7 @@ RenderTargetDescription Renderer::create2DRenderTargetDescription(
 	init.m_layerCount = 1;
 	init.m_type = TextureType::_2D;
 	init.m_format = format;
-	init.m_mipmapsCount = 1;
+	init.m_mipmapCount = 1;
 	init.m_samples = 1;
 	init.m_usage = usage;
 
@@ -346,13 +349,13 @@ TexturePtr Renderer::createAndClearRenderTarget(const TextureInitInfo& inf, cons
 	// Clear all surfaces
 	CommandBufferInitInfo cmdbinit;
 	cmdbinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
-	if((inf.m_mipmapsCount * faceCount * inf.m_layerCount * 4) < COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS)
+	if((inf.m_mipmapCount * faceCount * inf.m_layerCount * 4) < COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS)
 	{
 		cmdbinit.m_flags |= CommandBufferFlag::SMALL_BATCH;
 	}
 	CommandBufferPtr cmdb = m_gr->newCommandBuffer(cmdbinit);
 
-	for(U mip = 0; mip < inf.m_mipmapsCount; ++mip)
+	for(U mip = 0; mip < inf.m_mipmapCount; ++mip)
 	{
 		for(U face = 0; face < faceCount; ++face)
 		{
@@ -364,23 +367,35 @@ TexturePtr Renderer::createAndClearRenderTarget(const TextureInitInfo& inf, cons
 				Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS> colUsage = {};
 				TextureUsageBit dsUsage = TextureUsageBit::NONE;
 
-				if(inf.m_format.m_components >= ComponentFormat::FIRST_DEPTH_STENCIL
-					&& inf.m_format.m_components <= ComponentFormat::LAST_DEPTH_STENCIL)
+				if(componentFormatIsDepthStencil(inf.m_format.m_components))
 				{
-					fbInit.m_depthStencilAttachment.m_texture = tex;
-					fbInit.m_depthStencilAttachment.m_surface = surf;
-					fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH_STENCIL;
+					DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE;
+					if(componentFormatIsDepth(inf.m_format.m_components))
+					{
+						aspect |= DepthStencilAspectBit::DEPTH;
+					}
+
+					if(componentFormatIsStencil(inf.m_format.m_components))
+					{
+						aspect |= DepthStencilAspectBit::STENCIL;
+					}
+
+					TextureViewPtr view = getGrManager().newTextureView(TextureViewInitInfo(tex, surf, aspect));
+
+					fbInit.m_depthStencilAttachment.m_textureView = view;
 					fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
+					fbInit.m_depthStencilAttachment.m_stencilLoadOperation = AttachmentLoadOperation::CLEAR;
+					fbInit.m_depthStencilAttachment.m_clearValue = clearVal;
 
 					dsUsage = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
 				}
 				else
 				{
+					TextureViewPtr view = getGrManager().newTextureView(TextureViewInitInfo(tex, surf));
+
 					fbInit.m_colorAttachmentCount = 1;
-					fbInit.m_colorAttachments[0].m_texture = tex;
-					fbInit.m_colorAttachments[0].m_surface = surf;
+					fbInit.m_colorAttachments[0].m_textureView = view;
 					fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
-					fbInit.m_colorAttachments[0].m_stencilLoadOperation = AttachmentLoadOperation::CLEAR;
 					fbInit.m_colorAttachments[0].m_clearValue = clearVal;
 
 					colUsage[0] = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;

+ 3 - 3
src/anki/renderer/Renderer.h

@@ -285,9 +285,9 @@ anki_internal:
 		return m_willDrawToDefaultFbo;
 	}
 
-	TexturePtr getDummyTexture() const
+	TextureViewPtr getDummyTextureView() const
 	{
-		return m_dummyTex;
+		return m_dummyTexView;
 	}
 
 	BufferPtr getDummyBuffer() const
@@ -365,7 +365,7 @@ private:
 	Array<Mat4, 16> m_jitteredMats16x;
 	Array<Mat4, 8> m_jitteredMats8x;
 
-	TexturePtr m_dummyTex;
+	TextureViewPtr m_dummyTexView;
 	BufferPtr m_dummyBuff;
 
 	SamplerPtr m_nearestSampler;

+ 7 - 6
src/anki/renderer/ShadowMapping.cpp

@@ -149,7 +149,8 @@ void ShadowMapping::runEsm(RenderPassWorkContext& rgraphCtx)
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 	cmdb->bindShaderProgram(m_esmResolveGrProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_scratchRt, m_r->getLinearSampler());
+	rgraphCtx.bindTextureAndSampler(
+		0, 0, m_scratchRt, TextureSubresourceInfo(DepthStencilAspectBit::DEPTH), m_r->getLinearSampler());
 
 	for(const EsmResolveWorkItem& workItem : m_esmResolveWorkItems)
 	{
@@ -228,10 +229,9 @@ void ShadowMapping::populateRenderGraph(RenderingContext& ctx)
 				threadCountForScratchPass && threadCountForScratchPass <= m_r->getThreadPool().getThreadCount());
 			pass.setWork(runShadowmappingCallback, this, threadCountForScratchPass);
 
-			pass.newConsumer(
-				{m_scratchRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, DepthStencilAspectBit::DEPTH});
-			pass.newProducer(
-				{m_scratchRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, DepthStencilAspectBit::DEPTH});
+			TextureSubresourceInfo subresource = TextureSubresourceInfo(DepthStencilAspectBit::DEPTH);
+			pass.newConsumer({m_scratchRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
+			pass.newProducer({m_scratchRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
 		}
 
 		// ESM pass
@@ -242,7 +242,8 @@ void ShadowMapping::populateRenderGraph(RenderingContext& ctx)
 			pass.setFramebufferInfo(m_esmFbDescr, {{m_esmRt}}, {});
 			pass.setWork(runEsmCallback, this, 0);
 
-			pass.newConsumer({m_scratchRt, TextureUsageBit::SAMPLED_FRAGMENT, DepthStencilAspectBit::DEPTH});
+			pass.newConsumer(
+				{m_scratchRt, TextureUsageBit::SAMPLED_FRAGMENT, TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
 			pass.newConsumer({m_esmRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 			pass.newProducer({m_esmRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 		}

+ 12 - 9
src/anki/renderer/Ssao.cpp

@@ -131,11 +131,14 @@ void Ssao::runMain(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx
 	cmdb->setViewport(0, 0, m_width, m_height);
 	cmdb->bindShaderProgram(m_main.m_grProg);
 
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
-	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getGBuffer().getColorRt(2), m_r->getLinearSampler());
-	cmdb->bindTextureAndSampler(
-		0, 2, m_main.m_noiseTex->getGrTexture(), m_r->getTrilinearRepeatSampler(), TextureUsageBit::SAMPLED_FRAGMENT);
-	rgraphCtx.bindTextureAndSampler(0, 3, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getGBuffer().getColorRt(2), m_r->getLinearSampler());
+	cmdb->bindTextureAndSampler(0,
+		2,
+		m_main.m_noiseTex->getGrTextureView(),
+		m_r->getTrilinearRepeatSampler(),
+		TextureUsageBit::SAMPLED_FRAGMENT);
+	rgraphCtx.bindColorTextureAndSampler(0, 3, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
 
 	struct Unis
 	{
@@ -162,8 +165,8 @@ void Ssao::runHBlur(RenderPassWorkContext& rgraphCtx)
 
 	cmdb->setViewport(0, 0, m_width, m_height);
 	cmdb->bindShaderProgram(m_hblur.m_grProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_runCtx.m_rts[m_r->getFrameCount() & 1], m_r->getLinearSampler());
-	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_rts[m_r->getFrameCount() & 1], m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
 	drawQuad(cmdb);
 }
 
@@ -173,8 +176,8 @@ void Ssao::runVBlur(RenderPassWorkContext& rgraphCtx)
 
 	cmdb->setViewport(0, 0, m_width, m_height);
 	cmdb->bindShaderProgram(m_vblur.m_grProg);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
-	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
 	drawQuad(cmdb);
 }
 

+ 10 - 4
src/anki/renderer/TemporalAA.cpp

@@ -74,9 +74,13 @@ void TemporalAA::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphC
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
 	cmdb->bindShaderProgram(m_grProgs[m_r->getFrameCount() & 1]);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getGBuffer().getDepthRt(), m_r->getLinearSampler());
-	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getLightShading().getRt(), m_r->getLinearSampler());
-	rgraphCtx.bindTextureAndSampler(0, 2, m_runCtx.m_historyRt, m_r->getLinearSampler());
+	rgraphCtx.bindTextureAndSampler(0,
+		0,
+		m_r->getGBuffer().getDepthRt(),
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+		m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getLightShading().getRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 2, m_runCtx.m_historyRt, m_r->getLinearSampler());
 
 	Mat4* unis = allocateAndBindUniforms<Mat4*>(sizeof(Mat4), cmdb, 0, 0);
 	*unis = ctx.m_jitterMat * ctx.m_prevViewProjMat * ctx.m_viewProjMatJitter.getInverse();
@@ -104,7 +108,9 @@ void TemporalAA::populateRenderGraph(RenderingContext& ctx)
 	pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_renderRt}}, {});
 
 	pass.newConsumer({m_runCtx.m_renderRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-	pass.newConsumer({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_FRAGMENT, DepthStencilAspectBit::DEPTH});
+	pass.newConsumer({m_r->getGBuffer().getDepthRt(),
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
 	pass.newConsumer({m_r->getLightShading().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newConsumer({m_runCtx.m_historyRt, TextureUsageBit::SAMPLED_FRAGMENT});
 

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

@@ -68,7 +68,7 @@ void Tonemapping::run(RenderPassWorkContext& rgraphCtx)
 
 	cmdb->bindShaderProgram(m_grProg);
 	rgraphCtx.bindStorageBuffer(0, 0, m_runCtx.m_buffHandle);
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDownscaleBlur().getPassRt(m_rtIdx), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getDownscaleBlur().getPassRt(m_rtIdx), m_r->getLinearSampler());
 
 	cmdb->dispatchCompute(1, 1, 1);
 }

+ 10 - 7
src/anki/renderer/Volumetric.cpp

@@ -124,11 +124,14 @@ void Volumetric::runMain(const RenderingContext& ctx, RenderPassWorkContext& rgr
 
 	cmdb->setViewport(0, 0, m_width, m_height);
 
-	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
-	cmdb->bindTextureAndSampler(
-		0, 1, m_main.m_noiseTex->getGrTexture(), m_r->getTrilinearRepeatSampler(), TextureUsageBit::SAMPLED_FRAGMENT);
-	rgraphCtx.bindTextureAndSampler(0, 2, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
-	rgraphCtx.bindTextureAndSampler(0, 3, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getLinearSampler());
+	cmdb->bindTextureAndSampler(0,
+		1,
+		m_main.m_noiseTex->getGrTextureView(),
+		m_r->getTrilinearRepeatSampler(),
+		TextureUsageBit::SAMPLED_FRAGMENT);
+	rgraphCtx.bindColorTextureAndSampler(0, 2, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 3, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
 
 	const LightShadingResources& rsrc = m_r->getLightShading().getResources();
 	bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
@@ -166,7 +169,7 @@ void Volumetric::runHBlur(RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
-	rgraphCtx.bindTextureAndSampler(0, 0, m_runCtx.m_rts[m_r->getFrameCount() & 1], m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_rts[m_r->getFrameCount() & 1], m_r->getLinearSampler());
 	cmdb->bindShaderProgram(m_hblur.m_grProg);
 	cmdb->setViewport(0, 0, m_width, m_height);
 
@@ -177,7 +180,7 @@ void Volumetric::runVBlur(RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
-	rgraphCtx.bindTextureAndSampler(0, 0, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
 	cmdb->bindShaderProgram(m_vblur.m_grProg);
 	cmdb->setViewport(0, 0, m_width, m_height);
 

+ 1 - 1
src/anki/resource/ShaderProgramResource.cpp

@@ -1073,7 +1073,7 @@ void ShaderProgramResource::initVariant(WeakArray<const ShaderProgramResourceMut
 	shaderHeaderSrc.join("", shaderHeader);
 
 	// Create the shaders and the program
-	ShaderProgramInitInfo progInf;
+	ShaderProgramInitInfo progInf("RsrcProg");
 	for(ShaderType i = ShaderType::FIRST; i < ShaderType::COUNT; ++i)
 	{
 		if(!m_sources[i])

+ 6 - 1
src/anki/resource/TextureAtlas.h

@@ -42,11 +42,16 @@ public:
 	/// Load a texture atlas.
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
-	TexturePtr getGrTexture() const
+	const TexturePtr& getGrTexture() const
 	{
 		return m_tex->getGrTexture();
 	}
 
+	const TextureViewPtr& getGrTextureView() const
+	{
+		return m_tex->getGrTextureView();
+	}
+
 	U getWidth() const
 	{
 		return m_size[0];

+ 21 - 11
src/anki/resource/TextureResource.cpp

@@ -70,6 +70,7 @@ Error TextureResource::load(const ResourceFilename& filename, Bool async)
 	TextureInitInfo init("RsrcTex");
 	init.m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION
 		| TextureUsageBit::TRANSFER_DESTINATION;
+	init.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION;
 	U faces = 0;
 
 	ResourceFilePtr file;
@@ -160,7 +161,7 @@ Error TextureResource::load(const ResourceFilename& filename, Bool async)
 	}
 
 	// mipmapsCount
-	init.m_mipmapsCount = loader.getMipLevelsCount();
+	init.m_mipmapCount = loader.getMipLevelsCount();
 
 	// Create the texture
 	m_tex = getManager().getGrManager().newTexture(init);
@@ -193,6 +194,11 @@ Error TextureResource::load(const ResourceFilename& filename, Bool async)
 
 	m_size = UVec3(init.m_width, init.m_height, init.m_depth);
 	m_layerCount = init.m_layerCount;
+
+	// Create the texture view
+	TextureViewInitInfo viewInit(m_tex, "Rsrc");
+	m_texView = getManager().getGrManager().newTextureView(viewInit);
+
 	return Error::NONE;
 }
 
@@ -247,7 +253,10 @@ Error TextureResource::load(LoadingContext& ctx)
 				surfOrVolSize = vol.m_data.getSize();
 				surfOrVolData = &vol.m_data[0];
 
-				ctx.m_gr->getTextureVolumeUploadInfo(ctx.m_tex, TextureVolumeInfo(mip), allocationSize);
+				allocationSize = computeVolumeSize(ctx.m_tex->getWidth() >> mip,
+					ctx.m_tex->getHeight() >> mip,
+					ctx.m_tex->getDepth() >> mip,
+					ctx.m_tex->getPixelFormat());
 			}
 			else
 			{
@@ -255,8 +264,8 @@ Error TextureResource::load(LoadingContext& ctx)
 				surfOrVolSize = surf.m_data.getSize();
 				surfOrVolData = &surf.m_data[0];
 
-				ctx.m_gr->getTextureSurfaceUploadInfo(
-					ctx.m_tex, TextureSurfaceInfo(mip, 0, face, layer), allocationSize);
+				allocationSize = computeSurfaceSize(
+					ctx.m_tex->getWidth() >> mip, ctx.m_tex->getHeight() >> mip, ctx.m_tex->getPixelFormat());
 			}
 
 			ANKI_ASSERT(allocationSize >= surfOrVolSize);
@@ -267,19 +276,20 @@ Error TextureResource::load(LoadingContext& ctx)
 
 			memcpy(data, surfOrVolData, surfOrVolSize);
 
+			// Create temp tex view
+			TextureSubresourceInfo subresource;
 			if(ctx.m_texType == TextureType::_3D)
 			{
-				TextureVolumeInfo vol(mip);
-				cmdb->copyBufferToTextureVolume(
-					handle.getBuffer(), handle.getOffset(), handle.getRange(), ctx.m_tex, vol);
+				subresource = TextureSubresourceInfo(TextureVolumeInfo(mip));
 			}
 			else
 			{
-				TextureSurfaceInfo surf(mip, 0, face, layer);
-
-				cmdb->copyBufferToTextureSurface(
-					handle.getBuffer(), handle.getOffset(), handle.getRange(), ctx.m_tex, surf);
+				subresource = TextureSubresourceInfo(TextureSurfaceInfo(mip, 0, face, layer));
 			}
+
+			TextureViewPtr tmpView = ctx.m_gr->newTextureView(TextureViewInitInfo(ctx.m_tex, subresource, "RsrcTmp"));
+
+			cmdb->copyBufferToTextureView(handle.getBuffer(), handle.getOffset(), handle.getRange(), tmpView);
 		}
 
 		// Set the barriers of the batch

+ 8 - 7
src/anki/resource/TextureResource.h

@@ -31,22 +31,22 @@ public:
 	/// Load a texture
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
-	/// Get the texture
+	/// Get the texture.
 	const TexturePtr& getGrTexture() const
 	{
 		return m_tex;
 	}
 
-	/// Get the sampler.
-	const SamplerPtr& getSampler() const
+	/// Get the texture view.
+	const TextureViewPtr& getGrTextureView() const
 	{
-		return m_sampler;
+		return m_texView;
 	}
 
-	/// Get the texture
-	TexturePtr& getGrTexture()
+	/// Get the sampler.
+	const SamplerPtr& getSampler() const
 	{
-		return m_tex;
+		return m_sampler;
 	}
 
 	U getWidth() const
@@ -80,6 +80,7 @@ private:
 	class LoadingContext;
 
 	TexturePtr m_tex;
+	TextureViewPtr m_texView;
 	SamplerPtr m_sampler;
 	UVec3 m_size = UVec3(0u);
 	U32 m_layerCount = 0;

+ 2 - 2
src/anki/scene/DecalComponent.h

@@ -121,10 +121,10 @@ public:
 	void setupDecalQueueElement(DecalQueueElement& el) const
 	{
 		el.m_diffuseAtlas = (m_layers[LayerType::DIFFUSE].m_atlas)
-			? m_layers[LayerType::DIFFUSE].m_atlas->getGrTexture().get()
+			? m_layers[LayerType::DIFFUSE].m_atlas->getGrTextureView().get()
 			: nullptr;
 		el.m_normalRoughnessAtlas = (m_layers[LayerType::NORMAL_ROUGHNESS].m_atlas)
-			? m_layers[LayerType::NORMAL_ROUGHNESS].m_atlas->getGrTexture().get()
+			? m_layers[LayerType::NORMAL_ROUGHNESS].m_atlas->getGrTextureView().get()
 			: nullptr;
 		el.m_diffuseAtlasUv = m_layers[LayerType::DIFFUSE].m_uv;
 		el.m_normalRoughnessAtlasUv = m_layers[LayerType::NORMAL_ROUGHNESS].m_uv;

+ 1 - 1
src/anki/scene/LensFlareComponent.h

@@ -85,7 +85,7 @@ public:
 		el.m_worldPosition = m_worldPosition.xyz();
 		el.m_firstFlareSize = m_firstFlareSize;
 		el.m_colorMultiplier = m_colorMul;
-		el.m_texture = m_tex->getGrTexture().get();
+		el.m_textureView = m_tex->getGrTextureView().get();
 		el.m_userData = this;
 		el.m_drawCallback = debugDrawCallback;
 	}

+ 1 - 1
src/anki/scene/RenderComponent.cpp

@@ -187,7 +187,7 @@ void RenderComponent::allocateAndSetupUniforms(
 		{
 			ctx.m_commandBuffer->bindTextureAndSampler(set,
 				progVariant.getTextureUnit(progvar),
-				mvar.getValue<TextureResourcePtr>()->getGrTexture(),
+				mvar.getValue<TextureResourcePtr>()->getGrTextureView(),
 				mvar.getValue<TextureResourcePtr>()->getSampler(),
 				TextureUsageBit::SAMPLED_FRAGMENT);
 			break;

+ 2 - 2
src/anki/ui/Canvas.cpp

@@ -233,8 +233,8 @@ void Canvas::appendToCommandBuffer(CommandBufferPtr cmdb)
 		{
 			progToBind = m_grProgs[RGBA_TEX];
 
-			Texture* t = numberToPtr<Texture*>(ptrToNumber(cmd->texture.ptr) & ~FONT_TEXTURE_MASK);
-			cmdb->bindTextureAndSampler(0, 0, TexturePtr(t), m_sampler, TextureUsageBit::SAMPLED_FRAGMENT);
+			TextureView* t = numberToPtr<TextureView*>(ptrToNumber(cmd->texture.ptr) & ~FONT_TEXTURE_MASK);
+			cmdb->bindTextureAndSampler(0, 0, TextureViewPtr(t), m_sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		}
 		else
 		{

+ 17 - 9
src/anki/ui/Font.cpp

@@ -56,7 +56,7 @@ Error Font::init(const CString& filename, const std::initializer_list<U32>& font
 
 	// End building
 	nk_handle texHandle;
-	texHandle.ptr = numberToPtr<void*>(ptrToNumber(m_tex.get()) | FONT_TEXTURE_MASK);
+	texHandle.ptr = numberToPtr<void*>(ptrToNumber(m_texView.get()) | FONT_TEXTURE_MASK);
 	nk_font_atlas_end(&m_atlas, texHandle, nullptr);
 
 	nk_font_atlas_cleanup(&m_atlas);
@@ -77,28 +77,36 @@ void Font::createTexture(const void* data, U32 width, U32 height)
 	buff->unmap();
 
 	// Create the texture
-	TextureInitInfo texInit;
+	TextureInitInfo texInit("Font");
 	texInit.m_width = width;
 	texInit.m_height = height;
 	texInit.m_format = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
 	texInit.m_usage =
 		TextureUsageBit::TRANSFER_DESTINATION | TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::GENERATE_MIPMAPS;
-	texInit.m_mipmapsCount = 4;
+	texInit.m_mipmapCount = 4;
 
 	m_tex = m_manager->getGrManager().newTexture(texInit);
 
+	// Create the whole texture view
+	m_texView = m_manager->getGrManager().newTextureView(TextureViewInitInfo(m_tex, "Font"));
+
 	// Do the copy
+	static const TextureSurfaceInfo surf(0, 0, 0, 0);
 	CommandBufferInitInfo cmdbInit;
 	cmdbInit.m_flags = CommandBufferFlag::TRANSFER_WORK | CommandBufferFlag::SMALL_BATCH;
 	CommandBufferPtr cmdb = m_manager->getGrManager().newCommandBuffer(cmdbInit);
+	{
+		TextureViewInitInfo viewInit(m_tex, surf, DepthStencilAspectBit::NONE, "TempFont");
+		TextureViewPtr tmpView = m_manager->getGrManager().newTextureView(viewInit);
 
-	TextureSurfaceInfo surf(0, 0, 0, 0);
+		cmdb->setTextureSurfaceBarrier(m_tex, TextureUsageBit::NONE, TextureUsageBit::TRANSFER_DESTINATION, surf);
+		cmdb->copyBufferToTextureView(buff, 0, buffSize, tmpView);
+		cmdb->setTextureSurfaceBarrier(
+			m_tex, TextureUsageBit::TRANSFER_DESTINATION, TextureUsageBit::GENERATE_MIPMAPS, surf);
+	}
 
-	cmdb->setTextureSurfaceBarrier(m_tex, TextureUsageBit::NONE, TextureUsageBit::TRANSFER_DESTINATION, surf);
-	cmdb->copyBufferToTextureSurface(buff, 0, buffSize, m_tex, surf);
-	cmdb->setTextureSurfaceBarrier(
-		m_tex, TextureUsageBit::TRANSFER_DESTINATION, TextureUsageBit::GENERATE_MIPMAPS, surf);
-	cmdb->generateMipmaps2d(m_tex, 0, 0);
+	// Gen mips
+	cmdb->generateMipmaps2d(m_texView);
 	cmdb->setTextureSurfaceBarrier(m_tex, TextureUsageBit::GENERATE_MIPMAPS, TextureUsageBit::SAMPLED_FRAGMENT, surf);
 
 	cmdb->flush();

+ 4 - 3
src/anki/ui/Font.h

@@ -31,9 +31,9 @@ public:
 
 anki_internal:
 	/// Get font image atlas.
-	const TexturePtr& getTexture() const
+	const TextureViewPtr& getTextureView() const
 	{
-		return m_tex;
+		return m_texView;
 	}
 
 	const nk_user_font& getFont(U32 fontHeight) const
@@ -47,7 +47,7 @@ anki_internal:
 		}
 
 		ANKI_ASSERT(0);
-		return *static_cast<const nk_user_font*>(nullptr);
+		return m_fonts[0].m_font->handle;
 	}
 
 private:
@@ -62,6 +62,7 @@ private:
 	DynamicArray<NkFont> m_fonts;
 
 	TexturePtr m_tex;
+	TextureViewPtr m_texView; ///< Whole texture view
 
 	void createTexture(const void* data, U32 width, U32 height);
 };

+ 223 - 52
tests/gr/Gr.cpp

@@ -330,22 +330,24 @@ static void* setStorage(PtrSize size, CommandBufferPtr& cmdb, U set, U binding)
 #define SET_UNIFORMS(type_, size_, cmdb_, set_, binding_) static_cast<type_>(setUniforms(size_, cmdb_, set_, binding_))
 #define SET_STORAGE(type_, size_, cmdb_, set_, binding_) static_cast<type_>(setStorage(size_, cmdb_, set_, binding_))
 
-#define UPLOAD_TEX_SURFACE(cmdb_, tex_, surf_, ptr_, size_, handle_)                                                  \
-	do                                                                                                                \
-	{                                                                                                                 \
-		ANKI_TEST_EXPECT_NO_ERR(transfAlloc->allocate(size_, handle_));                                               \
-		void* f = handle_.getMappedMemory();                                                                          \
-		memcpy(f, ptr_, size_);                                                                                       \
-		cmdb_->copyBufferToTextureSurface(handle_.getBuffer(), handle_.getOffset(), handle_.getRange(), tex_, surf_); \
+#define UPLOAD_TEX_SURFACE(cmdb_, tex_, surf_, ptr_, size_, handle_)                                        \
+	do                                                                                                      \
+	{                                                                                                       \
+		ANKI_TEST_EXPECT_NO_ERR(transfAlloc->allocate(size_, handle_));                                     \
+		void* f = handle_.getMappedMemory();                                                                \
+		memcpy(f, ptr_, size_);                                                                             \
+		TextureViewPtr view = gr->newTextureView(TextureViewInitInfo(tex_, surf_));                         \
+		cmdb_->copyBufferToTextureView(handle_.getBuffer(), handle_.getOffset(), handle_.getRange(), view); \
 	} while(0)
 
-#define UPLOAD_TEX_VOL(cmdb_, tex_, vol_, ptr_, size_, handle_)                                                     \
-	do                                                                                                              \
-	{                                                                                                               \
-		ANKI_TEST_EXPECT_NO_ERR(transfAlloc->allocate(size_, handle_));                                             \
-		void* f = handle_.getMappedMemory();                                                                        \
-		memcpy(f, ptr_, size_);                                                                                     \
-		cmdb_->copyBufferToTextureVolume(handle_.getBuffer(), handle_.getOffset(), handle_.getRange(), tex_, vol_); \
+#define UPLOAD_TEX_VOL(cmdb_, tex_, vol_, ptr_, size_, handle_)                                             \
+	do                                                                                                      \
+	{                                                                                                       \
+		ANKI_TEST_EXPECT_NO_ERR(transfAlloc->allocate(size_, handle_));                                     \
+		void* f = handle_.getMappedMemory();                                                                \
+		memcpy(f, ptr_, size_);                                                                             \
+		TextureViewPtr view = gr->newTextureView(TextureViewInitInfo(tex_, vol_));                          \
+		cmdb_->copyBufferToTextureView(handle_.getBuffer(), handle_.getOffset(), handle_.getRange(), view); \
 	} while(0)
 
 const PixelFormat DS_FORMAT = PixelFormat(ComponentFormat::D24S8, TransformFormat::UNORM);
@@ -354,11 +356,7 @@ static ShaderProgramPtr createProgram(CString vertSrc, CString fragSrc, GrManage
 {
 	ShaderPtr vert = gr.newShader({ShaderType::VERTEX, vertSrc});
 	ShaderPtr frag = gr.newShader({ShaderType::FRAGMENT, fragSrc});
-
-	ShaderProgramInitInfo inf;
-	inf.m_shaders[ShaderType::VERTEX] = vert;
-	inf.m_shaders[ShaderType::FRAGMENT] = frag;
-	return gr.newShaderProgram(inf);
+	return gr.newShaderProgram(ShaderProgramInitInfo(vert, frag));
 }
 
 static FramebufferPtr createDefaultFb(GrManager& gr)
@@ -574,20 +572,26 @@ ANKI_TEST(Gr, ViewportAndScissorOffscreen)
 	init.m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE;
 	init.m_height = RT_HEIGHT;
 	init.m_width = RT_WIDTH;
-	init.m_mipmapsCount = 1;
+	init.m_mipmapCount = 1;
 	init.m_depth = 1;
 	init.m_layerCount = 1;
 	init.m_samples = 1;
 	init.m_type = TextureType::_2D;
 	TexturePtr rt = gr->newTexture(init);
 
+	TextureViewInitInfo viewInit(rt);
+	TextureViewPtr texView = gr->newTextureView(viewInit);
+
 	Array<FramebufferPtr, 4> fb;
 	for(FramebufferPtr& f : fb)
 	{
+		TextureViewInitInfo viewInf(rt);
+		TextureViewPtr view = gr->newTextureView(viewInf);
+
 		FramebufferInitInfo fbinit;
 		fbinit.m_colorAttachmentCount = 1;
 		fbinit.m_colorAttachments[0].m_clearValue.m_colorf = {{randFloat(1.0), randFloat(1.0), randFloat(1.0), 1.0}};
-		fbinit.m_colorAttachments[0].m_texture = rt;
+		fbinit.m_colorAttachments[0].m_textureView = view;
 
 		f = gr->newFramebuffer(fbinit);
 	}
@@ -666,7 +670,7 @@ ANKI_TEST(Gr, ViewportAndScissorOffscreen)
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureUsageBit::SAMPLED_FRAGMENT,
 			TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->bindTextureAndSampler(0, 0, rt, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
+		cmdb->bindTextureAndSampler(0, 0, texView, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->beginRenderPass(defaultFb, {}, {});
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
@@ -875,7 +879,7 @@ ANKI_TEST(Gr, Texture)
 	init.m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
 	init.m_height = 4;
 	init.m_width = 4;
-	init.m_mipmapsCount = 2;
+	init.m_mipmapCount = 2;
 	init.m_depth = 1;
 	init.m_layerCount = 1;
 	init.m_samples = 1;
@@ -883,6 +887,9 @@ ANKI_TEST(Gr, Texture)
 
 	TexturePtr b = gr->newTexture(init);
 
+	TextureViewInitInfo view(b);
+	TextureViewPtr v = gr->newTextureView(view);
+
 	COMMON_END()
 }
 
@@ -909,7 +916,7 @@ ANKI_TEST(Gr, DrawWithTexture)
 	init.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
 	init.m_height = 2;
 	init.m_width = 2;
-	init.m_mipmapsCount = 2;
+	init.m_mipmapCount = 2;
 	init.m_samples = 1;
 	init.m_depth = 1;
 	init.m_layerCount = 1;
@@ -917,18 +924,22 @@ ANKI_TEST(Gr, DrawWithTexture)
 
 	TexturePtr a = gr->newTexture(init);
 
+	TextureViewPtr aView = gr->newTextureView(TextureViewInitInfo(a));
+
 	//
 	// Create texture B
 	//
 	init.m_width = 4;
 	init.m_height = 4;
-	init.m_mipmapsCount = 3;
+	init.m_mipmapCount = 3;
 	init.m_usage =
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::TRANSFER_DESTINATION | TextureUsageBit::GENERATE_MIPMAPS;
 	init.m_initialUsage = TextureUsageBit::NONE;
 
 	TexturePtr b = gr->newTexture(init);
 
+	TextureViewPtr bView = gr->newTextureView(TextureViewInitInfo(b));
+
 	//
 	// Upload all textures
 	//
@@ -1010,7 +1021,7 @@ ANKI_TEST(Gr, DrawWithTexture)
 	cmdb->setTextureSurfaceBarrier(
 		b, TextureUsageBit::TRANSFER_DESTINATION, TextureUsageBit::GENERATE_MIPMAPS, TextureSurfaceInfo(0, 0, 0, 0));
 
-	cmdb->generateMipmaps2d(b, 0, 0);
+	cmdb->generateMipmaps2d(gr->newTextureView(TextureViewInitInfo(b)));
 
 	// Set barriers
 	cmdb->setTextureSurfaceBarrier(
@@ -1054,15 +1065,15 @@ ANKI_TEST(Gr, DrawWithTexture)
 		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
-		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
 		CommandBufferPtr cmdb = gr->newCommandBuffer(cinit);
 
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 		cmdb->bindShaderProgram(prog);
 		cmdb->beginRenderPass(fb, {}, {});
 
-		cmdb->bindTextureAndSampler(0, 0, a, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
-		cmdb->bindTextureAndSampler(0, 1, b, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
+		cmdb->bindTextureAndSampler(0, 0, aView, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
+		cmdb->bindTextureAndSampler(0, 1, bView, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
 		cmdb->flush();
@@ -1138,20 +1149,18 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 	const U TEX_SIZE = 256;
 
 	TextureInitInfo init;
-	init.m_depth = 1;
 	init.m_format = COL_FORMAT;
 	init.m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE;
 	init.m_height = TEX_SIZE;
 	init.m_width = TEX_SIZE;
-	init.m_mipmapsCount = 1;
-	init.m_depth = 1;
-	init.m_layerCount = 1;
-	init.m_samples = 1;
 	init.m_type = TextureType::_2D;
 
 	TexturePtr col0 = gr.newTexture(init);
 	TexturePtr col1 = gr.newTexture(init);
 
+	TextureViewPtr col0View = gr.newTextureView(TextureViewInitInfo(col0));
+	TextureViewPtr col1View = gr.newTextureView(TextureViewInitInfo(col1));
+
 	init.m_format = DS_FORMAT;
 	TexturePtr dp = gr.newTexture(init);
 
@@ -1160,12 +1169,13 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 	//
 	FramebufferInitInfo fbinit;
 	fbinit.m_colorAttachmentCount = 2;
-	fbinit.m_colorAttachments[0].m_texture = col0;
+	fbinit.m_colorAttachments[0].m_textureView = gr.newTextureView(TextureViewInitInfo(col0));
 	fbinit.m_colorAttachments[0].m_clearValue.m_colorf = {{0.1, 0.0, 0.0, 0.0}};
-	fbinit.m_colorAttachments[1].m_texture = col1;
+	fbinit.m_colorAttachments[1].m_textureView = gr.newTextureView(TextureViewInitInfo(col1));
 	fbinit.m_colorAttachments[1].m_clearValue.m_colorf = {{0.0, 0.1, 0.0, 0.0}};
-	fbinit.m_depthStencilAttachment.m_texture = dp;
-	fbinit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+	TextureViewInitInfo viewInit(dp);
+	viewInit.m_depthStencilAspect = DepthStencilAspectBit::DEPTH;
+	fbinit.m_depthStencilAttachment.m_textureView = gr.newTextureView(viewInit);
 	fbinit.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
 
 	FramebufferPtr fb = gr.newFramebuffer(fbinit);
@@ -1253,8 +1263,8 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 		cmdb->beginRenderPass(dfb, {}, {});
 		cmdb->bindShaderProgram(resolveProg);
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
-		cmdb->bindTextureAndSampler(0, 0, col0, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
-		cmdb->bindTextureAndSampler(0, 1, col1, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
+		cmdb->bindTextureAndSampler(0, 0, col0View, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
+		cmdb->bindTextureAndSampler(0, 1, col1View, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
 
@@ -1301,13 +1311,18 @@ ANKI_TEST(Gr, ImageLoadStore)
 
 	TextureInitInfo init;
 	init.m_width = init.m_height = 4;
-	init.m_mipmapsCount = 2;
+	init.m_mipmapCount = 2;
 	init.m_usage = TextureUsageBit::CLEAR | TextureUsageBit::SAMPLED_ALL | TextureUsageBit::IMAGE_COMPUTE_WRITE;
 	init.m_type = TextureType::_2D;
 	init.m_format = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
 
 	TexturePtr tex = gr->newTexture(init);
 
+	TextureViewInitInfo viewInit(tex);
+	viewInit.m_firstMipmap = 1;
+	viewInit.m_mipmapCount = 1;
+	TextureViewPtr view = gr->newTextureView(viewInit);
+
 	// Prog
 	ShaderProgramPtr prog = createProgram(VERT_QUAD_SRC, FRAG_SIMPLE_TEX_SRC, *gr);
 
@@ -1328,7 +1343,8 @@ ANKI_TEST(Gr, ImageLoadStore)
 
 	ClearValue clear;
 	clear.m_colorf = {{0.0, 1.0, 0.0, 1.0}};
-	cmdb->clearTextureSurface(tex, TextureSurfaceInfo(0, 0, 0, 0), clear);
+	TextureViewInitInfo viewInit2(tex, TextureSurfaceInfo(0, 0, 0, 0));
+	cmdb->clearTextureView(gr->newTextureView(viewInit2), clear);
 
 	cmdb->setTextureSurfaceBarrier(
 		tex, TextureUsageBit::CLEAR, TextureUsageBit::SAMPLED_FRAGMENT, TextureSurfaceInfo(0, 0, 0, 0));
@@ -1336,7 +1352,8 @@ ANKI_TEST(Gr, ImageLoadStore)
 	cmdb->setTextureSurfaceBarrier(tex, TextureUsageBit::NONE, TextureUsageBit::CLEAR, TextureSurfaceInfo(1, 0, 0, 0));
 
 	clear.m_colorf = {{0.0, 0.0, 1.0, 1.0}};
-	cmdb->clearTextureSurface(tex, TextureSurfaceInfo(1, 0, 0, 0), clear);
+	TextureViewInitInfo viewInit3(tex, TextureSurfaceInfo(1, 0, 0, 0));
+	cmdb->clearTextureView(gr->newTextureView(viewInit3), clear);
 
 	cmdb->setTextureSurfaceBarrier(
 		tex, TextureUsageBit::CLEAR, TextureUsageBit::IMAGE_COMPUTE_WRITE, TextureSurfaceInfo(1, 0, 0, 0));
@@ -1352,7 +1369,8 @@ ANKI_TEST(Gr, ImageLoadStore)
 		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
-		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::COMPUTE_WORK;
+		cinit.m_flags =
+			CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::COMPUTE_WORK | CommandBufferFlag::SMALL_BATCH;
 		CommandBufferPtr cmdb = gr->newCommandBuffer(cinit);
 
 		// Write image
@@ -1362,7 +1380,7 @@ ANKI_TEST(Gr, ImageLoadStore)
 		cmdb->setTextureSurfaceBarrier(
 			tex, TextureUsageBit::NONE, TextureUsageBit::IMAGE_COMPUTE_WRITE, TextureSurfaceInfo(1, 0, 0, 0));
 		cmdb->bindShaderProgram(compProg);
-		cmdb->bindImage(0, 0, tex, 1);
+		cmdb->bindImage(0, 0, view);
 		cmdb->dispatchCompute(WIDTH / 2, HEIGHT / 2, 1);
 		cmdb->setTextureSurfaceBarrier(tex,
 			TextureUsageBit::IMAGE_COMPUTE_WRITE,
@@ -1374,7 +1392,8 @@ ANKI_TEST(Gr, ImageLoadStore)
 
 		cmdb->bindShaderProgram(prog);
 		cmdb->beginRenderPass(dfb, {}, {});
-		cmdb->bindTextureAndSampler(0, 0, tex, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
+		cmdb->bindTextureAndSampler(
+			0, 0, gr->newTextureView(TextureViewInitInfo(tex)), sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
 
@@ -1414,7 +1433,7 @@ ANKI_TEST(Gr, 3DTextures)
 	init.m_initialUsage = TextureUsageBit::TRANSFER_DESTINATION;
 	init.m_height = 2;
 	init.m_width = 2;
-	init.m_mipmapsCount = 2;
+	init.m_mipmapCount = 2;
 	init.m_samples = 1;
 	init.m_depth = 2;
 	init.m_layerCount = 1;
@@ -1525,7 +1544,8 @@ ANKI_TEST(Gr, 3DTextures)
 		U idx = (F32(ITERATION_COUNT - iterations - 1) / ITERATION_COUNT) * TEX_COORDS_LOD.getSize();
 		*uv = TEX_COORDS_LOD[idx];
 
-		cmdb->bindTextureAndSampler(0, 0, a, sampler, TextureUsageBit::SAMPLED_FRAGMENT);
+		cmdb->bindTextureAndSampler(
+			0, 0, gr->newTextureView(TextureViewInitInfo(a)), sampler, TextureUsageBit::SAMPLED_FRAGMENT);
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 
 		cmdb->endRenderPass();
@@ -1608,16 +1628,16 @@ ANKI_TEST(Gr, RenderGraph)
 	RenderTargetHandle giGiLightRt = descr.importRenderTarget("GI light", dummyTex, TextureUsageBit::SAMPLED_FRAGMENT);
 	for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
 	{
+		TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, 0));
+
 		GraphicsRenderPassDescription& pass =
 			descr.newGraphicsRenderPass(StringAuto(alloc).sprintf("GI lp%u", faceIdx).toCString());
-		pass.newConsumer(
-			{giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, faceIdx, 0)});
+		pass.newConsumer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, subresource});
 		pass.newConsumer({giGbuffNormRt, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({giGbuffDepthRt, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({giGbuffDiffRt, TextureUsageBit::SAMPLED_FRAGMENT});
 
-		pass.newProducer(
-			{giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, faceIdx, 0)});
+		pass.newProducer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, subresource});
 	}
 
 	// GI light mips
@@ -1758,4 +1778,155 @@ ANKI_TEST(Gr, RenderGraph)
 	COMMON_END()
 }
 
+/// Test workarounds for some unsupported formats
+ANKI_TEST(Gr, VkWorkarounds)
+{
+	COMMON_BEGIN()
+
+	// Create program
+	static const char* COMP_SRC = R"(
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 2) in;
+
+layout(ANKI_TEX_BINDING(0, 0)) uniform usampler2D u_tex;
+	
+layout(ANKI_SS_BINDING(0, 0)) buffer s_
+{
+	uvec4 u_result;
+};
+	
+shared uint g_wrong;
+	
+void main()
+{
+	g_wrong = 0;
+	memoryBarrierShared();
+	barrier();
+	
+	int lod = -1;
+	uint idx;
+	
+	if(gl_LocalInvocationID.z == 0)
+	{
+		// First mip
+	
+		lod = 0;
+		idx = gl_LocalInvocationID.y * 8 + gl_LocalInvocationID.x;
+	}
+	else if(gl_LocalInvocationID.x < 4u && gl_LocalInvocationID.y < 4u)
+	{
+		lod = 1;
+		idx = gl_LocalInvocationID.y * 4 + gl_LocalInvocationID.x;
+	}
+	
+	if(lod != -1)
+	{
+		uvec3 col = texelFetch(u_tex, ivec2(gl_LocalInvocationID.x, gl_LocalInvocationID.y), lod).rgb;
+		if(col.x != idx || col.y != idx + 1 || col.z != idx + 2)
+		{
+			atomicAdd(g_wrong, 1);
+		}
+	}
+	
+	memoryBarrierShared();
+	barrier();
+	
+	if(g_wrong != 0)
+	{
+		u_result = uvec4(1);
+	}
+	else
+	{
+		u_result = uvec4(2);
+	}
+})";
+
+	ShaderPtr comp = gr->newShader(ShaderInitInfo(ShaderType::COMPUTE, COMP_SRC));
+	ShaderProgramPtr prog = gr->newShaderProgram(ShaderProgramInitInfo(comp));
+
+	// Create the texture
+	TextureInitInfo texInit;
+	texInit.m_width = texInit.m_height = 8;
+	texInit.m_format = PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UINT);
+	texInit.m_type = TextureType::_2D;
+	texInit.m_usage = TextureUsageBit::TRANSFER_DESTINATION | TextureUsageBit::SAMPLED_ALL;
+	texInit.m_mipmapCount = 2;
+	TexturePtr tex = gr->newTexture(texInit);
+	TextureViewPtr texView = gr->newTextureView(TextureViewInitInfo(tex));
+
+	SamplerInitInfo samplerInit;
+	SamplerPtr sampler = gr->newSampler(samplerInit);
+
+	// Create the buffer to copy to the texture
+	BufferPtr uploadBuff = gr->newBuffer(BufferInitInfo(
+		texInit.m_width * texInit.m_height * 3, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::WRITE));
+	U8* data = static_cast<U8*>(uploadBuff->map(0, uploadBuff->getSize(), BufferMapAccessBit::WRITE));
+	for(U i = 0; i < texInit.m_width * texInit.m_height; ++i)
+	{
+		data[0] = i;
+		data[1] = i + 1;
+		data[2] = i + 2;
+		data += 3;
+	}
+	uploadBuff->unmap();
+
+	BufferPtr uploadBuff2 = gr->newBuffer(BufferInitInfo(
+		(texInit.m_width >> 1) * (texInit.m_height >> 1) * 3, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::WRITE));
+	data = static_cast<U8*>(uploadBuff2->map(0, uploadBuff2->getSize(), BufferMapAccessBit::WRITE));
+	for(U i = 0; i < (texInit.m_width >> 1) * (texInit.m_height >> 1); ++i)
+	{
+		data[0] = i;
+		data[1] = i + 1;
+		data[2] = i + 2;
+		data += 3;
+	}
+	uploadBuff2->unmap();
+
+	// Create the result buffer
+	BufferPtr resultBuff =
+		gr->newBuffer(BufferInitInfo(sizeof(UVec4), BufferUsageBit::STORAGE_COMPUTE_WRITE, BufferMapAccessBit::READ));
+
+	// Upload data and test them
+	CommandBufferInitInfo cmdbInit;
+	cmdbInit.m_flags =
+		CommandBufferFlag::TRANSFER_WORK | CommandBufferFlag::COMPUTE_WORK | CommandBufferFlag::SMALL_BATCH;
+	CommandBufferPtr cmdb = gr->newCommandBuffer(cmdbInit);
+
+	TextureSubresourceInfo subresource;
+	subresource.m_mipmapCount = texInit.m_mipmapCount;
+	cmdb->setTextureBarrier(tex, TextureUsageBit::NONE, TextureUsageBit::TRANSFER_DESTINATION, subresource);
+	cmdb->copyBufferToTextureView(uploadBuff,
+		0,
+		uploadBuff->getSize(),
+		gr->newTextureView(TextureViewInitInfo(tex, TextureSurfaceInfo(0, 0, 0, 0))));
+	cmdb->copyBufferToTextureView(uploadBuff2,
+		0,
+		uploadBuff2->getSize(),
+		gr->newTextureView(TextureViewInitInfo(tex, TextureSurfaceInfo(1, 0, 0, 0))));
+
+	cmdb->setTextureBarrier(tex, TextureUsageBit::TRANSFER_DESTINATION, TextureUsageBit::SAMPLED_COMPUTE, subresource);
+	cmdb->bindShaderProgram(prog);
+	cmdb->bindTextureAndSampler(0, 0, texView, sampler, TextureUsageBit::SAMPLED_COMPUTE);
+	cmdb->bindStorageBuffer(0, 0, resultBuff, 0, resultBuff->getSize());
+	cmdb->dispatchCompute(1, 1, 1);
+
+	cmdb->setBufferBarrier(resultBuff,
+		BufferUsageBit::STORAGE_COMPUTE_WRITE,
+		BufferUsageBit::STORAGE_COMPUTE_WRITE,
+		0,
+		resultBuff->getSize());
+
+	cmdb->flush();
+	gr->finish();
+
+	// Get the result
+	UVec4* result = static_cast<UVec4*>(resultBuff->map(0, resultBuff->getSize(), BufferMapAccessBit::READ));
+	ANKI_TEST_EXPECT_EQ(result->x(), 2);
+	ANKI_TEST_EXPECT_EQ(result->y(), 2);
+	ANKI_TEST_EXPECT_EQ(result->z(), 2);
+	ANKI_TEST_EXPECT_EQ(result->w(), 2);
+	resultBuff->unmap();
+
+	COMMON_END()
+}
+
 } // end namespace anki

+ 1 - 1
tools/format_source.sh

@@ -13,7 +13,7 @@ do
 	count=$((${count}+1))
 
 	# Throttle the parallel commands
-	if !((count % 8)); then
+	if !((count % 16)); then
 		wait
 	fi
 done