Jelajahi Sumber

Merge pull request #23 from godlikepanos/rendergraph

Merge RenderGraph work to master
Panagiotis Christopoulos Charitos 8 tahun lalu
induk
melakukan
7c6f5522de
100 mengubah file dengan 4539 tambahan dan 3913 penghapusan
  1. 1 1
      programs/FinalComposite.ankiprog
  2. 0 56
      programs/LensFlareOcclusionTest.ankiprog
  3. 46 12
      programs/LensFlareUpdateIndirectInfo.ankiprog
  4. 2 1
      programs/TemporalAAResolve.ankiprog
  5. 8 3
      programs/TonemappingAverageLuminance.ankiprog
  6. 1 1
      samples/common/Framework.cpp
  7. 3 3
      sandbox/Main.cpp
  8. 4 2
      shaders/Common.glsl
  9. 1 1
      shaders/Functions.glsl
  10. 38 5
      shaders/Pack.glsl
  11. 9 5
      shaders/TonemappingResources.glsl
  12. 4 0
      src/anki/Config.h.cmake
  13. 1 1
      src/anki/core/App.cpp
  14. 13 0
      src/anki/core/NativeWindowSdl.cpp
  15. 2 1
      src/anki/core/StagingGpuMemoryManager.cpp
  16. 1 0
      src/anki/core/Trace.cpp
  17. 1 0
      src/anki/core/Trace.h
  18. 25 1
      src/anki/gr/Buffer.h
  19. 31 26
      src/anki/gr/CommandBuffer.h
  20. 7 1
      src/anki/gr/Common.h
  21. 3 3
      src/anki/gr/GrObjectCache.cpp
  22. 527 190
      src/anki/gr/RenderGraph.cpp
  23. 352 94
      src/anki/gr/RenderGraph.h
  24. 2 2
      src/anki/gr/gl/Buffer.cpp
  25. 33 36
      src/anki/gr/gl/CommandBuffer.cpp
  26. 1 1
      src/anki/gr/gl/CommandBufferImpl.cpp
  27. 34 9
      src/anki/gr/gl/FramebufferImpl.cpp
  28. 2 1
      src/anki/gr/gl/FramebufferImpl.h
  29. 1 1
      src/anki/gr/gl/GlState.cpp
  30. 1 1
      src/anki/gr/gl/GlState.h
  31. 9 9
      src/anki/gr/gl/StateTracker.h
  32. 2 2
      src/anki/gr/vulkan/Buffer.cpp
  33. 16 4
      src/anki/gr/vulkan/BufferImpl.cpp
  34. 1 1
      src/anki/gr/vulkan/BufferImpl.h
  35. 17 28
      src/anki/gr/vulkan/CommandBuffer.cpp
  36. 62 31
      src/anki/gr/vulkan/CommandBufferImpl.cpp
  37. 70 41
      src/anki/gr/vulkan/CommandBufferImpl.h
  38. 22 45
      src/anki/gr/vulkan/CommandBufferImpl.inl.h
  39. 2 2
      src/anki/gr/vulkan/DescriptorSet.cpp
  40. 141 144
      src/anki/gr/vulkan/FramebufferImpl.cpp
  41. 79 36
      src/anki/gr/vulkan/FramebufferImpl.h
  42. 89 191
      src/anki/gr/vulkan/GrManagerImpl.cpp
  43. 38 47
      src/anki/gr/vulkan/GrManagerImpl.h
  44. 8 8
      src/anki/gr/vulkan/GrManagerImplSdl.cpp
  45. 7 0
      src/anki/gr/vulkan/Pipeline.cpp
  46. 9 0
      src/anki/gr/vulkan/Pipeline.h
  47. 2 0
      src/anki/gr/vulkan/SamplerImpl.cpp
  48. 381 0
      src/anki/gr/vulkan/SwapchainFactory.cpp
  49. 133 0
      src/anki/gr/vulkan/SwapchainFactory.h
  50. 30 5
      src/anki/gr/vulkan/TextureImpl.cpp
  51. 5 11
      src/anki/gr/vulkan/TextureImpl.h
  52. 0 115
      src/anki/gr/vulkan/TextureImpl.inl.h
  53. 0 55
      src/anki/gr/vulkan/TextureUsageTracker.h
  54. 116 96
      src/anki/renderer/Bloom.cpp
  55. 68 74
      src/anki/renderer/Bloom.h
  56. 1 1
      src/anki/renderer/Clusterer.cpp
  57. 1 1
      src/anki/renderer/Common.cpp
  58. 3 1
      src/anki/renderer/Common.h
  59. 49 177
      src/anki/renderer/Dbg.cpp
  60. 27 40
      src/anki/renderer/Dbg.h
  61. 2 2
      src/anki/renderer/DebugDrawer.cpp
  62. 97 114
      src/anki/renderer/DepthDownscale.cpp
  63. 59 56
      src/anki/renderer/DepthDownscale.h
  64. 68 44
      src/anki/renderer/DownscaleBlur.cpp
  65. 26 11
      src/anki/renderer/DownscaleBlur.h
  66. 63 46
      src/anki/renderer/FinalComposite.cpp
  67. 24 11
      src/anki/renderer/FinalComposite.h
  68. 91 101
      src/anki/renderer/ForwardShading.cpp
  69. 33 26
      src/anki/renderer/ForwardShading.h
  70. 106 166
      src/anki/renderer/GBuffer.cpp
  71. 28 16
      src/anki/renderer/GBuffer.h
  72. 233 157
      src/anki/renderer/Indirect.cpp
  73. 65 19
      src/anki/renderer/Indirect.h
  74. 45 106
      src/anki/renderer/LensFlare.cpp
  75. 27 14
      src/anki/renderer/LensFlare.h
  76. 25 36
      src/anki/renderer/LightBin.cpp
  77. 16 8
      src/anki/renderer/LightBin.h
  78. 70 82
      src/anki/renderer/LightShading.cpp
  79. 38 18
      src/anki/renderer/LightShading.h
  80. 56 36
      src/anki/renderer/MainRenderer.cpp
  81. 11 4
      src/anki/renderer/MainRenderer.h
  82. 2 1
      src/anki/renderer/RenderQueue.h
  83. 60 267
      src/anki/renderer/Renderer.cpp
  84. 24 76
      src/anki/renderer/Renderer.h
  85. 24 13
      src/anki/renderer/RendererObject.cpp
  86. 29 14
      src/anki/renderer/RendererObject.h
  87. 0 60
      src/anki/renderer/ScreenSpaceLensFlare.cpp
  88. 0 37
      src/anki/renderer/ScreenSpaceLensFlare.h
  89. 94 132
      src/anki/renderer/ShadowMapping.cpp
  90. 37 18
      src/anki/renderer/ShadowMapping.h
  91. 148 145
      src/anki/renderer/Ssao.cpp
  92. 58 91
      src/anki/renderer/Ssao.h
  93. 36 37
      src/anki/renderer/TemporalAA.cpp
  94. 27 9
      src/anki/renderer/TemporalAA.h
  95. 26 6
      src/anki/renderer/Tonemapping.cpp
  96. 27 6
      src/anki/renderer/Tonemapping.h
  97. 153 186
      src/anki/renderer/Volumetric.cpp
  98. 60 113
      src/anki/renderer/Volumetric.h
  99. 6 4
      src/anki/resource/Mesh.cpp
  100. 3 2
      src/anki/resource/ResourceManager.cpp

+ 1 - 1
programs/FinalComposite.ankiprog

@@ -43,7 +43,7 @@ layout(std140, ANKI_UBO_BINDING(0, 0)) uniform u0_
 	vec4 u_blueNoiseLayerPad3;
 };
 
-#define TONEMAPPING_RESOURCE readonly buffer
+#define TONEMAPPING_BINDING 1
 #include "shaders/TonemappingResources.glsl"
 
 layout(location = 0) in vec2 in_uv;

+ 0 - 56
programs/LensFlareOcclusionTest.ankiprog

@@ -1,56 +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
--->
-<shaderProgram>
-	<shaders>
-		<shader type="vert">
-			<source><![CDATA[
-#include "shaders/Common.glsl"
-
-layout(ANKI_UBO_BINDING(0, 0), std140, row_major) uniform _block
-{
-	mat4 u_mvp;
-};
-
-layout(location = 0) in vec3 in_position;
-
-out gl_PerVertex
-{
-	vec4 gl_Position;
-	float gl_PointSize;
-};
-
-void main()
-{
-	gl_Position = u_mvp * vec4(in_position, 1.0);
-	gl_PointSize = 16.0;
-}
-			]]></source>
-		</shader>
-		
-		<shader type="frag">
-			<source><![CDATA[
-#include "shaders/Common.glsl"
-
-// WORKAROUND: For some reason validation layer complains
-#ifdef ANKI_VK
-layout(location = 0) out vec4 out_msRt0;
-layout(location = 1) out vec4 out_msRt1;
-layout(location = 2) out vec4 out_msRt2;
-#endif
-
-void main()
-{
-#ifdef ANKI_VK
-	out_msRt0 = vec4(0.0);
-	out_msRt1 = vec4(0.0);
-	out_msRt2 = vec4(0.0);
-#endif
-}
-			]]></source>
-		</shader>
-	</shaders>
-</shaderProgram>

+ 46 - 12
programs/LensFlareUpdateIndirectInfo.ankiprog

@@ -5,12 +5,17 @@ Code licensed under the BSD License.
 http://www.anki3d.org/LICENSE
 -->
 <shaderProgram>
+	<inputs>
+		<input name="IN_DEPTH_MAP_SIZE" type="vec2" const="1"/>
+	</inputs>
+
 	<shaders>
 		<shader type="comp">
 			<source><![CDATA[
 #include "shaders/Common.glsl"
 
-layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+const uint WORKGROUP_SIZE = 8;
+layout(local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1) in;
 
 struct DrawArraysIndirectInfo
 {
@@ -20,30 +25,59 @@ struct DrawArraysIndirectInfo
 	uint baseInstance;
 };
 
-layout(ANKI_SS_BINDING(0, 0), std430) buffer ss0_
+layout(ANKI_SS_BINDING(0, 0), std430, row_major) readonly buffer ss0_
 {
-	uint u_queryResults[];
+	mat4 u_mvp;
+	vec4 u_flarePositions[];
 };
 
-layout(ANKI_SS_BINDING(0, 1), std430) buffer ss1_
+layout(ANKI_SS_BINDING(0, 1), std430) writeonly buffer ss1_
 {
 	DrawArraysIndirectInfo u_indirectInfo[];
 };
 
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthMap;
+
+shared uint g_maxDepth;
+
 void main()
 {
-	if(u_queryResults[gl_WorkGroupID.x] == 0)
+	// Init the g_maxDepth
+	if(gl_LocalInvocationIndex == 0)
 	{
-		u_indirectInfo[gl_WorkGroupID.x].count = 0;
+		g_maxDepth = 0;
 	}
-	else
+	memoryBarrierShared();
+	barrier();
+	
+	// Project the flare
+	uint flareIdx = gl_WorkGroupID.x;
+	vec4 posClip =  u_mvp * u_flarePositions[flareIdx];
+	vec3 posNdc = posClip.xyz / posClip.w;
+	float depth = posNdc.z;
+
+	// Compute the UVs to sample the depth map
+	vec2 displacement = vec2(gl_LocalInvocationID.xy) - vec2(WORKGROUP_SIZE / 2u); // [-WORKGROUP_SIZE, WORKGROUP_SIZE]
+	const vec2 TEXEL_SIZE = 1.0 / IN_DEPTH_MAP_SIZE;
+	vec2 uv = NDC_TO_UV(posNdc.xy) + displacement * TEXEL_SIZE;
+
+	// Sample and store depth
+	float refDepth = textureLod(u_depthMap, uv, 0.0).r;
+	atomicMax(g_maxDepth, uint(refDepth * float(MAX_U32)));
+
+	// Sync
+	memoryBarrierShared();
+	barrier();
+
+	if(gl_LocalInvocationIndex == 0)
 	{
-		u_indirectInfo[gl_WorkGroupID.x].count = 4;
-	}
+		float refDepth = float(g_maxDepth) / float(MAX_U32);
+		u_indirectInfo[flareIdx].count = (depth > refDepth) ? 0u : 4u;
 
-	u_indirectInfo[gl_WorkGroupID.x].instanceCount = 1;
-	u_indirectInfo[gl_WorkGroupID.x].first = 0;
-	u_indirectInfo[gl_WorkGroupID.x].baseInstance = 0;
+		u_indirectInfo[flareIdx].instanceCount = 1u;
+		u_indirectInfo[flareIdx].first = 0u;
+		u_indirectInfo[flareIdx].baseInstance = 0u;
+	}
 }
 			]]></source>
 		</shader>

+ 2 - 1
programs/TemporalAAResolve.ankiprog

@@ -39,7 +39,8 @@ layout(ANKI_UBO_BINDING(0, 0), std140, row_major) uniform u0_
 };
 
 #if TONEMAP_FIX
-#define TONEMAPPING_RESOURCE readonly buffer
+#define TONEMAPPING_SET 0
+#define TONEMAPPING_BINDING 1
 #include "shaders/TonemappingResources.glsl"
 #endif
 

+ 8 - 3
programs/TonemappingAverageLuminance.ankiprog

@@ -28,6 +28,7 @@ const uint PIXEL_READ_Y = INPUT_TEX_SIZE.y / WORKGROUP_SIZE_Y;
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_tex;
 
+#define TONEMAPPING_RESOURCE_AS_BUFFER 1
 #include "shaders/TonemappingResources.glsl"
 
 shared float g_avgLum[WORKGROUP_SIZE];
@@ -78,17 +79,21 @@ void main()
 #else
 		float crntLum = g_avgLum[0] / float(INPUT_TEX_SIZE.x * INPUT_TEX_SIZE.y);
 #endif
-		crntLum = max(crntLum, 0.04);
 
 #if 1
 		float prevLum = u_averageLuminance;
 
 		// Lerp between previous and new L value
 		const float INTERPOLATION_FACTOR = 0.05;
-		u_averageLuminance = mix(prevLum, crntLum, INTERPOLATION_FACTOR);
+		float finalAvgLum = mix(prevLum, crntLum, INTERPOLATION_FACTOR);
 #else
-		u_averageLuminance = crntLum;
+		float finalAvgLum = crntLum;
 #endif
+
+		// This is a workaround because sometimes the avg lum becomes nan
+		finalAvgLum = clamp(finalAvgLum, EPSILON, FLT_MAX);
+
+		u_averageLuminance = finalAvgLum;
 		u_exposureThreshold0 = computeExposure(u_averageLuminance, 0.0);
 	}
 }

+ 1 - 1
samples/common/Framework.cpp

@@ -70,7 +70,7 @@ Error SampleApp::userMainLoop(Bool& quit)
 	}
 	if(in.getKey(KeyCode::F2) == 1)
 	{
-		renderer.getDbg().flipFlags(DbgFlag::SPATIAL_COMPONENT);
+		// renderer.getDbg().flipFlags(DbgFlag::SPATIAL_COMPONENT);
 	}
 	if(in.getKey(KeyCode::F6) == 1)
 	{

+ 3 - 3
sandbox/Main.cpp

@@ -129,15 +129,15 @@ Error MyApp::userMainLoop(Bool& quit)
 	}
 	if(in.getKey(KeyCode::F2) == 1)
 	{
-		renderer.getDbg().flipFlags(DbgFlag::SPATIAL_COMPONENT);
+		// renderer.getDbg().flipFlags(DbgFlag::SPATIAL_COMPONENT);
 	}
 	if(in.getKey(KeyCode::F3) == 1)
 	{
-		renderer.getDbg().flipFlags(DbgFlag::PHYSICS);
+		// renderer.getDbg().flipFlags(DbgFlag::PHYSICS);
 	}
 	if(in.getKey(KeyCode::F4) == 1)
 	{
-		renderer.getDbg().flipFlags(DbgFlag::SECTOR_COMPONENT);
+		// renderer.getDbg().flipFlags(DbgFlag::SECTOR_COMPONENT);
 	}
 	if(in.getKey(KeyCode::F6) == 1)
 	{

+ 4 - 2
shaders/Common.glsl

@@ -28,13 +28,15 @@ precision DEFAULT_FLOAT_PRECISION float;
 precision DEFAULT_INT_PRECISION int;
 
 const float EPSILON = 0.000001;
+const float FLT_MAX = 3.402823e+38;
+const uint MAX_U32 = 0xFFFFFFFFu;
+
 const float PI = 3.14159265358979323846;
 const uint UBO_MAX_SIZE = 16384u;
 
-const uint MAX_U32 = 0xFFFFFFFFu;
-
 #define UV_TO_NDC(x_) ((x_)*2.0 - 1.0)
 #define NDC_TO_UV(x_) ((x_)*0.5 + 0.5)
+#define saturate(x_) clamp((x_), 0.0, 1.0)
 
 // Common locations
 #define POSITION_LOCATION 0

+ 1 - 1
shaders/Functions.glsl

@@ -249,7 +249,7 @@ vec3 grayScale(vec3 col)
 	return vec3(grey);
 }
 
-vec3 saturate(vec3 col, float factor)
+vec3 saturateColor(vec3 col, float factor)
 {
 	const vec3 LUM_COEFF = vec3(0.2125, 0.7154, 0.0721);
 	vec3 intensity = vec3(dot(col, LUM_COEFF));

+ 38 - 5
shaders/Pack.glsl

@@ -29,6 +29,37 @@ vec3 unpackNormal(in vec2 enc)
 	return normalize(normal);
 }
 
+// See http://johnwhite3d.blogspot.no/2017/10/signed-octahedron-normal-encoding.html
+// Result in [0.0, 1.0]
+vec3 signedOctEncode(vec3 n)
+{
+	vec3 outn;
+
+	vec3 nabs = abs(n);
+	n /= nabs.x + nabs.y + nabs.z;
+
+	outn.y = n.y * 0.5 + 0.5;
+	outn.x = n.x * 0.5 + outn.y;
+	outn.y = n.x * -0.5 + outn.y;
+
+	outn.z = saturate(n.z * FLT_MAX);
+	return outn;
+}
+
+// See http://johnwhite3d.blogspot.no/2017/10/signed-octahedron-normal-encoding.html
+vec3 signedOctDecode(vec3 n)
+{
+	vec3 outn;
+
+	outn.x = n.x - n.y;
+	outn.y = n.x + n.y - 1.0;
+	outn.z = n.z * 2.0 - 1.0;
+	outn.z = outn.z * (1.0 - abs(outn.x) - abs(outn.y));
+
+	outn = normalize(outn);
+	return outn;
+}
+
 #if GL_ES || __VERSION__ < 400
 
 // Vectorized version. See clean one at <= r1048
@@ -97,7 +128,7 @@ vec2 unpackUnorm1ToUnorm2(in float c)
 }
 
 // Max emission. Keep as low as possible
-const float MAX_EMISSION = 10.0;
+const float MAX_EMISSION = 20.0;
 
 // G-Buffer structure
 struct GbufferInfo
@@ -117,13 +148,15 @@ void writeGBuffer(in GbufferInfo g, out vec4 rt0, out vec4 rt1, out vec4 rt2)
 	float comp = packUnorm2ToUnorm1(vec2(g.subsurface, g.metallic));
 	rt0 = vec4(g.diffuse, comp);
 	rt1 = vec4(g.specular, g.roughness);
-	rt2 = vec4(g.normal * 0.5 + 0.5, g.emission / MAX_EMISSION);
+
+	vec3 encNorm = signedOctEncode(g.normal);
+	rt2 = vec4(encNorm.xy, g.emission / MAX_EMISSION, encNorm.z);
 }
 
 // Read from G-buffer
 void readNormalFromGBuffer(in sampler2D rt2, in vec2 uv, out vec3 normal)
 {
-	normal = texture(rt2, uv).rgb * 2.0 - 1.0;
+	normal = signedOctDecode(texture(rt2, uv).rga);
 }
 
 // Read from the G buffer
@@ -140,8 +173,8 @@ void readGBuffer(in sampler2D rt0, in sampler2D rt1, in sampler2D rt2, in vec2 u
 	g.roughness = comp.w;
 
 	comp = textureLod(rt2, uv, lod);
-	g.normal = comp.xyz * 2.0 - 1.0;
-	g.emission = comp.w * MAX_EMISSION;
+	g.normal = signedOctDecode(comp.xyw);
+	g.emission = comp.z * MAX_EMISSION;
 
 	// Fix values
 	g.specular = mix(g.specular, g.diffuse, g.metallic);

+ 9 - 5
shaders/TonemappingResources.glsl

@@ -14,15 +14,19 @@
 #define TONEMAPPING_SET 0
 #endif
 
-#ifndef TONEMAPPING_LOCATION
-#define TONEMAPPING_LOCATION 0
+#ifndef TONEMAPPING_BINDING
+#define TONEMAPPING_BINDING 0
 #endif
 
-#ifndef TONEMAPPING_RESOURCE
-#define TONEMAPPING_RESOURCE buffer
+#ifndef TONEMAPPING_RESOURCE_AS_BUFFER
+#define TONEMAPPING_RESOURCE_AS_BUFFER 0
 #endif
 
-layout(std140, ANKI_SS_BINDING(TONEMAPPING_SET, TONEMAPPING_LOCATION)) TONEMAPPING_RESOURCE tmrss0_
+#if TONEMAPPING_RESOURCE_AS_BUFFER
+layout(std140, ANKI_SS_BINDING(TONEMAPPING_SET, TONEMAPPING_BINDING)) buffer tmss0_
+#else
+layout(std140, ANKI_UBO_BINDING(TONEMAPPING_SET, TONEMAPPING_BINDING)) uniform tmu0_
+#endif
 {
 	vec4 u_averageLuminanceExposurePad2;
 };

+ 4 - 0
src/anki/Config.h.cmake

@@ -154,6 +154,8 @@
 #	define ANKI_FORCE_INLINE __attribute__((always_inline))
 #	define ANKI_DONT_INLINE __attribute__((noinline))
 #	define ANKI_UNUSED __attribute__((__unused__))
+#	define ANKI_COLD __attribute__((cold, optimize("Os")))
+#	define ANKI_HOT __attribute__ ((hot))
 #else
 #	define ANKI_LIKELY(x) ((x) == 1)
 #	define ANKI_UNLIKELY(x) ((x) == 1)
@@ -162,6 +164,8 @@
 #	define ANKI_FORCE_INLINE
 #	define ANKI_DONT_INLINE
 #	define ANKI_UNUSED
+#	define ANKI_COLD
+#	define ANKI_HOT
 #endif
 
 // Pack structs

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

@@ -281,7 +281,7 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 
 	m_renderer = m_heapAlloc.newInstance<MainRenderer>();
 
-	ANKI_CHECK(m_renderer->create(
+	ANKI_CHECK(m_renderer->init(
 		m_threadpool, m_resources, m_gr, m_stagingMem, m_allocCb, m_allocCbData, config, &m_globalTimestamp));
 
 	//

+ 13 - 0
src/anki/core/NativeWindowSdl.cpp

@@ -5,6 +5,9 @@
 
 #include <anki/core/NativeWindowSdl.h>
 #include <anki/util/Logger.h>
+#if ANKI_GR_BACKEND == ANKI_GR_BACKEND_VULKAN
+#include <SDL_vulkan.h>
+#endif
 
 namespace anki
 {
@@ -22,6 +25,14 @@ Error NativeWindow::init(NativeWindowInitInfo& init, HeapAllocator<U8>& alloc)
 		return Error::FUNCTION_FAILED;
 	}
 
+#if ANKI_GR_BACKEND == ANKI_GR_BACKEND_VULKAN
+	if(SDL_Vulkan_LoadLibrary(nullptr))
+	{
+		ANKI_CORE_LOGE("SDL_Vulkan_LoadLibrary() failed");
+		return Error::FUNCTION_FAILED;
+	}
+#endif
+
 	//
 	// Set GL attributes
 	//
@@ -47,6 +58,8 @@ Error NativeWindow::init(NativeWindowInitInfo& init, HeapAllocator<U8>& alloc)
 
 #if ANKI_GR_BACKEND == ANKI_GR_BACKEND_GL
 	flags |= SDL_WINDOW_OPENGL;
+#elif ANKI_GR_BACKEND == ANKI_GR_BACKEND_VULKAN
+	flags |= SDL_WINDOW_VULKAN;
 #endif
 
 	if(init.m_fullscreenDesktopRez)

+ 2 - 1
src/anki/core/StagingGpuMemoryManager.cpp

@@ -53,7 +53,8 @@ void StagingGpuMemoryManager::initBuffer(
 {
 	auto& perframe = m_perFrameBuffers[type];
 
-	perframe.m_buff = gr.newInstance<Buffer>(perframe.m_size, usage, BufferMapAccessBit::WRITE);
+	perframe.m_buff =
+		gr.newInstance<Buffer>(BufferInitInfo(perframe.m_size, usage, BufferMapAccessBit::WRITE, "Staging"));
 	perframe.m_alloc.init(perframe.m_size, alignment, maxAllocSize);
 	perframe.m_mappedMem = static_cast<U8*>(perframe.m_buff->map(0, perframe.m_size, BufferMapAccessBit::WRITE));
 }

+ 1 - 0
src/anki/core/Trace.cpp

@@ -35,6 +35,7 @@ static Array<const char*, U(TraceEventType::COUNT)> eventNames = {{"RESOURCE_ALL
 	"RENDER_DRAWER",
 	"RENDERER_COMMAND_BUFFER_BUILDING",
 	"RENDERER_LIGHT_BINNING",
+	"GR_RENDER_GRAPH",
 	"GL_THREAD",
 	"GL_2ND_LEVEL_CMD_BUFFER",
 	"GL_BIND_RESOURCES",

+ 1 - 0
src/anki/core/Trace.h

@@ -46,6 +46,7 @@ enum class TraceEventType
 	RENDER_DRAWER,
 	RENDERER_COMMAND_BUFFER_BUILDING,
 	RENDERER_LIGHT_BINNING,
+	GR_RENDER_GRAPH,
 	GL_THREAD,
 	GL_2ND_LEVEL_CMD_BUFFER,
 	GL_BIND_RESOURCES,

+ 25 - 1
src/anki/gr/Buffer.h

@@ -13,6 +13,30 @@ namespace anki
 /// @addtogroup graphics
 /// @{
 
+/// Buffer init info.
+class BufferInitInfo : public GrBaseInitInfo
+{
+public:
+	PtrSize m_size = 0;
+	BufferUsageBit m_usage = BufferUsageBit::NONE;
+	BufferMapAccessBit m_access = BufferMapAccessBit::NONE;
+
+	BufferInitInfo() = default;
+
+	BufferInitInfo(CString name)
+		: GrBaseInitInfo(name)
+	{
+	}
+
+	BufferInitInfo(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access, CString name = {})
+		: GrBaseInitInfo(name)
+		, m_size(size)
+		, m_usage(usage)
+		, m_access(access)
+	{
+	}
+};
+
 /// GPU buffer.
 class Buffer final : public GrObject
 {
@@ -37,7 +61,7 @@ anki_internal:
 	~Buffer();
 
 	/// Allocate the buffer.
-	void init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access);
+	void init(const BufferInitInfo& init);
 };
 /// @}
 

+ 31 - 26
src/anki/gr/CommandBuffer.h

@@ -118,6 +118,8 @@ class CommandBufferInitInfo
 {
 public:
 	FramebufferPtr m_framebuffer; ///< For second level command buffers.
+	Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS> m_colorAttachmentUsages = {};
+	TextureUsageBit m_depthStencilAttachmentUsage = TextureUsageBit::NONE;
 	CommandBufferInitHints m_hints;
 
 	CommandBufferFlag m_flags = CommandBufferFlag::NONE;
@@ -156,10 +158,10 @@ public:
 	void setPrimitiveRestart(Bool enable);
 
 	/// Set the viewport.
-	void setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy);
+	void setViewport(U32 minx, U32 miny, U32 width, U32 height);
 
 	/// Set the scissor rect. To disable the scissor just set a rect bigger than the viewport. By default it's disabled.
-	void setScissor(U16 minx, U16 miny, U16 maxx, U16 maxy);
+	void setScissor(U32 minx, U32 miny, U32 width, U32 height);
 
 	/// Set fill mode.
 	void setFillMode(FillMode mode);
@@ -220,13 +222,29 @@ public:
 	}
 
 	/// Bind texture.
-	void bindTexture(U32 set, U32 binding, TexturePtr tex, DepthStencilAspectBit aspect = DepthStencilAspectBit::DEPTH);
+	/// @param set The set to bind to.
+	/// @param binding The binding to bind to.
+	/// @param tex The texture to bind.
+	/// @param usage The state the tex is in.
+	/// @param aspect The depth stencil aspect.
+	void bindTexture(U32 set,
+		U32 binding,
+		TexturePtr tex,
+		TextureUsageBit usage,
+		DepthStencilAspectBit aspect = DepthStencilAspectBit::DEPTH);
 
 	/// 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 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);
 
 	/// Bind uniform buffer.
@@ -257,9 +275,15 @@ public:
 	void bindShaderProgram(ShaderProgramPtr prog);
 
 	/// Begin renderpass.
-	/// The minx, miny, maxx, maxy control the area that the load and store operations will happen. If the scissor is
+	/// The minx, miny, width, height control the area that the load and store operations will happen. If the scissor is
 	/// bigger than the render area the results are undefined.
-	void beginRenderPass(FramebufferPtr fb, U16 minx = 0, U16 miny = 0, U16 maxx = MAX_U16, U16 maxy = MAX_U16);
+	void beginRenderPass(FramebufferPtr fb,
+		const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorAttachmentUsages,
+		TextureUsageBit depthStencilAttachmentUsage,
+		U32 minx = 0,
+		U32 miny = 0,
+		U32 width = MAX_U32,
+		U32 height = MAX_U32);
 
 	/// End renderpass.
 	void endRenderPass();
@@ -282,7 +306,8 @@ public:
 
 	void dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ);
 
-	/// Generate mipmaps for non-3D textures.
+	/// 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.
@@ -359,26 +384,6 @@ public:
 
 	void setBufferBarrier(
 		BufferPtr buff, BufferUsageBit prevUsage, BufferUsageBit nextUsage, PtrSize offset, PtrSize size);
-
-	/// The command buffer will have to know the current usage of a texture. That can be known if there was a barrier
-	/// but if it wasn't use this method.
-	/// @param tex The texture.
-	/// @param surf The texture surface.
-	/// @param crntUsage The texture's current usage.
-	void informTextureSurfaceCurrentUsage(TexturePtr tex, const TextureSurfaceInfo& surf, TextureUsageBit crntUsage);
-
-	/// The command buffer will have to know the current usage of a texture. That can be known if there was a barrier
-	/// but if it wasn't use this method.
-	/// @param tex The texture.
-	/// @param vol The texture volume.
-	/// @param crntUsage The texture's current usage.
-	void informTextureVolumeCurrentUsage(TexturePtr tex, const TextureVolumeInfo& vol, TextureUsageBit crntUsage);
-
-	/// The command buffer will have to know the current usage of a texture. That can be known if there was a barrier
-	/// but if it wasn't use this method.
-	/// @param tex The texture.
-	/// @param crntUsage The texture's current usage.
-	void informTextureCurrentUsage(TexturePtr tex, TextureUsageBit crntUsage);
 	/// @}
 
 	/// @name Other

+ 7 - 1
src/anki/gr/Common.h

@@ -23,6 +23,7 @@ class TextureInitInfo;
 class SamplerInitInfo;
 class GrManagerInitInfo;
 class FramebufferInitInfo;
+class BufferInitInfo;
 
 /// @addtogroup graphics
 /// @{
@@ -155,6 +156,11 @@ public:
 		return m_level == b.m_level && m_depth == b.m_depth && m_face == b.m_face && m_layer == b.m_layer;
 	}
 
+	Bool operator!=(const TextureSurfaceInfo& b) const
+	{
+		return !(*this == b);
+	}
+
 	U64 computeHash() const
 	{
 		return anki::computeHash(this, sizeof(*this), 0x1234567);
@@ -229,7 +235,7 @@ public:
 		// Zero it because the derived classes may be hashed.
 		memset(&m_name[0], 0, sizeof(m_name));
 
-		if(name)
+		if(name && name.getLength())
 		{
 			ANKI_ASSERT(name.getLength() <= MAX_GR_OBJECT_NAME_LENGTH);
 			memcpy(&m_name[0], &name[0], name.getLength() + 1);

+ 3 - 3
src/anki/gr/GrObjectCache.cpp

@@ -11,10 +11,10 @@ namespace anki
 GrObjectCache::~GrObjectCache()
 {
 	// Some GrObjects may be in flight but someone destroys the cache. Do a manual destruction of the map
-	for(auto it : m_map)
+	while(!m_map.isEmpty())
 	{
-		GrObject* ptr = it;
-		unregisterObject(ptr);
+		auto it = m_map.getBegin();
+		unregisterObject(*it);
 	}
 }
 

+ 527 - 190
src/anki/gr/RenderGraph.cpp

@@ -6,8 +6,10 @@
 #include <anki/gr/RenderGraph.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/Texture.h>
+#include <anki/gr/Sampler.h>
 #include <anki/gr/Framebuffer.h>
 #include <anki/gr/CommandBuffer.h>
+#include <anki/core/Trace.h>
 #include <anki/util/BitSet.h>
 #include <anki/util/File.h>
 #include <anki/util/StringList.h>
@@ -15,6 +17,8 @@
 namespace anki
 {
 
+#define ANKI_DBG_RENDER_GRAPH 0
+
 /// Contains some extra things for render targets.
 class RenderGraph::RT
 {
@@ -89,6 +93,21 @@ public:
 
 	DynamicArray<CommandBufferPtr> m_secondLevelCmdbs;
 	FramebufferPtr m_fb;
+	Array<U32, 4> m_fbRenderArea;
+	Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS> m_colorUsages = {}; ///< For beginRender pass
+	TextureUsageBit m_dsUsage = TextureUsageBit::NONE; ///< For beginRender pass
+
+	/// WARNING: Should be the same as RenderPassDependency::TextureInfo
+	class ConsumedTextureInfo
+	{
+	public:
+		RenderTargetHandle m_handle;
+		TextureUsageBit m_usage;
+		TextureSurfaceInfo m_surface;
+		Bool8 m_wholeTex;
+		DepthStencilAspectBit m_aspect;
+	};
+	DynamicArray<ConsumedTextureInfo> m_consumedTextures;
 };
 
 /// A batch of render passes. These passes can run in parallel.
@@ -105,7 +124,7 @@ class RenderGraph::BakeContext
 public:
 	StackAllocator<U8> m_alloc;
 	DynamicArray<Pass> m_passes;
-	BitSet<MAX_RENDER_GRAPH_PASSES> m_passIsInBatch = {false};
+	BitSet<MAX_RENDER_GRAPH_PASSES, U64> m_passIsInBatch = {false};
 	DynamicArray<Batch> m_batches;
 	DynamicArray<RT> m_rts;
 	DynamicArray<Buffer> m_buffers;
@@ -118,16 +137,46 @@ public:
 	}
 };
 
-void GraphicsRenderPassFramebufferInfo::bake()
+void FramebufferDescription::bake()
 {
+	ANKI_ASSERT(m_hash == 0 && "Already baked");
 	if(m_defaultFb)
 	{
+		m_fbInitInfo.m_colorAttachmentCount = 1;
 		m_hash = 1;
 		return;
 	}
 
-	m_hash = 0;
+	// 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);
 
 	// First the depth attachments
@@ -246,20 +295,22 @@ void RenderGraph::reset()
 
 	m_ctx->m_alloc = StackAllocator<U8>();
 	m_ctx = nullptr;
+	++m_version;
 }
 
-TexturePtr RenderGraph::getOrCreateRenderTarget(const TextureInitInfo& initInf)
+TexturePtr RenderGraph::getOrCreateRenderTarget(const TextureInitInfo& initInf, U64 hash)
 {
+	ANKI_ASSERT(hash);
 	auto alloc = getManager().getAllocator();
 
 	// Find a cache entry
 	RenderTargetCacheEntry* entry = nullptr;
-	auto it = m_renderTargetCache.find(initInf);
+	auto it = m_renderTargetCache.find(hash);
 	if(ANKI_UNLIKELY(it == m_renderTargetCache.getEnd()))
 	{
 		// Didn't found the entry, create a new one
 
-		auto it2 = m_renderTargetCache.emplace(getAllocator(), initInf);
+		auto it2 = m_renderTargetCache.emplace(getAllocator(), hash);
 		entry = &(*it2);
 	}
 	else
@@ -283,7 +334,7 @@ TexturePtr RenderGraph::getOrCreateRenderTarget(const TextureInitInfo& initInf)
 
 		tex = getManager().newInstance<Texture>(initInf);
 
-		ANKI_ASSERT(entry->m_texturesInUse == 0);
+		ANKI_ASSERT(entry->m_texturesInUse == entry->m_textures.getSize());
 		entry->m_textures.resize(alloc, entry->m_textures.getSize() + 1);
 		entry->m_textures[entry->m_textures.getSize() - 1] = tex;
 		++entry->m_texturesInUse;
@@ -303,7 +354,19 @@ FramebufferPtr RenderGraph::getOrCreateFramebuffer(
 	if(!defaultFb)
 	{
 		// Create a hash that includes the render targets
-		hash = appendHash(rtHandles, sizeof(RenderTargetHandle) * (MAX_COLOR_ATTACHMENTS + 1), hash);
+		Array<U64, MAX_COLOR_ATTACHMENTS + 1> uuids;
+		U count = 0;
+		for(U i = 0; i < fbInit_.m_colorAttachmentCount; ++i)
+		{
+			uuids[count++] = m_ctx->m_rts[rtHandles[i].m_idx].m_texture->getUuid();
+		}
+
+		if(!!fbInit_.m_depthStencilAttachment.m_aspect)
+		{
+			uuids[count++] = m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS].m_idx].m_texture->getUuid();
+		}
+
+		hash = appendHash(&uuids[0], sizeof(U64) * count, hash);
 	}
 
 	FramebufferPtr fb;
@@ -320,12 +383,15 @@ FramebufferPtr RenderGraph::getOrCreateFramebuffer(
 		{
 			for(U i = 0; i < fbInit.m_colorAttachmentCount; ++i)
 			{
-				fbInit.m_colorAttachments[i].m_texture = m_ctx->m_rts[rtHandles[i]].m_texture;
+				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());
 			}
 
 			if(!!fbInit.m_depthStencilAttachment.m_aspect)
 			{
-				fbInit.m_depthStencilAttachment.m_texture = m_ctx->m_rts[rtHandles[MAX_COLOR_ATTACHMENTS]].m_texture;
+				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());
 			}
 		}
 
@@ -338,16 +404,32 @@ FramebufferPtr RenderGraph::getOrCreateFramebuffer(
 	return fb;
 }
 
-Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, const RenderPassBase& b)
+template<Bool isTexture>
+Bool RenderGraph::overlappingDependency(const RenderPassDependency& a, const RenderPassDependency& b)
+{
+	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);
+	}
+	else
+	{
+		return a.m_buffer.m_handle == b.m_buffer.m_handle;
+	}
+}
+
+Bool RenderGraph::passADependsOnB(const RenderPassDescriptionBase& a, const RenderPassDescriptionBase& b)
 {
 	// Render targets
 	{
-		/// Compute the 3 types of dependencies
-		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aReadBWrite = a.m_consumerRtMask & b.m_producerRtMask;
-		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBRead = a.m_producerRtMask & b.m_consumerRtMask;
-		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBWrite = a.m_producerRtMask & b.m_producerRtMask;
+		// Compute the 3 types of dependencies
+		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64> aReadBWrite = a.m_consumerRtMask & b.m_producerRtMask;
+		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64> aWriteBRead = a.m_producerRtMask & b.m_consumerRtMask;
+		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64> aWriteBWrite = a.m_producerRtMask & b.m_producerRtMask;
 
-		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
+		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
 
 		if(fullDep.getAny())
 		{
@@ -355,12 +437,11 @@ Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, con
 
 			for(const RenderPassDependency& consumer : a.m_consumers)
 			{
-				if(consumer.m_isTexture && fullDep.get(consumer.m_texture.m_handle))
+				if(consumer.m_isTexture && fullDep.get(consumer.m_texture.m_handle.m_idx))
 				{
 					for(const RenderPassDependency& producer : b.m_producers)
 					{
-						if(producer.m_isTexture && producer.m_texture.m_handle == consumer.m_texture.m_handle
-							&& producer.m_texture.m_surface == consumer.m_texture.m_surface)
+						if(overlappingDependency<true>(producer, consumer))
 						{
 							return true;
 						}
@@ -371,12 +452,13 @@ Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, con
 	}
 
 	// Buffers
+	if(a.m_hasBufferDeps)
 	{
-		BitSet<MAX_RENDER_GRAPH_BUFFERS> aReadBWrite = a.m_consumerBufferMask & b.m_producerBufferMask;
-		BitSet<MAX_RENDER_GRAPH_BUFFERS> aWriteBRead = a.m_producerBufferMask & b.m_consumerBufferMask;
-		BitSet<MAX_RENDER_GRAPH_BUFFERS> aWriteBWrite = a.m_producerBufferMask & b.m_producerBufferMask;
+		BitSet<MAX_RENDER_GRAPH_BUFFERS, U64> aReadBWrite = a.m_consumerBufferMask & b.m_producerBufferMask;
+		BitSet<MAX_RENDER_GRAPH_BUFFERS, U64> aWriteBRead = a.m_producerBufferMask & b.m_consumerBufferMask;
+		BitSet<MAX_RENDER_GRAPH_BUFFERS, U64> aWriteBWrite = a.m_producerBufferMask & b.m_producerBufferMask;
 
-		BitSet<MAX_RENDER_GRAPH_BUFFERS> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
+		BitSet<MAX_RENDER_GRAPH_BUFFERS, U64> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
 
 		if(fullDep.getAny())
 		{
@@ -384,11 +466,11 @@ Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, con
 
 			for(const RenderPassDependency& consumer : a.m_consumers)
 			{
-				if(!consumer.m_isTexture && fullDep.get(consumer.m_buffer.m_handle))
+				if(!consumer.m_isTexture && fullDep.get(consumer.m_buffer.m_handle.m_idx))
 				{
 					for(const RenderPassDependency& producer : b.m_producers)
 					{
-						if(!producer.m_isTexture && producer.m_buffer.m_handle == consumer.m_buffer.m_handle)
+						if(overlappingDependency<false>(producer, consumer))
 						{
 							return true;
 						}
@@ -445,7 +527,8 @@ RenderGraph::BakeContext* RenderGraph::newContext(const RenderGraphDescription&
 		}
 		else
 		{
-			TexturePtr rt = getOrCreateRenderTarget(descr.m_renderTargets[rtIdx].m_initInfo);
+			TexturePtr rt =
+				getOrCreateRenderTarget(descr.m_renderTargets[rtIdx].m_initInfo, descr.m_renderTargets[rtIdx].m_hash);
 			ctx->m_rts[rtIdx].m_texture = rt;
 		}
 	}
@@ -469,30 +552,81 @@ void RenderGraph::initRenderPassesAndSetDeps(const RenderGraphDescription& descr
 	ANKI_ASSERT(passCount > 0);
 
 	ctx.m_passes.create(alloc, passCount);
-	for(U i = 0; i < passCount; ++i)
+	for(U passIdx = 0; passIdx < passCount; ++passIdx)
 	{
-		const RenderPassBase& inPass = *descr.m_passes[i];
-		Pass& outPass = ctx.m_passes[i];
+		const RenderPassDescriptionBase& inPass = *descr.m_passes[passIdx];
+		Pass& outPass = ctx.m_passes[passIdx];
 
 		outPass.m_callback = inPass.m_callback;
 		outPass.m_userData = inPass.m_userData;
 
+		// Create consumer info
+		for(U consumerIdx = 0; consumerIdx < inPass.m_consumers.getSize(); ++consumerIdx)
+		{
+			const RenderPassDependency& inConsumer = inPass.m_consumers[consumerIdx];
+			if(inConsumer.m_isTexture)
+			{
+				outPass.m_consumedTextures.emplaceBack(alloc);
+				Pass::ConsumedTextureInfo& inf = outPass.m_consumedTextures.getBack();
+
+				ANKI_ASSERT(sizeof(inf) == sizeof(inConsumer.m_texture));
+				memcpy(&inf, &inConsumer.m_texture, sizeof(inf));
+			}
+		}
+
 		// Create command buffers and framebuffer
-		if(inPass.m_type == RenderPassBase::Type::GRAPHICS)
+		if(inPass.m_type == RenderPassDescriptionBase::Type::GRAPHICS)
 		{
-			const GraphicsRenderPassInfo& graphicsPass = static_cast<const GraphicsRenderPassInfo&>(inPass);
+			const GraphicsRenderPassDescription& graphicsPass =
+				static_cast<const GraphicsRenderPassDescription&>(inPass);
+
 			if(graphicsPass.hasFramebuffer())
 			{
 				outPass.m_fb = getOrCreateFramebuffer(
 					graphicsPass.m_fbInitInfo, &graphicsPass.m_rtHandles[0], graphicsPass.m_fbHash);
 
+				outPass.m_fbRenderArea = graphicsPass.m_fbRenderArea;
+
+				// Init the usage bits
+				if(graphicsPass.m_fbHash != 1)
+				{
+					TextureUsageBit usage;
+					DepthStencilAspectBit aspect;
+
+					for(U i = 0; i < graphicsPass.m_fbInitInfo.m_colorAttachmentCount; ++i)
+					{
+						getCrntUsageAndAspect(graphicsPass.m_rtHandles[i],
+							passIdx,
+							graphicsPass.m_fbInitInfo.m_colorAttachments[i].m_surface,
+							usage,
+							aspect);
+
+						outPass.m_colorUsages[i] = usage;
+						ANKI_ASSERT(!aspect);
+					}
+
+					if(!!graphicsPass.m_fbInitInfo.m_depthStencilAttachment.m_aspect)
+					{
+						getCrntUsageAndAspect(graphicsPass.m_rtHandles[MAX_COLOR_ATTACHMENTS],
+							passIdx,
+							graphicsPass.m_fbInitInfo.m_depthStencilAttachment.m_surface,
+							usage,
+							aspect);
+
+						outPass.m_dsUsage = usage;
+						ANKI_ASSERT(!!aspect);
+					}
+				}
+
 				// Create the second level command buffers
 				if(inPass.m_secondLevelCmdbsCount)
 				{
 					outPass.m_secondLevelCmdbs.create(alloc, inPass.m_secondLevelCmdbsCount);
 					CommandBufferInitInfo cmdbInit;
-					cmdbInit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
+					cmdbInit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SECOND_LEVEL;
 					cmdbInit.m_framebuffer = outPass.m_fb;
+					cmdbInit.m_colorAttachmentUsages = outPass.m_colorUsages;
+					cmdbInit.m_depthStencilAttachmentUsage = outPass.m_dsUsage;
 					for(U cmdbIdx = 0; cmdbIdx < inPass.m_secondLevelCmdbsCount; ++cmdbIdx)
 					{
 						outPass.m_secondLevelCmdbs[cmdbIdx] = getManager().newInstance<CommandBuffer>(cmdbInit);
@@ -509,11 +643,12 @@ void RenderGraph::initRenderPassesAndSetDeps(const RenderGraphDescription& descr
 			ANKI_ASSERT(inPass.m_secondLevelCmdbsCount == 0 && "Can't have second level cmdbs");
 		}
 
-		U j = i;
+		// Set dependencies
+		U j = passIdx;
 		while(j--)
 		{
-			const RenderPassBase& prevPass = *descr.m_passes[j];
-			if(passADependsOnB(ctx, inPass, prevPass))
+			const RenderPassDescriptionBase& prevPass = *descr.m_passes[j];
+			if(passADependsOnB(inPass, prevPass))
 			{
 				outPass.m_dependsOn.emplaceBack(alloc, j);
 			}
@@ -553,32 +688,82 @@ void RenderGraph::initBatches()
 	}
 }
 
-void RenderGraph::compileNewGraph(const RenderGraphDescription& descr, StackAllocator<U8>& alloc)
+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
 {
-	// Init the context
-	BakeContext& ctx = *newContext(descr, alloc);
-	m_ctx = &ctx;
+	ANKI_ASSERT(consumer.m_isTexture);
 
-	// Init the passes and find the dependencies between passes
-	initRenderPassesAndSetDeps(descr, alloc);
+	const StackAllocator<U8>& alloc = ctx.m_alloc;
 
-	// Walk the graph and create pass batches
-	initBatches();
+	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;
 
-	// Create barriers between batches
-	setBatchBarriers(descr, ctx);
+	Bool anySurfaceFound = false;
+	for(RT::Usage& u : ctx.m_rts[rtIdx].m_surfUsages)
+	{
+		if(!consumerWholeTex && u.m_surface != consumer.m_texture.m_surface)
+		{
+			// Not the right surface, continue
+			continue;
+		}
 
-	// Misc
-	CommandBufferInitInfo cmdbInit;
-	cmdbInit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::COMPUTE_WORK;
-	ctx.m_cmdb = getManager().newInstance<CommandBuffer>(cmdbInit);
+		anySurfaceFound = true;
 
-#if 1
-	if(dumpDependencyDotFile(descr, ctx, "./"))
+		if(u.m_usage == consumerUsage)
+		{
+			// Surface in the correct usage, continue
+			continue;
+		}
+
+		// Check if we can merge barriers
+		const Bool rtHasBarrier = rtHasBarrierMask.get(rtIdx);
+		Barrier* barrierToMergeTo = nullptr;
+		if(rtHasBarrier)
+		{
+			for(Barrier& b : batch.m_barriersBefore)
+			{
+				if(b.m_isTexture && b.m_texture.m_idx == rtIdx && b.m_texture.m_surface == u.m_surface)
+				{
+					barrierToMergeTo = &b;
+					break;
+				}
+			}
+		}
+
+		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);
+
+			u.m_usage = consumer.m_texture.m_usage;
+			rtHasBarrierMask.set(rtIdx);
+		}
+		else
+		{
+			// RT already in a barrier, merge the 2 barriers
+
+			ANKI_ASSERT(!!barrierToMergeTo->m_texture.m_usageAfter);
+			ANKI_ASSERT(!!u.m_usage);
+			barrierToMergeTo->m_texture.m_usageAfter |= consumerUsage;
+			u.m_usage |= consumerUsage;
+		}
+	} // end for
+
+	// Create the transition from the initial state
+	if(!anySurfaceFound && descr.m_renderTargets[rtIdx].m_usage != consumerUsage)
 	{
-		ANKI_LOGF("Won't recover on debug code");
+		batch.m_barriersBefore.emplaceBack(
+			alloc, rtIdx, descr.m_renderTargets[rtIdx].m_usage, consumerUsage, consumer.m_texture.m_surface);
+
+		RT::Usage usage{consumerUsage, consumer.m_texture.m_surface};
+		ctx.m_rts[rtIdx].m_surfUsages.emplaceBack(alloc, usage);
 	}
-#endif
 }
 
 void RenderGraph::setBatchBarriers(const RenderGraphDescription& descr, BakeContext& ctx) const
@@ -588,66 +773,284 @@ void RenderGraph::setBatchBarriers(const RenderGraphDescription& descr, BakeCont
 	// 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
 		for(U passIdx : batch.m_passIndices)
 		{
-			const RenderPassBase& pass = *descr.m_passes[passIdx];
+			const RenderPassDescriptionBase& pass = *descr.m_passes[passIdx];
 
 			// For all consumers
 			for(const RenderPassDependency& consumer : pass.m_consumers)
 			{
 				if(consumer.m_isTexture)
 				{
-					const U32 rtIdx = consumer.m_texture.m_handle;
-					const TextureUsageBit consumerUsage = consumer.m_texture.m_usage;
+					setTextureBarrier(batch, pass, consumer, rtHasBarrierMask, descr, ctx);
+				}
+				else
+				{
+					const U32 buffIdx = consumer.m_buffer.m_handle.m_idx;
+					const BufferUsageBit consumerUsage = consumer.m_buffer.m_usage;
 
-					Bool anySurfaceFound = false;
-					const Bool wholeTex = consumer.m_texture.m_wholeTex;
-					for(RT::Usage& u : ctx.m_rts[rtIdx].m_surfUsages)
+					if(consumerUsage != ctx.m_buffers[buffIdx].m_usage)
 					{
-						if(wholeTex || u.m_surface == consumer.m_texture.m_surface)
+						const Bool buffHasBarrier = buffHasBarrierMask.get(buffIdx);
+
+						if(!buffHasBarrier)
 						{
-							anySurfaceFound = true;
-							if(u.m_usage != consumerUsage)
-							{
-								batch.m_barriersBefore.emplaceBack(
-									alloc, consumer.m_texture.m_handle, u.m_usage, consumerUsage, u.m_surface);
+							// Buff hasn't had a barrier in this batch, add a new barrier
+
+							batch.m_barriersBefore.emplaceBack(
+								alloc, buffIdx, ctx.m_buffers[buffIdx].m_usage, consumerUsage);
+
+							ctx.m_buffers[buffIdx].m_usage = consumerUsage;
+							buffHasBarrierMask.set(buffIdx);
+						}
+						else
+						{
+							// Buff already in a barrier, merge the 2 barriers
 
-								u.m_usage = consumer.m_texture.m_usage;
+							Barrier* barrierToMergeTo = nullptr;
+							for(Barrier& b : batch.m_barriersBefore)
+							{
+								if(!b.m_isTexture && b.m_buffer.m_idx == buffIdx)
+								{
+									barrierToMergeTo = &b;
+									break;
+								}
 							}
+
+							ANKI_ASSERT(barrierToMergeTo);
+							ANKI_ASSERT(!!barrierToMergeTo->m_buffer.m_usageAfter);
+							barrierToMergeTo->m_buffer.m_usageAfter |= consumerUsage;
+							ctx.m_buffers[buffIdx].m_usage = barrierToMergeTo->m_buffer.m_usageAfter;
 						}
 					}
+				}
+			} // For all consumers
+		} // For all passes
+
+#if ANKI_DBG_RENDER_GRAPH
+		// Sort the barriers to ease the dumped graph
+		std::sort(batch.m_barriersBefore.getBegin(),
+			batch.m_barriersBefore.getEnd(),
+			[&](const Barrier& a, const Barrier& b) {
+				const U aidx = (a.m_isTexture) ? a.m_texture.m_idx : a.m_buffer.m_idx;
+				const U bidx = (b.m_isTexture) ? b.m_texture.m_idx : b.m_buffer.m_idx;
 
-					if(!anySurfaceFound && descr.m_renderTargets[rtIdx].m_usage != consumerUsage)
+				if(aidx == bidx && a.m_isTexture && b.m_isTexture)
+				{
+					if(a.m_texture.m_surface.m_level != b.m_texture.m_surface.m_level)
+					{
+						return a.m_texture.m_surface.m_level < b.m_texture.m_surface.m_level;
+					}
+					else if(a.m_texture.m_surface.m_face != b.m_texture.m_surface.m_face)
 					{
-						batch.m_barriersBefore.emplaceBack(alloc,
-							rtIdx,
-							descr.m_renderTargets[rtIdx].m_usage,
-							consumerUsage,
-							consumer.m_texture.m_surface);
-
-						RT::Usage usage{consumerUsage, consumer.m_texture.m_surface};
-						ctx.m_rts[rtIdx].m_surfUsages.emplaceBack(alloc, usage);
+						return a.m_texture.m_surface.m_face < b.m_texture.m_surface.m_face;
+					}
+					else if(a.m_texture.m_surface.m_layer != b.m_texture.m_surface.m_layer)
+					{
+						return a.m_texture.m_surface.m_layer < b.m_texture.m_surface.m_layer;
+					}
+					else
+					{
+						return false;
 					}
 				}
 				else
 				{
-					const U32 buffIdx = consumer.m_buffer.m_handle;
-					const BufferUsageBit consumerUsage = consumer.m_buffer.m_usage;
+					return aidx < bidx;
+				}
+			});
+#endif
+	} // For all batches
+}
 
-					if(consumerUsage != ctx.m_buffers[buffIdx].m_usage)
-					{
-						batch.m_barriersBefore.emplaceBack(
-							alloc, buffIdx, ctx.m_buffers[buffIdx].m_usage, consumerUsage);
+void RenderGraph::compileNewGraph(const RenderGraphDescription& descr, StackAllocator<U8>& alloc)
+{
+	ANKI_TRACE_SCOPED_EVENT(GR_RENDER_GRAPH);
 
-						ctx.m_buffers[buffIdx].m_usage = consumerUsage;
-					}
+	// Init the context
+	BakeContext& ctx = *newContext(descr, alloc);
+	m_ctx = &ctx;
+
+	// Init the passes and find the dependencies between passes
+	initRenderPassesAndSetDeps(descr, alloc);
+
+	// Walk the graph and create pass batches
+	initBatches();
+
+	// Create barriers between batches
+	setBatchBarriers(descr, ctx);
+
+	// Create main command buffer
+	CommandBufferInitInfo cmdbInit;
+	cmdbInit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::COMPUTE_WORK;
+	m_ctx->m_cmdb = getManager().newInstance<CommandBuffer>(cmdbInit);
+
+#if ANKI_DBG_RENDER_GRAPH
+	if(dumpDependencyDotFile(descr, ctx, "./"))
+	{
+		ANKI_LOGF("Won't recover on debug code");
+	}
+#endif
+}
+
+TexturePtr RenderGraph::getTexture(RenderTargetHandle handle) const
+{
+	ANKI_ASSERT(m_ctx->m_rts[handle.m_idx].m_texture.isCreated());
+	return m_ctx->m_rts[handle.m_idx].m_texture;
+}
+
+BufferPtr RenderGraph::getBuffer(RenderPassBufferHandle handle) const
+{
+	ANKI_ASSERT(m_ctx->m_buffers[handle.m_idx].m_buffer.isCreated());
+	return m_ctx->m_buffers[handle.m_idx].m_buffer;
+}
+
+void RenderGraph::runSecondLevel(U32 threadIdx) const
+{
+	ANKI_TRACE_SCOPED_EVENT(GR_RENDER_GRAPH);
+	ANKI_ASSERT(m_ctx);
+
+	RenderPassWorkContext ctx;
+	ctx.m_rgraph = this;
+	ctx.m_currentSecondLevelCommandBufferIndex = threadIdx;
+
+	for(const Pass& p : m_ctx->m_passes)
+	{
+		const U size = p.m_secondLevelCmdbs.getSize();
+		if(threadIdx < size)
+		{
+			ctx.m_commandBuffer = p.m_secondLevelCmdbs[threadIdx];
+			ctx.m_secondLevelCommandBufferCount = size;
+			ctx.m_passIdx = &p - &m_ctx->m_passes[0];
+			ctx.m_userData = p.m_userData;
+
+			ANKI_ASSERT(ctx.m_commandBuffer.isCreated());
+			p.m_callback(ctx);
+
+			ctx.m_commandBuffer->flush();
+		}
+	}
+}
+
+void RenderGraph::run() const
+{
+	ANKI_TRACE_SCOPED_EVENT(GR_RENDER_GRAPH);
+	ANKI_ASSERT(m_ctx);
+	CommandBufferPtr& cmdb = m_ctx->m_cmdb;
+
+	RenderPassWorkContext ctx;
+	ctx.m_rgraph = this;
+	ctx.m_currentSecondLevelCommandBufferIndex = 0;
+	ctx.m_secondLevelCommandBufferCount = 0;
+	ctx.m_commandBuffer = cmdb;
+
+	for(const Batch& batch : m_ctx->m_batches)
+	{
+		// Set the barriers
+		for(const Barrier& barrier : batch.m_barriersBefore)
+		{
+			if(barrier.m_isTexture)
+			{
+				cmdb->setTextureSurfaceBarrier(m_ctx->m_rts[barrier.m_texture.m_idx].m_texture,
+					barrier.m_texture.m_usageBefore,
+					barrier.m_texture.m_usageAfter,
+					barrier.m_texture.m_surface);
+			}
+			else
+			{
+				cmdb->setBufferBarrier(m_ctx->m_buffers[barrier.m_buffer.m_idx].m_buffer,
+					barrier.m_buffer.m_usageBefore,
+					barrier.m_buffer.m_usageAfter,
+					0,
+					MAX_PTR_SIZE);
+			}
+		}
+
+		// Call the passes
+		for(U passIdx : batch.m_passIndices)
+		{
+			const Pass& pass = m_ctx->m_passes[passIdx];
+
+			if(pass.m_fb.isCreated())
+			{
+				cmdb->beginRenderPass(pass.m_fb,
+					pass.m_colorUsages,
+					pass.m_dsUsage,
+					pass.m_fbRenderArea[0],
+					pass.m_fbRenderArea[1],
+					pass.m_fbRenderArea[2],
+					pass.m_fbRenderArea[3]);
+			}
+
+			const U size = pass.m_secondLevelCmdbs.getSize();
+			if(size == 0)
+			{
+				ctx.m_userData = pass.m_userData;
+				ctx.m_passIdx = passIdx;
+
+				pass.m_callback(ctx);
+			}
+			else
+			{
+				for(const CommandBufferPtr& cmdb2nd : pass.m_secondLevelCmdbs)
+				{
+					cmdb->pushSecondLevelCommandBuffer(cmdb2nd);
 				}
-			} // For all consumers
-		} // For all passes
-	} // For all batches
+			}
+
+			if(pass.m_fb.isCreated())
+			{
+				cmdb->endRenderPass();
+			}
+		}
+	}
 }
 
+void RenderGraph::flush()
+{
+	m_ctx->m_cmdb->flush();
+}
+
+void RenderGraph::getCrntUsageAndAspect(RenderTargetHandle handle,
+	U32 passIdx,
+	const TextureSurfaceInfo& surf,
+	TextureUsageBit& usage,
+	DepthStencilAspectBit& aspect) 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");
+}
+
+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)
+		{
+			usage = consumer.m_usage;
+			aspect = consumer.m_aspect;
+			return;
+		}
+	}
+
+	ANKI_ASSERT(!"Handle not found");
+}
+
+#if ANKI_DBG_RENDER_GRAPH
 StringAuto RenderGraph::textureUsageToStr(StackAllocator<U8>& alloc, TextureUsageBit usage)
 {
 	StringListAuto slist(alloc);
@@ -663,6 +1066,7 @@ StringAuto RenderGraph::textureUsageToStr(StackAllocator<U8>& alloc, TextureUsag
 	ANKI_TEX_USAGE(SAMPLED_TESSELLATION_EVALUATION);
 	ANKI_TEX_USAGE(SAMPLED_GEOMETRY);
 	ANKI_TEX_USAGE(SAMPLED_FRAGMENT);
+	ANKI_TEX_USAGE(SAMPLED_COMPUTE);
 	ANKI_TEX_USAGE(IMAGE_COMPUTE_READ);
 	ANKI_TEX_USAGE(IMAGE_COMPUTE_WRITE);
 	ANKI_TEX_USAGE(FRAMEBUFFER_ATTACHMENT_READ);
@@ -678,6 +1082,7 @@ StringAuto RenderGraph::textureUsageToStr(StackAllocator<U8>& alloc, TextureUsag
 
 #undef ANKI_TEX_USAGE
 
+	ANKI_ASSERT(!slist.isEmpty());
 	StringAuto str(alloc);
 	slist.join(" | ", str);
 	return str;
@@ -726,8 +1131,14 @@ StringAuto RenderGraph::bufferUsageToStr(StackAllocator<U8>& alloc, BufferUsageB
 	ANKI_BUFF_USAGE(TEXTURE_UPLOAD_SOURCE);
 	ANKI_BUFF_USAGE(QUERY_RESULT);
 
+	if(!usage)
+	{
+		slist.pushBackSprintf("NONE");
+	}
+
 #undef ANKI_BUFF_USAGE
 
+	ANKI_ASSERT(!slist.isEmpty());
 	StringAuto str(alloc);
 	slist.join(" | ", str);
 	return str;
@@ -758,7 +1169,10 @@ Error RenderGraph::dumpDependencyDotFile(
 		{
 			CString passName = descr.m_passes[passIdx]->m_name.toCString();
 
-			slist.pushBackSprintf("\t\"%s\"[color=%s,style=bold,shape=box];\n", passName.cstr(), COLORS[batchIdx % 6]);
+			slist.pushBackSprintf("\t\"%s\"[color=%s,style=%s,shape=box];\n",
+				passName.cstr(),
+				COLORS[batchIdx % 6],
+				(descr.m_passes[passIdx]->m_type == RenderPassDescriptionBase::Type::GRAPHICS) ? "bold" : "dashed");
 
 			for(U32 depIdx : ctx.m_passes[passIdx].m_dependsOn)
 			{
@@ -772,6 +1186,7 @@ Error RenderGraph::dumpDependencyDotFile(
 		}
 	}
 
+#if 0
 	// Color the resources
 	slist.pushBackSprintf("subgraph cluster_0 {\n");
 	for(U rtIdx = 0; rtIdx < descr.m_renderTargets.getSize(); ++rtIdx)
@@ -779,9 +1194,10 @@ Error RenderGraph::dumpDependencyDotFile(
 		slist.pushBackSprintf("\t\"%s\"[color=%s];\n", &descr.m_renderTargets[rtIdx].m_name[0], COLORS[rtIdx % 6]);
 	}
 	slist.pushBackSprintf("}\n");
+#endif
 
 	// Barriers
-	slist.pushBackSprintf("subgraph cluster_1 {\n");
+	// slist.pushBackSprintf("subgraph cluster_1 {\n");
 	StringAuto prevBubble(ctx.m_alloc);
 	prevBubble.create("START");
 	for(U batchIdx = 0; batchIdx < ctx.m_batches.getSize(); ++batchIdx)
@@ -795,31 +1211,34 @@ Error RenderGraph::dumpDependencyDotFile(
 		{
 			const Barrier& barrier = batch.m_barriersBefore[barrierIdx];
 			StringAuto barrierName(ctx.m_alloc);
+			StringAuto barrierLabel(ctx.m_alloc);
 			if(barrier.m_isTexture)
 			{
-				barrierName.sprintf("%s barrier%u\n%s (mip,dp,f,l)=(%u,%u,%u,%u)\n%s -> %s",
-					batchName.cstr(),
-					barrierIdx,
+				barrierLabel.sprintf("<b>%s</b> (mip,dp,f,l)=(%u,%u,%u,%u)<br/>%s <b>-&gt;</b> %s",
 					&descr.m_renderTargets[barrier.m_texture.m_idx].m_name[0],
 					barrier.m_texture.m_surface.m_level,
 					barrier.m_texture.m_surface.m_depth,
 					barrier.m_texture.m_surface.m_face,
 					barrier.m_texture.m_surface.m_layer,
-					&textureUsageToStr(alloc, barrier.m_texture.m_usageBefore).toCString()[0],
-					&textureUsageToStr(alloc, barrier.m_texture.m_usageAfter).toCString()[0]);
+					textureUsageToStr(alloc, barrier.m_texture.m_usageBefore).cstr(),
+					textureUsageToStr(alloc, barrier.m_texture.m_usageAfter).cstr());
+
+				barrierName.sprintf("%s barrier%u", batchName.cstr(), barrierIdx);
 			}
 			else
 			{
-				barrierName.sprintf("%s barrier%u\n%s\n%s -> %s",
-					batchName.cstr(),
-					barrierIdx,
+				barrierLabel.sprintf("<b>%s</b><br/>%s <b>-&gt;</b> %s",
 					&descr.m_buffers[barrier.m_buffer.m_idx].m_name[0],
-					bufferUsageToStr(alloc, barrier.m_buffer.m_usageBefore).toCString().cstr(),
-					bufferUsageToStr(alloc, barrier.m_buffer.m_usageAfter).toCString().cstr());
+					bufferUsageToStr(alloc, barrier.m_buffer.m_usageBefore).cstr(),
+					bufferUsageToStr(alloc, barrier.m_buffer.m_usageAfter).cstr());
+
+				barrierName.sprintf("%s barrier%u", batchName.cstr(), barrierIdx);
 			}
 
-			slist.pushBackSprintf(
-				"\t\"%s\"[color=%s,style=bold,shape=box];\n", barrierName.cstr(), COLORS[batchIdx % 6]);
+			slist.pushBackSprintf("\t\"%s\"[color=%s,style=bold,shape=box,label=< %s >];\n",
+				barrierName.cstr(),
+				COLORS[batchIdx % 6],
+				barrierLabel.cstr());
 			slist.pushBackSprintf("\t\"%s\"->\"%s\";\n", prevBubble.cstr(), barrierName.cstr());
 
 			prevBubble = barrierName;
@@ -827,21 +1246,22 @@ Error RenderGraph::dumpDependencyDotFile(
 
 		for(U passIdx : batch.m_passIndices)
 		{
-			const RenderPassBase& pass = *descr.m_passes[passIdx];
+			const RenderPassDescriptionBase& pass = *descr.m_passes[passIdx];
 			StringAuto passName(alloc);
-			passName.sprintf("_%s_", pass.m_name.cstr());
-			slist.pushBackSprintf("\t\"pass: %s\"[color=%s,style=bold];\n", passName.cstr(), COLORS[batchIdx % 6]);
-			slist.pushBackSprintf("\t\"%s\"->\"pass: %s\";\n", prevBubble.cstr(), passName.cstr());
+			passName.sprintf("%s pass", pass.m_name.cstr());
+			slist.pushBackSprintf("\t\"%s\"[color=%s,style=bold];\n", passName.cstr(), COLORS[batchIdx % 6]);
+			slist.pushBackSprintf("\t\"%s\"->\"%s\";\n", prevBubble.cstr(), passName.cstr());
 
 			prevBubble = passName;
 		}
 	}
-	slist.pushBackSprintf("}\n");
+	// slist.pushBackSprintf("}\n");
 
 	slist.pushBackSprintf("}");
 
 	File file;
-	ANKI_CHECK(file.open(StringAuto(alloc).sprintf("%s/rgraph.dot", &path[0]).toCString(), FileOpenFlag::WRITE));
+	ANKI_CHECK(
+		file.open(StringAuto(alloc).sprintf("%s/rgraph_%u.dot", &path[0], m_version).toCString(), FileOpenFlag::WRITE));
 	for(const String& s : slist)
 	{
 		ANKI_CHECK(file.writeText("%s", &s[0]));
@@ -849,89 +1269,6 @@ Error RenderGraph::dumpDependencyDotFile(
 
 	return Error::NONE;
 }
-
-TexturePtr RenderGraph::getTexture(RenderTargetHandle handle) const
-{
-	ANKI_ASSERT(m_ctx->m_rts[handle].m_texture.isCreated());
-	return m_ctx->m_rts[handle].m_texture;
-}
-
-BufferPtr RenderGraph::getBuffer(RenderPassBufferHandle handle) const
-{
-	ANKI_ASSERT(m_ctx->m_buffers[handle].m_buffer.isCreated());
-	return m_ctx->m_buffers[handle].m_buffer;
-}
-
-void RenderGraph::runSecondLevel()
-{
-	ANKI_ASSERT(m_ctx);
-	for(Pass& p : m_ctx->m_passes)
-	{
-		const U size = p.m_secondLevelCmdbs.getSize();
-		for(U i = 0; i < size; ++i)
-		{
-			p.m_callback(p.m_userData, p.m_secondLevelCmdbs[i], i, size);
-		}
-	}
-}
-
-void RenderGraph::run()
-{
-	ANKI_ASSERT(m_ctx);
-
-	CommandBufferPtr& cmdb = m_ctx->m_cmdb;
-
-	for(const Batch& batch : m_ctx->m_batches)
-	{
-		// Set the barriers
-		for(const Barrier& barrier : batch.m_barriersBefore)
-		{
-			if(barrier.m_isTexture)
-			{
-				cmdb->setTextureSurfaceBarrier(m_ctx->m_rts[barrier.m_texture.m_idx].m_texture,
-					barrier.m_texture.m_usageBefore,
-					barrier.m_texture.m_usageAfter,
-					barrier.m_texture.m_surface);
-			}
-			else
-			{
-				cmdb->setBufferBarrier(m_ctx->m_buffers[barrier.m_buffer.m_idx].m_buffer,
-					barrier.m_buffer.m_usageBefore,
-					barrier.m_buffer.m_usageAfter,
-					0,
-					MAX_PTR_SIZE);
-			}
-		}
-
-		// Call the passes
-		for(U passIdx : batch.m_passIndices)
-		{
-			const Pass& pass = m_ctx->m_passes[passIdx];
-
-			if(pass.m_fb.isCreated())
-			{
-				cmdb->beginRenderPass(pass.m_fb); // TODO: Render area
-			}
-
-			const U size = pass.m_secondLevelCmdbs.getSize();
-			if(size == 0)
-			{
-				pass.m_callback(pass.m_userData, cmdb, 0, 0);
-			}
-			else
-			{
-				for(const CommandBufferPtr& cmdb2nd : pass.m_secondLevelCmdbs)
-				{
-					cmdb->pushSecondLevelCommandBuffer(cmdb2nd);
-				}
-			}
-
-			if(pass.m_fb.isCreated())
-			{
-				cmdb->endRenderPass();
-			}
-		}
-	}
-}
+#endif
 
 } // end namespace anki

+ 352 - 94
src/anki/gr/RenderGraph.h

@@ -10,66 +10,222 @@
 #include <anki/gr/Texture.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/Framebuffer.h>
+#include <anki/gr/CommandBuffer.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/BitSet.h>
 
 namespace anki
 {
 
+// Forward
+class RenderGraph;
+
 /// @addtogroup graphics
 /// @{
 
 /// @name RenderGraph constants
 /// @{
 static constexpr U MAX_RENDER_GRAPH_PASSES = 128;
-static constexpr U MAX_RENDER_GRAPH_RENDER_TARGETS = 128; ///< Max imported or not render targets in RenderGraph.
+static constexpr U MAX_RENDER_GRAPH_RENDER_TARGETS = 64; ///< Max imported or not render targets in RenderGraph.
 static constexpr U MAX_RENDER_GRAPH_BUFFERS = 64;
 /// @}
 
 /// Render target handle used in the RenderGraph.
-using RenderTargetHandle = U32;
+class RenderTargetHandle
+{
+	friend class RenderPassDependency;
+	friend class RenderGraphDescription;
+	friend class RenderGraph;
+	friend class RenderPassDescriptionBase;
+
+public:
+	bool operator==(const RenderTargetHandle& b) const
+	{
+		return m_idx == b.m_idx;
+	}
+
+	Bool isValid() const
+	{
+		return m_idx != MAX_U32;
+	}
+
+private:
+	U32 m_idx = MAX_U32;
+
+	Bool valid() const
+	{
+		return m_idx != MAX_U32;
+	}
+};
 
 /// Buffer handle used in the RenderGraph.
-using RenderPassBufferHandle = U32;
+class RenderPassBufferHandle
+{
+	friend class RenderPassDependency;
+	friend class RenderGraphDescription;
+	friend class RenderGraph;
+	friend class RenderPassDescriptionBase;
+
+public:
+	operator BufferPtr() const;
+
+	bool operator==(const RenderPassBufferHandle& b) const
+	{
+		return m_idx == b.m_idx;
+	}
+
+private:
+	U32 m_idx = MAX_U32;
+
+	Bool valid() const
+	{
+		return m_idx != MAX_U32;
+	}
+};
+
+/// Describes the render target.
+class RenderTargetDescription : public TextureInitInfo
+{
+	friend class RenderGraphDescription;
+
+public:
+	RenderTargetDescription()
+	{
+	}
+
+	RenderTargetDescription(CString name)
+		: TextureInitInfo(name)
+	{
+	}
+
+	/// Create an internal hash.
+	void bake()
+	{
+		m_hash = computeHash();
+	}
+
+private:
+	U64 m_hash = 0;
+};
+
+/// The only parameter of RenderPassWorkCallback.
+class RenderPassWorkContext
+{
+	friend class RenderGraph;
+
+public:
+	void* m_userData ANKI_DBG_NULLIFY; ///< The userData passed in RenderPassDescriptionBase::setWork
+	CommandBufferPtr m_commandBuffer;
+	U32 m_currentSecondLevelCommandBufferIndex ANKI_DBG_NULLIFY;
+	U32 m_secondLevelCommandBufferCount ANKI_DBG_NULLIFY;
+
+	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,
+		TexturePtr& tex,
+		TextureUsageBit& usage,
+		DepthStencilAspectBit& aspect) const;
+
+	/// Convenience method.
+	void bindTexture(U32 set, U32 binding, RenderTargetHandle handle)
+	{
+		TexturePtr tex;
+		TextureUsageBit usage;
+		DepthStencilAspectBit aspect;
+		getRenderTargetState(handle, tex, usage, aspect);
+		m_commandBuffer->bindTexture(set, binding, tex, usage, aspect);
+	}
+
+	/// Convenience method.
+	void bindTextureAndSampler(U32 set, U32 binding, RenderTargetHandle handle, SamplerPtr sampler)
+	{
+		TexturePtr tex;
+		TextureUsageBit usage;
+		DepthStencilAspectBit aspect;
+		getRenderTargetState(handle, tex, usage, aspect);
+		m_commandBuffer->bindTextureAndSampler(set, binding, tex, sampler, usage, aspect);
+	}
+
+	/// Convenience method.
+	void bindStorageBuffer(U32 set, U32 binding, RenderPassBufferHandle handle)
+	{
+		BufferPtr buff;
+		getBufferState(handle, buff);
+		m_commandBuffer->bindStorageBuffer(set, binding, buff, 0, MAX_PTR_SIZE);
+	}
+
+	/// Convenience method.
+	void bindUniformBuffer(U32 set, U32 binding, RenderPassBufferHandle handle)
+	{
+		BufferPtr buff;
+		getBufferState(handle, buff);
+		m_commandBuffer->bindUniformBuffer(set, binding, buff, 0, MAX_PTR_SIZE);
+	}
+
+private:
+	const RenderGraph* m_rgraph ANKI_DBG_NULLIFY;
+	U32 m_passIdx ANKI_DBG_NULLIFY;
+};
 
 /// Work callback for a RenderGraph pass.
-using RenderPassWorkCallback = void (*)(
-	void* userData, CommandBufferPtr cmdb, U32 secondLevelCmdbIdx, U32 secondLevelCmdbCount);
+using RenderPassWorkCallback = void (*)(RenderPassWorkContext& ctx);
 
 /// RenderGraph pass dependency.
 class RenderPassDependency
 {
 	friend class RenderGraph;
-	friend class RenderPassBase;
+	friend class RenderPassDescriptionBase;
 
 public:
 	/// Dependency to an individual surface.
-	RenderPassDependency(RenderTargetHandle handle, TextureUsageBit usage, const TextureSurfaceInfo& surface)
-		: m_texture({handle, usage, surface, false})
+	RenderPassDependency(RenderTargetHandle handle,
+		TextureUsageBit usage,
+		const TextureSurfaceInfo& surface,
+		DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE)
+		: m_texture({handle, usage, surface, false, aspect})
 		, m_isTexture(true)
 	{
+		ANKI_ASSERT(handle.valid());
 	}
 
 	/// Dependency to the whole texture.
-	RenderPassDependency(RenderTargetHandle handle, TextureUsageBit usage)
-		: m_texture({handle, usage, TextureSurfaceInfo(0, 0, 0, 0), true})
+	RenderPassDependency(
+		RenderTargetHandle handle, TextureUsageBit usage, DepthStencilAspectBit aspect = DepthStencilAspectBit::NONE)
+		: m_texture({handle, usage, TextureSurfaceInfo(0, 0, 0, 0), true, aspect})
 		, m_isTexture(true)
 	{
+		ANKI_ASSERT(handle.valid());
 	}
 
 	RenderPassDependency(RenderPassBufferHandle handle, BufferUsageBit usage)
 		: m_buffer({handle, usage})
 		, m_isTexture(false)
 	{
+		ANKI_ASSERT(handle.valid());
 	}
 
 private:
-	struct TextureInfo
+	class TextureInfo
 	{
+	public:
 		RenderTargetHandle m_handle;
 		TextureUsageBit m_usage;
 		TextureSurfaceInfo m_surface;
 		Bool8 m_wholeTex;
+		DepthStencilAspectBit m_aspect;
 	};
 
 	struct BufferInfo
@@ -88,12 +244,12 @@ private:
 };
 
 /// The base of compute/transfer and graphics renderpasses for RenderGraph.
-class RenderPassBase
+class RenderPassDescriptionBase
 {
 	friend class RenderGraph;
 
 public:
-	virtual ~RenderPassBase()
+	virtual ~RenderPassDescriptionBase()
 	{
 		m_name.destroy(m_alloc); // To avoid the assertion
 		m_consumers.destroy(m_alloc);
@@ -103,7 +259,7 @@ public:
 	void setWork(RenderPassWorkCallback callback, void* userData, U32 secondLeveCmdbCount)
 	{
 		ANKI_ASSERT(callback);
-		ANKI_ASSERT(m_type == Type::GRAPHICS || secondLeveCmdbCount != 0);
+		ANKI_ASSERT(m_type == Type::GRAPHICS || secondLeveCmdbCount == 0);
 		m_callback = callback;
 		m_userData = userData;
 		m_secondLevelCmdbsCount = secondLeveCmdbCount;
@@ -116,11 +272,12 @@ public:
 
 		if(dep.m_isTexture && dep.m_texture.m_usage != TextureUsageBit::NONE)
 		{
-			m_consumerRtMask.set(dep.m_texture.m_handle);
+			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_consumerBufferMask.set(dep.m_buffer.m_handle.m_idx);
+			m_hasBufferDeps = true;
 		}
 	}
 
@@ -130,11 +287,12 @@ public:
 		m_producers.emplaceBack(m_alloc, dep);
 		if(dep.m_isTexture)
 		{
-			m_producerRtMask.set(dep.m_texture.m_handle);
+			m_producerRtMask.set(dep.m_texture.m_handle.m_idx);
 		}
 		else
 		{
-			m_producerBufferMask.set(dep.m_buffer.m_handle);
+			m_producerBufferMask.set(dep.m_buffer.m_handle.m_idx);
+			m_hasBufferDeps = true;
 		}
 	}
 
@@ -156,14 +314,15 @@ protected:
 	DynamicArray<RenderPassDependency> m_consumers;
 	DynamicArray<RenderPassDependency> m_producers;
 
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> m_consumerRtMask = {false};
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> m_producerRtMask = {false};
-	BitSet<MAX_RENDER_GRAPH_BUFFERS> m_consumerBufferMask = {false};
-	BitSet<MAX_RENDER_GRAPH_BUFFERS> m_producerBufferMask = {false};
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64> m_consumerRtMask = {false};
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS, U64> m_producerRtMask = {false};
+	BitSet<MAX_RENDER_GRAPH_BUFFERS, U64> m_consumerBufferMask = {false};
+	BitSet<MAX_RENDER_GRAPH_BUFFERS, U64> m_producerBufferMask = {false};
+	Bool8 m_hasBufferDeps = false; ///< Opt.
 
 	String m_name;
 
-	RenderPassBase(Type t)
+	RenderPassDescriptionBase(Type t)
 		: m_type(t)
 	{
 	}
@@ -174,51 +333,30 @@ protected:
 	}
 };
 
-/// Describes a framebuffer.
-class GraphicsRenderPassFramebufferInfo
+/// Framebuffer attachment info.
+class FramebufferDescriptionAttachment
 {
-	friend class GraphicsRenderPassInfo;
-
 public:
-	void attachRenderTarget(U32 location,
-		const TextureSurfaceInfo& surf,
-		AttachmentLoadOperation loadOperation = AttachmentLoadOperation::DONT_CARE,
-		AttachmentStoreOperation storeOperation = AttachmentStoreOperation::STORE,
-		const ClearValue& clearValue = ClearValue())
-	{
-		FramebufferAttachmentInfo& att = m_fbInitInfo.m_colorAttachments[location];
-		att.m_surface = surf;
-		att.m_loadOperation = loadOperation;
-		att.m_storeOperation = storeOperation;
-		att.m_clearValue = clearValue;
+	TextureSurfaceInfo m_surface;
+	AttachmentLoadOperation m_loadOperation = AttachmentLoadOperation::CLEAR;
+	AttachmentStoreOperation m_storeOperation = AttachmentStoreOperation::STORE;
+	ClearValue m_clearValue;
 
-		m_fbInitInfo.m_colorAttachmentCount = location + 1;
-		m_defaultFb = false;
-	}
+	AttachmentLoadOperation m_stencilLoadOperation = AttachmentLoadOperation::CLEAR;
+	AttachmentStoreOperation m_stencilStoreOperation = AttachmentStoreOperation::STORE;
 
-	void attachDepthStencilRenderTarget(const TextureSurfaceInfo& surf,
-		AttachmentLoadOperation loadOperation = AttachmentLoadOperation::CLEAR,
-		AttachmentStoreOperation storeOperation = AttachmentStoreOperation::STORE,
-		AttachmentLoadOperation stencilLoadOperation = AttachmentLoadOperation::CLEAR,
-		AttachmentStoreOperation stencilStoreOperation = AttachmentStoreOperation::STORE,
-		DepthStencilAspectBit aspect = DepthStencilAspectBit::DEPTH,
-		F32 depthClear = 1.0f,
-		I32 stencilClear = 0)
-	{
-		ANKI_ASSERT(!!(aspect & DepthStencilAspectBit::DEPTH_STENCIL));
+	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE; ///< Relevant only for depth stencil textures.
+};
 
-		FramebufferAttachmentInfo& att = m_fbInitInfo.m_depthStencilAttachment;
-		att.m_surface = surf;
-		att.m_loadOperation = loadOperation;
-		att.m_storeOperation = storeOperation;
-		att.m_clearValue.m_depthStencil.m_depth = depthClear;
-		att.m_clearValue.m_depthStencil.m_stencil = stencilClear;
-		att.m_stencilLoadOperation = stencilLoadOperation;
-		att.m_stencilStoreOperation = stencilStoreOperation;
-		att.m_aspect = aspect;
+/// Describes a framebuffer.
+class FramebufferDescription
+{
+	friend class GraphicsRenderPassDescription;
 
-		m_defaultFb = false;
-	}
+public:
+	Array<FramebufferDescriptionAttachment, MAX_COLOR_ATTACHMENTS> m_colorAttachments;
+	U32 m_colorAttachmentCount = 0;
+	FramebufferDescriptionAttachment m_depthStencilAttachment;
 
 	void setDefaultFramebuffer()
 	{
@@ -228,6 +366,11 @@ public:
 	/// Calculate the hash for the framebuffer.
 	void bake();
 
+	Bool isBacked() const
+	{
+		return m_hash != 0;
+	}
+
 private:
 	FramebufferInitInfo m_fbInitInfo;
 	Bool8 m_defaultFb = false;
@@ -236,22 +379,50 @@ private:
 };
 
 /// A graphics render pass for RenderGraph.
-class GraphicsRenderPassInfo : public RenderPassBase
+class GraphicsRenderPassDescription : public RenderPassDescriptionBase
 {
 	friend class RenderGraphDescription;
 	friend class RenderGraph;
 
 public:
-	GraphicsRenderPassInfo()
-		: RenderPassBase(Type::GRAPHICS)
+	GraphicsRenderPassDescription()
+		: RenderPassDescriptionBase(Type::GRAPHICS)
 	{
+		memset(&m_rtHandles[0], 0xFF, sizeof(m_rtHandles));
 	}
 
-	void setFramebufferInfo(const GraphicsRenderPassFramebufferInfo& fbInfo,
+	void setFramebufferInfo(const FramebufferDescription& fbInfo,
 		const Array<RenderTargetHandle, MAX_COLOR_ATTACHMENTS>& colorRenderTargetHandles,
-		RenderTargetHandle depthStencilRenderTargetHandle)
-	{
-		ANKI_ASSERT(fbInfo.m_hash != 0 && "Forgot call GraphicsRenderPassFramebufferInfo::bake");
+		RenderTargetHandle depthStencilRenderTargetHandle,
+		U32 minx = 0,
+		U32 miny = 0,
+		U32 maxx = MAX_U32,
+		U32 maxy = MAX_U32)
+	{
+#if ANKI_EXTRA_CHECKS
+		ANKI_ASSERT(fbInfo.isBacked() && "Forgot call GraphicsRenderPassFramebufferInfo::bake");
+		for(U i = 0; i < colorRenderTargetHandles.getSize(); ++i)
+		{
+			if(fbInfo.m_defaultFb || i >= fbInfo.m_colorAttachmentCount)
+			{
+				ANKI_ASSERT(!colorRenderTargetHandles[i].isValid());
+			}
+			else
+			{
+				ANKI_ASSERT(colorRenderTargetHandles[i].isValid());
+			}
+		}
+
+		if(fbInfo.m_defaultFb || !fbInfo.m_depthStencilAttachment.m_aspect)
+		{
+			ANKI_ASSERT(!depthStencilRenderTargetHandle.isValid());
+		}
+		else
+		{
+			ANKI_ASSERT(depthStencilRenderTargetHandle.isValid());
+		}
+#endif
+
 		if(fbInfo.m_defaultFb)
 		{
 			m_fbInitInfo.m_colorAttachmentCount = 1;
@@ -264,11 +435,13 @@ public:
 		}
 		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;
+	Array<U32, 4> m_fbRenderArea = {};
 	U64 m_fbHash = 0;
 
 	Bool hasFramebuffer() const
@@ -278,18 +451,18 @@ private:
 };
 
 /// A compute render pass for RenderGraph.
-class ComputeRenderPassInfo : public RenderPassBase
+class ComputeRenderPassDescription : public RenderPassDescriptionBase
 {
 	friend class RenderGraphDescription;
 
 public:
-	ComputeRenderPassInfo()
-		: RenderPassBase(Type::NO_GRAPHICS)
+	ComputeRenderPassDescription()
+		: RenderPassDescriptionBase(Type::NO_GRAPHICS)
 	{
 	}
 };
 
-/// Describes the render graph by creating passes and setting dependencies on them.
+/// Builds the description of the frame's render passes and their interactions.
 class RenderGraphDescription
 {
 	friend class RenderGraph;
@@ -302,18 +475,19 @@ public:
 
 	~RenderGraphDescription()
 	{
-		for(RenderPassBase* pass : m_passes)
+		for(RenderPassDescriptionBase* pass : m_passes)
 		{
 			m_alloc.deleteInstance(pass);
 		}
 		m_passes.destroy(m_alloc);
 		m_renderTargets.destroy(m_alloc);
+		m_buffers.destroy(m_alloc);
 	}
 
 	/// Create a new graphics render pass.
-	GraphicsRenderPassInfo& newGraphicsRenderPass(CString name)
+	GraphicsRenderPassDescription& newGraphicsRenderPass(CString name)
 	{
-		GraphicsRenderPassInfo* pass = m_alloc.newInstance<GraphicsRenderPassInfo>();
+		GraphicsRenderPassDescription* pass = m_alloc.newInstance<GraphicsRenderPassDescription>();
 		pass->m_alloc = m_alloc;
 		pass->setName(name);
 		m_passes.emplaceBack(m_alloc, pass);
@@ -321,9 +495,9 @@ public:
 	}
 
 	/// Create a new compute render pass.
-	ComputeRenderPassInfo& newComputeRenderPass(CString name)
+	ComputeRenderPassDescription& newComputeRenderPass(CString name)
 	{
-		ComputeRenderPassInfo* pass = m_alloc.newInstance<ComputeRenderPassInfo>();
+		ComputeRenderPassDescription* pass = m_alloc.newInstance<ComputeRenderPassDescription>();
 		pass->m_alloc = m_alloc;
 		pass->setName(name);
 		m_passes.emplaceBack(m_alloc, pass);
@@ -337,17 +511,25 @@ public:
 		rt.m_importedTex = tex;
 		rt.m_usage = usage;
 		rt.setName(name);
-		return m_renderTargets.getSize() - 1;
+
+		RenderTargetHandle out;
+		out.m_idx = m_renderTargets.getSize() - 1;
+		return out;
 	}
 
 	/// Get or create a new render target.
-	RenderTargetHandle newRenderTarget(CString name, const TextureInitInfo& initInf)
+	RenderTargetHandle newRenderTarget(const RenderTargetDescription& initInf)
 	{
+		ANKI_ASSERT(initInf.m_hash && "Forgot to call RenderTargetDescription::bake");
 		RT& rt = m_renderTargets.emplaceBack(m_alloc);
 		rt.m_initInfo = initInf;
+		rt.m_hash = initInf.m_hash;
 		rt.m_usage = TextureUsageBit::NONE;
-		rt.setName(name);
-		return m_renderTargets.getSize() - 1;
+		rt.setName(initInf.getName());
+
+		RenderTargetHandle out;
+		out.m_idx = m_renderTargets.getSize() - 1;
+		return out;
 	}
 
 	/// Import a buffer.
@@ -357,7 +539,10 @@ public:
 		b.setName(name);
 		b.m_usage = usage;
 		b.m_importedBuff = buff;
-		return m_buffers.getSize() - 1;
+
+		RenderPassBufferHandle out;
+		out.m_idx = m_buffers.getSize() - 1;
+		return out;
 	}
 
 private:
@@ -378,6 +563,7 @@ private:
 	{
 	public:
 		TextureInitInfo m_initInfo;
+		U64 m_hash = 0;
 		TexturePtr m_importedTex;
 		TextureUsageBit m_usage;
 	};
@@ -390,16 +576,27 @@ private:
 	};
 
 	StackAllocator<U8> m_alloc;
-	DynamicArray<RenderPassBase*> m_passes;
+	DynamicArray<RenderPassDescriptionBase*> m_passes;
 	DynamicArray<RT> m_renderTargets;
 	DynamicArray<Buffer> m_buffers;
 };
 
 /// Accepts a descriptor of the frame's render passes and sets the dependencies between them.
+///
+/// The idea for the RenderGraph is to automate:
+/// - Synchronization (barriers, events etc) between passes.
+/// - Command buffer creation for primary and secondary command buffers.
+/// - Framebuffer creation.
+/// - Render target creation (optional since textures can be imported as well).
+///
+/// It accepts a description of the frame's render passes (compute and graphics), compiles that description to calculate
+/// dependencies and then populates command buffers with the help of multiple RenderPassWorkCallback.
 class RenderGraph final : public GrObject
 {
 	ANKI_GR_OBJECT
 
+	friend class RenderPassWorkContext;
+
 public:
 	static const GrObjectType CLASS_TYPE = GrObjectType::RENDER_GRAPH;
 
@@ -415,7 +612,7 @@ public:
 
 	void init()
 	{
-		// Do nothing, implement the method for the interface
+		// Do nothing, implement the method to align with the general interface
 	}
 
 	/// @name 1st step methods
@@ -427,20 +624,19 @@ public:
 	/// @{
 
 	/// Will call a number of RenderPassWorkCallback that populate 2nd level command buffers.
-	void runSecondLevel();
+	void runSecondLevel(U32 threadIdx) const;
 	/// @}
 
 	/// @name 3rd step methods
 	/// @{
 
 	/// Will call a number of RenderPassWorkCallback that populate 1st level command buffers.
-	void run();
+	void run() const;
 	/// @}
 
-	/// @name 2nd and 3rd step methods
+	/// @name 3rd step methods
 	/// @{
-	TexturePtr getTexture(RenderTargetHandle handle) const;
-	BufferPtr getBuffer(RenderPassBufferHandle handle) const;
+	void flush();
 	/// @}
 
 	/// @name 4th step methods
@@ -459,7 +655,7 @@ private:
 		U32 m_texturesInUse = 0;
 	};
 
-	HashMap<TextureInitInfo, RenderTargetCacheEntry> m_renderTargetCache; ///< Non-imported render targets.
+	HashMap<U64, RenderTargetCacheEntry> m_renderTargetCache; ///< Non-imported render targets.
 
 	HashMap<U64, FramebufferPtr> m_fbCache; ///< Framebuffer cache.
 
@@ -472,25 +668,87 @@ private:
 	class Barrier;
 
 	BakeContext* m_ctx = nullptr;
+	U64 m_version = 0;
 
 	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;
 
-	TexturePtr getOrCreateRenderTarget(const TextureInitInfo& initInf);
+	TexturePtr getOrCreateRenderTarget(const TextureInitInfo& initInf, U64 hash);
 	FramebufferPtr getOrCreateFramebuffer(
 		const FramebufferInitInfo& fbInit, const RenderTargetHandle* rtHandles, U64 hash);
 
-	static Bool passADependsOnB(BakeContext& ctx, const RenderPassBase& a, const RenderPassBase& b);
+	static ANKI_HOT Bool passADependsOnB(const RenderPassDescriptionBase& a, const RenderPassDescriptionBase& b);
+
+	template<Bool isTexture>
+	static ANKI_HOT Bool overlappingDependency(const RenderPassDependency& a, const RenderPassDependency& b);
+
 	static Bool passHasUnmetDependencies(const BakeContext& ctx, U32 passIdx);
 
-	/// Dump the dependency graph into a file.
+	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 getCrntUsageAndAspect(
+		RenderTargetHandle handle, U32 passIdx, TextureUsageBit& usage, DepthStencilAspectBit& aspect) const;
+	void getCrntUsageAndAspect(RenderTargetHandle handle,
+		U32 passIdx,
+		const TextureSurfaceInfo& surf,
+		TextureUsageBit& usage,
+		DepthStencilAspectBit& aspect) const;
+
+	/// @name Dump the dependency graph into a file.
+	/// @{
 	ANKI_USE_RESULT Error dumpDependencyDotFile(
 		const RenderGraphDescription& descr, const BakeContext& ctx, CString path) const;
 	static StringAuto textureUsageToStr(StackAllocator<U8>& alloc, TextureUsageBit usage);
 	static StringAuto bufferUsageToStr(StackAllocator<U8>& alloc, BufferUsageBit usage);
+	/// @}
+
+	TexturePtr getTexture(RenderTargetHandle handle) const;
+	BufferPtr getBuffer(RenderPassBufferHandle handle) const;
 };
 /// @}
 
+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

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

@@ -51,13 +51,13 @@ public:
 	}
 };
 
-void Buffer::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access)
+void Buffer::init(const BufferInitInfo& inf)
 {
 	m_impl.reset(getAllocator().newInstance<BufferImpl>(&getManager()));
 
 	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(CommandBufferInitInfo());
 
-	cmdb->m_impl->pushBackNewCommand<BufferCreateCommand>(this, size, usage, access);
+	cmdb->m_impl->pushBackNewCommand<BufferCreateCommand>(this, inf.m_size, inf.m_usage, inf.m_access);
 	cmdb->flush();
 }
 

+ 33 - 36
src/anki/gr/gl/CommandBuffer.cpp

@@ -219,14 +219,14 @@ void CommandBuffer::setPrimitiveRestart(Bool enable)
 	}
 }
 
-void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
+void CommandBuffer::setViewport(U32 minx, U32 miny, U32 width, U32 height)
 {
 	class ViewportCommand final : public GlCommand
 	{
 	public:
-		Array<U16, 4> m_value;
+		Array<U32, 4> m_value;
 
-		ViewportCommand(U16 a, U16 b, U16 c, U16 d)
+		ViewportCommand(U32 a, U32 b, U32 c, U32 d)
 		{
 			m_value = {{a, b, c, d}};
 		}
@@ -238,20 +238,23 @@ void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
 		}
 	};
 
-	if(m_impl->m_state.setViewport(minx, miny, maxx, maxy))
+	if(m_impl->m_state.setViewport(minx, miny, width, height))
 	{
-		m_impl->pushBackNewCommand<ViewportCommand>(minx, miny, maxx - minx, maxy - miny);
+		m_impl->pushBackNewCommand<ViewportCommand>(minx, miny, width, height);
 	}
 }
 
-void CommandBuffer::setScissor(U16 minx, U16 miny, U16 maxx, U16 maxy)
+void CommandBuffer::setScissor(U32 minx, U32 miny, U32 width, U32 height)
 {
+	ANKI_ASSERT(minx < MAX_U32 && miny < MAX_U32);
+	ANKI_ASSERT(width > 0 && height > 0);
+
 	class ScissorCommand final : public GlCommand
 	{
 	public:
-		Array<U16, 4> m_value;
+		Array<GLsizei, 4> m_value;
 
-		ScissorCommand(U16 a, U16 b, U16 c, U16 d)
+		ScissorCommand(GLsizei a, GLsizei b, GLsizei c, GLsizei d)
 		{
 			m_value = {{a, b, c, d}};
 		}
@@ -268,9 +271,15 @@ void CommandBuffer::setScissor(U16 minx, U16 miny, U16 maxx, U16 maxy)
 		}
 	};
 
-	if(m_impl->m_state.setScissor(minx, miny, maxx, maxy))
+	// Limit the width and height to GLsizei
+	const GLsizei iwidth = (width == MAX_U32) ? MAX_I32 : width;
+	const GLsizei iheight = (height == MAX_U32) ? MAX_I32 : height;
+	const GLsizei iminx = minx;
+	const GLsizei iminy = miny;
+
+	if(m_impl->m_state.setScissor(iminx, iminy, iwidth, iheight))
 	{
-		m_impl->pushBackNewCommand<ScissorCommand>(minx, miny, maxx - minx, maxy - miny);
+		m_impl->pushBackNewCommand<ScissorCommand>(iminx, iminy, iwidth, iheight);
 	}
 }
 
@@ -612,7 +621,8 @@ void CommandBuffer::setBlendOperation(U32 attachment, BlendOperation funcRgb, Bl
 	}
 }
 
-void CommandBuffer::bindTexture(U32 set, U32 binding, TexturePtr tex, DepthStencilAspectBit aspect)
+void CommandBuffer::bindTexture(
+	U32 set, U32 binding, TexturePtr tex, TextureUsageBit usage, DepthStencilAspectBit aspect)
 {
 	class Cmd final : public GlCommand
 	{
@@ -652,7 +662,7 @@ void CommandBuffer::bindTexture(U32 set, U32 binding, TexturePtr tex, DepthStenc
 }
 
 void CommandBuffer::bindTextureAndSampler(
-	U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, DepthStencilAspectBit aspect)
+	U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, TextureUsageBit usage, DepthStencilAspectBit aspect)
 {
 	class Cmd final : public GlCommand
 	{
@@ -870,19 +880,23 @@ void CommandBuffer::bindShaderProgram(ShaderProgramPtr prog)
 	}
 }
 
-void CommandBuffer::beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy)
+void CommandBuffer::beginRenderPass(FramebufferPtr fb,
+	const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorAttachmentUsages,
+	TextureUsageBit depthStencilAttachmentUsage,
+	U32 minx,
+	U32 miny,
+	U32 width,
+	U32 height)
 {
-	ANKI_ASSERT(minx < maxx && miny < maxy);
-
 	class BindFramebufferCommand final : public GlCommand
 	{
 	public:
 		FramebufferPtr m_fb;
-		Array<U16, 4> m_renderArea;
+		Array<U32, 4> m_renderArea;
 
-		BindFramebufferCommand(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy)
+		BindFramebufferCommand(FramebufferPtr fb, U32 minx, U32 miny, U32 width, U32 height)
 			: m_fb(fb)
-			, m_renderArea{{minx, miny, maxx, maxy}}
+			, m_renderArea{{minx, miny, width, height}}
 		{
 		}
 
@@ -895,7 +909,7 @@ void CommandBuffer::beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 m
 
 	if(m_impl->m_state.beginRenderPass(fb))
 	{
-		m_impl->pushBackNewCommand<BindFramebufferCommand>(fb, minx, miny, maxx, maxy);
+		m_impl->pushBackNewCommand<BindFramebufferCommand>(fb, minx, miny, width, height);
 	}
 }
 
@@ -1545,21 +1559,4 @@ void CommandBuffer::writeOcclusionQueryResultToBuffer(OcclusionQueryPtr query, P
 	m_impl->pushBackNewCommand<WriteOcclResultToBuff>(query, offset, buff);
 }
 
-void CommandBuffer::informTextureSurfaceCurrentUsage(
-	TexturePtr tex, const TextureSurfaceInfo& surf, TextureUsageBit crntUsage)
-{
-	// Nothing for GL
-}
-
-void CommandBuffer::informTextureVolumeCurrentUsage(
-	TexturePtr tex, const TextureVolumeInfo& vol, TextureUsageBit crntUsage)
-{
-	// Nothing for GL
-}
-
-void CommandBuffer::informTextureCurrentUsage(TexturePtr tex, TextureUsageBit crntUsage)
-{
-	// Nothing for GL
-}
-
 } // end namespace anki

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

@@ -187,7 +187,7 @@ void CommandBufferImpl::flushDrawcall(CommandBuffer& cmdb)
 
 		if(!m_state.m_scissorSet)
 		{
-			cmdb.setScissor(0, 0, MAX_U16, MAX_U16);
+			cmdb.setScissor(0, 0, MAX_U32, MAX_U32);
 		}
 	}
 

+ 34 - 9
src/anki/gr/gl/FramebufferImpl.cpp

@@ -22,6 +22,7 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 
 	if(m_in.m_colorAttachmentCount == 1 && !m_in.m_colorAttachments[0].m_texture.isCreated())
 	{
+		m_fbSize[0] = m_fbSize[1] = MAX_U16;
 		m_bindDefault = true;
 		return Error::NONE;
 	}
@@ -47,6 +48,17 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 		{
 			m_invalidateBuffers[m_invalidateBuffersCount++] = binding;
 		}
+
+		if(m_fbSize[0] == 0)
+		{
+			m_fbSize[0] = att.m_texture->m_impl->m_width;
+			m_fbSize[1] = att.m_texture->m_impl->m_height;
+		}
+		else
+		{
+			ANKI_ASSERT(m_fbSize[0] == att.m_texture->m_impl->m_width);
+			ANKI_ASSERT(m_fbSize[1] == att.m_texture->m_impl->m_height);
+		}
 	}
 
 	// Attach depth/stencil
@@ -97,6 +109,17 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 		{
 			m_invalidateBuffers[m_invalidateBuffersCount++] = binding;
 		}
+
+		if(m_fbSize[0] == 0)
+		{
+			m_fbSize[0] = att.m_texture->m_impl->m_width;
+			m_fbSize[1] = att.m_texture->m_impl->m_height;
+		}
+		else
+		{
+			ANKI_ASSERT(m_fbSize[0] == att.m_texture->m_impl->m_width);
+			ANKI_ASSERT(m_fbSize[1] == att.m_texture->m_impl->m_height);
+		}
 	}
 
 	// Check completeness
@@ -151,15 +174,22 @@ void FramebufferImpl::attachTextureInternal(
 	}
 }
 
-void FramebufferImpl::bind(const GlState& state, U16 minx, U16 miny, U16 maxx, U16 maxy)
+void FramebufferImpl::bind(const GlState& state, U32 minx, U32 miny, U32 width, U32 height)
 {
+	ANKI_ASSERT(width > 0 && height > 0);
+
 	if(m_in.getName() && getManager().getImplementation().debugMarkersEnabled())
 	{
 		glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, m_glName, 0, &m_in.getName()[0]);
 	}
 
 	// Clear in the render area
-	glScissor(minx, miny, maxx - minx, maxy - miny);
+	const U maxx = min<U>(minx + width, m_fbSize[0]);
+	const U maxy = min<U>(miny + height, m_fbSize[1]);
+	ANKI_ASSERT(minx < m_fbSize[0] && miny < m_fbSize[1] && maxx <= m_fbSize[0] && maxy <= m_fbSize[1]);
+	width = maxx - minx;
+	height = maxy - miny;
+	glScissor(minx, miny, width, height);
 
 	if(m_bindDefault)
 	{
@@ -193,13 +223,8 @@ void FramebufferImpl::bind(const GlState& state, U16 minx, U16 miny, U16 maxx, U
 		// Invalidate
 		if(m_invalidateBuffersCount)
 		{
-			glInvalidateSubFramebuffer(GL_FRAMEBUFFER,
-				m_invalidateBuffersCount,
-				&m_invalidateBuffers[0],
-				minx,
-				miny,
-				maxx - minx,
-				maxy - miny);
+			glInvalidateSubFramebuffer(
+				GL_FRAMEBUFFER, m_invalidateBuffersCount, &m_invalidateBuffers[0], minx, miny, width, height);
 		}
 
 		// Clear buffers

+ 2 - 1
src/anki/gr/gl/FramebufferImpl.h

@@ -33,7 +33,7 @@ public:
 	ANKI_USE_RESULT Error init(const FramebufferInitInfo& init);
 
 	/// Bind it to the state. Call it in rendering thread
-	void bind(const GlState& state, U16 minx, U16 miny, U16 maxx, U16 maxy);
+	void bind(const GlState& state, U32 minx, U32 miny, U32 width, U32 height);
 
 	void endRenderPass() const;
 
@@ -57,6 +57,7 @@ public:
 private:
 	FramebufferInitInfo m_in;
 
+	Array<U32, 2> m_fbSize = {};
 	Array<GLenum, MAX_COLOR_ATTACHMENTS> m_drawBuffers;
 	Array<GLenum, MAX_COLOR_ATTACHMENTS + 1> m_invalidateBuffers;
 	U8 m_invalidateBuffersCount = 0;

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

@@ -135,7 +135,7 @@ void GlState::initRenderThread()
 	glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
 	glEnable(GL_CULL_FACE);
 	glEnable(GL_SCISSOR_TEST);
-	glScissor(0, 0, MAX_U16, MAX_U16);
+	glScissor(0, 0, MAX_I16, MAX_I16);
 	glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
 
 	// Create default VAO

+ 1 - 1
src/anki/gr/gl/GlState.h

@@ -50,7 +50,7 @@ public:
 
 	Array2d<GLuint, MAX_DESCRIPTOR_SETS, MAX_TEXTURE_BUFFER_BINDINGS> m_texBuffTextures = {};
 
-	Array<U16, 4> m_scissor = {{0, 0, MAX_U16, MAX_U16}};
+	Array<GLsizei, 4> m_scissor = {{0, 0, MAX_I32, MAX_I32}};
 
 	GlState(GrManager* manager)
 		: m_manager(manager)

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

@@ -113,14 +113,14 @@ public:
 
 	/// @name viewport_state
 	/// @{
-	Array<U16, 4> m_viewport = {{MAX_U16, MAX_U16, MAX_U16, MAX_U16}};
+	Array<U32, 4> m_viewport = {{MAX_U32, MAX_U32, MAX_U32, MAX_U32}};
 
-	Bool setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
+	Bool setViewport(U32 minx, U32 miny, U32 width, U32 height)
 	{
-		ANKI_ASSERT(minx != MAX_U16 && miny != MAX_U16 && maxx != MAX_U16 && maxy != MAX_U16);
-		if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != maxx || m_viewport[3] != maxy)
+		ANKI_ASSERT(minx != MAX_U32 && miny != MAX_U32 && width != MAX_U32 && height != MAX_U32);
+		if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != width || m_viewport[3] != height)
 		{
-			m_viewport = {{minx, miny, maxx, maxy}};
+			m_viewport = {{minx, miny, width, height}};
 			return true;
 		}
 		return false;
@@ -129,15 +129,15 @@ public:
 
 	/// @name scissor_state
 	/// @{
-	Array<U16, 4> m_scissor = {{0, 0, MAX_U16, MAX_U16}};
+	Array<GLsizei, 4> m_scissor = {{0, 0, MAX_I32, MAX_I32}};
 	Bool8 m_scissorSet = false;
 
-	Bool setScissor(U16 minx, U16 miny, U16 maxx, U16 maxy)
+	Bool setScissor(GLsizei minx, GLsizei miny, GLsizei width, GLsizei height)
 	{
 		if(!m_scissorSet
-			|| (m_scissor[0] != minx || m_scissor[1] != miny || m_scissor[2] != maxx || m_scissor[3] != maxy))
+			|| (m_scissor[0] != minx || m_scissor[1] != miny || m_scissor[2] != width || m_scissor[3] != height))
 		{
-			m_scissor = {{minx, miny, maxx, maxy}};
+			m_scissor = {{minx, miny, width, height}};
 			m_scissorSet = true;
 			return true;
 		}

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

@@ -18,11 +18,11 @@ Buffer::~Buffer()
 {
 }
 
-void Buffer::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access)
+void Buffer::init(const BufferInitInfo& inf)
 {
 	m_impl.reset(getAllocator().newInstance<BufferImpl>(&getManager()));
 
-	if(m_impl->init(size, usage, access))
+	if(m_impl->init(inf))
 	{
 		ANKI_VK_LOGF("Cannot recover");
 	}

+ 16 - 4
src/anki/gr/vulkan/BufferImpl.cpp

@@ -24,9 +24,14 @@ BufferImpl::~BufferImpl()
 	}
 }
 
-Error BufferImpl::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access)
+Error BufferImpl::init(const BufferInitInfo& inf)
 {
 	ANKI_ASSERT(!isCreated());
+
+	PtrSize size = inf.m_size;
+	BufferMapAccessBit access = inf.m_access;
+	BufferUsageBit usage = inf.m_usage;
+
 	ANKI_ASSERT(size > 0);
 	ANKI_ASSERT(usage != BufferUsageBit::NONE);
 
@@ -43,6 +48,7 @@ Error BufferImpl::init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit ac
 	U32 queueIdx = getGrManagerImpl().getGraphicsQueueFamily();
 	ci.pQueueFamilyIndices = &queueIdx;
 	ANKI_VK_CHECK(vkCreateBuffer(getDevice(), &ci, nullptr, &m_handle));
+	getGrManagerImpl().trySetVulkanHandleName(inf.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_handle);
 
 	// Get mem requirements
 	VkMemoryRequirements req;
@@ -195,8 +201,9 @@ VkPipelineStageFlags BufferImpl::computePplineStage(BufferUsageBit usage)
 
 	if(!!(usage & (BufferUsageBit::INDEX | BufferUsageBit::VERTEX)))
 	{
-		stageMask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT
-			| VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
+		stageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
+			| VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT
+			| VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
 	}
 
 	if(!!(usage & BufferUsageBit::INDIRECT))
@@ -214,6 +221,11 @@ VkPipelineStageFlags BufferImpl::computePplineStage(BufferUsageBit usage)
 		stageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
 	}
 
+	if(!stageMask)
+	{
+		stageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+	}
+
 	ANKI_ASSERT(stageMask);
 	return stageMask;
 }
@@ -277,7 +289,6 @@ VkAccessFlags BufferImpl::computeAccessMask(BufferUsageBit usage)
 		mask |= VK_ACCESS_TRANSFER_WRITE_BIT;
 	}
 
-	ANKI_ASSERT(mask);
 	return mask;
 }
 
@@ -289,6 +300,7 @@ void BufferImpl::computeBarrierInfo(BufferUsageBit before,
 	VkAccessFlags& dstAccesses) const
 {
 	ANKI_ASSERT(usageValid(before) && usageValid(after));
+	ANKI_ASSERT(!!after);
 
 	srcStages = computePplineStage(before);
 	dstStages = computePplineStage(after);

+ 1 - 1
src/anki/gr/vulkan/BufferImpl.h

@@ -25,7 +25,7 @@ public:
 
 	~BufferImpl();
 
-	ANKI_USE_RESULT Error init(PtrSize size, BufferUsageBit usage, BufferMapAccessBit access);
+	ANKI_USE_RESULT Error init(const BufferInitInfo& inf);
 
 	ANKI_USE_RESULT void* map(PtrSize offset, PtrSize range, BufferMapAccessBit access);
 

+ 17 - 28
src/anki/gr/vulkan/CommandBuffer.cpp

@@ -81,14 +81,14 @@ void CommandBuffer::setPrimitiveRestart(Bool enable)
 	m_impl->setPrimitiveRestart(enable);
 }
 
-void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
+void CommandBuffer::setViewport(U32 minx, U32 miny, U32 width, U32 height)
 {
-	m_impl->setViewport(minx, miny, maxx, maxy);
+	m_impl->setViewport(minx, miny, width, height);
 }
 
-void CommandBuffer::setScissor(U16 minx, U16 miny, U16 maxx, U16 maxy)
+void CommandBuffer::setScissor(U32 minx, U32 miny, U32 width, U32 height)
 {
-	m_impl->setScissor(minx, miny, maxx, maxy);
+	m_impl->setScissor(minx, miny, width, height);
 }
 
 void CommandBuffer::setFillMode(FillMode mode)
@@ -165,15 +165,16 @@ void CommandBuffer::setBlendOperation(U32 attachment, BlendOperation funcRgb, Bl
 	m_impl->setBlendOperation(attachment, funcRgb, funcA);
 }
 
-void CommandBuffer::bindTexture(U32 set, U32 binding, TexturePtr tex, DepthStencilAspectBit aspect)
+void CommandBuffer::bindTexture(
+	U32 set, U32 binding, TexturePtr tex, TextureUsageBit usage, DepthStencilAspectBit aspect)
 {
-	m_impl->bindTexture(set, binding, tex, aspect);
+	m_impl->bindTexture(set, binding, tex, usage, aspect);
 }
 
 void CommandBuffer::bindTextureAndSampler(
-	U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, DepthStencilAspectBit aspect)
+	U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, TextureUsageBit usage, DepthStencilAspectBit aspect)
 {
-	m_impl->bindTextureAndSampler(set, binding, tex, sampler, aspect);
+	m_impl->bindTextureAndSampler(set, binding, tex, sampler, usage, aspect);
 }
 
 void CommandBuffer::bindUniformBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range)
@@ -202,10 +203,15 @@ void CommandBuffer::bindShaderProgram(ShaderProgramPtr prog)
 	m_impl->bindShaderProgram(prog);
 }
 
-void CommandBuffer::beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy)
+void CommandBuffer::beginRenderPass(FramebufferPtr fb,
+	const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorAttachmentUsages,
+	TextureUsageBit depthStencilAttachmentUsage,
+	U32 minx,
+	U32 miny,
+	U32 width,
+	U32 height)
 {
-	ANKI_ASSERT(minx < maxx && miny < maxy);
-	m_impl->beginRenderPass(fb, minx, miny, maxx, maxy);
+	m_impl->beginRenderPass(fb, colorAttachmentUsages, depthStencilAttachmentUsage, minx, miny, width, height);
 }
 
 void CommandBuffer::endRenderPass()
@@ -319,23 +325,6 @@ void CommandBuffer::setBufferBarrier(
 	m_impl->setBufferBarrier(buff, before, after, offset, size);
 }
 
-void CommandBuffer::informTextureSurfaceCurrentUsage(
-	TexturePtr tex, const TextureSurfaceInfo& surf, TextureUsageBit crntUsage)
-{
-	m_impl->informTextureSurfaceCurrentUsage(tex, surf, crntUsage);
-}
-
-void CommandBuffer::informTextureVolumeCurrentUsage(
-	TexturePtr tex, const TextureVolumeInfo& vol, TextureUsageBit crntUsage)
-{
-	m_impl->informTextureVolumeCurrentUsage(tex, vol, crntUsage);
-}
-
-void CommandBuffer::informTextureCurrentUsage(TexturePtr tex, TextureUsageBit crntUsage)
-{
-	m_impl->informTextureCurrentUsage(tex, crntUsage);
-}
-
 void CommandBuffer::resetOcclusionQuery(OcclusionQueryPtr query)
 {
 	m_impl->resetOcclusionQuery(query);

+ 62 - 31
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -46,6 +46,8 @@ Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
 	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
 	{
 		m_activeFb = init.m_framebuffer;
+		m_colorAttachmentUsages = init.m_colorAttachmentUsages;
+		m_depthStencilAttachmentUsage = init.m_depthStencilAttachmentUsage;
 	}
 
 	return Error::NONE;
@@ -65,20 +67,19 @@ void CommandBufferImpl::beginRecording()
 	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
 	{
 		FramebufferImpl& impl = *m_activeFb->m_impl;
+		impl.sync();
 
 		// Calc the layouts
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
 		for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
 		{
-			colAttLayouts[i] = impl.getColorAttachment(i)->m_impl->findLayoutFromTracker(
-				impl.getAttachedSurfaces()[i], m_texUsageTracker);
+			colAttLayouts[i] = impl.getColorAttachment(i)->m_impl->computeLayout(m_colorAttachmentUsages[i], 0);
 		}
 
 		VkImageLayout dsAttLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
 		if(impl.hasDepthStencil())
 		{
-			dsAttLayout = impl.getDepthStencilAttachment()->m_impl->findLayoutFromTracker(
-				impl.getAttachedSurfaces()[MAX_COLOR_ATTACHMENTS], m_texUsageTracker);
+			dsAttLayout = impl.getDepthStencilAttachment()->m_impl->computeLayout(m_depthStencilAttachmentUsage, 0);
 		}
 
 		inheritance.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
@@ -90,7 +91,11 @@ void CommandBufferImpl::beginRecording()
 		}
 		else
 		{
-			inheritance.framebuffer = impl.getFramebufferHandle(getGrManagerImpl().getCurrentBackbufferIndex());
+			MicroSwapchainPtr swapchain;
+			U32 backbufferIdx;
+			impl.getDefaultFramebufferInfo(swapchain, backbufferIdx);
+
+			inheritance.framebuffer = impl.getFramebufferHandle(backbufferIdx);
 		}
 
 		begin.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
@@ -99,7 +104,13 @@ void CommandBufferImpl::beginRecording()
 	vkBeginCommandBuffer(m_handle, &begin);
 }
 
-void CommandBufferImpl::beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy)
+void CommandBufferImpl::beginRenderPass(FramebufferPtr fb,
+	const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorAttachmentUsages,
+	TextureUsageBit depthStencilAttachmentUsage,
+	U32 minx,
+	U32 miny,
+	U32 width,
+	U32 height)
 {
 	commandCommon();
 	ANKI_ASSERT(!insideRenderPass());
@@ -107,20 +118,36 @@ void CommandBufferImpl::beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U
 	m_rpCommandCount = 0;
 	m_activeFb = fb;
 
+	fb->m_impl->sync();
+
 	U32 fbWidth, fbHeight;
 	fb->m_impl->getAttachmentsSize(fbWidth, fbHeight);
 	m_fbSize[0] = fbWidth;
 	m_fbSize[1] = fbHeight;
 
-	m_renderArea[0] = max<U16>(0, minx);
-	m_renderArea[1] = max<U16>(0, miny);
-	m_renderArea[2] = min<U16>(m_fbSize[0], maxx);
-	m_renderArea[3] = min<U16>(m_fbSize[1], maxy);
-	ANKI_ASSERT(m_renderArea[0] < m_renderArea[2] && m_renderArea[1] < m_renderArea[3]);
+	ANKI_ASSERT(minx < fbWidth && miny < fbHeight);
+
+	const U32 maxx = min<U32>(minx + width, fbWidth);
+	const U32 maxy = min<U32>(miny + height, fbHeight);
+	width = maxx - minx;
+	height = maxy - miny;
+	ANKI_ASSERT(minx + width <= fbWidth && miny + height <= fbHeight);
+
+	m_renderArea[0] = minx;
+	m_renderArea[1] = miny;
+	m_renderArea[2] = width;
+	m_renderArea[3] = height;
+
+	m_colorAttachmentUsages = colorAttachmentUsages;
+	m_depthStencilAttachmentUsage = depthStencilAttachmentUsage;
 
 	m_microCmdb->pushObjectRef(fb);
 
 	m_subpassContents = VK_SUBPASS_CONTENTS_MAX_ENUM;
+
+	// Re-set the viewport and scissor because sometimes they are set clamped
+	m_viewportDirty = true;
+	m_scissorDirty = true;
 }
 
 void CommandBufferImpl::beginRenderPassInternal()
@@ -144,15 +171,13 @@ void CommandBufferImpl::beginRenderPassInternal()
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
 		for(U i = 0; i < impl.getColorAttachmentCount(); ++i)
 		{
-			colAttLayouts[i] = impl.getColorAttachment(i)->m_impl->findLayoutFromTracker(
-				impl.getAttachedSurfaces()[i], m_texUsageTracker);
+			colAttLayouts[i] = impl.getColorAttachment(i)->m_impl->computeLayout(m_colorAttachmentUsages[i], 0);
 		}
 
 		VkImageLayout dsAttLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
 		if(impl.hasDepthStencil())
 		{
-			dsAttLayout = impl.getDepthStencilAttachment()->m_impl->findLayoutFromTracker(
-				impl.getAttachedSurfaces()[MAX_COLOR_ATTACHMENTS], m_texUsageTracker);
+			dsAttLayout = impl.getDepthStencilAttachment()->m_impl->computeLayout(m_depthStencilAttachmentUsage, 0);
 		}
 
 		bi.renderPass = impl.getRenderPassHandle(colAttLayouts, dsAttLayout);
@@ -162,9 +187,12 @@ void CommandBufferImpl::beginRenderPassInternal()
 		// Bind the default FB
 		m_renderedToDefaultFb = true;
 
-		bi.framebuffer = impl.getFramebufferHandle(getGrManagerImpl().getCurrentBackbufferIndex());
-		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> dummy;
-		bi.renderPass = impl.getRenderPassHandle(dummy, VK_IMAGE_LAYOUT_MAX_ENUM);
+		MicroSwapchainPtr swapchain;
+		U32 backbufferIdx;
+		impl.getDefaultFramebufferInfo(swapchain, backbufferIdx);
+
+		bi.framebuffer = impl.getFramebufferHandle(backbufferIdx);
+		bi.renderPass = impl.getRenderPassHandle({}, VK_IMAGE_LAYOUT_MAX_ENUM);
 
 		// Perform the transition
 		setImageBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
@@ -173,7 +201,7 @@ void CommandBufferImpl::beginRenderPassInternal()
 			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-			getGrManagerImpl().getDefaultSurfaceImage(getGrManagerImpl().getCurrentBackbufferIndex()),
+			swapchain->m_images[backbufferIdx],
 			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
 	}
 
@@ -183,9 +211,9 @@ void CommandBufferImpl::beginRenderPassInternal()
 	{
 		ANKI_ASSERT(m_renderArea[3] <= m_fbSize[1]);
 	}
-	bi.renderArea.offset.y = (flipvp) ? m_fbSize[1] - m_renderArea[3] : m_renderArea[1];
-	bi.renderArea.extent.width = m_renderArea[2] - m_renderArea[0];
-	bi.renderArea.extent.height = m_renderArea[3] - m_renderArea[1];
+	bi.renderArea.offset.y = (flipvp) ? m_fbSize[1] - (m_renderArea[1] + m_renderArea[3]) : m_renderArea[1];
+	bi.renderArea.extent.width = m_renderArea[2];
+	bi.renderArea.extent.height = m_renderArea[3];
 
 	ANKI_CMD(vkCmdBeginRenderPass(m_handle, &bi, m_subpassContents), ANY_OTHER_COMMAND);
 }
@@ -205,13 +233,17 @@ void CommandBufferImpl::endRenderPass()
 	// Default FB barrier/transition
 	if(m_activeFb->m_impl->isDefaultFramebuffer())
 	{
+		MicroSwapchainPtr swapchain;
+		U32 backbufferIdx;
+		m_activeFb->m_impl->getDefaultFramebufferInfo(swapchain, backbufferIdx);
+
 		setImageBarrier(VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
 			VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
 			VK_ACCESS_MEMORY_READ_BIT,
 			VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
-			getGrManagerImpl().getDefaultSurfaceImage(getGrManagerImpl().getCurrentBackbufferIndex()),
+			swapchain->m_images[backbufferIdx],
 			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
 	}
 
@@ -616,7 +648,7 @@ void CommandBufferImpl::copyBufferToTextureSurface(
 	TextureImpl& impl = *tex->m_impl;
 	impl.checkSurfaceOrVolume(surf);
 	ANKI_ASSERT(impl.usageValid(TextureUsageBit::TRANSFER_DESTINATION));
-	const VkImageLayout layout = impl.findLayoutFromTracker(surf, m_texUsageTracker);
+	const VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
 
 	if(!impl.m_workarounds)
 	{
@@ -649,8 +681,8 @@ void CommandBufferImpl::copyBufferToTextureSurface(
 		// Create a new shadow buffer
 		const PtrSize shadowSize =
 			computeSurfaceSize(width, height, PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM));
-		BufferPtr shadow =
-			getGrManager().newInstance<Buffer>(shadowSize, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::NONE);
+		BufferPtr shadow = getGrManager().newInstance<Buffer>(
+			BufferInitInfo(shadowSize, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::NONE, "Workaround"));
 		const VkBuffer shadowHandle = shadow->m_impl->getHandle();
 		m_microCmdb->pushObjectRef(shadow);
 
@@ -716,14 +748,13 @@ void CommandBufferImpl::copyBufferToTextureVolume(
 	TextureImpl& impl = *tex->m_impl;
 	impl.checkSurfaceOrVolume(vol);
 	ANKI_ASSERT(impl.usageValid(TextureUsageBit::TRANSFER_DESTINATION));
-	const VkImageLayout layout = impl.findLayoutFromTracker(vol, m_texUsageTracker);
+	const VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
 
 	if(!impl.m_workarounds)
 	{
 		U width = impl.m_width >> vol.m_level;
 		U height = impl.m_height >> vol.m_level;
 		U depth = impl.m_depth >> vol.m_level;
-		(void)depth;
 		ANKI_ASSERT(range == computeVolumeSize(width, height, depth, impl.m_format));
 
 		// Copy
@@ -735,7 +766,7 @@ void CommandBufferImpl::copyBufferToTextureVolume(
 		region.imageOffset = {0, 0, 0};
 		region.imageExtent.width = width;
 		region.imageExtent.height = height;
-		region.imageExtent.depth = impl.m_depth;
+		region.imageExtent.depth = depth;
 		region.bufferOffset = offset;
 		region.bufferImageHeight = 0;
 		region.bufferRowLength = 0;
@@ -753,8 +784,8 @@ void CommandBufferImpl::copyBufferToTextureVolume(
 		// Create a new shadow buffer
 		const PtrSize shadowSize =
 			computeVolumeSize(width, height, depth, PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM));
-		BufferPtr shadow =
-			getGrManager().newInstance<Buffer>(shadowSize, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::NONE);
+		BufferPtr shadow = getGrManager().newInstance<Buffer>(
+			BufferInitInfo(shadowSize, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::NONE, "Workaround"));
 		const VkBuffer shadowHandle = shadow->m_impl->getHandle();
 		m_microCmdb->pushObjectRef(shadow);
 

+ 70 - 41
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -12,7 +12,6 @@
 #include <anki/gr/Buffer.h>
 #include <anki/gr/vulkan/BufferImpl.h>
 #include <anki/gr/vulkan/TextureImpl.h>
-#include <anki/gr/vulkan/TextureUsageTracker.h>
 #include <anki/gr/vulkan/Pipeline.h>
 #include <anki/util/List.h>
 
@@ -119,35 +118,35 @@ public:
 		m_state.setCullMode(mode);
 	}
 
-	void setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
+	void setViewport(U32 minx, U32 miny, U32 width, U32 height)
 	{
-		ANKI_ASSERT(minx < maxx && miny < maxy);
+		ANKI_ASSERT(width > 0 && height > 0);
 		commandCommon();
 
-		if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != maxx || m_viewport[3] != maxy)
+		if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != width || m_viewport[3] != height)
 		{
 			m_viewportDirty = true;
 
 			m_viewport[0] = minx;
 			m_viewport[1] = miny;
-			m_viewport[2] = maxx;
-			m_viewport[3] = maxy;
+			m_viewport[2] = width;
+			m_viewport[3] = height;
 		}
 	}
 
-	void setScissor(U16 minx, U16 miny, U16 maxx, U16 maxy)
+	void setScissor(U32 minx, U32 miny, U32 width, U32 height)
 	{
-		ANKI_ASSERT(minx < maxx && miny < maxy);
+		ANKI_ASSERT(width > 0 && height > 0);
 		commandCommon();
 
-		if(m_scissor[0] != minx || m_scissor[1] != miny || m_scissor[2] != maxx || m_scissor[3] != maxy)
+		if(m_scissor[0] != minx || m_scissor[1] != miny || m_scissor[2] != width || m_scissor[3] != height)
 		{
 			m_scissorDirty = true;
 
 			m_scissor[0] = minx;
 			m_scissor[1] = miny;
-			m_scissor[2] = maxx;
-			m_scissor[3] = maxy;
+			m_scissor[2] = width;
+			m_scissor[3] = height;
 		}
 	}
 
@@ -214,22 +213,25 @@ public:
 		m_state.setBlendOperation(attachment, funcRgb, funcA);
 	}
 
-	void bindTexture(U32 set, U32 binding, TexturePtr tex_, DepthStencilAspectBit aspect)
+	void bindTexture(U32 set, U32 binding, TexturePtr tex_, TextureUsageBit usage, DepthStencilAspectBit aspect)
 	{
 		commandCommon();
 		const U realBinding = binding;
 		Texture& tex = *tex_;
-		const VkImageLayout lay = tex.m_impl->findLayoutFromTracker(m_texUsageTracker);
+		ANKI_ASSERT((!tex.m_impl->m_depthStencil || !!aspect) && "Need to set aspect for DS textures");
+		const VkImageLayout lay = tex.m_impl->computeLayout(usage, 0);
 		m_dsetState[set].bindTexture(realBinding, &tex, aspect, lay);
 		m_microCmdb->pushObjectRef(tex_);
 	}
 
-	void bindTextureAndSampler(U32 set, U32 binding, TexturePtr& tex_, SamplerPtr sampler, DepthStencilAspectBit aspect)
+	void bindTextureAndSampler(
+		U32 set, U32 binding, TexturePtr& tex_, SamplerPtr sampler, TextureUsageBit usage, DepthStencilAspectBit aspect)
 	{
 		commandCommon();
 		const U realBinding = binding;
 		Texture& tex = *tex_;
-		const VkImageLayout lay = tex.m_impl->findLayoutFromTracker(m_texUsageTracker);
+		ANKI_ASSERT((!tex.m_impl->m_depthStencil || !!aspect) && "Need to set aspect for DS textures");
+		const VkImageLayout lay = tex.m_impl->computeLayout(usage, 0);
 		m_dsetState[set].bindTextureAndSampler(realBinding, &tex, sampler.get(), aspect, lay);
 		m_microCmdb->pushObjectRef(tex_);
 		m_microCmdb->pushObjectRef(sampler);
@@ -244,7 +246,13 @@ public:
 		m_microCmdb->pushObjectRef(img);
 	}
 
-	void beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy);
+	void beginRenderPass(FramebufferPtr fb,
+		const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorAttachmentUsages,
+		TextureUsageBit depthStencilAttachmentUsage,
+		U32 minx,
+		U32 miny,
+		U32 width,
+		U32 height);
 
 	void endRenderPass();
 
@@ -324,24 +332,6 @@ public:
 	void copyBufferToTextureVolume(
 		BufferPtr buff, PtrSize offset, PtrSize range, TexturePtr tex, const TextureVolumeInfo& vol);
 
-	void informTextureSurfaceCurrentUsage(TexturePtr& tex, const TextureSurfaceInfo& surf, TextureUsageBit crntUsage)
-	{
-		lazyInit();
-		tex->m_impl->updateTracker(surf, crntUsage, m_texUsageTracker);
-	}
-
-	void informTextureVolumeCurrentUsage(TexturePtr& tex, const TextureVolumeInfo& vol, TextureUsageBit crntUsage)
-	{
-		lazyInit();
-		tex->m_impl->updateTracker(vol, crntUsage, m_texUsageTracker);
-	}
-
-	void informTextureCurrentUsage(TexturePtr& tex, TextureUsageBit crntUsage)
-	{
-		lazyInit();
-		tex->m_impl->updateTracker(crntUsage, m_texUsageTracker);
-	}
-
 	void copyBufferToBuffer(BufferPtr& src, PtrSize srcOffset, BufferPtr& dst, PtrSize dstOffset, PtrSize range);
 
 private:
@@ -362,8 +352,10 @@ private:
 
 	U m_rpCommandCount = 0; ///< Number of drawcalls or pushed cmdbs in rp.
 	FramebufferPtr m_activeFb;
-	Array<U16, 4> m_renderArea = {{0, 0, MAX_U16, MAX_U16}};
-	Array<U16, 2> m_fbSize = {{0, 0}};
+	Array<U32, 4> m_renderArea = {{0, 0, MAX_U32, MAX_U32}};
+	Array<U32, 2> m_fbSize = {{0, 0}};
+	Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS> m_colorAttachmentUsages = {};
+	TextureUsageBit m_depthStencilAttachmentUsage = TextureUsageBit::NONE;
 
 	ShaderProgramImpl* m_graphicsProg ANKI_DBG_NULLIFY; ///< Last bound graphics program
 
@@ -379,10 +371,12 @@ private:
 
 	/// @name state_opts
 	/// @{
-	Array<U16, 4> m_viewport = {{0, 0, 0, 0}};
-	Array<U16, 4> m_scissor = {{0, 0, MAX_U16, MAX_U16}};
+	Array<U32, 4> m_viewport = {{0, 0, 0, 0}};
+	Array<U32, 4> m_scissor = {{0, 0, MAX_U32, MAX_U32}};
 	Bool8 m_viewportDirty = true;
+	VkViewport m_lastViewport = {};
 	Bool8 m_scissorDirty = true;
+	VkRect2D m_lastScissor = {{-1, -1}, {MAX_U32, MAX_U32}};
 	Array<U32, 2> m_stencilCompareMasks = {{0x5A5A5A5A, 0x5A5A5A5A}}; ///< Use a stupid number to initialize.
 	Array<U32, 2> m_stencilWriteMasks = {{0x5A5A5A5A, 0x5A5A5A5A}};
 	Array<U32, 2> m_stencilReferenceMasks = {{0x5A5A5A5A, 0x5A5A5A5A}};
@@ -432,9 +426,6 @@ private:
 	U16 m_secondLevelAtomCount = 0;
 	/// @}
 
-	/// Track texture usage.
-	TextureUsageTracker m_texUsageTracker;
-
 	void lazyInit();
 
 	/// Some common operations per command.
@@ -479,6 +470,44 @@ private:
 	void beginRecording();
 
 	Bool flipViewport() const;
+
+	static VkViewport computeViewport(U32* viewport, U32 fbWidth, U32 fbHeight, Bool flipvp)
+	{
+		const U32 minx = viewport[0];
+		const U32 miny = viewport[1];
+		const U32 width = min<U32>(fbWidth, viewport[2]);
+		const U32 height = min<U32>(fbHeight, viewport[3]);
+		ANKI_ASSERT(width > 0 && height > 0);
+		ANKI_ASSERT(minx + width <= fbWidth);
+		ANKI_ASSERT(miny + height <= fbHeight);
+
+		VkViewport s = {};
+		s.x = minx;
+		s.y = (flipvp) ? (fbHeight - miny) : miny; // Move to the bottom;
+		s.width = width;
+		s.height = (flipvp) ? -F32(height) : height;
+		s.minDepth = 0.0f;
+		s.maxDepth = 1.0f;
+		return s;
+	}
+
+	static VkRect2D computeScissor(U32* scissor, U32 fbWidth, U32 fbHeight, Bool flipvp)
+	{
+		const U32 minx = scissor[0];
+		const U32 miny = scissor[1];
+		const U32 width = min<U32>(fbWidth, scissor[2]);
+		const U32 height = min<U32>(fbHeight, scissor[3]);
+		ANKI_ASSERT(minx + width <= fbWidth);
+		ANKI_ASSERT(miny + height <= fbHeight);
+
+		VkRect2D out = {};
+		out.extent.width = width;
+		out.extent.height = height;
+		out.offset.x = minx;
+		out.offset.y = (flipvp) ? (fbHeight - (miny + height)) : miny;
+
+		return out;
+	}
 };
 /// @}
 

+ 22 - 45
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -151,10 +151,10 @@ inline void CommandBufferImpl::setTextureBarrierRange(
 inline void CommandBufferImpl::setTextureSurfaceBarrier(
 	TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage, const TextureSurfaceInfo& surf)
 {
-	if(surf.m_level > 0)
+	if(ANKI_UNLIKELY(surf.m_level > 0 && nextUsage == TextureUsageBit::GENERATE_MIPMAPS))
 	{
-		ANKI_ASSERT(!(nextUsage & TextureUsageBit::GENERATE_MIPMAPS)
-			&& "This transition happens inside CommandBufferImpl::generateMipmapsX");
+		// This transition happens inside CommandBufferImpl::generateMipmapsX. No need to do something
+		return;
 	}
 
 	const TextureImpl& impl = *tex->m_impl;
@@ -163,8 +163,6 @@ inline void CommandBufferImpl::setTextureSurfaceBarrier(
 	VkImageSubresourceRange range;
 	impl.computeSubResourceRange(surf, impl.m_akAspect, range);
 	setTextureBarrierRange(tex, prevUsage, nextUsage, range);
-
-	impl.updateTracker(surf, nextUsage, m_texUsageTracker);
 }
 
 inline void CommandBufferImpl::setTextureVolumeBarrier(
@@ -182,8 +180,6 @@ inline void CommandBufferImpl::setTextureVolumeBarrier(
 	VkImageSubresourceRange range;
 	impl.computeSubResourceRange(vol, impl.m_akAspect, range);
 	setTextureBarrierRange(tex, prevUsage, nextUsage, range);
-
-	impl.updateTracker(vol, nextUsage, m_texUsageTracker);
 }
 
 inline void CommandBufferImpl::setBufferBarrier(VkPipelineStageFlags srcStage,
@@ -521,22 +517,17 @@ inline void CommandBufferImpl::drawcallCommon()
 	{
 		const Bool flipvp = flipViewport();
 
-		const I minx = m_viewport[0];
-		const I miny = m_viewport[1];
-		const I maxx = m_viewport[2];
-		const I maxy = m_viewport[3];
-
 		U32 fbWidth, fbHeight;
 		m_activeFb->m_impl->getAttachmentsSize(fbWidth, fbHeight);
 
-		VkViewport s;
-		s.x = minx;
-		s.y = (flipvp) ? (fbHeight - miny) : miny; // Move to the bottom;
-		s.width = maxx - minx;
-		s.height = (flipvp) ? -(maxy - miny) : (maxy - miny);
-		s.minDepth = 0.0;
-		s.maxDepth = 1.0;
-		ANKI_CMD(vkCmdSetViewport(m_handle, 0, 1, &s), ANY_OTHER_COMMAND);
+		VkViewport vp = computeViewport(&m_viewport[0], fbWidth, fbHeight, flipvp);
+
+		// Additional optimization
+		if(memcmp(&vp, &m_lastViewport, sizeof(vp)) != 0)
+		{
+			ANKI_CMD(vkCmdSetViewport(m_handle, 0, 1, &vp), ANY_OTHER_COMMAND);
+			m_lastViewport = vp;
+		}
 
 		m_viewportDirty = false;
 	}
@@ -544,34 +535,20 @@ inline void CommandBufferImpl::drawcallCommon()
 	// Flush scissor
 	if(ANKI_UNLIKELY(m_scissorDirty))
 	{
-		VkRect2D scissor = {};
-
-		if(m_scissor[0] == 0 && m_scissor[1] == 0 && m_scissor[2] == MAX_U16 && m_scissor[3] == MAX_U16)
-		{
-			scissor.extent.width = MAX_U16;
-			scissor.extent.height = MAX_U16;
-			scissor.offset.x = 0;
-			scissor.offset.y = 0;
-		}
-		else
-		{
-			const Bool flipvp = flipViewport();
+		const Bool flipvp = flipViewport();
 
-			U32 fbWidth, fbHeight;
-			m_activeFb->m_impl->getAttachmentsSize(fbWidth, fbHeight);
+		U32 fbWidth, fbHeight;
+		m_activeFb->m_impl->getAttachmentsSize(fbWidth, fbHeight);
 
-			const I minx = min<U>(fbWidth, m_scissor[0]);
-			const I miny = min<U>(fbHeight, m_scissor[1]);
-			const I maxx = min<U>(fbWidth, m_scissor[2]);
-			const I maxy = min<U>(fbHeight, m_scissor[3]);
+		VkRect2D scissor = computeScissor(&m_scissor[0], fbWidth, fbHeight, flipvp);
 
-			scissor.extent.width = maxx - minx;
-			scissor.extent.height = maxy - miny;
-			scissor.offset.x = minx;
-			scissor.offset.y = (flipvp) ? (fbHeight - maxy) : miny;
+		// Additional optimization
+		if(memcmp(&scissor, &m_lastScissor, sizeof(scissor)) != 0)
+		{
+			ANKI_CMD(vkCmdSetScissor(m_handle, 0, 1, &scissor), ANY_OTHER_COMMAND);
+			m_lastScissor = scissor;
 		}
 
-		ANKI_CMD(vkCmdSetScissor(m_handle, 0, 1, &scissor), ANY_OTHER_COMMAND);
 		m_scissorDirty = false;
 	}
 
@@ -620,8 +597,6 @@ inline void CommandBufferImpl::lazyInit()
 
 	m_alloc = m_microCmdb->getFastAllocator();
 
-	m_texUsageTracker.init(m_alloc);
-
 	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
 	{
 		m_state.beginRenderPass(m_activeFb);
@@ -723,6 +698,8 @@ inline void CommandBufferImpl::writeOcclusionQueryResultToBuffer(
 
 inline void CommandBufferImpl::bindShaderProgram(ShaderProgramPtr& prog)
 {
+	commandCommon();
+
 	ShaderProgramImpl& impl = *prog->m_impl;
 
 	if(impl.isGraphics())

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

@@ -305,8 +305,8 @@ 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->getOrCreateSingleSurfaceView(
-					TextureSurfaceInfo(b.m_image.m_level, 0, 0, 0), b.m_tex.m_aspect);
+				tex[texCount].imageView =
+					b.m_image.m_tex->getOrCreateSingleLevelView(b.m_image.m_level, b.m_tex.m_aspect);
 				tex[texCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
 
 				w.pImageInfo = &tex[texCount];

+ 141 - 144
src/anki/gr/vulkan/FramebufferImpl.cpp

@@ -13,41 +13,85 @@ namespace anki
 
 FramebufferImpl::~FramebufferImpl()
 {
-	for(VkFramebuffer fb : m_fbs)
+	if(m_noDflt.m_fb)
 	{
-		if(fb)
-		{
-			vkDestroyFramebuffer(getDevice(), fb, nullptr);
-		}
+		vkDestroyFramebuffer(getDevice(), m_noDflt.m_fb, nullptr);
 	}
 
-	for(auto it : m_rpasses)
+	for(auto it : m_noDflt.m_rpasses)
 	{
 		VkRenderPass rpass = it;
 		ANKI_ASSERT(rpass);
 		vkDestroyRenderPass(getDevice(), rpass, nullptr);
 	}
 
-	m_rpasses.destroy(getAllocator());
+	m_noDflt.m_rpasses.destroy(getAllocator());
 
-	if(m_compatibleOrDefaultRpass)
+	if(m_noDflt.m_compatibleRpass)
 	{
-		vkDestroyRenderPass(getDevice(), m_compatibleOrDefaultRpass, nullptr);
+		vkDestroyRenderPass(getDevice(), m_noDflt.m_compatibleRpass, nullptr);
 	}
 }
 
 Error FramebufferImpl::init(const FramebufferInitInfo& init)
 {
+	// Init common
 	m_defaultFb = init.refersToDefaultFramebuffer();
+	strcpy(&m_name[0], (init.getName()) ? init.getName().cstr() : "");
+
+	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
+	{
+		m_colorAttachmentMask.set(i);
+		m_colorAttCount = i + 1;
+	}
 
-	// Create a renderpass.
-	initRpassCreateInfo(init);
-	ANKI_VK_CHECK(vkCreateRenderPass(getDevice(), &m_rpassCi, nullptr, &m_compatibleOrDefaultRpass));
+	if(!m_defaultFb && init.m_depthStencilAttachment.m_texture)
+	{
+		const TextureImpl& tex = *init.m_depthStencilAttachment.m_texture->m_impl;
+
+		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;
+		}
+	}
+
+	initClearValues(init);
+
+	if(m_defaultFb)
+	{
+		m_dflt.m_swapchain = getGrManagerImpl().getSwapchain();
+		m_dflt.m_loadOp = convertLoadOp(init.m_colorAttachments[0].m_loadOperation);
+	}
+	else
+	{
+		// Create a renderpass.
+		initRpassCreateInfo(init);
+		ANKI_VK_CHECK(vkCreateRenderPass(getDevice(), &m_noDflt.m_rpassCi, nullptr, &m_noDflt.m_compatibleRpass));
+		getGrManagerImpl().trySetVulkanHandleName(
+			init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, m_noDflt.m_compatibleRpass);
+
+		// Create the FB
+		ANKI_CHECK(initFbs(init));
+	}
 
-	// Create the FBs
-	ANKI_CHECK(initFbs(init));
+	return Error::NONE;
+}
 
-	// Set clear values and some other stuff
+void FramebufferImpl::initClearValues(const FramebufferInitInfo& init)
+{
 	for(U i = 0; i < m_colorAttCount; ++i)
 	{
 		if(init.m_colorAttachments[i].m_loadOperation == AttachmentLoadOperation::CLEAR)
@@ -62,8 +106,6 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 		{
 			m_clearVals[i] = {};
 		}
-
-		m_attachedSurfaces[i] = init.m_colorAttachments[i].m_surface;
 	}
 
 	if(hasDepthStencil())
@@ -81,125 +123,70 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 		{
 			m_clearVals[m_colorAttCount] = {};
 		}
-
-		m_attachedSurfaces[MAX_COLOR_ATTACHMENTS] = init.m_depthStencilAttachment.m_surface;
 	}
-
-	return Error::NONE;
 }
 
 Error FramebufferImpl::initFbs(const FramebufferInitInfo& init)
 {
-	const Bool hasDepthStencil = init.m_depthStencilAttachment.m_texture == true;
-
 	VkFramebufferCreateInfo ci = {};
 	ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-	ci.renderPass = m_compatibleOrDefaultRpass;
-	ci.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil) ? 1 : 0);
-
+	ci.renderPass = m_noDflt.m_compatibleRpass;
+	ci.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil()) ? 1 : 0);
 	ci.layers = 1;
 
-	if(m_defaultFb)
+	Array<VkImageView, MAX_COLOR_ATTACHMENTS + 1> imgViews;
+	U count = 0;
+
+	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	{
-		for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
-		{
-			VkImageView view = getGrManagerImpl().getDefaultSurfaceImageView(i);
-			ci.pAttachments = &view;
+		const FramebufferAttachmentInfo& att = init.m_colorAttachments[i];
+		TextureImpl& tex = *att.m_texture->m_impl;
 
-			m_width = getGrManagerImpl().getDefaultSurfaceWidth();
-			m_height = getGrManagerImpl().getDefaultSurfaceHeight();
-			ci.width = m_width;
-			ci.height = m_height;
+		imgViews[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, att.m_aspect);
 
-			ANKI_VK_CHECK(vkCreateFramebuffer(getDevice(), &ci, nullptr, &m_fbs[i]));
+		if(m_noDflt.m_width == 0)
+		{
+			m_noDflt.m_width = tex.m_width >> att.m_surface.m_level;
+			m_noDflt.m_height = tex.m_height >> att.m_surface.m_level;
 		}
 
-		m_colorAttachmentMask.set(0);
-		m_colorAttCount = 1;
+		m_noDflt.m_refs[i] = att.m_texture;
 	}
-	else
-	{
-		Array<VkImageView, MAX_COLOR_ATTACHMENTS + 1> attachments;
-		U count = 0;
-
-		for(U i = 0; i < init.m_colorAttachmentCount; ++i)
-		{
-			const FramebufferAttachmentInfo& att = init.m_colorAttachments[i];
-			TextureImpl& tex = *att.m_texture->m_impl;
 
-			attachments[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, att.m_aspect);
-
-			if(m_width == 0)
-			{
-				m_width = tex.m_width >> att.m_surface.m_level;
-				m_height = tex.m_height >> att.m_surface.m_level;
-			}
+	if(hasDepthStencil())
+	{
+		const FramebufferAttachmentInfo& att = init.m_depthStencilAttachment;
+		TextureImpl& tex = *att.m_texture->m_impl;
 
-			m_refs[i] = att.m_texture;
-			m_colorAttachmentMask.set(i);
-			m_colorAttCount = i + 1;
-		}
+		imgViews[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, m_aspect);
 
-		if(hasDepthStencil)
+		if(m_noDflt.m_width == 0)
 		{
-			const FramebufferAttachmentInfo& att = init.m_depthStencilAttachment;
-			TextureImpl& tex = *att.m_texture->m_impl;
-
-			DepthStencilAspectBit aspect;
-			if(!!(tex.m_workarounds & TextureImplWorkaround::S8_TO_D24S8))
-			{
-				aspect = DepthStencilAspectBit::STENCIL;
-			}
-			else if(tex.m_akAspect == DepthStencilAspectBit::DEPTH)
-			{
-				aspect = DepthStencilAspectBit::DEPTH;
-			}
-			else if(tex.m_akAspect == DepthStencilAspectBit::STENCIL)
-			{
-				aspect = DepthStencilAspectBit::STENCIL;
-			}
-			else
-			{
-				ANKI_ASSERT(!!att.m_aspect);
-				aspect = att.m_aspect;
-			}
-
-			m_depthAttachment = !!(aspect & DepthStencilAspectBit::DEPTH);
-			m_stencilAttachment = !!(aspect & DepthStencilAspectBit::STENCIL);
-
-			attachments[count++] = tex.getOrCreateSingleSurfaceView(att.m_surface, aspect);
-
-			if(m_width == 0)
-			{
-				m_width = tex.m_width >> att.m_surface.m_level;
-				m_height = tex.m_height >> att.m_surface.m_level;
-			}
-
-			m_refs[MAX_COLOR_ATTACHMENTS] = att.m_texture;
+			m_noDflt.m_width = tex.m_width >> att.m_surface.m_level;
+			m_noDflt.m_height = tex.m_height >> att.m_surface.m_level;
 		}
 
-		ci.width = m_width;
-		ci.height = m_height;
+		m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS] = att.m_texture;
+	}
 
-		ci.pAttachments = &attachments[0];
-		ANKI_ASSERT(count == ci.attachmentCount);
+	ci.width = m_noDflt.m_width;
+	ci.height = m_noDflt.m_height;
 
-		ANKI_VK_CHECK(vkCreateFramebuffer(getDevice(), &ci, nullptr, &m_fbs[0]));
-	}
+	ci.pAttachments = &imgViews[0];
+	ANKI_ASSERT(count == ci.attachmentCount);
+
+	ANKI_VK_CHECK(vkCreateFramebuffer(getDevice(), &ci, nullptr, &m_noDflt.m_fb));
+	getGrManagerImpl().trySetVulkanHandleName(
+		init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, m_noDflt.m_fb);
 
 	return Error::NONE;
 }
 
 void FramebufferImpl::setupAttachmentDescriptor(
-	const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc) const
+	const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc, VkImageLayout layout) const
 {
-	// TODO This func won't work if it's default but this is a depth attachment
-
-	const VkImageLayout layout = (m_defaultFb) ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL;
-
 	desc = {};
-	desc.format = (m_defaultFb) ? getGrManagerImpl().getDefaultFramebufferSurfaceFormat()
-								: convertFormat(att.m_texture->m_impl->m_format);
+	desc.format = convertFormat(att.m_texture->m_impl->m_format);
 	desc.samples = VK_SAMPLE_COUNT_1_BIT;
 	desc.loadOp = convertLoadOp(att.m_loadOperation);
 	desc.storeOp = convertStoreOp(att.m_storeOperation);
@@ -211,62 +198,60 @@ void FramebufferImpl::setupAttachmentDescriptor(
 
 void FramebufferImpl::initRpassCreateInfo(const FramebufferInitInfo& init)
 {
+	// Setup attachments and references
 	for(U i = 0; i < init.m_colorAttachmentCount; ++i)
 	{
-		const FramebufferAttachmentInfo& att = init.m_colorAttachments[i];
+		setupAttachmentDescriptor(
+			init.m_colorAttachments[i], m_noDflt.m_attachmentDescriptions[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 
-		setupAttachmentDescriptor(att, m_attachmentDescriptions[i]);
-
-		m_references[i].attachment = i;
-		m_references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+		m_noDflt.m_references[i].attachment = i;
+		m_noDflt.m_references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 	}
 
-	const Bool hasDepthStencil = init.m_depthStencilAttachment.m_texture == true;
-	if(hasDepthStencil)
+	if(hasDepthStencil())
 	{
-		VkAttachmentReference& dsReference = m_references[init.m_colorAttachmentCount];
-
-		setupAttachmentDescriptor(init.m_depthStencilAttachment, m_attachmentDescriptions[init.m_colorAttachmentCount]);
+		setupAttachmentDescriptor(init.m_depthStencilAttachment,
+			m_noDflt.m_attachmentDescriptions[init.m_colorAttachmentCount],
+			VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
 
+		VkAttachmentReference& dsReference = m_noDflt.m_references[init.m_colorAttachmentCount];
 		dsReference.attachment = init.m_colorAttachmentCount;
 		dsReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 	}
 
 	// Setup the render pass
-	m_rpassCi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-	m_rpassCi.pAttachments = &m_attachmentDescriptions[0];
-	m_rpassCi.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil) ? 1 : 0);
+	m_noDflt.m_rpassCi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+	m_noDflt.m_rpassCi.pAttachments = &m_noDflt.m_attachmentDescriptions[0];
+	m_noDflt.m_rpassCi.attachmentCount = init.m_colorAttachmentCount + ((hasDepthStencil()) ? 1 : 0);
 
 	// Subpass
-	m_subpassDescr.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-	m_subpassDescr.colorAttachmentCount = init.m_colorAttachmentCount;
-	m_subpassDescr.pColorAttachments = (init.m_colorAttachmentCount) ? &m_references[0] : nullptr;
-	m_subpassDescr.pDepthStencilAttachment = (hasDepthStencil) ? &m_references[init.m_colorAttachmentCount] : nullptr;
-
-	m_rpassCi.subpassCount = 1;
-	m_rpassCi.pSubpasses = &m_subpassDescr;
+	m_noDflt.m_subpassDescr.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+	m_noDflt.m_subpassDescr.colorAttachmentCount = init.m_colorAttachmentCount;
+	m_noDflt.m_subpassDescr.pColorAttachments = (init.m_colorAttachmentCount) ? &m_noDflt.m_references[0] : nullptr;
+	m_noDflt.m_subpassDescr.pDepthStencilAttachment =
+		(hasDepthStencil()) ? &m_noDflt.m_references[init.m_colorAttachmentCount] : nullptr;
+
+	m_noDflt.m_rpassCi.subpassCount = 1;
+	m_noDflt.m_rpassCi.pSubpasses = &m_noDflt.m_subpassDescr;
 }
 
 VkRenderPass FramebufferImpl::getRenderPassHandle(
 	const Array<VkImageLayout, MAX_COLOR_ATTACHMENTS>& colorLayouts, VkImageLayout dsLayout)
 {
-	VkRenderPass out;
+	VkRenderPass out = {};
 
 	if(!m_defaultFb)
 	{
 		// Create hash
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS + 1> allLayouts;
 		U allLayoutCount = 0;
-		for(U i = 0; i < MAX_COLOR_ATTACHMENTS; ++i)
+		for(U i = 0; i < m_colorAttCount; ++i)
 		{
-			if(m_colorAttachmentMask.get(i))
-			{
-				ANKI_ASSERT(colorLayouts[i] != VK_IMAGE_LAYOUT_UNDEFINED);
-				allLayouts[allLayoutCount++] = colorLayouts[i];
-			}
+			ANKI_ASSERT(colorLayouts[i] != VK_IMAGE_LAYOUT_UNDEFINED);
+			allLayouts[allLayoutCount++] = colorLayouts[i];
 		}
 
-		if(m_depthAttachment || m_stencilAttachment)
+		if(hasDepthStencil())
 		{
 			ANKI_ASSERT(dsLayout != VK_IMAGE_LAYOUT_UNDEFINED);
 			allLayouts[allLayoutCount++] = dsLayout;
@@ -275,9 +260,9 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 		U64 hash = computeHash(&allLayouts[0], allLayoutCount * sizeof(allLayouts[0]));
 
 		// Get or create
-		LockGuard<Mutex> lock(m_rpassesMtx);
-		auto it = m_rpasses.find(hash);
-		if(it != m_rpasses.getEnd())
+		LockGuard<Mutex> lock(m_noDflt.m_rpassesMtx);
+		auto it = m_noDflt.m_rpasses.find(hash);
+		if(it != m_noDflt.m_rpasses.getEnd())
 		{
 			out = *it;
 		}
@@ -285,10 +270,11 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 		{
 			// Create
 
-			VkRenderPassCreateInfo ci = m_rpassCi;
-			Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1> attachmentDescriptions = m_attachmentDescriptions;
-			Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS + 1> references = m_references;
-			VkSubpassDescription subpassDescr = m_subpassDescr;
+			VkRenderPassCreateInfo ci = m_noDflt.m_rpassCi;
+			Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1> attachmentDescriptions =
+				m_noDflt.m_attachmentDescriptions;
+			Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS + 1> references = m_noDflt.m_references;
+			VkSubpassDescription subpassDescr = m_noDflt.m_subpassDescr;
 
 			// Fix pointers
 			subpassDescr.pColorAttachments = &references[0];
@@ -306,7 +292,7 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 				references[i].layout = lay;
 			}
 
-			if(m_refs[MAX_COLOR_ATTACHMENTS])
+			if(hasDepthStencil())
 			{
 				const U i = subpassDescr.colorAttachmentCount;
 				const VkImageLayout lay = dsLayout;
@@ -320,17 +306,28 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 			}
 
 			ANKI_VK_CHECKF(vkCreateRenderPass(getDevice(), &ci, nullptr, &out));
+			getGrManagerImpl().trySetVulkanHandleName(&m_name[0], VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, out);
 
-			m_rpasses.emplace(getAllocator(), hash, out);
+			m_noDflt.m_rpasses.emplace(getAllocator(), hash, out);
 		}
 	}
 	else
 	{
-		out = m_compatibleOrDefaultRpass;
+		out = m_dflt.m_swapchain->getRenderPass(m_dflt.m_loadOp);
 	}
 
 	ANKI_ASSERT(out);
 	return out;
 }
 
+void FramebufferImpl::sync()
+{
+	if(m_defaultFb)
+	{
+		LockGuard<SpinLock> lock(m_dflt.m_swapchainLock);
+		m_dflt.m_swapchain = getGrManagerImpl().getSwapchain();
+		m_dflt.m_currentBackbufferIndex = m_dflt.m_swapchain->m_currentBackbufferIndex;
+	}
+}
+
 } // end namespace anki

+ 79 - 36
src/anki/gr/vulkan/FramebufferImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/SwapchainFactory.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/BitSet.h>
 
@@ -34,25 +35,42 @@ public:
 	/// Good for pipeline creation.
 	VkRenderPass getCompatibleRenderPass() const
 	{
-		ANKI_ASSERT(m_compatibleOrDefaultRpass);
-		return m_compatibleOrDefaultRpass;
+		if(!m_defaultFb)
+		{
+			ANKI_ASSERT(m_noDflt.m_compatibleRpass);
+			return m_noDflt.m_compatibleRpass;
+		}
+		else
+		{
+			return m_dflt.m_swapchain->getRenderPass(m_dflt.m_loadOp);
+		}
 	}
 
-	/// Use it for binding.
+	/// Sync it before you bind it. It's thread-safe
+	void sync();
+
+	/// Use it for binding. It's thread-safe
 	VkRenderPass getRenderPassHandle(
 		const Array<VkImageLayout, MAX_COLOR_ATTACHMENTS>& colorLayouts, VkImageLayout dsLayout);
 
 	VkFramebuffer getFramebufferHandle(U frame) const
 	{
-		ANKI_ASSERT(m_fbs[frame]);
-		return m_fbs[frame];
+		if(!m_defaultFb)
+		{
+			ANKI_ASSERT(m_noDflt.m_fb);
+			return m_noDflt.m_fb;
+		}
+		else
+		{
+			return m_dflt.m_swapchain->m_framebuffers[frame];
+		}
 	}
 
 	void getAttachmentInfo(BitSet<MAX_COLOR_ATTACHMENTS, U8>& colorAttachments, Bool& depth, Bool& stencil) const
 	{
 		colorAttachments = m_colorAttachmentMask;
-		depth = m_depthAttachment;
-		stencil = m_stencilAttachment;
+		depth = !!(m_aspect & DepthStencilAspectBit::DEPTH);
+		stencil = !!(m_aspect & DepthStencilAspectBit::STENCIL);
 	}
 
 	U getColorAttachmentCount() const
@@ -62,7 +80,7 @@ public:
 
 	Bool hasDepthStencil() const
 	{
-		return m_refs[MAX_COLOR_ATTACHMENTS].isCreated();
+		return !!m_aspect;
 	}
 
 	U getAttachmentCount() const
@@ -72,14 +90,14 @@ public:
 
 	TexturePtr getColorAttachment(U att) const
 	{
-		ANKI_ASSERT(m_refs[att].get());
-		return m_refs[att];
+		ANKI_ASSERT(m_noDflt.m_refs[att].get());
+		return m_noDflt.m_refs[att];
 	}
 
 	TexturePtr getDepthStencilAttachment() const
 	{
-		ANKI_ASSERT(m_refs[MAX_COLOR_ATTACHMENTS].get());
-		return m_refs[MAX_COLOR_ATTACHMENTS];
+		ANKI_ASSERT(m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS].get());
+		return m_noDflt.m_refs[MAX_COLOR_ATTACHMENTS];
 	}
 
 	const VkClearValue* getClearValues() const
@@ -94,48 +112,73 @@ public:
 
 	void getAttachmentsSize(U32& width, U32& height) const
 	{
-		ANKI_ASSERT(m_width != 0 && m_height != 0);
-		width = m_width;
-		height = m_height;
+		if(!m_defaultFb)
+		{
+			ANKI_ASSERT(m_noDflt.m_width != 0 && m_noDflt.m_height != 0);
+			width = m_noDflt.m_width;
+			height = m_noDflt.m_height;
+		}
+		else
+		{
+			width = m_dflt.m_swapchain->m_surfaceWidth;
+			height = m_dflt.m_swapchain->m_surfaceHeight;
+		}
 	}
 
-	const Array<TextureSurfaceInfo, MAX_COLOR_ATTACHMENTS + 1>& getAttachedSurfaces() const
+	void getDefaultFramebufferInfo(MicroSwapchainPtr& swapchain, U32& crntBackBufferIdx)
 	{
-		return m_attachedSurfaces;
+		ANKI_ASSERT(m_defaultFb);
+		swapchain = m_dflt.m_swapchain;
+		crntBackBufferIdx = m_dflt.m_currentBackbufferIndex;
 	}
 
 private:
-	U32 m_width = 0;
-	U32 m_height = 0;
+	Array<char, MAX_GR_OBJECT_NAME_LENGTH + 1> m_name;
 
 	Bool8 m_defaultFb = false;
 
 	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_colorAttachmentMask = {false};
-	Bool8 m_depthAttachment = false;
-	Bool8 m_stencilAttachment = false;
+	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE;
 
 	U8 m_colorAttCount = 0;
 	Array<VkClearValue, MAX_COLOR_ATTACHMENTS + 1> m_clearVals;
 
-	Array<TexturePtr, MAX_COLOR_ATTACHMENTS + 1> m_refs; ///< @note The pos of every attachment is fixed.
-	Array<TextureSurfaceInfo, MAX_COLOR_ATTACHMENTS + 1> m_attachedSurfaces = {};
-
-	// RenderPass create info
-	VkRenderPassCreateInfo m_rpassCi = {};
-	Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1> m_attachmentDescriptions = {};
-	Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS + 1> m_references = {};
-	VkSubpassDescription m_subpassDescr = {};
-
-	// VK objects
-	VkRenderPass m_compatibleOrDefaultRpass = {}; ///< Compatible renderpass or default FB's renderpass.
-	HashMap<U64, VkRenderPass> m_rpasses;
-	Mutex m_rpassesMtx;
-	Array<VkFramebuffer, MAX_FRAMES_IN_FLIGHT> m_fbs = {};
+	class
+	{
+	public:
+		U32 m_width = 0;
+		U32 m_height = 0;
+
+		Array<TexturePtr, MAX_COLOR_ATTACHMENTS + 1> m_refs; ///< @note The pos of every attachment is fixed.
+
+		// RenderPass create info
+		VkRenderPassCreateInfo m_rpassCi = {};
+		Array<VkAttachmentDescription, MAX_COLOR_ATTACHMENTS + 1> m_attachmentDescriptions = {};
+		Array<VkAttachmentReference, MAX_COLOR_ATTACHMENTS + 1> m_references = {};
+		VkSubpassDescription m_subpassDescr = {};
+
+		// VK objects
+		VkRenderPass m_compatibleRpass = {}; ///< Compatible renderpass or default FB's renderpass.
+		HashMap<U64, VkRenderPass> m_rpasses;
+		Mutex m_rpassesMtx;
+		VkFramebuffer m_fb = {};
+	} m_noDflt; ///< Not default FB
+
+	class
+	{
+	public:
+		MicroSwapchainPtr m_swapchain;
+		SpinLock m_swapchainLock;
+		VkAttachmentLoadOp m_loadOp = {};
+		U8 m_currentBackbufferIndex = 0;
+	} m_dflt; ///< Default FB
 
 	// Methods
 	ANKI_USE_RESULT Error initFbs(const FramebufferInitInfo& init);
 	void initRpassCreateInfo(const FramebufferInitInfo& init);
-	void setupAttachmentDescriptor(const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc) const;
+	void initClearValues(const FramebufferInitInfo& init);
+	void setupAttachmentDescriptor(
+		const FramebufferAttachmentInfo& att, VkAttachmentDescription& desc, VkImageLayout layout) const;
 };
 /// @}
 

+ 89 - 191
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -17,31 +17,6 @@
 namespace anki
 {
 
-static VkBool32 debugReportCallbackEXT(VkDebugReportFlagsEXT flags,
-	VkDebugReportObjectTypeEXT objectType,
-	uint64_t object,
-	size_t location,
-	int32_t messageCode,
-	const char* pLayerPrefix,
-	const char* pMessage,
-	void* pUserData)
-{
-	if(flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
-	{
-		ANKI_VK_LOGE("%s", pMessage);
-	}
-	else if(flags & (VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT))
-	{
-		ANKI_VK_LOGW("%s", pMessage);
-	}
-	else
-	{
-		ANKI_VK_LOGI("%s", pMessage);
-	}
-
-	return false;
-}
-
 GrManagerImpl::~GrManagerImpl()
 {
 	// FIRST THING: wait for the GPU
@@ -55,15 +30,6 @@ GrManagerImpl::~GrManagerImpl()
 	m_cmdbFactory.destroy();
 
 	// SECOND THING: The destroy everything that has a reference to GrObjects.
-	for(auto& x : m_backbuffers)
-	{
-		if(x.m_imageView)
-		{
-			vkDestroyImageView(m_device, x.m_imageView, nullptr);
-			x.m_imageView = VK_NULL_HANDLE;
-		}
-	}
-
 	for(auto& x : m_perFrame)
 	{
 		x.m_presentFence.reset(nullptr);
@@ -76,21 +42,19 @@ GrManagerImpl::~GrManagerImpl()
 		getAllocator().deleteInstance(m_samplerCache);
 	}
 
+	m_crntSwapchain.reset(nullptr);
+
 	// THIRD THING: Continue with the rest
 	m_gpuMemManager.destroy();
 
 	m_barrierFactory.destroy(); // Destroy before fences
 	m_semaphores.destroy(); // Destroy before fences
+	m_swapchainFactory.destroy(); // Destroy before fences
 	m_fences.destroy();
 
 	m_pplineLayoutFactory.destroy();
 	m_descrFactory.destroy();
 
-	if(m_swapchain)
-	{
-		vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
-	}
-
 	m_pplineCache.destroy(m_device, m_physicalDevice, getAllocator());
 
 	if(m_device)
@@ -122,6 +86,8 @@ GrManagerImpl::~GrManagerImpl()
 
 		vkDestroyInstance(m_instance, pallocCbs);
 	}
+
+	m_vkHandleToName.destroy(getAllocator());
 }
 
 GrAllocator<U8> GrManagerImpl::getAllocator() const
@@ -148,7 +114,10 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	ANKI_CHECK(initSurface(init));
 	ANKI_CHECK(initDevice(init));
 	vkGetDeviceQueue(m_device, m_queueIdx, 0, &m_queue);
-	ANKI_CHECK(initSwapchain(init));
+
+	m_swapchainFactory.init(this, init.m_config->getNumber("window.vsync"));
+
+	m_crntSwapchain = m_swapchainFactory.newInstance();
 
 	ANKI_CHECK(m_pplineCache.init(m_device, m_physicalDevice, init.m_cacheDirectory, *init.m_config, getAllocator()));
 
@@ -396,6 +365,7 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 		ci.pfnCallback = debugReportCallbackEXT;
 		ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT
 			| VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
+		ci.pUserData = this;
 
 		PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT =
 			reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
@@ -592,143 +562,6 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 	return Error::NONE;
 }
 
-Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
-{
-	VkSurfaceCapabilitiesKHR surfaceProperties;
-	ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &surfaceProperties));
-
-	if(surfaceProperties.currentExtent.width == MAX_U32 || surfaceProperties.currentExtent.height == MAX_U32)
-	{
-		ANKI_VK_LOGE("Wrong surface size");
-		return Error::FUNCTION_FAILED;
-	}
-	m_surfaceWidth = surfaceProperties.currentExtent.width;
-	m_surfaceHeight = surfaceProperties.currentExtent.height;
-
-	uint32_t formatCount;
-	ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr));
-
-	DynamicArrayAuto<VkSurfaceFormatKHR> formats(getAllocator());
-	formats.create(formatCount);
-	ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, &formats[0]));
-
-	VkColorSpaceKHR colorspace = VK_COLOR_SPACE_MAX_ENUM_KHR;
-	while(formatCount--)
-	{
-		if(formats[formatCount].format == VK_FORMAT_B8G8R8A8_UNORM)
-		{
-			m_surfaceFormat = formats[formatCount].format;
-			colorspace = formats[formatCount].colorSpace;
-			break;
-		}
-	}
-
-	if(m_surfaceFormat == VK_FORMAT_UNDEFINED)
-	{
-		ANKI_VK_LOGE("Surface format not found");
-		return Error::FUNCTION_FAILED;
-	}
-
-	// Chose present mode
-	uint32_t presentModeCount;
-	vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, nullptr);
-	presentModeCount = min(presentModeCount, 4u);
-	Array<VkPresentModeKHR, 4> presentModes;
-	vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, &presentModes[0]);
-
-	VkPresentModeKHR presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
-	if(init.m_config->getNumber("window.vsync"))
-	{
-		ANKI_VK_LOGI("vsync is on");
-		presentMode = VK_PRESENT_MODE_FIFO_KHR;
-	}
-	else
-	{
-		ANKI_VK_LOGI("vsync is off");
-		for(U i = 0; i < presentModeCount; ++i)
-		{
-			if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
-			{
-				presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
-				break;
-			}
-			else if(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
-			{
-				presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
-				break;
-			}
-		}
-	}
-
-	if(presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR)
-	{
-		ANKI_VK_LOGE("Couldn't find a present mode");
-		return Error::FUNCTION_FAILED;
-	}
-
-	// Create swapchain
-	VkSwapchainCreateInfoKHR ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
-	ci.surface = m_surface;
-	ci.minImageCount = MAX_FRAMES_IN_FLIGHT;
-	ci.imageFormat = m_surfaceFormat;
-	ci.imageColorSpace = colorspace;
-	ci.imageExtent = surfaceProperties.currentExtent;
-	ci.imageArrayLayers = 1;
-	ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
-	ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
-	ci.queueFamilyIndexCount = 1;
-	ci.pQueueFamilyIndices = &m_queueIdx;
-	ci.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-	ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
-	ci.presentMode = presentMode;
-	ci.clipped = false;
-	ci.oldSwapchain = VK_NULL_HANDLE;
-
-	ANKI_VK_CHECK(vkCreateSwapchainKHR(m_device, &ci, nullptr, &m_swapchain));
-
-	// Get images
-	uint32_t count = 0;
-	ANKI_VK_CHECK(vkGetSwapchainImagesKHR(m_device, m_swapchain, &count, nullptr));
-	if(count != MAX_FRAMES_IN_FLIGHT)
-	{
-		ANKI_VK_LOGE("Requested a swapchain with %u images but got one with %u", MAX_FRAMES_IN_FLIGHT, count);
-		return Error::FUNCTION_FAILED;
-	}
-
-	ANKI_VK_LOGI("Created a swapchain. Image count: %u, present mode: %u", count, presentMode);
-
-	Array<VkImage, MAX_FRAMES_IN_FLIGHT> images;
-	ANKI_VK_CHECK(vkGetSwapchainImagesKHR(m_device, m_swapchain, &count, &images[0]));
-	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
-	{
-		m_backbuffers[i].m_image = images[i];
-		ANKI_ASSERT(images[i]);
-	}
-
-	// Create img views
-	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
-	{
-		VkImageViewCreateInfo ci = {};
-		ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-		ci.flags = 0;
-		ci.image = m_backbuffers[i].m_image;
-		ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
-		ci.format = m_surfaceFormat;
-		ci.components = {
-			VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
-		ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-		ci.subresourceRange.baseMipLevel = 0;
-		ci.subresourceRange.levelCount = 1;
-		ci.subresourceRange.baseArrayLayer = 0;
-		ci.subresourceRange.layerCount = 1;
-
-		ANKI_VK_CHECK(vkCreateImageView(m_device, &ci, nullptr, &m_backbuffers[i].m_imageView));
-	}
-
-	return Error::NONE;
-}
-
 Error GrManagerImpl::initMemory(const ConfigSet& cfg)
 {
 	vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_memoryProperties);
@@ -806,12 +639,16 @@ void GrManagerImpl::beginFrame()
 	// Get new image
 	uint32_t imageIdx;
 	ANKI_TRACE_START_EVENT(VK_ACQUIRE_IMAGE);
-	ANKI_VK_CHECKF(vkAcquireNextImageKHR(
-		m_device, m_swapchain, UINT64_MAX, frame.m_acquireSemaphore->getHandle(), fence->getHandle(), &imageIdx));
+	ANKI_VK_CHECKF(vkAcquireNextImageKHR(m_device,
+		m_crntSwapchain->m_swapchain,
+		UINT64_MAX,
+		frame.m_acquireSemaphore->getHandle(),
+		fence->getHandle(),
+		&imageIdx));
 	ANKI_TRACE_STOP_EVENT(VK_ACQUIRE_IMAGE);
 
 	ANKI_ASSERT(imageIdx < MAX_FRAMES_IN_FLIGHT);
-	m_crntBackbufferIdx = imageIdx;
+	m_crntSwapchain->m_currentBackbufferIndex = imageIdx;
 }
 
 void GrManagerImpl::endFrame()
@@ -842,12 +679,23 @@ void GrManagerImpl::endFrame()
 	present.waitSemaphoreCount = (frame.m_renderSemaphore) ? 1 : 0;
 	present.pWaitSemaphores = (frame.m_renderSemaphore) ? &frame.m_renderSemaphore->getHandle() : nullptr;
 	present.swapchainCount = 1;
-	present.pSwapchains = &m_swapchain;
-	present.pImageIndices = &m_crntBackbufferIdx;
+	present.pSwapchains = &m_crntSwapchain->m_swapchain;
+	U32 idx = m_crntSwapchain->m_currentBackbufferIndex;
+	present.pImageIndices = &idx;
 	present.pResults = &res;
 
-	ANKI_VK_CHECKF(vkQueuePresentKHR(m_queue, &present));
-	ANKI_VK_CHECKF(res);
+	VkResult res1 = vkQueuePresentKHR(m_queue, &present);
+	if(res1 == VK_ERROR_OUT_OF_DATE_KHR)
+	{
+		ANKI_VK_LOGW("Swapchain is out of date. Will wait for the queue and create a new one");
+		vkQueueWaitIdle(m_queue);
+		m_crntSwapchain = m_swapchainFactory.newInstance();
+	}
+	else
+	{
+		ANKI_VK_CHECKF(res1);
+		ANKI_VK_CHECKF(res);
+	}
 
 	m_descrFactory.endFrame();
 
@@ -901,6 +749,9 @@ void GrManagerImpl::flushCommandBuffer(CommandBufferPtr cmdb, FencePtr* outFence
 		submit.pSignalSemaphores = &frame.m_renderSemaphore->getHandle();
 
 		frame.m_presentFence = fence;
+
+		// Update the swapchain's fence
+		m_crntSwapchain->setFence(fence);
 	}
 
 	submit.commandBufferCount = 1;
@@ -920,16 +771,63 @@ void GrManagerImpl::flushCommandBuffer(CommandBufferPtr cmdb, FencePtr* outFence
 
 void GrManagerImpl::trySetVulkanHandleName(CString name, VkDebugReportObjectTypeEXT type, U64 handle) const
 {
-	if(m_pfnDebugMarkerSetObjectNameEXT && name)
+	if(name && name.getLength())
 	{
-		VkDebugMarkerObjectNameInfoEXT inf = {};
-		inf.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
-		inf.objectType = type;
-		inf.pObjectName = name.get();
-		inf.object = handle;
+		if(m_pfnDebugMarkerSetObjectNameEXT)
+		{
+			VkDebugMarkerObjectNameInfoEXT inf = {};
+			inf.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
+			inf.objectType = type;
+			inf.pObjectName = name.get();
+			inf.object = handle;
+
+			m_pfnDebugMarkerSetObjectNameEXT(m_device, &inf);
+		}
+
+		LockGuard<SpinLock> lock(m_vkHandleToNameLock);
+		StringAuto newName(getAllocator());
+		newName.create(name);
+		m_vkHandleToName.emplace(getAllocator(), computeHash(&handle, sizeof(handle)), std::move(newName));
+	}
+}
+
+VkBool32 GrManagerImpl::debugReportCallbackEXT(VkDebugReportFlagsEXT flags,
+	VkDebugReportObjectTypeEXT objectType,
+	uint64_t object,
+	size_t location,
+	int32_t messageCode,
+	const char* pLayerPrefix,
+	const char* pMessage,
+	void* pUserData)
+{
+	// Get the object name
+	GrManagerImpl* self = static_cast<GrManagerImpl*>(pUserData);
+	LockGuard<SpinLock> lock(self->m_vkHandleToNameLock);
+	auto it = self->m_vkHandleToName.find(computeHash(&object, sizeof(object)));
+	CString objName;
+	if(it != self->m_vkHandleToName.getEnd())
+	{
+		objName = it->toCString();
+	}
+	else
+	{
+		objName = "Unnamed";
+	}
 
-		m_pfnDebugMarkerSetObjectNameEXT(m_device, &inf);
+	if(flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
+	{
+		ANKI_VK_LOGE("%s (handle: %s)", pMessage, objName.cstr());
 	}
+	else if(flags & (VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT))
+	{
+		ANKI_VK_LOGW("%s (handle: %s)", pMessage, objName.cstr());
+	}
+	else
+	{
+		ANKI_VK_LOGI("%s (handle: %s)", pMessage, objName.cstr());
+	}
+
+	return false;
 }
 
 } // end namespace anki

+ 38 - 47
src/anki/gr/vulkan/GrManagerImpl.h

@@ -13,6 +13,7 @@
 #include <anki/gr/vulkan/QueryExtra.h>
 #include <anki/gr/vulkan/DescriptorSet.h>
 #include <anki/gr/vulkan/CommandBufferFactory.h>
+#include <anki/gr/vulkan/SwapchainFactory.h>
 #include <anki/gr/vulkan/PipelineLayout.h>
 #include <anki/gr/vulkan/PipelineCache.h>
 #include <anki/util/HashMap.h>
@@ -85,40 +86,6 @@ public:
 	}
 	/// @}
 
-	VkFormat getDefaultFramebufferSurfaceFormat() const
-	{
-		return m_surfaceFormat;
-	}
-
-	VkImageView getDefaultSurfaceImageView(U idx) const
-	{
-		ANKI_ASSERT(m_backbuffers[idx].m_imageView);
-		return m_backbuffers[idx].m_imageView;
-	}
-
-	VkImage getDefaultSurfaceImage(U idx) const
-	{
-		ANKI_ASSERT(m_backbuffers[idx].m_image);
-		return m_backbuffers[idx].m_image;
-	}
-
-	U getCurrentBackbufferIndex() const
-	{
-		return m_crntBackbufferIdx;
-	}
-
-	U getDefaultSurfaceWidth() const
-	{
-		ANKI_ASSERT(m_surfaceWidth);
-		return m_surfaceWidth;
-	}
-
-	U getDefaultSurfaceHeight() const
-	{
-		ANKI_ASSERT(m_surfaceHeight);
-		return m_surfaceHeight;
-	}
-
 	void flushCommandBuffer(CommandBufferPtr ptr, FencePtr* fence, Bool wait = false);
 
 	void finishCommandBuffer(CommandBufferPtr ptr)
@@ -191,8 +158,29 @@ public:
 		return m_extensions;
 	}
 
+	MicroSwapchainPtr getSwapchain() const
+	{
+		return m_crntSwapchain;
+	}
+
+	VkSurfaceKHR getSurface() const
+	{
+		ANKI_ASSERT(m_surface);
+		return m_surface;
+	}
+
+	U32 getGraphicsQueueIndex() const
+	{
+		return m_queueIdx;
+	}
+
 	void trySetVulkanHandleName(CString name, VkDebugReportObjectTypeEXT type, U64 handle) const;
 
+	void trySetVulkanHandleName(CString name, VkDebugReportObjectTypeEXT type, void* handle) const
+	{
+		trySetVulkanHandleName(name, type, U64(ptrToNumber(handle)));
+	}
+
 private:
 	GrManager* m_manager = nullptr;
 
@@ -227,13 +215,6 @@ private:
 
 	/// @name Surface_related
 	/// @{
-	class Backbuffer
-	{
-	public:
-		VkImage m_image = VK_NULL_HANDLE;
-		VkImageView m_imageView = VK_NULL_HANDLE;
-	};
-
 	class PerFrame
 	{
 	public:
@@ -245,12 +226,9 @@ private:
 	};
 
 	VkSurfaceKHR m_surface = VK_NULL_HANDLE;
-	U32 m_surfaceWidth = 0, m_surfaceHeight = 0;
-	VkFormat m_surfaceFormat = VK_FORMAT_UNDEFINED;
-	VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
+	MicroSwapchainPtr m_crntSwapchain;
+
 	Array<PerFrame, MAX_FRAMES_IN_FLIGHT> m_perFrame;
-	Array<Backbuffer, MAX_FRAMES_IN_FLIGHT> m_backbuffers;
-	U32 m_crntBackbufferIdx = 0;
 	/// @}
 
 	/// @name Memory
@@ -268,6 +246,8 @@ private:
 	DeferredBarrierFactory m_barrierFactory;
 	/// @}
 
+	SwapchainFactory m_swapchainFactory;
+
 	PipelineLayoutFactory m_pplineLayoutFactory;
 
 	DescriptorSetFactory m_descrFactory;
@@ -282,11 +262,13 @@ private:
 
 	GrObjectCache* m_samplerCache = nullptr;
 
+	mutable HashMap<U64, StringAuto> m_vkHandleToName;
+	mutable SpinLock m_vkHandleToNameLock;
+
 	ANKI_USE_RESULT Error initInternal(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initInstance(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initSurface(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initDevice(const GrManagerInitInfo& init);
-	ANKI_USE_RESULT Error initSwapchain(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initFramebuffers(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initMemory(const ConfigSet& cfg);
 
@@ -301,6 +283,15 @@ private:
 #endif
 
 	void resetFrame(PerFrame& frame);
+
+	static VkBool32 debugReportCallbackEXT(VkDebugReportFlagsEXT flags,
+		VkDebugReportObjectTypeEXT objectType,
+		uint64_t object,
+		size_t location,
+		int32_t messageCode,
+		const char* pLayerPrefix,
+		const char* pMessage,
+		void* pUserData);
 };
 /// @}
 

+ 8 - 8
src/anki/gr/vulkan/GrManagerImplSdl.cpp

@@ -8,6 +8,7 @@
 #include <anki/core/NativeWindow.h>
 #include <anki/core/NativeWindowSdl.h>
 #include <SDL_syswm.h>
+#include <SDL_vulkan.h>
 #if ANKI_OS == ANKI_OS_LINUX
 #include <X11/Xlib-xcb.h>
 #elif ANKI_OS == ANKI_OS_WINDOWS
@@ -21,6 +22,13 @@ namespace anki
 
 Error GrManagerImpl::initSurface(const GrManagerInitInfo& init)
 {
+#if ANKI_OS == ANKI_OS_LINUX
+	if(!SDL_Vulkan_CreateSurface(init.m_window->getNative().m_window, m_instance, &m_surface))
+	{
+		ANKI_VK_LOGE("SDL_Vulkan_CreateSurface() failed");
+		return Error::FUNCTION_FAILED;
+	}
+#elif ANKI_OS == ANKI_OS_WINDOWS
 	SDL_SysWMinfo wminfo;
 	SDL_VERSION(&wminfo.version);
 	if(!SDL_GetWindowWMInfo(init.m_window->getNative().m_window, &wminfo))
@@ -29,14 +37,6 @@ Error GrManagerImpl::initSurface(const GrManagerInitInfo& init)
 		return Error::NONE;
 	}
 
-#if ANKI_OS == ANKI_OS_LINUX
-	VkXcbSurfaceCreateInfoKHR ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
-	ci.connection = XGetXCBConnection(wminfo.info.x11.display);
-	ci.window = wminfo.info.x11.window;
-
-	ANKI_VK_CHECK(vkCreateXcbSurfaceKHR(m_instance, &ci, nullptr, &m_surface));
-#elif ANKI_OS == ANKI_OS_WINDOWS
 	Array<TCHAR, 512> className;
 	GetClassName(wminfo.info.win.window, &className[0], className.getSize());
 

+ 7 - 0
src/anki/gr/vulkan/Pipeline.cpp

@@ -23,6 +23,7 @@ void PipelineStateTracker::reset()
 	m_defaultFb = false;
 	m_fbColorAttachmentMask.unsetAll();
 	m_rpass = VK_NULL_HANDLE;
+	m_fb.reset(nullptr);
 }
 
 Bool PipelineStateTracker::updateHashes()
@@ -382,6 +383,10 @@ class PipelineFactory::PipelineInternal
 {
 public:
 	VkPipeline m_handle = VK_NULL_HANDLE;
+
+	/// The pipeline needs a render pass and the framebuffers are the owners of that. So the internal pipeline will
+	/// hold a ref to the FB in order to hold a ref to the render pass.
+	FramebufferPtr m_fb;
 };
 
 class PipelineFactory::Hasher
@@ -400,6 +405,7 @@ void PipelineFactory::destroy()
 		if(it.m_handle)
 		{
 			vkDestroyPipeline(m_dev, it.m_handle, nullptr);
+			it.m_fb.reset(nullptr);
 		}
 	}
 
@@ -428,6 +434,7 @@ void PipelineFactory::newPipeline(PipelineStateTracker& state, Pipeline& ppline,
 	{
 		PipelineInternal pp;
 		const VkGraphicsPipelineCreateInfo& ci = state.updatePipelineCreateInfo();
+		pp.m_fb = state.getFb();
 
 		ANKI_TRACE_START_EVENT(VK_PIPELINE_CREATE);
 		ANKI_VK_CHECKF(vkCreateGraphicsPipelines(m_dev, m_pplineCache, 1, &ci, nullptr, &pp.m_handle));

+ 9 - 0
src/anki/gr/vulkan/Pipeline.h

@@ -371,12 +371,14 @@ public:
 		m_fbStencil = s;
 		m_rpass = fb->m_impl->getCompatibleRenderPass();
 		m_defaultFb = fb->m_impl->isDefaultFramebuffer();
+		m_fb = fb;
 	}
 
 	void endRenderPass()
 	{
 		ANKI_ASSERT(m_rpass);
 		m_rpass = VK_NULL_HANDLE;
+		m_fb.reset(nullptr);
 	}
 
 	void setPrimitiveTopology(PrimitiveTopology topology)
@@ -414,6 +416,12 @@ public:
 	/// Populate the internal pipeline create info structure.
 	const VkGraphicsPipelineCreateInfo& updatePipelineCreateInfo();
 
+	FramebufferPtr getFb() const
+	{
+		ANKI_ASSERT(m_fb.isCreated());
+		return m_fb;
+	}
+
 	void reset();
 
 private:
@@ -472,6 +480,7 @@ private:
 	Bool8 m_defaultFb = false;
 	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_fbColorAttachmentMask = {false};
 	VkRenderPass m_rpass = VK_NULL_HANDLE;
+	FramebufferPtr m_fb; ///< Hold the reference.
 
 	// Create info
 	class CreateInfo

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

@@ -5,6 +5,7 @@
 
 #include <anki/gr/vulkan/SamplerImpl.h>
 #include <anki/gr/Texture.h>
+#include <anki/gr/vulkan/GrManagerImpl.h>
 #include <cstring>
 
 namespace anki
@@ -75,6 +76,7 @@ Error SamplerImpl::init(const SamplerInitInfo& ii)
 
 	// Create
 	ANKI_VK_CHECK(vkCreateSampler(getDevice(), &ci, nullptr, &m_handle));
+	getGrManagerImpl().trySetVulkanHandleName(ii.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT, m_handle);
 
 	return Error::NONE;
 }

+ 381 - 0
src/anki/gr/vulkan/SwapchainFactory.cpp

@@ -0,0 +1,381 @@
+// 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/SwapchainFactory.h>
+#include <anki/gr/vulkan/GrManagerImpl.h>
+
+namespace anki
+{
+
+MicroSwapchain::MicroSwapchain(SwapchainFactory* factory)
+	: m_factory(factory)
+{
+	ANKI_ASSERT(factory);
+
+	if(initInternal())
+	{
+		ANKI_VK_LOGF("Error creating the swapchain. Will not try to recover");
+	}
+}
+
+MicroSwapchain::~MicroSwapchain()
+{
+	const VkDevice dev = m_factory->m_gr->getDevice();
+
+	for(VkFramebuffer& fb : m_framebuffers)
+	{
+		if(fb)
+		{
+			vkDestroyFramebuffer(dev, fb, nullptr);
+			fb = {};
+		}
+	}
+
+	for(VkRenderPass& rpass : m_rpasses)
+	{
+		if(rpass)
+		{
+			vkDestroyRenderPass(dev, rpass, nullptr);
+			rpass = {};
+		}
+	}
+
+	for(VkImageView& iview : m_imageViews)
+	{
+		if(iview)
+		{
+			vkDestroyImageView(dev, iview, nullptr);
+			iview = {};
+		}
+	}
+
+	if(m_swapchain)
+	{
+		vkDestroySwapchainKHR(dev, m_swapchain, nullptr);
+		m_swapchain = {};
+		m_images = {};
+	}
+}
+
+Error MicroSwapchain::initInternal()
+{
+	const VkDevice dev = m_factory->m_gr->getDevice();
+
+	// Get the surface size
+	VkSurfaceCapabilitiesKHR surfaceProperties;
+	{
+		ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &surfaceProperties));
+
+		if(surfaceProperties.currentExtent.width == MAX_U32 || surfaceProperties.currentExtent.height == MAX_U32)
+		{
+			ANKI_VK_LOGE("Wrong surface size");
+			return Error::FUNCTION_FAILED;
+		}
+		m_surfaceWidth = surfaceProperties.currentExtent.width;
+		m_surfaceHeight = surfaceProperties.currentExtent.height;
+	}
+
+	// Get the surface format
+	VkColorSpaceKHR colorspace = VK_COLOR_SPACE_MAX_ENUM_KHR;
+	{
+		uint32_t formatCount;
+		ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &formatCount, nullptr));
+
+		DynamicArrayAuto<VkSurfaceFormatKHR> formats(getAllocator());
+		formats.create(formatCount);
+		ANKI_VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &formatCount, &formats[0]));
+
+		while(formatCount--)
+		{
+			if(formats[formatCount].format == VK_FORMAT_B8G8R8A8_UNORM)
+			{
+				m_surfaceFormat = formats[formatCount].format;
+				colorspace = formats[formatCount].colorSpace;
+				break;
+			}
+		}
+
+		if(m_surfaceFormat == VK_FORMAT_UNDEFINED)
+		{
+			ANKI_VK_LOGE("Surface format not found");
+			return Error::FUNCTION_FAILED;
+		}
+	}
+
+	// Chose present mode
+	VkPresentModeKHR presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
+	{
+		uint32_t presentModeCount;
+		vkGetPhysicalDeviceSurfacePresentModesKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &presentModeCount, nullptr);
+		presentModeCount = min(presentModeCount, 4u);
+		Array<VkPresentModeKHR, 4> presentModes;
+		vkGetPhysicalDeviceSurfacePresentModesKHR(
+			m_factory->m_gr->getPhysicalDevice(), m_factory->m_gr->getSurface(), &presentModeCount, &presentModes[0]);
+
+		if(m_factory->m_vsync)
+		{
+			presentMode = VK_PRESENT_MODE_FIFO_KHR;
+		}
+		else
+		{
+			for(U i = 0; i < presentModeCount; ++i)
+			{
+				if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
+				{
+					presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
+					break;
+				}
+				else if(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
+				{
+					presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+					break;
+				}
+			}
+		}
+
+		if(presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR)
+		{
+			ANKI_VK_LOGE("Couldn't find a present mode");
+			return Error::FUNCTION_FAILED;
+		}
+	}
+
+	// Create swapchain
+	{
+		VkSwapchainCreateInfoKHR ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+		ci.surface = m_factory->m_gr->getSurface();
+		ci.minImageCount = MAX_FRAMES_IN_FLIGHT;
+		ci.imageFormat = m_surfaceFormat;
+		ci.imageColorSpace = colorspace;
+		ci.imageExtent = surfaceProperties.currentExtent;
+		ci.imageArrayLayers = 1;
+		ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+		ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+		ci.queueFamilyIndexCount = 1;
+		U32 idx = m_factory->m_gr->getGraphicsQueueIndex();
+		ci.pQueueFamilyIndices = &idx;
+		ci.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+		ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+		ci.presentMode = presentMode;
+		ci.clipped = false;
+		ci.oldSwapchain = VK_NULL_HANDLE;
+
+		ANKI_VK_CHECK(vkCreateSwapchainKHR(dev, &ci, nullptr, &m_swapchain));
+	}
+
+	// Get images
+	{
+		uint32_t count = 0;
+		ANKI_VK_CHECK(vkGetSwapchainImagesKHR(dev, m_swapchain, &count, nullptr));
+		if(count != MAX_FRAMES_IN_FLIGHT)
+		{
+			ANKI_VK_LOGE("Requested a swapchain with %u images but got one with %u", MAX_FRAMES_IN_FLIGHT, count);
+			return Error::FUNCTION_FAILED;
+		}
+
+		ANKI_VK_LOGI("Created a swapchain. Image count: %u, present mode: %u, size: %ux%u, vsync: %u",
+			count,
+			presentMode,
+			m_surfaceWidth,
+			m_surfaceHeight,
+			U32(m_factory->m_vsync));
+
+		Array<VkImage, MAX_FRAMES_IN_FLIGHT> images;
+		ANKI_VK_CHECK(vkGetSwapchainImagesKHR(dev, m_swapchain, &count, &images[0]));
+		for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
+		{
+			m_images[i] = images[i];
+			ANKI_ASSERT(images[i]);
+		}
+	}
+
+	// Create img views
+	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
+	{
+		VkImageViewCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+		ci.flags = 0;
+		ci.image = m_images[i];
+		ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
+		ci.format = m_surfaceFormat;
+		ci.components = {
+			VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
+		ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+		ci.subresourceRange.baseMipLevel = 0;
+		ci.subresourceRange.levelCount = 1;
+		ci.subresourceRange.baseArrayLayer = 0;
+		ci.subresourceRange.layerCount = 1;
+
+		ANKI_VK_CHECK(vkCreateImageView(dev, &ci, nullptr, &m_imageViews[i]));
+		m_factory->m_gr->trySetVulkanHandleName(
+			"DfldImgView", VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT, m_imageViews[i]);
+	}
+
+	// Create the render passes
+	static const Array<VkAttachmentLoadOp, RPASS_COUNT> loadOps = {
+		{VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_LOAD_OP_DONT_CARE}};
+	for(U i = 0; i < RPASS_COUNT; ++i)
+	{
+		const VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+		VkAttachmentDescription desc = {};
+		desc.format = m_surfaceFormat;
+		desc.samples = VK_SAMPLE_COUNT_1_BIT;
+		desc.loadOp = loadOps[i];
+		desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+		desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+		desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+		desc.initialLayout = layout;
+		desc.finalLayout = layout;
+
+		VkAttachmentReference ref = {0, layout};
+
+		VkSubpassDescription subpass = {};
+		subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+		subpass.colorAttachmentCount = 1;
+		subpass.pColorAttachments = &ref;
+		subpass.pDepthStencilAttachment = nullptr;
+
+		VkRenderPassCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+		ci.attachmentCount = 1;
+		ci.pAttachments = &desc;
+		ci.subpassCount = 1;
+		ci.pSubpasses = &subpass;
+
+		ANKI_VK_CHECK(vkCreateRenderPass(dev, &ci, nullptr, &m_rpasses[i]));
+		m_factory->m_gr->trySetVulkanHandleName(
+			"Dfld Rpass", VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, m_rpasses[i]);
+	}
+
+	// Create FBs
+	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
+	{
+		VkFramebufferCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+		ci.renderPass = m_rpasses[0]; // Use that, it's compatible
+		ci.attachmentCount = 1;
+		ci.pAttachments = &m_imageViews[i];
+		ci.width = m_surfaceWidth;
+		ci.height = m_surfaceHeight;
+		ci.layers = 1;
+
+		ANKI_VK_CHECK(vkCreateFramebuffer(dev, &ci, nullptr, &m_framebuffers[i]));
+		m_factory->m_gr->trySetVulkanHandleName(
+			"Dfld FB", VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, m_framebuffers[i]);
+	}
+
+	return Error::NONE;
+}
+
+GrAllocator<U8> MicroSwapchain::getAllocator() const
+{
+	return m_factory->m_gr->getAllocator();
+}
+
+void SwapchainFactory::destroy()
+{
+	LockGuard<Mutex> lock(m_mtx);
+
+	U count = m_swapchainCount;
+	while(count--)
+	{
+		if(m_swapchains[count]->m_fence)
+		{
+			ANKI_ASSERT(m_swapchains[count]->m_fence->done());
+		}
+
+		m_gr->getAllocator().deleteInstance(m_swapchains[count]);
+#if ANKI_EXTRA_CHECKS
+		--m_swapchainsInFlight;
+#endif
+	}
+
+	m_swapchains.destroy(m_gr->getAllocator());
+
+	ANKI_ASSERT(m_swapchainsInFlight == 0 && "Wrong destroy order");
+}
+
+MicroSwapchainPtr SwapchainFactory::newInstance()
+{
+	LockGuard<Mutex> lock(m_mtx);
+
+	MicroSwapchain* out = nullptr;
+
+	if(m_swapchainCount > 0)
+	{
+		releaseFences();
+
+		U count = m_swapchainCount;
+		while(count--)
+		{
+			if(!m_swapchains[count]->m_fence)
+			{
+				out = m_swapchains[count];
+
+				// Pop it
+				for(U i = count; i < m_swapchainCount - 1; ++i)
+				{
+					m_swapchains[i] = m_swapchains[i + 1];
+				}
+
+				--m_swapchainCount;
+
+				break;
+			}
+		}
+	}
+
+	if(out == nullptr)
+	{
+		// Create a new one
+		out = m_gr->getAllocator().newInstance<MicroSwapchain>(this);
+#if ANKI_EXTRA_CHECKS
+		++m_swapchainsInFlight;
+#endif
+	}
+
+	ANKI_ASSERT(out->m_refcount.get() == 0);
+	return MicroSwapchainPtr(out);
+}
+
+void SwapchainFactory::releaseFences()
+{
+	U count = m_swapchainCount;
+	while(count--)
+	{
+		MicroSwapchain& schain = *m_swapchains[count];
+		if(schain.m_fence && schain.m_fence->done())
+		{
+			schain.m_fence.reset(nullptr);
+		}
+	}
+}
+
+void SwapchainFactory::destroySwapchain(MicroSwapchain* schain)
+{
+	ANKI_ASSERT(schain);
+	ANKI_ASSERT(schain->m_refcount.get() == 0);
+
+	LockGuard<Mutex> lock(m_mtx);
+
+	releaseFences();
+
+	if(m_swapchains.getSize() <= m_swapchainCount)
+	{
+		// Grow storage
+		m_swapchains.resize(m_gr->getAllocator(), max<U>(1, m_swapchains.getSize() * 2));
+	}
+
+	m_swapchains[m_swapchainCount] = schain;
+	++m_swapchainCount;
+}
+
+} // end namespace anki

+ 133 - 0
src/anki/gr/vulkan/SwapchainFactory.h

@@ -0,0 +1,133 @@
+// 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/vulkan/FenceFactory.h>
+#include <anki/util/Ptr.h>
+
+namespace anki
+{
+
+// Forward
+class SwapchainFactory;
+
+/// @addtogroup vulkan
+/// @{
+
+/// A wrapper for the swapchain.
+class MicroSwapchain
+{
+	friend class MicroSwapchainPtrDeleter;
+	friend class SwapchainFactory;
+
+public:
+	VkSwapchainKHR m_swapchain = {};
+
+	Array<VkImage, MAX_FRAMES_IN_FLIGHT> m_images = {};
+	Array<VkImageView, MAX_FRAMES_IN_FLIGHT> m_imageViews = {};
+
+	VkFormat m_surfaceFormat = {};
+
+	U32 m_surfaceWidth = 0;
+	U32 m_surfaceHeight = 0;
+
+	Array<VkFramebuffer, MAX_FRAMES_IN_FLIGHT> m_framebuffers = {};
+
+	U8 m_currentBackbufferIndex = 0;
+
+	MicroSwapchain(SwapchainFactory* factory);
+
+	~MicroSwapchain();
+
+	Atomic<U32>& getRefcount()
+	{
+		return m_refcount;
+	}
+
+	GrAllocator<U8> getAllocator() const;
+
+	void setFence(MicroFencePtr fence)
+	{
+		m_fence = fence;
+	}
+
+	VkRenderPass getRenderPass(VkAttachmentLoadOp loadOp) const
+	{
+		const U idx = (loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) ? RPASS_LOAD_DONT_CARE : RPASS_LOAD_CLEAR;
+		return m_rpasses[idx];
+	}
+
+private:
+	Atomic<U32> m_refcount = {0};
+	SwapchainFactory* m_factory = nullptr;
+
+	enum
+	{
+		RPASS_LOAD_CLEAR,
+		RPASS_LOAD_DONT_CARE,
+		RPASS_COUNT
+	};
+
+	Array<VkRenderPass, RPASS_COUNT> m_rpasses = {};
+
+	MicroFencePtr m_fence;
+
+	ANKI_USE_RESULT Error initInternal();
+};
+
+/// Deleter for MicroSwapchainPtr smart pointer.
+class MicroSwapchainPtrDeleter
+{
+public:
+	void operator()(MicroSwapchain* x);
+};
+
+/// MicroSwapchain smart pointer.
+using MicroSwapchainPtr = IntrusivePtr<MicroSwapchain, MicroSwapchainPtrDeleter>;
+
+/// Swapchain factory.
+class SwapchainFactory
+{
+	friend class MicroSwapchainPtrDeleter;
+	friend class MicroSwapchain;
+
+public:
+	void init(GrManagerImpl* manager, Bool vsync)
+	{
+		ANKI_ASSERT(manager);
+		m_gr = manager;
+		m_vsync = vsync;
+	}
+
+	void destroy();
+
+	MicroSwapchainPtr newInstance();
+
+private:
+	GrManagerImpl* m_gr = nullptr;
+	Bool8 m_vsync = false;
+
+	Mutex m_mtx;
+
+	DynamicArray<MicroSwapchain*> m_swapchains;
+	U32 m_swapchainCount = 0;
+#if ANKI_EXTRA_CHECKS
+	U32 m_swapchainsInFlight = 0;
+#endif
+
+	void destroySwapchain(MicroSwapchain* schain);
+
+	void releaseFences();
+};
+/// @}
+
+inline void MicroSwapchainPtrDeleter::operator()(MicroSwapchain* s)
+{
+	ANKI_ASSERT(s);
+	s->m_factory->destroySwapchain(s);
+}
+
+} // end namespace anki

+ 30 - 5
src/anki/gr/vulkan/TextureImpl.cpp

@@ -52,6 +52,7 @@ TextureImpl::~TextureImpl()
 Error TextureImpl::init(const TextureInitInfo& init_, Texture* tex)
 {
 	TextureInitInfo init = init_;
+	init.m_sampling.setName(init.getName());
 	ANKI_ASSERT(textureInitInfoValid(init));
 	m_sampler = getGrManagerImpl().getSamplerCache().newInstance<Sampler>(init.m_sampling);
 
@@ -60,6 +61,14 @@ Error TextureImpl::init(const TextureInitInfo& init_, Texture* tex)
 	m_height = init.m_height;
 	m_depth = init.m_depth;
 	m_type = init.m_type;
+	if(init.getName())
+	{
+		strcpy(&m_name[0], init.getName().cstr());
+	}
+	else
+	{
+		m_name[0] = '\0';
+	}
 
 	if(m_type == TextureType::_3D)
 	{
@@ -318,8 +327,7 @@ 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, ptrToNumber(m_imageHandle));
+	getGrManagerImpl().trySetVulkanHandleName(init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_imageHandle);
 
 	// Allocate memory
 	//
@@ -361,6 +369,8 @@ Error TextureImpl::initImage(const TextureInitInfo& init_)
 		memAllocCi.memoryTypeIndex = memIdx;
 
 		ANKI_VK_CHECK(vkAllocateMemory(getDevice(), &memAllocCi, nullptr, &m_dedicatedMem));
+		getGrManagerImpl().trySetVulkanHandleName(
+			init.getName(), VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT, ptrToNumber(m_dedicatedMem));
 
 		ANKI_TRACE_START_EVENT(VK_BIND_OBJECT);
 		ANKI_VK_CHECK(vkBindImageMemory(getDevice(), m_imageHandle, m_dedicatedMem, 0));
@@ -440,21 +450,21 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit before,
 		}
 		else
 		{
-			srcStages |= VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
+			srcStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; // See Table 4 in the spec
 			srcAccesses |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
 		}
 	}
 
 	if(!!(before & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE))
 	{
-		srcStages |= VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
-
 		if(m_depthStencil)
 		{
+			srcStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
 			srcAccesses |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
 		}
 		else
 		{
+			srcStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
 			srcAccesses |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
 		}
 	}
@@ -662,6 +672,18 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 	return out;
 }
 
+VkImageView TextureImpl::getOrCreateSingleLevelView(U32 mip, DepthStencilAspectBit aspect)
+{
+	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)
 {
 	checkSurfaceOrVolume(surf);
@@ -694,6 +716,9 @@ VkImageView TextureImpl::getOrCreateView(const VkImageViewCreateInfo& ci)
 	{
 		VkImageView view = VK_NULL_HANDLE;
 		ANKI_VK_CHECKF(vkCreateImageView(getDevice(), &ci, 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);
 
 		return view;

+ 5 - 11
src/anki/gr/vulkan/TextureImpl.h

@@ -14,7 +14,6 @@ namespace anki
 {
 
 // Forward
-class TextureUsageTracker;
 class TextureUsageState;
 
 /// @addtogroup vulkan
@@ -92,6 +91,9 @@ public:
 		return (usage & m_usage) == usage;
 	}
 
+	/// For image load/store.
+	VkImageView getOrCreateSingleLevelView(U32 mip, DepthStencilAspectBit aspect);
+
 	VkImageView getOrCreateSingleSurfaceView(const TextureSurfaceInfo& surf, DepthStencilAspectBit aspect);
 
 	/// That view will be used in descriptor sets.
@@ -119,16 +121,6 @@ public:
 		ANKI_ASSERT(range.baseMipLevel + range.levelCount <= m_mipCount);
 	}
 
-	template<typename TextureInfo>
-	VkImageLayout findLayoutFromTracker(const TextureInfo& surfOrVol, const TextureUsageTracker& tracker) const;
-
-	VkImageLayout findLayoutFromTracker(const TextureUsageTracker& tracker) const;
-
-	template<typename TextureInfo>
-	void updateTracker(const TextureInfo& surfOrVol, TextureUsageBit usage, TextureUsageTracker& tracker) const;
-
-	void updateTracker(TextureUsageBit usage, TextureUsageTracker& tracker) const;
-
 private:
 	class ViewHasher
 	{
@@ -146,6 +138,8 @@ private:
 
 	VkDeviceMemory m_dedicatedMem = VK_NULL_HANDLE;
 
+	Array<char, MAX_GR_OBJECT_NAME_LENGTH + 1> m_name;
+
 	ANKI_USE_RESULT static VkFormatFeatureFlags calcFeatures(const TextureInitInfo& init);
 
 	ANKI_USE_RESULT static VkImageCreateFlags calcCreateFlags(const TextureInitInfo& init);

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

@@ -4,7 +4,6 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/gr/vulkan/TextureImpl.h>
-#include <anki/gr/vulkan/TextureUsageTracker.h>
 
 namespace anki
 {
@@ -122,118 +121,4 @@ inline U TextureImpl::computeSubresourceIdx(const TextureSurfaceInfo& surf) cons
 	}
 }
 
-template<typename TextureInfo>
-inline VkImageLayout TextureImpl::findLayoutFromTracker(
-	const TextureInfo& surfOrVol, const TextureUsageTracker& tracker) const
-{
-	checkSurfaceOrVolume(surfOrVol);
-
-	auto it = tracker.m_map.find(m_uuid);
-	if(it == tracker.m_map.getEnd())
-	{
-		ANKI_ASSERT(m_usageWhenEncountered != TextureUsageBit::NONE && "Cannot find the layout of the tex");
-		return computeLayout(m_usageWhenEncountered, surfOrVol.m_level);
-	}
-	else
-	{
-		const U idx = computeSubresourceIdx(surfOrVol);
-		const VkImageLayout out = it->m_subResources[idx];
-		ANKI_ASSERT(out != VK_IMAGE_LAYOUT_UNDEFINED);
-		return out;
-	}
-}
-
-inline VkImageLayout TextureImpl::findLayoutFromTracker(const TextureUsageTracker& tracker) const
-{
-	auto it = tracker.m_map.find(m_uuid);
-	if(it == tracker.m_map.getEnd())
-	{
-		ANKI_ASSERT(m_usageWhenEncountered != TextureUsageBit::NONE && "Cannot find the layout of the tex");
-		return computeLayout(m_usageWhenEncountered, 0);
-	}
-	else
-	{
-		for(U i = 0; i < it->m_subResources.getSize(); ++i)
-		{
-			ANKI_ASSERT(
-				it->m_subResources[i] == it->m_subResources[0] && "Not all image subresources are in the same layout");
-		}
-		const VkImageLayout out = it->m_subResources[0];
-		ANKI_ASSERT(out != VK_IMAGE_LAYOUT_UNDEFINED);
-		return out;
-	}
-}
-
-template<typename TextureInfo>
-inline void TextureImpl::updateTracker(
-	const TextureInfo& surfOrVol, TextureUsageBit usage, TextureUsageTracker& tracker) const
-{
-	ANKI_ASSERT(usage != TextureUsageBit::NONE);
-	ANKI_ASSERT(usageValid(usage));
-	checkSurfaceOrVolume(surfOrVol);
-
-	auto it = tracker.m_map.find(m_uuid);
-	if(it != tracker.m_map.getEnd())
-	{
-		// Found
-		updateUsageState(surfOrVol, usage, tracker.m_alloc, *it);
-	}
-	else
-	{
-		// Not found
-		TextureUsageState state;
-		updateUsageState(surfOrVol, usage, tracker.m_alloc, state);
-		tracker.m_map.emplace(tracker.m_alloc, m_uuid, std::move(state));
-	}
-}
-
-inline void TextureImpl::updateTracker(TextureUsageBit usage, TextureUsageTracker& tracker) const
-{
-	ANKI_ASSERT(usage != TextureUsageBit::NONE);
-	ANKI_ASSERT(usageValid(usage));
-
-	auto it = tracker.m_map.find(m_uuid);
-	if(it != tracker.m_map.getEnd())
-	{
-		// Found
-		updateUsageState(usage, tracker.m_alloc, *it);
-	}
-	else
-	{
-		// Not found
-		TextureUsageState state;
-		updateUsageState(usage, tracker.m_alloc, state);
-		tracker.m_map.emplace(tracker.m_alloc, m_uuid, std::move(state));
-	}
-}
-
-template<typename TextureInfo>
-inline void TextureImpl::updateUsageState(
-	const TextureInfo& surfOrVol, TextureUsageBit usage, StackAllocator<U8>& alloc, TextureUsageState& state) const
-{
-	checkSurfaceOrVolume(surfOrVol);
-	if(ANKI_UNLIKELY(state.m_subResources.getSize() == 0))
-	{
-		state.m_subResources.create(alloc, m_surfaceOrVolumeCount);
-		memorySet(
-			reinterpret_cast<int*>(&state.m_subResources[0]), int(VK_IMAGE_LAYOUT_UNDEFINED), m_surfaceOrVolumeCount);
-	}
-	const VkImageLayout lay = computeLayout(usage, surfOrVol.m_level);
-	ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
-	state.m_subResources[computeSubresourceIdx(surfOrVol)] = lay;
-}
-
-inline void TextureImpl::updateUsageState(
-	TextureUsageBit usage, StackAllocator<U8>& alloc, TextureUsageState& state) const
-{
-	if(ANKI_UNLIKELY(state.m_subResources.getSize() == 0))
-	{
-		state.m_subResources.create(alloc, m_surfaceOrVolumeCount);
-	}
-	const VkImageLayout lay = computeLayout(usage, 0);
-	ANKI_ASSERT(lay != VK_IMAGE_LAYOUT_UNDEFINED);
-
-	memorySet(reinterpret_cast<int*>(&state.m_subResources[0]), int(lay), m_surfaceOrVolumeCount);
-}
-
 } // end namespace anki

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

@@ -1,55 +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
-
-#pragma once
-
-#include <anki/gr/Texture.h>
-#include <anki/gr/vulkan/TextureImpl.h>
-#include <anki/util/HashMap.h>
-
-namespace anki
-{
-
-/// @addtogroup vulkan
-/// @{
-
-class TextureUsageState
-{
-public:
-	DynamicArray<VkImageLayout> m_subResources; ///< Volumes or surfaces.
-
-	void destroy(StackAllocator<U8>& alloc)
-	{
-		m_subResources.destroy(alloc);
-	}
-};
-
-class TextureUsageTracker
-{
-	friend class TextureImpl;
-
-public:
-	~TextureUsageTracker()
-	{
-		for(auto& it : m_map)
-		{
-			it.destroy(m_alloc);
-		}
-
-		m_map.destroy(m_alloc);
-	}
-
-	void init(StackAllocator<U8> alloc)
-	{
-		m_alloc = alloc;
-	}
-
-private:
-	StackAllocator<U8> m_alloc;
-	HashMap<U64, TextureUsageState> m_map;
-};
-/// @}
-
-} // end namespace anki

+ 116 - 96
src/anki/renderer/Bloom.cpp

@@ -13,148 +13,168 @@
 namespace anki
 {
 
-BloomExposure::~BloomExposure()
+Bloom::Bloom(Renderer* r)
+	: RendererObject(r)
 {
 }
 
-Error BloomExposure::init(const ConfigSet& config)
+Bloom::~Bloom()
 {
-	GrManager& gr = getGrManager();
+}
 
-	m_width = m_r->getDownscaleBlur().getPassWidth(MAX_U) * 2;
-	m_height = m_r->getDownscaleBlur().getPassHeight(MAX_U) * 2;
+Error Bloom::initExposure(const ConfigSet& config)
+{
+	m_exposure.m_width = m_r->getDownscaleBlur().getPassWidth(MAX_U) * 2;
+	m_exposure.m_height = m_r->getDownscaleBlur().getPassHeight(MAX_U) * 2;
 
-	m_threshold = config.getNumber("r.bloom.threshold");
-	m_scale = config.getNumber("r.bloom.scale");
+	m_exposure.m_threshold = config.getNumber("r.bloom.threshold");
+	m_exposure.m_scale = config.getNumber("r.bloom.scale");
 
-	// Create RT
-	m_rt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_width,
-		m_height,
+	// Create RT info
+	m_exposure.m_rtDescr = m_r->create2DRenderTargetDescription(m_exposure.m_width,
+		m_exposure.m_height,
 		BLOOM_RT_PIXEL_FORMAT,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		SamplingFilter::LINEAR,
-		1,
-		"bloomexp"));
+		"Bloom Exp");
+	m_exposure.m_rtDescr.bake();
 
-	// Create FBs
-	FramebufferInitInfo fbInit("bloomexp");
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_fb = gr.newInstance<Framebuffer>(fbInit);
+	// Create FB info
+	m_exposure.m_fbDescr.m_colorAttachmentCount = 1;
+	m_exposure.m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_exposure.m_fbDescr.bake();
 
 	// init shaders
-	ANKI_CHECK(getResourceManager().loadResource("programs/Bloom.ankiprog", m_prog));
+	ANKI_CHECK(getResourceManager().loadResource("programs/Bloom.ankiprog", m_exposure.m_prog));
 
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
+	ShaderProgramResourceConstantValueInitList<1> consts(m_exposure.m_prog);
 	consts.add(
 		"TEX_SIZE", Vec2(m_r->getDownscaleBlur().getPassWidth(MAX_U), m_r->getDownscaleBlur().getPassHeight(MAX_U)));
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(consts.get(), variant);
-	m_grProg = variant->getProgram();
+	m_exposure.m_prog->getOrCreateVariant(consts.get(), variant);
+	m_exposure.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void BloomExposure::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(
-		m_rt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void BloomExposure::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void BloomExposure::run(RenderingContext& ctx)
+Error Bloom::initUpscale(const ConfigSet& config)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	m_upscale.m_width = m_r->getWidth() / BLOOM_FRACTION;
+	m_upscale.m_height = m_r->getHeight() / BLOOM_FRACTION;
 
-	cmdb->beginRenderPass(m_fb);
-	cmdb->setViewport(0, 0, m_width, m_height);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTexture(0, 0, m_r->getDownscaleBlur().getPassTexture(MAX_U));
-
-	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
-	*uniforms = Vec4(m_threshold, m_scale, 0.0, 0.0);
-
-	cmdb->bindStorageBuffer(0, 0, m_r->getTonemapping().m_luminanceBuff, 0, MAX_PTR_SIZE);
-
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-BloomUpscale::~BloomUpscale()
-{
-}
-
-Error BloomUpscale::init(const ConfigSet& config)
-{
-	GrManager& gr = getGrManager();
-
-	m_width = m_r->getWidth() / BLOOM_FRACTION;
-	m_height = m_r->getHeight() / BLOOM_FRACTION;
-
-	// Create RTs
-	m_rt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_width,
-		m_height,
+	// Create RT descr
+	m_upscale.m_rtDescr = m_r->create2DRenderTargetDescription(m_upscale.m_width,
+		m_upscale.m_height,
 		BLOOM_RT_PIXEL_FORMAT,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		SamplingFilter::LINEAR,
-		1,
-		"bloomupscale"));
+		"Bloom Upscale");
+	m_upscale.m_rtDescr.bake();
 
-	// Create FBs
-	FramebufferInitInfo fbInit("bloomupscale");
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_fb = gr.newInstance<Framebuffer>(fbInit);
+	// Create FB descr
+	m_upscale.m_fbDescr.m_colorAttachmentCount = 1;
+	m_upscale.m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_upscale.m_fbDescr.bake();
 
 	// init shaders
-	ANKI_CHECK(getResourceManager().loadResource("programs/BloomUpscale.ankiprog", m_prog));
+	ANKI_CHECK(getResourceManager().loadResource("programs/BloomUpscale.ankiprog", m_upscale.m_prog));
 
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add("TEX_SIZE", Vec2(m_width, m_height));
+	ShaderProgramResourceConstantValueInitList<1> consts(m_upscale.m_prog);
+	consts.add("TEX_SIZE", Vec2(m_upscale.m_width, m_upscale.m_height));
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(consts.get(), variant);
-	m_grProg = variant->getProgram();
+	m_upscale.m_prog->getOrCreateVariant(consts.get(), variant);
+	m_upscale.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void BloomUpscale::setPreRunBarriers(RenderingContext& ctx)
+Error Bloom::initSslf(const ConfigSet& cfg)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(
-		m_rt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/LensDirt.ankitex", m_sslf.m_lensDirtTex));
+	ANKI_CHECK(getResourceManager().loadResource("programs/ScreenSpaceLensFlare.ankiprog", m_sslf.m_prog));
+
+	ShaderProgramResourceConstantValueInitList<1> consts(m_sslf.m_prog);
+	consts.add("INPUT_TEX_SIZE", UVec2(m_exposure.m_width, m_exposure.m_height));
+
+	const ShaderProgramResourceVariant* variant;
+	m_sslf.m_prog->getOrCreateVariant(consts.get(), variant);
+	m_sslf.m_grProg = variant->getProgram();
+
+	return Error::NONE;
 }
 
-void BloomUpscale::setPostRunBarriers(RenderingContext& ctx)
+void Bloom::populateRenderGraph(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	// Main pass
+	{
+		// Ask for render target
+		m_runCtx.m_exposureRt = rgraph.newRenderTarget(m_exposure.m_rtDescr);
+
+		// Set the render pass
+		GraphicsRenderPassDescription& rpass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Bloom Main");
+		rpass.setWork(runExposureCallback, this, 0);
+		rpass.setFramebufferInfo(m_exposure.m_fbDescr, {{m_runCtx.m_exposureRt}}, {});
+
+		rpass.newConsumer({m_runCtx.m_exposureRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		rpass.newConsumer({m_r->getDownscaleBlur().getPassRt(MAX_U), TextureUsageBit::SAMPLED_FRAGMENT});
+		rpass.newConsumer({m_r->getTonemapping().getAverageLuminanceBuffer(), BufferUsageBit::STORAGE_FRAGMENT_READ});
+		rpass.newProducer({m_runCtx.m_exposureRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	// Upscale & SSLF pass
+	{
+		// Ask for render target
+		m_runCtx.m_upscaleRt = rgraph.newRenderTarget(m_upscale.m_rtDescr);
+
+		// Set the render pass
+		GraphicsRenderPassDescription& rpass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Bloom Upscale");
+		rpass.setWork(runUpscaleAndSslfCallback, this, 0);
+		rpass.setFramebufferInfo(m_upscale.m_fbDescr, {{m_runCtx.m_upscaleRt}}, {});
+
+		rpass.newConsumer({m_runCtx.m_upscaleRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		rpass.newConsumer({m_runCtx.m_exposureRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		rpass.newProducer({m_runCtx.m_upscaleRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
 }
 
-void BloomUpscale::run(RenderingContext& ctx)
+void Bloom::runExposure(RenderPassWorkContext& rgraphCtx)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	cmdb->setViewport(0, 0, m_exposure.m_width, m_exposure.m_height);
+	cmdb->bindShaderProgram(m_exposure.m_grProg);
+	rgraphCtx.bindTexture(0, 0, m_r->getDownscaleBlur().getPassRt(MAX_U));
+
+	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
+	*uniforms = Vec4(m_exposure.m_threshold, m_exposure.m_scale, 0.0, 0.0);
 
-	cmdb->setViewport(0, 0, m_width, m_height);
-	cmdb->beginRenderPass(m_fb);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTexture(0, 0, m_r->getBloom().m_extractExposure.m_rt);
-	m_r->drawQuad(cmdb);
+	rgraphCtx.bindStorageBuffer(0, 0, m_r->getTonemapping().getAverageLuminanceBuffer());
 
-	m_r->getBloom().m_sslf.run(ctx);
-	cmdb->endRenderPass();
+	drawQuad(cmdb);
+}
+
+void Bloom::runUpscaleAndSslf(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	// Upscale
+	cmdb->setViewport(0, 0, m_upscale.m_width, m_upscale.m_height);
+	cmdb->bindShaderProgram(m_upscale.m_grProg);
+	rgraphCtx.bindTexture(0, 0, m_runCtx.m_exposureRt);
+	drawQuad(cmdb);
+
+	// SSLF
+	cmdb->bindShaderProgram(m_sslf.m_grProg);
+	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
+	cmdb->bindTexture(0, 1, m_sslf.m_lensDirtTex->getGrTexture(), TextureUsageBit::SAMPLED_FRAGMENT);
+	drawQuad(cmdb);
+
+	// Retore state
+	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
 }
 
 } // end namespace anki

+ 68 - 74
src/anki/renderer/Bloom.h

@@ -5,8 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
-#include <anki/renderer/ScreenSpaceLensFlare.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/Gr.h>
 #include <anki/resource/TextureResource.h>
 
@@ -18,106 +17,101 @@ namespace anki
 
 const PixelFormat BLOOM_RT_PIXEL_FORMAT(ComponentFormat::R8G8B8, TransformFormat::UNORM);
 
-class BloomExposure : public RenderingPass
+/// Bloom passes.
+class Bloom : public RendererObject
 {
 anki_internal:
-	U32 m_width = 0;
-	U32 m_height = 0;
-	TexturePtr m_rt;
+	Bloom(Renderer* r);
+
+	~Bloom();
 
-	BloomExposure(Renderer* r)
-		: RenderingPass(r)
+	ANKI_USE_RESULT Error init(const ConfigSet& cfg)
 	{
+		ANKI_R_LOGI("Initializing bloom passes");
+		Error err = initInternal(cfg);
+		if(err)
+		{
+			ANKI_R_LOGE("Failed to initialize bloom passes");
+		}
+		return err;
 	}
 
-	~BloomExposure();
-
-	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
-
-	void setPreRunBarriers(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
-	void run(RenderingContext& ctx);
-
-	void setPostRunBarriers(RenderingContext& ctx);
+	RenderTargetHandle getRt() const
+	{
+		return m_runCtx.m_upscaleRt;
+	}
 
 private:
-	FramebufferPtr m_fb;
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
 
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
+		F32 m_threshold = 10.0; ///< How bright it is
+		F32 m_scale = 1.0;
+		U32 m_width = 0;
+		U32 m_height = 0;
 
-	F32 m_threshold = 10.0; ///< How bright it is
-	F32 m_scale = 1.0;
-};
+		FramebufferDescription m_fbDescr;
+		RenderTargetDescription m_rtDescr;
+	} m_exposure;
 
-class BloomUpscale : public RenderingPass
-{
-anki_internal:
-	U32 m_width = 0;
-	U32 m_height = 0;
-	TexturePtr m_rt;
-
-	BloomUpscale(Renderer* r)
-		: RenderingPass(r)
+	class
 	{
-	}
-
-	~BloomUpscale();
-
-	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
 
-	void setPreRunBarriers(RenderingContext& ctx);
+		U32 m_width = 0;
+		U32 m_height = 0;
 
-	void run(RenderingContext& ctx);
+		RenderTargetDescription m_rtDescr;
+		FramebufferDescription m_fbDescr;
+	} m_upscale;
 
-	void setPostRunBarriers(RenderingContext& ctx);
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+		TextureResourcePtr m_lensDirtTex;
+	} m_sslf;
 
-private:
-	FramebufferPtr m_fb;
+	class
+	{
+	public:
+		RenderTargetHandle m_exposureRt;
+		RenderTargetHandle m_upscaleRt;
+	} m_runCtx;
 
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
+	ANKI_USE_RESULT Error initExposure(const ConfigSet& cfg);
+	ANKI_USE_RESULT Error initUpscale(const ConfigSet& cfg);
+	ANKI_USE_RESULT Error initSslf(const ConfigSet& cfg);
 
-/// Bloom passes.
-class Bloom : public RenderingPass
-{
-anki_internal:
-	BloomExposure m_extractExposure;
-	BloomUpscale m_upscale;
-	ScreenSpaceLensFlare m_sslf;
-
-	Bloom(Renderer* r)
-		: RenderingPass(r)
-		, m_extractExposure(r)
-		, m_upscale(r)
-		, m_sslf(r)
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg)
 	{
+		ANKI_CHECK(initExposure(cfg));
+		ANKI_CHECK(initUpscale(cfg));
+		ANKI_CHECK(initSslf(cfg));
+		return Error::NONE;
 	}
 
-	~Bloom()
+	static void runExposureCallback(RenderPassWorkContext& rgraphCtx)
 	{
+		scast<Bloom*>(rgraphCtx.m_userData)->runExposure(rgraphCtx);
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& cfg)
+	static void runUpscaleAndSslfCallback(RenderPassWorkContext& rgraphCtx)
 	{
-		ANKI_R_LOGI("Initializing bloom passes");
-		Error err = initInternal(cfg);
-		if(err)
-		{
-			ANKI_R_LOGE("Failed to initialize bloom passes");
-		}
-		return err;
+		scast<Bloom*>(rgraphCtx.m_userData)->runUpscaleAndSslf(rgraphCtx);
 	}
 
-private:
-	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg)
-	{
-		ANKI_CHECK(m_extractExposure.init(cfg));
-		ANKI_CHECK(m_upscale.init(cfg));
-		ANKI_CHECK(m_sslf.init(cfg));
-		return Error::NONE;
-	}
+	void runExposure(RenderPassWorkContext& rgraphCtx);
+	void runUpscaleAndSslf(RenderPassWorkContext& rgraphCtx);
 };
 
 /// @}

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

@@ -209,7 +209,7 @@ void Clusterer::prepare(ThreadPool& threadPool, const ClustererPrepareInfo& inf)
 	//
 	Array<UpdatePlanesPerspectiveCameraTask, ThreadPool::MAX_THREADS> jobs;
 
-	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	for(U i = 0; i < threadPool.getThreadCount(); i++)
 	{
 		jobs[i].m_clusterer = this;
 		jobs[i].m_frustumChanged = frustumChanged;

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

@@ -11,6 +11,6 @@ namespace anki
 const Array<PixelFormat, GBUFFER_COLOR_ATTACHMENT_COUNT> MS_COLOR_ATTACHMENT_PIXEL_FORMATS = {
 	{PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM),
 		PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM),
-		PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM)}};
+		PixelFormat(ComponentFormat::R10G10B10A2, TransformFormat::UNORM)}};
 
 } // end namespace anki

+ 3 - 1
src/anki/renderer/Common.h

@@ -25,7 +25,6 @@ class LightShading;
 class ForwardShading;
 class LensFlare;
 class Ssao;
-class ScreenSpaceLensFlare;
 class Tonemapping;
 class Bloom;
 class FinalComposite;
@@ -51,6 +50,9 @@ class ShaderProgramResourceVariant;
 /// @addtogroup renderer
 /// @{
 
+/// Don't create second level command buffers if they contain more drawcalls than this constant.
+const U MIN_DRAWCALLS_PER_2ND_LEVEL_COMMAND_BUFFER = 16;
+
 /// FS size is rendererSize/FS_FRACTION.
 const U FS_FRACTION = 2;
 

+ 49 - 177
src/anki/renderer/Dbg.cpp

@@ -22,7 +22,7 @@ namespace anki
 {
 
 Dbg::Dbg(Renderer* r)
-	: RenderingPass(r)
+	: RendererObject(r)
 {
 }
 
@@ -37,7 +37,6 @@ Dbg::~Dbg()
 Error Dbg::init(const ConfigSet& initializer)
 {
 	m_enabled = initializer.getNumber("r.dbg.enabled");
-	m_flags.set(DbgFlag::ALL);
 	return Error::NONE;
 }
 
@@ -45,24 +44,21 @@ Error Dbg::lazyInit()
 {
 	ANKI_ASSERT(!m_initialized);
 
-	// RT
-	m_rt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
+	// RT descr
+	m_rtDescr = m_r->create2DRenderTargetDescription(m_r->getWidth(),
 		m_r->getHeight(),
 		DBG_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		SamplingFilter::LINEAR,
-		1));
+		"Dbg");
+	m_rtDescr.bake();
 
-	// Create FB
-	FramebufferInitInfo fbInit;
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
-	fbInit.m_depthStencilAttachment.m_texture = m_r->getGBuffer().m_depthRt;
-	fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::LOAD;
-	fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
-
-	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+	// Create FB descr
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
+	m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::LOAD;
+	m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+	m_fbDescr.bake();
 
 	m_drawer = getAllocator().newInstance<DebugDrawer>();
 	ANKI_CHECK(m_drawer->init(m_r));
@@ -70,18 +66,37 @@ Error Dbg::lazyInit()
 	return Error::NONE;
 }
 
-Error Dbg::run(RenderingContext& ctx)
+Bool Dbg::getDepthTestEnabled() const
+{
+	return m_drawer->getDepthTestEnabled();
+}
+
+void Dbg::setDepthTestEnabled(Bool enable)
+{
+	m_drawer->setDepthTestEnabled(enable);
+}
+
+void Dbg::switchDepthTestEnabled()
+{
+	Bool enabled = m_drawer->getDepthTestEnabled();
+	m_drawer->setDepthTestEnabled(!enabled);
+}
+
+void Dbg::run(RenderPassWorkContext& rgraphCtx, const RenderingContext& ctx)
 {
 	ANKI_ASSERT(m_enabled);
 
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
 	if(!m_initialized)
 	{
-		ANKI_CHECK(lazyInit());
+		if(lazyInit())
+		{
+			return;
+		}
 		m_initialized = true;
 	}
 
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-	cmdb->beginRenderPass(m_fb);
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
 	m_drawer->prepareFrame(cmdb);
@@ -112,170 +127,27 @@ Error Dbg::run(RenderingContext& ctx)
 		sceneDrawer.draw(plight);
 	}
 
-#if 0
-	{
-		m_drawer->setViewProjectionMatrix(camFrc.getViewProjectionMatrix());
-		m_drawer->setModelMatrix(Mat4::getIdentity());
-		CollisionDebugDrawer cd(m_drawer);
-		Mat4 proj = camFrc.getProjectionMatrix();
-
-		m_drawer->setViewProjectionMatrix(camFrc.getViewProjectionMatrix());
-
-		Sphere s(Vec4(1.2, 2.0, -1.1, 0.0), 2.1);
-
-		s.accept(cd);
-
-		Transform trf = scene.findSceneNode("light0").getComponent<MoveComponent>().getWorldTransform();
-		Vec4 rayOrigin = trf.getOrigin();
-		Vec3 rayDir = -trf.getRotation().getZAxis().getNormalized();
-		m_drawer->setModelMatrix(Mat4::getIdentity());
-		m_drawer->drawLine(rayOrigin.xyz(), rayOrigin.xyz() + rayDir.xyz() * 10.0, Vec4(1.0, 1.0, 1.0, 1.0));
-
-		Array<Vec4, 2> intersectionPoints;
-		U intersectionPointCount;
-		s.intersectsRay(rayDir.xyz0(), rayOrigin, intersectionPoints, intersectionPointCount);
-		for(U i = 0; i < intersectionPointCount; ++i)
-		{
-			m_drawer->drawLine(Vec3(0.0), intersectionPoints[i].xyz(), Vec4(0.0, 1.0, 0.0, 1.0));
-		}
-	}
-#endif
-
-#if 0
-	{
-		Clusterer c;
-		c.init(getAllocator(), 16, 12, 30);
-
-		const FrustumComponent& frc = scene.findSceneNode("cam0").getComponent<FrustumComponent>();
-		const MoveComponent& movc = scene.findSceneNode("cam0").getComponent<MoveComponent>();
-
-		ClustererPrepareInfo pinf;
-		pinf.m_viewMat = frc.getViewMatrix();
-		pinf.m_projMat = frc.getProjectionMatrix();
-		pinf.m_camTrf = frc.getFrustum().getTransform();
-		c.prepare(m_r->getThreadPool(), pinf);
-
-		class DD : public ClustererDebugDrawer
-		{
-		public:
-			DebugDrawer* m_d;
-
-			void operator()(const Vec3& lineA, const Vec3& lineB, const Vec3& color)
-			{
-				m_d->drawLine(lineA, lineB, color.xyz1());
-			}
-		};
-
-		DD dd;
-		dd.m_d = m_drawer;
-
-		CollisionDebugDrawer cd(m_drawer);
-
-		Sphere s(Vec4(1.0, 0.1, -1.2, 0.0), 1.2);
-		PerspectiveFrustum fr(toRad(25.), toRad(35.), 0.1, 5.);
-		fr.transform(Transform(Vec4(0., 1., 0., 0.), Mat3x4::getIdentity(), 1.0));
-
-		m_drawer->setModelMatrix(Mat4(movc.getWorldTransform()));
-		// c.debugDraw(dd);
-
-		if(frc.getFrustum().insideFrustum(fr))
-		{
-			ClustererTestResult rez;
-			c.initTestResults(getAllocator(), rez);
-			Aabb sbox;
-			fr.computeAabb(sbox);
-			c.binPerspectiveFrustum(fr, sbox, rez);
-			//c.bin(s, sbox, rez);
-
-			c.debugDrawResult(rez, dd);
-		}
-
-		m_drawer->setColor(Vec4(1.0, 1.0, 0.0, 1.0));
-		frc.getFrustum().accept(cd);
-		fr.accept(cd);
-	}
-#endif
-
-#if 0
-	{
-		CollisionDebugDrawer cd(m_drawer);
-
-		Array<Vec3, 4> poly;
-		poly[0] = Vec3(0.0, 0.0, 0.0);
-		poly[1] = Vec3(2.5, 0.0, 0.0);
-
-		Mat4 trf(Vec4(147.392776, -12.132728, 16.607138, 1.0),
-			Mat3(Euler(toRad(45.0), toRad(0.0), toRad(120.0))),
-			1.0);
-
-		Array<Vec3, 4> polyw;
-		polyw[0] = trf.transform(poly[0]);
-		polyw[1] = trf.transform(poly[1]);
-
-		m_drawer->setModelMatrix(Mat4::getIdentity());
-		m_drawer->drawLine(polyw[0], polyw[1], Vec4(1.0));
-
-		Vec4 p0 = camFrc.getViewMatrix() * polyw[0].xyz1();
-		p0.w() = 0.0;
-		Vec4 p1 = camFrc.getViewMatrix() * polyw[1].xyz1();
-		p1.w() = 0.0;
-
-		Vec4 r = p1 - p0;
-		r.normalize();
-
-		Vec4 a = camFrc.getProjectionMatrix() * p0.xyz1();
-		a /= a.w();
-
-		Vec4 i;
-		if(r.z() > 0)
-		{
-			// Plane near(Vec4(0, 0, -1, 0), camFrc.getFrustum().getNear() +
-			// 0.001);
-			// Bool in = near.intersectRay(p0, r * 100000.0, i);
-			i.z() = -camFrc.getFrustum().getNear();
-			F32 t = (i.z() - p0.z()) / r.z();
-			i.x() = p0.x() + t * r.x();
-			i.y() = p0.y() + t * r.y();
-
-			i = camFrc.getProjectionMatrix() * i.xyz1();
-			i /= i.w();
-		}
-		else
-		{
-			i = camFrc.getProjectionMatrix() * (r * 100000.0).xyz1();
-			i /= i.w();
-		}
-
-		/*r *= 0.01;
-		Vec4 b = polyw[0].xyz0() + r;
-		b = camFrc.getViewProjectionMatrix() * b.xyz1();
-		Vec4 d = b / b.w();*/
-
-		m_drawer->setViewProjectionMatrix(Mat4::getIdentity());
-		m_drawer->drawLine(
-			Vec3(a.xy(), 0.1), Vec3(i.xy(), 0.1), Vec4(1.0, 0, 0, 1));
-	}
-#endif
-
 	m_drawer->finishFrame();
-	cmdb->endRenderPass();
-	return Error::NONE;
 }
 
-Bool Dbg::getDepthTestEnabled() const
+void Dbg::populateRenderGraph(RenderingContext& ctx)
 {
-	return m_drawer->getDepthTestEnabled();
-}
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
-void Dbg::setDepthTestEnabled(Bool enable)
-{
-	m_drawer->setDepthTestEnabled(enable);
-}
+	// Create RT
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
 
-void Dbg::switchDepthTestEnabled()
-{
-	Bool enabled = m_drawer->getDepthTestEnabled();
-	m_drawer->setDepthTestEnabled(!enabled);
+	// Create pass
+	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("DBG");
+
+	pass.setWork(runCallback, this, 0);
+	pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rt}}, m_r->getGBuffer().getDepthRt());
+
+	pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	pass.newConsumer({m_r->getGBuffer().getDepthRt(),
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ});
+	pass.newProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 }
 
 } // end namespace anki

+ 27 - 40
src/anki/renderer/Dbg.h

@@ -5,9 +5,8 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/Gr.h>
-#include <anki/util/BitMask.h>
 #include <anki/util/Enum.h>
 
 namespace anki
@@ -16,21 +15,8 @@ namespace anki
 /// @addtogroup renderer
 /// @{
 
-/// Dbg flags. Define them first so they can be parameter to the bitset
-enum class DbgFlag : U16
-{
-	NONE = 0,
-	SPATIAL_COMPONENT = 1 << 0,
-	FRUSTUM_COMPONENT = 1 << 1,
-	SECTOR_COMPONENT = 1 << 2,
-	DECAL_COMPONENT = 1 << 3,
-	PHYSICS = 1 << 4,
-	ALL = SPATIAL_COMPONENT | FRUSTUM_COMPONENT | SECTOR_COMPONENT | DECAL_COMPONENT | PHYSICS
-};
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(DbgFlag, inline)
-
 /// Debugging stage
-class Dbg : public RenderingPass
+class Dbg : public RendererObject
 {
 public:
 	Bool getEnabled() const
@@ -47,26 +33,6 @@ public:
 	void setDepthTestEnabled(Bool enable);
 	void switchDepthTestEnabled();
 
-	void setFlags(DbgFlag flags)
-	{
-		m_flags.set(flags);
-	}
-
-	void unsetFlags(DbgFlag flags)
-	{
-		m_flags.unset(flags);
-	}
-
-	void flipFlags(DbgFlag flags)
-	{
-		m_flags.flip(flags);
-	}
-
-	TexturePtr getRt() const
-	{
-		return m_rt;
-	}
-
 anki_internal:
 	Dbg(Renderer* r);
 
@@ -74,17 +40,38 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
 
-	ANKI_USE_RESULT Error run(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
+
+	RenderTargetHandle getRt() const
+	{
+		return m_runCtx.m_rt;
+	}
 
 private:
 	Bool8 m_enabled = false;
 	Bool8 m_initialized = false; ///< Lazily initialize.
-	TexturePtr m_rt;
-	FramebufferPtr m_fb;
+	RenderTargetDescription m_rtDescr;
+	FramebufferDescription m_fbDescr;
 	DebugDrawer* m_drawer = nullptr;
-	BitMask<DbgFlag> m_flags;
+
+	class
+	{
+	public:
+		RenderTargetHandle m_rt;
+		RenderingContext* m_ctx = nullptr;
+	} m_runCtx;
 
 	ANKI_USE_RESULT Error lazyInit();
+
+	// A RenderPassWorkCallback for debug pass.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		Dbg* self = static_cast<Dbg*>(rgraphCtx.m_userData);
+		self->run(rgraphCtx, *self->m_runCtx.m_ctx);
+	}
+
+	void run(RenderPassWorkContext& rgraphCtx, const RenderingContext& ctx);
 };
 /// @}
 

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

@@ -39,8 +39,8 @@ Error DebugDrawer::init(Renderer* r)
 	// Create the vert buffs
 	for(BufferPtr& v : m_vertBuff)
 	{
-		v = gr.newInstance<Buffer>(
-			sizeof(Vertex) * MAX_VERTS_PER_FRAME, BufferUsageBit::VERTEX, BufferMapAccessBit::WRITE);
+		v = gr.newInstance<Buffer>(BufferInitInfo(
+			sizeof(Vertex) * MAX_VERTS_PER_FRAME, BufferUsageBit::VERTEX, BufferMapAccessBit::WRITE, "DbgDrawer"));
 	}
 
 	m_mMat.setIdentity();

+ 97 - 114
src/anki/renderer/DepthDownscale.cpp

@@ -10,186 +10,169 @@
 namespace anki
 {
 
-HalfDepth::~HalfDepth()
+DepthDownscale::~DepthDownscale()
 {
 }
 
-Error HalfDepth::init(const ConfigSet&)
+Error DepthDownscale::initHalf(const ConfigSet&)
 {
-	GrManager& gr = getGrManager();
 	U width = m_r->getWidth() / 2;
 	U height = m_r->getHeight() / 2;
 
-	// Create RTs
-	m_depthRt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(width,
+	// Create RT descrs
+	m_half.m_depthRtDescr = m_r->create2DRenderTargetDescription(width,
 		height,
 		GBUFFER_DEPTH_ATTACHMENT_PIXEL_FORMAT,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		SamplingFilter::LINEAR,
-		1,
-		"halfdepth"));
+		"Half depth");
+	m_half.m_depthRtDescr.bake();
 
-	m_colorRt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(width,
+	m_half.m_colorRtDescr = m_r->create2DRenderTargetDescription(width,
 		height,
 		PixelFormat(ComponentFormat::R32, TransformFormat::FLOAT),
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::SAMPLED_FRAGMENT,
 		SamplingFilter::LINEAR,
-		1,
-		"halfdepthcol"));
-
-	// Create FB
-	FramebufferInitInfo fbInit("halfdepth");
-	fbInit.m_colorAttachments[0].m_texture = m_colorRt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_depthStencilAttachment.m_texture = m_depthRt;
-	fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
-	m_fb = gr.newInstance<Framebuffer>(fbInit);
+		"Half depth col");
+	m_half.m_colorRtDescr.bake();
+
+	// Create FB descr
+	m_half.m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_half.m_fbDescr.m_colorAttachmentCount = 1;
+	m_half.m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_half.m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+	m_half.m_fbDescr.bake();
 
 	// Prog
-	ANKI_CHECK(getResourceManager().loadResource("programs/DepthDownscale.ankiprog", m_prog));
+	ANKI_CHECK(getResourceManager().loadResource("programs/DepthDownscale.ankiprog", m_half.m_prog));
 
-	ShaderProgramResourceMutationInitList<2> mutations(m_prog);
+	ShaderProgramResourceMutationInitList<2> mutations(m_half.m_prog);
 	mutations.add("TYPE", 0).add("SAMPLE_RESOLVE_TYPE", 0);
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutations.get(), variant);
-	m_grProg = variant->getProgram();
+	m_half.m_prog->getOrCreateVariant(mutations.get(), variant);
+	m_half.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void HalfDepth::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_depthRt,
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_colorRt,
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void HalfDepth::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_colorRt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void HalfDepth::run(RenderingContext& ctx)
+Error DepthDownscale::initQuarter(const ConfigSet&)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->beginRenderPass(m_fb);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTexture(0, 0, m_r->getGBuffer().m_depthRt);
-
-	cmdb->setViewport(0, 0, m_r->getWidth() / 2, m_r->getHeight() / 2);
-	cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
-
-	m_r->drawQuad(cmdb);
-
-	cmdb->endRenderPass();
-
-	// Restore state
-	cmdb->setDepthCompareOperation(CompareOperation::LESS);
-}
-
-QuarterDepth::~QuarterDepth()
-{
-}
-
-Error QuarterDepth::init(const ConfigSet&)
-{
-	GrManager& gr = getGrManager();
 	U width = m_r->getWidth() / 4;
 	U height = m_r->getHeight() / 4;
 
-	m_colorRt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(width,
+	// RT descr
+	m_quarter.m_colorRtDescr = m_r->create2DRenderTargetDescription(width,
 		height,
 		PixelFormat(ComponentFormat::R32, TransformFormat::FLOAT),
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::SAMPLED_FRAGMENT
+			| TextureUsageBit::SAMPLED_COMPUTE,
 		SamplingFilter::LINEAR,
-		1,
-		"quarterdepth"));
+		"quarterdepth");
+	m_quarter.m_colorRtDescr.bake();
 
-	FramebufferInitInfo fbInit("quarterdepth");
-	fbInit.m_colorAttachments[0].m_texture = m_colorRt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	fbInit.m_colorAttachmentCount = 1;
-	m_fb = gr.newInstance<Framebuffer>(fbInit);
+	// FB descr
+	m_quarter.m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_quarter.m_fbDescr.m_colorAttachmentCount = 1;
+	m_quarter.m_fbDescr.bake();
 
 	// Prog
-	ANKI_CHECK(getResourceManager().loadResource("programs/DepthDownscale.ankiprog", m_prog));
+	ANKI_CHECK(getResourceManager().loadResource("programs/DepthDownscale.ankiprog", m_quarter.m_prog));
 
-	ShaderProgramResourceMutationInitList<2> mutations(m_prog);
+	ShaderProgramResourceMutationInitList<2> mutations(m_quarter.m_prog);
 	mutations.add("TYPE", 1).add("SAMPLE_RESOLVE_TYPE", 0);
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutations.get(), variant);
-	m_grProg = variant->getProgram();
+	m_quarter.m_prog->getOrCreateVariant(mutations.get(), variant);
+	m_quarter.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void QuarterDepth::setPreRunBarriers(RenderingContext& ctx)
+Error DepthDownscale::initInternal(const ConfigSet& cfg)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_colorRt,
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
+	ANKI_CHECK(initHalf(cfg));
+	ANKI_CHECK(initQuarter(cfg));
+	return Error::NONE;
 }
 
-void QuarterDepth::setPostRunBarriers(RenderingContext& ctx)
+Error DepthDownscale::init(const ConfigSet& cfg)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_colorRt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
+	ANKI_R_LOGI("Initializing depth downscale passes");
+
+	Error err = initInternal(cfg);
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize depth downscale passes");
+	}
+
+	return err;
 }
 
-void QuarterDepth::run(RenderingContext& ctx)
+void DepthDownscale::runHalf(RenderPassWorkContext& rgraphCtx)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
-	cmdb->beginRenderPass(m_fb);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTexture(0, 0, m_parent->m_hd.m_colorRt);
+	cmdb->bindShaderProgram(m_half.m_grProg);
+	rgraphCtx.bindTexture(0, 0, m_r->getGBuffer().getDepthRt());
 
-	cmdb->setViewport(0, 0, m_r->getWidth() / 4, m_r->getHeight() / 4);
+	cmdb->setViewport(0, 0, m_r->getWidth() / 2, m_r->getHeight() / 2);
+	cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
 
-	m_r->drawQuad(cmdb);
+	drawQuad(cmdb);
 
-	cmdb->endRenderPass();
+	// Restore state
+	cmdb->setDepthCompareOperation(CompareOperation::LESS);
 }
 
-DepthDownscale::~DepthDownscale()
+void DepthDownscale::runQuarter(RenderPassWorkContext& rgraphCtx)
 {
-}
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
-Error DepthDownscale::initInternal(const ConfigSet& cfg)
-{
-	ANKI_CHECK(m_hd.init(cfg));
-	ANKI_CHECK(m_qd.init(cfg));
-	return Error::NONE;
+	cmdb->bindShaderProgram(m_quarter.m_grProg);
+	rgraphCtx.bindTexture(0, 0, m_runCtx.m_halfColorRt);
+	cmdb->setViewport(0, 0, m_r->getWidth() / 4, m_r->getHeight() / 4);
+
+	drawQuad(cmdb);
 }
 
-Error DepthDownscale::init(const ConfigSet& cfg)
+void DepthDownscale::populateRenderGraph(RenderingContext& ctx)
 {
-	ANKI_R_LOGI("Initializing depth downscale passes");
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
-	Error err = initInternal(cfg);
-	if(err)
+	// Create render targets
+	m_runCtx.m_halfDepthRt = rgraph.newRenderTarget(m_half.m_depthRtDescr);
+	m_runCtx.m_halfColorRt = rgraph.newRenderTarget(m_half.m_colorRtDescr);
+	m_runCtx.m_quarterRt = rgraph.newRenderTarget(m_quarter.m_colorRtDescr);
+
+	// Create half depth render pass
 	{
-		ANKI_R_LOGE("Failed to initialize depth downscale passes");
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Half depth");
+
+		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});
+		pass.newConsumer({m_runCtx.m_halfColorRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer(
+			{m_runCtx.m_halfDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, DepthStencilAspectBit::DEPTH});
+		pass.newProducer({m_runCtx.m_halfColorRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer(
+			{m_runCtx.m_halfDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, DepthStencilAspectBit::DEPTH});
 	}
 
-	return err;
+	// Create quarter depth render pass
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Quarter depth");
+
+		pass.setFramebufferInfo(m_quarter.m_fbDescr, {{m_runCtx.m_quarterRt}}, {});
+		pass.setWork(runQuarterCallback, this, 0);
+
+		pass.newConsumer({m_runCtx.m_halfColorRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({m_runCtx.m_quarterRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({m_runCtx.m_quarterRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
 }
 
 } // end namespace anki

+ 59 - 56
src/anki/renderer/DepthDownscale.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/Gr.h>
 #include <anki/resource/TextureResource.h>
 
@@ -18,85 +18,88 @@ class DepthDownscale;
 /// @addtogroup renderer
 /// @{
 
-/// Quick pass to downscale the depth buffer.
-class HalfDepth : public RenderingPass
+/// Downscales the depth buffer a few times.
+class DepthDownscale : public RendererObject
 {
 anki_internal:
-	TexturePtr m_depthRt;
-	TexturePtr m_colorRt;
-
-	HalfDepth(Renderer* r, DepthDownscale* depthDownscale)
-		: RenderingPass(r)
-		, m_parent(depthDownscale)
+	DepthDownscale(Renderer* r)
+		: RendererObject(r)
 	{
 	}
 
-	~HalfDepth();
+	~DepthDownscale();
 
 	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
-
-private:
-	DepthDownscale* m_parent;
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
-	FramebufferPtr m_fb;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
-
-/// Quick pass to downscale the depth buffer.
-class QuarterDepth : public RenderingPass
-{
-anki_internal:
-	TexturePtr m_colorRt;
+	RenderTargetHandle getHalfDepthColorRt() const
+	{
+		return m_runCtx.m_halfColorRt;
+	}
 
-	QuarterDepth(Renderer* r, DepthDownscale* depthDownscale)
-		: RenderingPass(r)
-		, m_parent(depthDownscale)
+	RenderTargetHandle getHalfDepthDepthRt() const
 	{
+		return m_runCtx.m_halfDepthRt;
 	}
 
-	~QuarterDepth();
+	RenderTargetHandle getQuarterColorRt() const
+	{
+		return m_runCtx.m_quarterRt;
+	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
+private:
+	class
+	{
+	public:
+		RenderTargetDescription m_depthRtDescr;
+		RenderTargetDescription m_colorRtDescr;
 
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
+		FramebufferDescription m_fbDescr;
 
-private:
-	DepthDownscale* m_parent;
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_half; ///< Half depth pass.
 
-	FramebufferPtr m_fb;
+	class
+	{
+	public:
+		RenderTargetDescription m_colorRtDescr;
 
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
+		FramebufferDescription m_fbDescr;
 
-class DepthDownscale : public RenderingPass
-{
-anki_internal:
-	HalfDepth m_hd;
-	QuarterDepth m_qd;
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_quarter; ///< Quarter depth pass.
 
-	DepthDownscale(Renderer* r)
-		: RenderingPass(r)
-		, m_hd(r, this)
-		, m_qd(r, this)
+	class
 	{
-	}
+	public:
+		RenderTargetHandle m_halfDepthRt;
+		RenderTargetHandle m_halfColorRt;
+		RenderTargetHandle m_quarterRt;
+	} m_runCtx; ///< Run context.
 
-	~DepthDownscale();
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
+	ANKI_USE_RESULT Error initHalf(const ConfigSet& cfg);
+	ANKI_USE_RESULT Error initQuarter(const ConfigSet& cfg);
 
-	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
+	void runHalf(RenderPassWorkContext& rgraphCtx);
+	void runQuarter(RenderPassWorkContext& rgraphCtx);
 
-private:
-	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
+	/// A RenderPassWorkCallback for half depth main pass.
+	static void runHalfCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		scast<DepthDownscale*>(rgraphCtx.m_userData)->runHalf(rgraphCtx);
+	}
+
+	/// A RenderPassWorkCallback for half depth main pass.
+	static void runQuarterCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		scast<DepthDownscale*>(rgraphCtx.m_userData)->runQuarter(rgraphCtx);
+	}
 };
 /// @}
 
-} // end namespace
+} // end namespace anki

+ 68 - 44
src/anki/renderer/DownscaleBlur.cpp

@@ -13,6 +13,7 @@ namespace anki
 DownscaleBlur::~DownscaleBlur()
 {
 	m_passes.destroy(getAllocator());
+	m_runCtx.m_rts.destroy(getAllocator());
 }
 
 Error DownscaleBlur::initSubpass(U idx, const UVec2& inputTexSize)
@@ -23,21 +24,16 @@ Error DownscaleBlur::initSubpass(U idx, const UVec2& inputTexSize)
 	pass.m_height = inputTexSize.y() / 2;
 
 	// RT
-	pass.m_rt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(pass.m_width,
+	StringAuto name(getAllocator());
+	name.sprintf("DownBlur #%u", idx);
+	pass.m_rtDescr = m_r->create2DRenderTargetDescription(pass.m_width,
 		pass.m_height,
 		LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
 			| TextureUsageBit::SAMPLED_COMPUTE,
 		SamplingFilter::LINEAR,
-		1,
-		"downblur"));
-
-	// FB
-	FramebufferInitInfo fbInit("downblur");
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = pass.m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	pass.m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+		name.toCString());
+	pass.m_rtDescr.bake();
 
 	return Error::NONE;
 }
@@ -67,6 +63,14 @@ Error DownscaleBlur::initInternal(const ConfigSet&)
 		size /= 2;
 	}
 
+	m_runCtx.m_rts.create(getAllocator(), passCount);
+
+	// FB descr
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_fbDescr.bake();
+
+	// Shader programs
 	ANKI_CHECK(getResourceManager().loadResource("programs/DownscaleBlur.ankiprog", m_prog));
 	const ShaderProgramResourceVariant* variant;
 	m_prog->getOrCreateVariant(variant);
@@ -75,54 +79,74 @@ Error DownscaleBlur::initInternal(const ConfigSet&)
 	return Error::NONE;
 }
 
-void DownscaleBlur::setPreRunBarriers(RenderingContext& ctx)
+void DownscaleBlur::run(RenderPassWorkContext& rgraphCtx)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_passes[0].m_rt,
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	const U passIdx = m_runCtx.m_crntPassIdx++;
+
+	if(passIdx > 0)
+	{
+		// Bind the previous pass' Rt
+
+		rgraphCtx.bindTexture(0, 0, m_runCtx.m_rts[passIdx - 1]);
+	}
+	else
+	{
+		rgraphCtx.bindTexture(0, 0, m_r->getTemporalAA().getRt());
+	}
+
+	const Subpass& pass = m_passes[passIdx];
+	cmdb->setViewport(0, 0, pass.m_width, pass.m_height);
+	cmdb->bindShaderProgram(m_grProg);
+	drawQuad(cmdb);
 }
 
-void DownscaleBlur::run(RenderingContext& ctx)
+void DownscaleBlur::populateRenderGraph(RenderingContext& ctx)
 {
-	CommandBufferPtr cmdb = ctx.m_commandBuffer;
-
-	cmdb->bindTexture(0, 0, m_r->getTemporalAA().getRt());
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+	m_runCtx.m_crntPassIdx = 0;
 
+	// Create RTs
 	for(U i = 0; i < m_passes.getSize(); ++i)
 	{
-		Subpass& pass = m_passes[i];
+		m_runCtx.m_rts[i] = rgraph.newRenderTarget(m_passes[i].m_rtDescr);
+	}
 
-		if(i > 0u)
-		{
-			cmdb->setTextureSurfaceBarrier(m_passes[i - 1].m_rt,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-				TextureUsageBit::SAMPLED_FRAGMENT,
-				TextureSurfaceInfo(0, 0, 0, 0));
+	// Create passes
+	Array<CString, 8> passNames = {{"DownBlur #0",
+		"Down/Blur #1",
+		"Down/Blur #2",
+		"Down/Blur #3",
+		"Down/Blur #4",
+		"Down/Blur #5",
+		"Down/Blur #6",
+		"Down/Blur #7"}};
+	for(U i = 0; i < m_passes.getSize(); ++i)
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass(passNames[i]);
 
-			cmdb->setTextureSurfaceBarrier(pass.m_rt,
-				TextureUsageBit::NONE,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-				TextureSurfaceInfo(0, 0, 0, 0));
+		pass.setWork(runCallback, this, 0);
 
-			cmdb->bindTexture(0, 0, m_passes[i - 1].m_rt);
+		RenderTargetHandle renderRt, sampleRt;
+		if(i > 0)
+		{
+			renderRt = m_runCtx.m_rts[i];
+			sampleRt = m_runCtx.m_rts[i - 1];
+		}
+		else
+		{
+			renderRt = m_runCtx.m_rts[0];
+			sampleRt = m_r->getTemporalAA().getRt();
 		}
 
-		cmdb->setViewport(0, 0, pass.m_width, pass.m_height);
-		cmdb->bindShaderProgram(m_grProg);
+		pass.setFramebufferInfo(m_fbDescr, {{renderRt}}, {});
 
-		cmdb->beginRenderPass(pass.m_fb);
-		m_r->drawQuad(cmdb);
-		cmdb->endRenderPass();
-	}
-}
+		pass.newConsumer({renderRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({sampleRt, TextureUsageBit::SAMPLED_FRAGMENT});
 
-void DownscaleBlur::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_passes.getBack().m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_COMPUTE | TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
+		pass.newProducer({renderRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
 }
 
 } // end namespace anki

+ 26 - 11
src/anki/renderer/DownscaleBlur.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 
 namespace anki
 {
@@ -14,11 +14,11 @@ namespace anki
 /// @{
 
 /// Downsample the IS and blur it at the same time.
-class DownscaleBlur : public RenderingPass
+class DownscaleBlur : public RendererObject
 {
 anki_internal:
 	DownscaleBlur(Renderer* r)
-		: RenderingPass(r)
+		: RendererObject(r)
 	{
 	}
 
@@ -26,9 +26,8 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
 	U getPassWidth(U pass) const
 	{
@@ -40,27 +39,43 @@ anki_internal:
 		return m_passes[min<U>(pass, m_passes.getSize() - 1)].m_height;
 	}
 
-	TexturePtr getPassTexture(U pass) const
+	RenderTargetHandle getPassRt(U pass) const
 	{
-		return m_passes[min<U>(pass, m_passes.getSize() - 1)].m_rt;
+		return m_runCtx.m_rts[min<U>(pass, m_runCtx.m_rts.getSize() - 1)];
 	}
 
 private:
 	class Subpass
 	{
 	public:
-		TexturePtr m_rt;
-		FramebufferPtr m_fb;
+		RenderTargetDescription m_rtDescr;
 		U32 m_width, m_height;
 	};
 
+	DynamicArray<Subpass> m_passes;
+
+	FramebufferDescription m_fbDescr;
+
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
 
-	DynamicArray<Subpass> m_passes;
+	class
+	{
+	public:
+		DynamicArray<RenderTargetHandle> m_rts;
+		U32 m_crntPassIdx = MAX_U32;
+	} m_runCtx;
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initSubpass(U idx, const UVec2& inputTexSize);
+
+	void run(RenderPassWorkContext& rgraphCtx);
+
+	/// A RenderPassWorkCallback for the downscall passes.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		scast<DownscaleBlur*>(rgraphCtx.m_userData)->run(rgraphCtx);
+	}
 };
 /// @}
 

+ 63 - 46
src/anki/renderer/FinalComposite.cpp

@@ -7,7 +7,6 @@
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Bloom.h>
 #include <anki/renderer/TemporalAA.h>
-#include <anki/renderer/ScreenSpaceLensFlare.h>
 #include <anki/renderer/Tonemapping.h>
 #include <anki/renderer/LightShading.h>
 #include <anki/renderer/GBuffer.h>
@@ -23,7 +22,7 @@ namespace anki
 const PixelFormat FinalComposite::RT_PIXEL_FORMAT(ComponentFormat::R8G8B8, TransformFormat::UNORM);
 
 FinalComposite::FinalComposite(Renderer* r)
-	: RenderingPass(r)
+	: RendererObject(r)
 {
 }
 
@@ -40,19 +39,22 @@ Error FinalComposite::initInternal(const ConfigSet& config)
 
 	if(!m_r->getDrawToDefaultFramebuffer())
 	{
-		m_rt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
+		m_rtDescr = m_r->create2DRenderTargetDescription(m_r->getWidth(),
 			m_r->getHeight(),
 			RT_PIXEL_FORMAT,
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::SAMPLED_FRAGMENT,
 			SamplingFilter::LINEAR,
-			1,
-			"pps"));
-
-		FramebufferInitInfo fbInit("pps");
-		fbInit.m_colorAttachmentCount = 1;
-		fbInit.m_colorAttachments[0].m_texture = m_rt;
-		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-		m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+			"Final Composite");
+		m_rtDescr.bake();
+
+		m_fbDescr.m_colorAttachmentCount = 1;
+		m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+		m_fbDescr.bake();
+	}
+	else
+	{
+		m_fbDescr.setDefaultFramebuffer();
+		m_fbDescr.bake();
 	}
 
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_blueNoise));
@@ -102,47 +104,33 @@ Error FinalComposite::loadColorGradingTexture(CString filename)
 	return Error::NONE;
 }
 
-Error FinalComposite::run(RenderingContext& ctx)
+void FinalComposite::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
-	// Get the drawing parameters
-	const Bool drawToDefaultFb = ctx.m_outFb.isCreated();
-	const Bool dbgEnabled = m_r->getDbg().getEnabled();
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
-	CommandBufferPtr cmdb;
-	if(drawToDefaultFb)
-	{
-		cmdb = ctx.m_defaultFbCommandBuffer;
-	}
-	else
-	{
-		cmdb = ctx.m_commandBuffer;
-	}
+	const Bool dbgEnabled = m_r->getDbg().getEnabled();
+	const Bool drawToDefaultFb = m_r->getDrawToDefaultFramebuffer();
 
 	// Bind stuff
-	cmdb->informTextureCurrentUsage(m_r->getTemporalAA().getRt(), TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->informTextureCurrentUsage(m_r->getBloom().m_upscale.m_rt, TextureUsageBit::SAMPLED_FRAGMENT);
-
-	cmdb->bindTextureAndSampler(
+	rgraphCtx.bindTextureAndSampler(
 		0, 0, m_r->getTemporalAA().getRt(), (drawToDefaultFb) ? m_r->getNearestSampler() : m_r->getLinearSampler());
-	cmdb->bindTexture(0, 1, m_r->getBloom().m_upscale.m_rt);
-	cmdb->bindTexture(0, 2, m_lut->getGrTexture());
-	cmdb->bindTexture(0, 3, m_blueNoise->getGrTexture());
+
+	rgraphCtx.bindTexture(0, 1, m_r->getBloom().getRt());
+	cmdb->bindTexture(0, 2, m_lut->getGrTexture(), TextureUsageBit::SAMPLED_FRAGMENT);
+	cmdb->bindTexture(0, 3, m_blueNoise->getGrTexture(), TextureUsageBit::SAMPLED_FRAGMENT);
 	if(dbgEnabled)
 	{
-		cmdb->bindTexture(0, 5, m_r->getDbg().getRt());
+		rgraphCtx.bindTexture(0, 5, m_r->getDbg().getRt());
 	}
 
-	cmdb->bindStorageBuffer(0, 0, m_r->getTonemapping().m_luminanceBuff, 0, MAX_PTR_SIZE);
+	rgraphCtx.bindUniformBuffer(0, 1, m_r->getTonemapping().getAverageLuminanceBuffer());
 
 	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	uniforms->x() = F32(m_r->getFrameCount() % m_blueNoise->getLayerCount());
 
-	// Get or create FB
-	FramebufferPtr* fb = nullptr;
 	U width, height;
 	if(drawToDefaultFb)
 	{
-		fb = &ctx.m_outFb;
 		width = ctx.m_outFbWidth;
 		height = ctx.m_outFbHeight;
 	}
@@ -150,24 +138,53 @@ Error FinalComposite::run(RenderingContext& ctx)
 	{
 		width = m_r->getWidth();
 		height = m_r->getHeight();
-		fb = &m_fb;
 	}
-
-	cmdb->beginRenderPass(*fb);
 	cmdb->setViewport(0, 0, width, height);
+
 	cmdb->bindShaderProgram(m_grProgs[dbgEnabled]);
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
+	drawQuad(cmdb);
+}
 
+void FinalComposite::populateRenderGraph(RenderingContext& ctx)
+{
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+	m_runCtx.m_ctx = &ctx;
+	const Bool drawToDefaultFb = m_r->getDrawToDefaultFramebuffer();
+	const Bool dbgEnabled = m_r->getDbg().getEnabled();
+
+	// Maybe create the RT
 	if(!drawToDefaultFb)
 	{
-		cmdb->setTextureSurfaceBarrier(m_rt,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			TextureUsageBit::SAMPLED_FRAGMENT,
-			TextureSurfaceInfo(0, 0, 0, 0));
+		m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
 	}
 
-	return Error::NONE;
+	// Create the pass
+	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Final Composite");
+
+	pass.setWork(runCallback, this, 0);
+
+	if(drawToDefaultFb)
+	{
+		pass.setFramebufferInfo(m_fbDescr, {}, {});
+	}
+	else
+	{
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rt}}, {});
+	}
+
+	if(!drawToDefaultFb)
+	{
+		pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	if(dbgEnabled)
+	{
+		pass.newConsumer({m_r->getDbg().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	}
+	pass.newConsumer({m_r->getTemporalAA().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getBloom().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getTonemapping().getAverageLuminanceBuffer(), BufferUsageBit::UNIFORM_FRAGMENT});
 }
 
 } // end namespace anki

+ 24 - 11
src/anki/renderer/FinalComposite.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/resource/TextureResource.h>
 
 namespace anki
@@ -15,7 +15,7 @@ namespace anki
 /// @{
 
 /// Post-processing stage.
-class FinalComposite : public RenderingPass
+class FinalComposite : public RendererObject
 {
 public:
 	/// Load the color grading texture.
@@ -28,23 +28,20 @@ anki_internal:
 	~FinalComposite();
 
 	ANKI_USE_RESULT Error init(const ConfigSet& config);
-	ANKI_USE_RESULT Error run(RenderingContext& ctx);
 
-	const TexturePtr& getRt() const
-	{
-		return m_rt;
-	}
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
-	TexturePtr& getRt()
+	RenderTargetHandle getRt() const
 	{
-		return m_rt;
+		return m_runCtx.m_rt;
 	}
 
 private:
 	static const U LUT_SIZE = 16;
 
-	FramebufferPtr m_fb;
-	TexturePtr m_rt;
+	FramebufferDescription m_fbDescr;
+	RenderTargetDescription m_rtDescr;
 
 	ShaderProgramResourcePtr m_prog;
 	Array<ShaderProgramPtr, 2> m_grProgs; ///< One with Dbg and one without
@@ -54,7 +51,23 @@ private:
 
 	Bool8 m_sharpenEnabled = false;
 
+	class
+	{
+	public:
+		RenderTargetHandle m_rt;
+		RenderingContext* m_ctx = nullptr;
+	} m_runCtx;
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
+
+	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+
+	/// A RenderPassWorkCallback for the composite pass.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		FinalComposite* self = scast<FinalComposite*>(rgraphCtx.m_userData);
+		self->run(*self->m_runCtx.m_ctx, rgraphCtx);
+	}
 };
 /// @}
 

+ 91 - 101
src/anki/renderer/ForwardShading.cpp

@@ -11,6 +11,7 @@
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/Volumetric.h>
 #include <anki/renderer/DepthDownscale.h>
+#include <anki/renderer/LensFlare.h>
 
 namespace anki
 {
@@ -37,24 +38,22 @@ Error ForwardShading::initInternal(const ConfigSet&)
 	m_width = m_r->getWidth() / FS_FRACTION;
 	m_height = m_r->getHeight() / FS_FRACTION;
 
-	// Create RT
-	m_rt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_width,
+	// Create RT descr
+	m_rtDescr = m_r->create2DRenderTargetDescription(m_width,
 		m_height,
 		FORWARD_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		SamplingFilter::LINEAR,
-		1,
-		"forward"));
-
-	FramebufferInitInfo fbInit("forward");
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
-	fbInit.m_colorAttachments[0].m_clearValue.m_colorf = {{0.0, 0.0, 0.0, 1.0}};
-	fbInit.m_depthStencilAttachment.m_texture = m_r->getDepthDownscale().m_hd.m_depthRt;
-	fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::LOAD;
-	fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
-	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+		"forward");
+	m_rtDescr.bake();
+
+	// Create FB descr
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
+	m_fbDescr.m_colorAttachments[0].m_clearValue.m_colorf = {{0.0, 0.0, 0.0, 1.0}};
+	m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::LOAD;
+	m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+	m_fbDescr.bake();
 
 	ANKI_CHECK(initVol());
 	ANKI_CHECK(initUpscale());
@@ -99,8 +98,11 @@ Error ForwardShading::initUpscale()
 	return Error::NONE;
 }
 
-void ForwardShading::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb)
+void ForwardShading::drawVolumetric(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	cmdb->setViewport(0, 0, m_width, m_height);
 	cmdb->bindShaderProgram(m_vol.m_grProg);
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
 	cmdb->setDepthWrite(false);
@@ -109,17 +111,12 @@ void ForwardShading::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb
 	Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	computeLinearizeDepthOptimal(ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, unis->x(), unis->y());
 
-	cmdb->informTextureSurfaceCurrentUsage(
-		m_r->getVolumetric().m_main.getRt(), TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->informTextureSurfaceCurrentUsage(
-		m_r->getDepthDownscale().m_qd.m_colorRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
-
-	cmdb->bindTextureAndSampler(0, 0, m_r->getDepthDownscale().m_hd.m_colorRt, m_r->getNearestSampler());
-	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_qd.m_colorRt, m_r->getNearestSampler());
-	cmdb->bindTexture(0, 2, m_r->getVolumetric().m_main.getRt());
-	cmdb->bindTexture(0, 3, m_vol.m_noiseTex->getGrTexture());
+	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDepthDownscale().getHalfDepthColorRt(), m_r->getNearestSampler());
+	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getDepthDownscale().getQuarterColorRt(), m_r->getNearestSampler());
+	rgraphCtx.bindTexture(0, 2, m_r->getVolumetric().getRt());
+	cmdb->bindTexture(0, 3, m_vol.m_noiseTex->getGrTexture(), TextureUsageBit::SAMPLED_FRAGMENT);
 
-	m_r->drawQuad(cmdb);
+	drawQuad(cmdb);
 
 	// Restore state
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
@@ -127,116 +124,109 @@ void ForwardShading::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb
 	cmdb->setDepthCompareOperation(CompareOperation::LESS);
 }
 
-void ForwardShading::drawUpscale(RenderingContext& ctx)
+void ForwardShading::drawUpscale(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
+	// **WARNING** Remember to update the consumers of the render pass that calls this method
 	Vec4* linearDepth = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	computeLinearizeDepthOptimal(
 		ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, linearDepth->x(), linearDepth->y());
 
-	cmdb->bindTexture(0, 0, m_r->getGBuffer().m_depthRt);
-	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_hd.m_colorRt, m_r->getNearestSampler());
-	cmdb->bindTexture(0, 2, m_rt);
-	cmdb->bindTexture(0, 3, m_upscale.m_noiseTex->getGrTexture());
+	rgraphCtx.bindTexture(0, 0, m_r->getGBuffer().getDepthRt());
+	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getDepthDownscale().getHalfDepthColorRt(), m_r->getNearestSampler());
+	rgraphCtx.bindTexture(0, 2, m_runCtx.m_rt);
+	cmdb->bindTexture(0, 3, m_upscale.m_noiseTex->getGrTexture(), TextureUsageBit::SAMPLED_FRAGMENT);
 
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::SRC_ALPHA);
 
 	cmdb->bindShaderProgram(m_upscale.m_grProg);
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
-	m_r->drawQuad(cmdb);
+	drawQuad(cmdb);
 
 	// Restore state
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
 }
 
-void ForwardShading::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount) const
+void ForwardShading::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	const U threadId = rgraphCtx.m_currentSecondLevelCommandBufferIndex;
+	const U threadCount = rgraphCtx.m_secondLevelCommandBufferCount;
 	const U problemSize = ctx.m_renderQueue->m_forwardShadingRenderables.getSize();
 	PtrSize start, end;
 	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
 
-	if(start == end)
+	if(start != end)
 	{
-		// Early exit
-		return;
+		const LightShadingResources& rsrc = m_r->getLightShading().getResources();
+		rgraphCtx.bindTexture(0, 0, m_r->getDepthDownscale().getQuarterColorRt());
+		rgraphCtx.bindTexture(0, 1, m_r->getShadowMapping().getShadowmapRt());
+		bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
+		bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
+		bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
+		bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
+		bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
+
+		cmdb->setViewport(0, 0, m_width, m_height);
+		cmdb->setBlendFactors(
+			0, BlendFactor::ONE_MINUS_SRC_ALPHA, BlendFactor::SRC_ALPHA, BlendFactor::DST_ALPHA, BlendFactor::ZERO);
+		cmdb->setBlendOperation(0, BlendOperation::ADD);
+		cmdb->setDepthWrite(false);
+
+		// Start drawing
+		m_r->getSceneDrawer().drawRange(Pass::GB_FS,
+			ctx.m_renderQueue->m_viewMatrix,
+			ctx.m_viewProjMatJitter,
+			cmdb,
+			ctx.m_renderQueue->m_forwardShadingRenderables.getBegin() + start,
+			ctx.m_renderQueue->m_forwardShadingRenderables.getBegin() + end);
 	}
 
-	// Create the command buffer and set some state
-	CommandBufferInitInfo cinf;
-	cinf.m_flags = CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::GRAPHICS_WORK;
-	if(end - start < COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS)
+	if(threadId == threadCount - 1)
 	{
-		cinf.m_flags |= CommandBufferFlag::SMALL_BATCH;
-	}
-	cinf.m_framebuffer = m_fb;
-	CommandBufferPtr cmdb = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
-	ctx.m_forwardShading.m_commandBuffers[threadId] = cmdb;
-
-	cmdb->informTextureCurrentUsage(m_r->getDepthDownscale().m_qd.m_colorRt, TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->informTextureSurfaceCurrentUsage(
-		m_r->getDepthDownscale().m_hd.m_colorRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->informTextureSurfaceCurrentUsage(m_r->getDepthDownscale().m_hd.m_depthRt,
-		TextureSurfaceInfo(0, 0, 0, 0),
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ);
-	cmdb->informTextureCurrentUsage(m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
-	cmdb->informTextureCurrentUsage(m_r->getShadowMapping().m_shadowAtlas, TextureUsageBit::SAMPLED_FRAGMENT);
-
-	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_colorRt);
-	cmdb->bindTexture(0, 1, m_r->getShadowMapping().m_shadowAtlas);
-	bindUniforms(cmdb, 0, 0, ctx.m_lightShading.m_commonToken);
-	bindUniforms(cmdb, 0, 1, ctx.m_lightShading.m_pointLightsToken);
-	bindUniforms(cmdb, 0, 2, ctx.m_lightShading.m_spotLightsToken);
-	bindStorage(cmdb, 0, 0, ctx.m_lightShading.m_clustersToken);
-	bindStorage(cmdb, 0, 1, ctx.m_lightShading.m_lightIndicesToken);
-
-	cmdb->setViewport(0, 0, m_width, m_height);
-	cmdb->setBlendFactors(
-		0, BlendFactor::ONE_MINUS_SRC_ALPHA, BlendFactor::SRC_ALPHA, BlendFactor::DST_ALPHA, BlendFactor::ZERO);
-	cmdb->setBlendOperation(0, BlendOperation::ADD);
-	cmdb->setDepthWrite(false);
-
-	// Start drawing
-	m_r->getSceneDrawer().drawRange(Pass::GB_FS,
-		ctx.m_renderQueue->m_viewMatrix,
-		ctx.m_viewProjMatJitter,
-		cmdb,
-		ctx.m_renderQueue->m_forwardShadingRenderables.getBegin() + start,
-		ctx.m_renderQueue->m_forwardShadingRenderables.getBegin() + end);
-}
+		drawVolumetric(ctx, rgraphCtx);
 
-void ForwardShading::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void ForwardShading::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
+		if(ctx.m_renderQueue->m_lensFlares.getSize())
+		{
+			m_r->getLensFlare().runDrawFlares(ctx, cmdb);
+		}
+	}
 }
 
-void ForwardShading::run(RenderingContext& ctx)
+void ForwardShading::populateRenderGraph(RenderingContext& ctx)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-	cmdb->beginRenderPass(m_fb);
-	cmdb->setViewport(0, 0, m_width, m_height);
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+	m_runCtx.m_ctx = &ctx;
 
-	for(U i = 0; i < m_r->getThreadPool().getThreadsCount(); ++i)
+	// Create RT
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+
+	// Create pass
+	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Forward shading");
+
+	pass.setWork(runCallback,
+		this,
+		computeNumberOfSecondLevelCommandBuffers(ctx.m_renderQueue->m_forwardShadingRenderables.getSize()));
+	pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rt}}, m_r->getDepthDownscale().getHalfDepthDepthRt());
+
+	pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE});
+	pass.newConsumer({m_r->getDepthDownscale().getHalfDepthDepthRt(),
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ,
+		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});
+	pass.newConsumer({m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+
+	if(ctx.m_renderQueue->m_lensFlares.getSize())
 	{
-		if(ctx.m_forwardShading.m_commandBuffers[i].isCreated())
-		{
-			cmdb->pushSecondLevelCommandBuffer(ctx.m_forwardShading.m_commandBuffers[i]);
-		}
+		pass.newConsumer({m_r->getLensFlare().getIndirectDrawBuffer(), BufferUsageBit::INDIRECT});
 	}
 
-	cmdb->endRenderPass();
+	pass.newProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE});
+	pass.newProducer({m_r->getDepthDownscale().getHalfDepthDepthRt(), TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ});
 }
 
 } // end namespace anki

+ 33 - 26
src/anki/renderer/ForwardShading.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 
 namespace anki
 {
@@ -14,11 +14,11 @@ namespace anki
 /// @{
 
 /// Forward rendering stage. The objects that blend must be handled differently
-class ForwardShading : public RenderingPass
+class ForwardShading : public RendererObject
 {
 anki_internal:
 	ForwardShading(Renderer* r)
-		: RenderingPass(r)
+		: RendererObject(r)
 	{
 	}
 
@@ -26,43 +26,32 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
 
-	void buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount) const;
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
-	void setPreRunBarriers(RenderingContext& ctx);
+	void drawUpscale(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
 
-	void run(RenderingContext& ctx);
-
-	void setPostRunBarriers(RenderingContext& ctx);
-
-	void drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb);
-
-	void drawUpscale(RenderingContext& ctx);
-
-	TexturePtr getRt() const
-	{
-		return m_rt;
-	}
-
-	U getWidth() const
+	U32 getWidth() const
 	{
 		return m_width;
 	}
 
-	U getHeight() const
+	U32 getHeight() const
 	{
 		return m_height;
 	}
 
-	FramebufferPtr getFramebuffer() const
+	RenderTargetHandle getRt() const
 	{
-		return m_fb;
+		return m_runCtx.m_rt;
 	}
 
 private:
-	U m_width;
-	U m_height;
-	FramebufferPtr m_fb;
-	TexturePtr m_rt;
+	U32 m_width;
+	U32 m_height;
+
+	FramebufferDescription m_fbDescr;
+	RenderTargetDescription m_rtDescr;
 
 	class Vol
 	{
@@ -80,9 +69,27 @@ private:
 		TextureResourcePtr m_noiseTex;
 	} m_upscale;
 
+	class
+	{
+	public:
+		RenderTargetHandle m_rt;
+		RenderingContext* m_ctx = nullptr;
+	} m_runCtx;
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 	ANKI_USE_RESULT Error initVol();
 	ANKI_USE_RESULT Error initUpscale();
+
+	/// A RenderPassWorkCallback.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		ForwardShading* self = scast<ForwardShading*>(rgraphCtx.m_userData);
+		self->run(*self->m_runCtx.m_ctx, rgraphCtx);
+	}
+
+	void run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+
+	void drawVolumetric(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
 };
 /// @}
 

+ 106 - 166
src/anki/renderer/GBuffer.cpp

@@ -6,6 +6,7 @@
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/RenderQueue.h>
+#include <anki/renderer/LensFlare.h>
 #include <anki/util/Logger.h>
 #include <anki/util/ThreadPool.h>
 #include <anki/misc/ConfigSet.h>
@@ -18,68 +19,6 @@ GBuffer::~GBuffer()
 {
 }
 
-Error GBuffer::createRt()
-{
-	m_depthRt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
-		m_r->getHeight(),
-		GBUFFER_DEPTH_ATTACHMENT_PIXEL_FORMAT,
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE
-			| TextureUsageBit::GENERATE_MIPMAPS,
-		SamplingFilter::NEAREST,
-		1,
-		"gbuffdepth"));
-
-	m_rt0 = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
-		m_r->getHeight(),
-		MS_COLOR_ATTACHMENT_PIXEL_FORMATS[0],
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		SamplingFilter::NEAREST,
-		1,
-		"gbuffrt0"));
-
-	m_rt1 = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
-		m_r->getHeight(),
-		MS_COLOR_ATTACHMENT_PIXEL_FORMATS[1],
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		SamplingFilter::NEAREST,
-		1,
-		"gbuffrt1"));
-
-	m_rt2 = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
-		m_r->getHeight(),
-		MS_COLOR_ATTACHMENT_PIXEL_FORMATS[2],
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
-			| TextureUsageBit::GENERATE_MIPMAPS,
-		SamplingFilter::NEAREST,
-		1,
-		"gbuffrt2"));
-
-	AttachmentLoadOperation loadop = AttachmentLoadOperation::DONT_CARE;
-#if ANKI_EXTRA_CHECKS
-	loadop = AttachmentLoadOperation::CLEAR;
-#endif
-
-	FramebufferInitInfo fbInit("gbuffer");
-	fbInit.m_colorAttachmentCount = GBUFFER_COLOR_ATTACHMENT_COUNT;
-	fbInit.m_colorAttachments[0].m_texture = m_rt0;
-	fbInit.m_colorAttachments[0].m_loadOperation = loadop;
-	fbInit.m_colorAttachments[0].m_clearValue.m_colorf = {{1.0, 0.0, 0.0, 0.0}};
-	fbInit.m_colorAttachments[1].m_texture = m_rt1;
-	fbInit.m_colorAttachments[1].m_loadOperation = loadop;
-	fbInit.m_colorAttachments[1].m_clearValue.m_colorf = {{0.0, 1.0, 0.0, 0.0}};
-	fbInit.m_colorAttachments[2].m_texture = m_rt2;
-	fbInit.m_colorAttachments[2].m_loadOperation = loadop;
-	fbInit.m_colorAttachments[2].m_clearValue.m_colorf = {{0.0, 0.0, 1.0, 0.0}};
-	fbInit.m_depthStencilAttachment.m_texture = m_depthRt;
-	fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
-	fbInit.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
-	fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
-
-	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
-
-	return Error::NONE;
-}
-
 Error GBuffer::init(const ConfigSet& initializer)
 {
 	ANKI_R_LOGI("Initializing g-buffer pass");
@@ -95,143 +34,144 @@ Error GBuffer::init(const ConfigSet& initializer)
 
 Error GBuffer::initInternal(const ConfigSet& initializer)
 {
-	ANKI_CHECK(createRt());
+	// RT descrs
+	m_depthRtDescr = m_r->create2DRenderTargetDescription(m_r->getWidth(),
+		m_r->getHeight(),
+		GBUFFER_DEPTH_ATTACHMENT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
+		SamplingFilter::NEAREST,
+		"GBuffer depth");
+	m_depthRtDescr.bake();
+
+	static const char* rtNames[GBUFFER_COLOR_ATTACHMENT_COUNT] = {"GBuffer rt0", "GBuffer rt1", "GBuffer rt2"};
+	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
+	{
+		m_colorRtDescrs[i] = m_r->create2DRenderTargetDescription(m_r->getWidth(),
+			m_r->getHeight(),
+			MS_COLOR_ATTACHMENT_PIXEL_FORMATS[i],
+			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+			SamplingFilter::NEAREST,
+			rtNames[i]);
+		m_colorRtDescrs[i].bake();
+	}
+
+	// FB descr
+	AttachmentLoadOperation loadop = AttachmentLoadOperation::DONT_CARE;
+#if ANKI_EXTRA_CHECKS
+	loadop = AttachmentLoadOperation::CLEAR;
+#endif
+
+	m_fbDescr.m_colorAttachmentCount = GBUFFER_COLOR_ATTACHMENT_COUNT;
+	m_fbDescr.m_colorAttachments[0].m_loadOperation = loadop;
+	m_fbDescr.m_colorAttachments[0].m_clearValue.m_colorf = {{1.0, 0.0, 0.0, 0.0}};
+	m_fbDescr.m_colorAttachments[1].m_loadOperation = loadop;
+	m_fbDescr.m_colorAttachments[1].m_clearValue.m_colorf = {{0.0, 1.0, 0.0, 0.0}};
+	m_fbDescr.m_colorAttachments[2].m_loadOperation = loadop;
+	m_fbDescr.m_colorAttachments[2].m_clearValue.m_colorf = {{0.0, 0.0, 1.0, 0.0}};
+	m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
+	m_fbDescr.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
+	m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+	m_fbDescr.bake();
+
 	return Error::NONE;
 }
 
-void GBuffer::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount) const
+void GBuffer::runInThread(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx) const
 {
 	ANKI_TRACE_SCOPED_EVENT(RENDER_MS);
 
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	const U threadId = rgraphCtx.m_currentSecondLevelCommandBufferIndex;
+	const U threadCount = rgraphCtx.m_secondLevelCommandBufferCount;
+
 	// Get some stuff
 	const PtrSize earlyZCount = ctx.m_renderQueue->m_earlyZRenderables.getSize();
 	const U problemSize = ctx.m_renderQueue->m_renderables.getSize() + earlyZCount;
 	PtrSize start, end;
 	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
+	ANKI_ASSERT(end != start);
+
+	// Set some state, leave the rest to default
+	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
-	if(start != end)
+	const I32 earlyZStart = max(I32(start), 0);
+	const I32 earlyZEnd = min(I32(end), I32(earlyZCount));
+	const I32 colorStart = max(I32(start) - I32(earlyZCount), 0);
+	const I32 colorEnd = I32(end) - I32(earlyZCount);
+
+	// First do early Z (if needed)
+	if(earlyZStart < earlyZEnd)
 	{
-		// Create the command buffer
-		CommandBufferInitInfo cinf;
-		cinf.m_flags = CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::GRAPHICS_WORK;
-		if(end - start < COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS)
+		for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
 		{
-			cinf.m_flags |= CommandBufferFlag::SMALL_BATCH;
+			cmdb->setColorChannelWriteMask(i, ColorBit::NONE);
 		}
-		cinf.m_framebuffer = m_fb;
-		CommandBufferPtr cmdb = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
-		ctx.m_gbuffer.m_commandBuffers[threadId] = cmdb;
-
-		// Inform on RTs
-		TextureSurfaceInfo surf(0, 0, 0, 0);
-		cmdb->informTextureSurfaceCurrentUsage(m_rt0, surf, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE);
-		cmdb->informTextureSurfaceCurrentUsage(m_rt1, surf, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE);
-		cmdb->informTextureSurfaceCurrentUsage(m_rt2, surf, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE);
-		cmdb->informTextureSurfaceCurrentUsage(m_depthRt, surf, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
-
-		// Set some state, leave the rest to default
-		cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-
-		const I32 earlyZStart = max(I32(start), 0);
-		const I32 earlyZEnd = min(I32(end), I32(earlyZCount));
-		const I32 colorStart = max(I32(start) - I32(earlyZCount), 0);
-		const I32 colorEnd = I32(end) - I32(earlyZCount);
-
-		// First do early Z (if needed)
-		if(earlyZStart < earlyZEnd)
-		{
-			for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
-			{
-				cmdb->setColorChannelWriteMask(i, ColorBit::NONE);
-			}
-
-			ANKI_ASSERT(earlyZStart < earlyZEnd && earlyZEnd <= I32(earlyZCount));
-			m_r->getSceneDrawer().drawRange(Pass::EZ,
-				ctx.m_renderQueue->m_viewMatrix,
-				ctx.m_viewProjMatJitter,
-				cmdb,
-				ctx.m_renderQueue->m_earlyZRenderables.getBegin() + earlyZStart,
-				ctx.m_renderQueue->m_earlyZRenderables.getBegin() + earlyZEnd);
 
-			// Restore state for the color write
-			if(colorStart < colorEnd)
-			{
-				for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
-				{
-					cmdb->setColorChannelWriteMask(i, ColorBit::ALL);
-				}
-			}
-		}
+		ANKI_ASSERT(earlyZStart < earlyZEnd && earlyZEnd <= I32(earlyZCount));
+		m_r->getSceneDrawer().drawRange(Pass::EZ,
+			ctx.m_renderQueue->m_viewMatrix,
+			ctx.m_viewProjMatJitter,
+			cmdb,
+			ctx.m_renderQueue->m_earlyZRenderables.getBegin() + earlyZStart,
+			ctx.m_renderQueue->m_earlyZRenderables.getBegin() + earlyZEnd);
 
-		// Do the color writes
+		// Restore state for the color write
 		if(colorStart < colorEnd)
 		{
-			cmdb->setDepthCompareOperation(CompareOperation::LESS_EQUAL);
-
-			ANKI_ASSERT(colorStart < colorEnd && colorEnd <= I32(ctx.m_renderQueue->m_renderables.getSize()));
-			m_r->getSceneDrawer().drawRange(Pass::GB_FS,
-				ctx.m_renderQueue->m_viewMatrix,
-				ctx.m_viewProjMatJitter,
-				cmdb,
-				ctx.m_renderQueue->m_renderables.getBegin() + colorStart,
-				ctx.m_renderQueue->m_renderables.getBegin() + colorEnd);
+			for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
+			{
+				cmdb->setColorChannelWriteMask(i, ColorBit::ALL);
+			}
 		}
 	}
-}
 
-void GBuffer::run(RenderingContext& ctx)
-{
-	ANKI_TRACE_SCOPED_EVENT(RENDER_MS);
-
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-	cmdb->beginRenderPass(m_fb);
-
-	// Set some state anyway because other stages may depend on it
-	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-
-	for(U i = 0; i < m_r->getThreadPool().getThreadsCount(); ++i)
+	// Do the color writes
+	if(colorStart < colorEnd)
 	{
-		if(ctx.m_gbuffer.m_commandBuffers[i].isCreated())
-		{
-			cmdb->pushSecondLevelCommandBuffer(ctx.m_gbuffer.m_commandBuffers[i]);
-		}
+		cmdb->setDepthCompareOperation(CompareOperation::LESS_EQUAL);
+
+		ANKI_ASSERT(colorStart < colorEnd && colorEnd <= I32(ctx.m_renderQueue->m_renderables.getSize()));
+		m_r->getSceneDrawer().drawRange(Pass::GB_FS,
+			ctx.m_renderQueue->m_viewMatrix,
+			ctx.m_viewProjMatJitter,
+			cmdb,
+			ctx.m_renderQueue->m_renderables.getBegin() + colorStart,
+			ctx.m_renderQueue->m_renderables.getBegin() + colorEnd);
 	}
-
-	cmdb->endRenderPass();
 }
 
-void GBuffer::setPreRunBarriers(RenderingContext& ctx)
+void GBuffer::populateRenderGraph(RenderingContext& ctx)
 {
 	ANKI_TRACE_SCOPED_EVENT(RENDER_MS);
 
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-	TextureSurfaceInfo surf(0, 0, 0, 0);
-
-	cmdb->setTextureSurfaceBarrier(m_rt0, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf);
-	cmdb->setTextureSurfaceBarrier(m_rt1, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf);
-	cmdb->setTextureSurfaceBarrier(m_rt2, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf);
-	cmdb->setTextureSurfaceBarrier(
-		m_depthRt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, surf);
-}
+	m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
-void GBuffer::setPostRunBarriers(RenderingContext& ctx)
-{
-	ANKI_TRACE_SCOPED_EVENT(RENDER_MS);
-
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-	TextureSurfaceInfo surf(0, 0, 0, 0);
-
-	cmdb->setTextureSurfaceBarrier(
-		m_rt0, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureUsageBit::SAMPLED_FRAGMENT, surf);
+	// Create RTs
+	Array<RenderTargetHandle, MAX_COLOR_ATTACHMENTS> rts;
+	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
+	{
+		m_colorRts[i] = rgraph.newRenderTarget(m_colorRtDescrs[i]);
+		rts[i] = m_colorRts[i];
+	}
+	m_depthRt = rgraph.newRenderTarget(m_depthRtDescr);
 
-	cmdb->setTextureSurfaceBarrier(
-		m_rt1, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureUsageBit::SAMPLED_FRAGMENT, surf);
+	// Create pass
+	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("GBuffer");
 
-	cmdb->setTextureSurfaceBarrier(
-		m_rt2, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureUsageBit::SAMPLED_FRAGMENT, surf);
+	pass.setFramebufferInfo(m_fbDescr, rts, m_depthRt);
+	pass.setWork(runCallback,
+		this,
+		computeNumberOfSecondLevelCommandBuffers(
+			ctx.m_renderQueue->m_earlyZRenderables.getSize() + ctx.m_renderQueue->m_renderables.getSize()));
 
-	cmdb->setTextureSurfaceBarrier(
-		m_depthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, TextureUsageBit::SAMPLED_FRAGMENT, surf);
+	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
+	{
+		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});
 }
 
 } // end namespace anki

+ 28 - 16
src/anki/renderer/GBuffer.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/Gr.h>
 
 namespace anki
@@ -15,16 +15,11 @@ namespace anki
 /// @{
 
 /// G buffer stage. It populates the G buffer
-class GBuffer : public RenderingPass
+class GBuffer : public RendererObject
 {
 anki_internal:
-	TexturePtr m_rt0;
-	TexturePtr m_rt1;
-	TexturePtr m_rt2;
-	TexturePtr m_depthRt;
-
 	GBuffer(Renderer* r)
-		: RenderingPass(r)
+		: RendererObject(r)
 	{
 	}
 
@@ -32,21 +27,38 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
 
-	void buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount) const;
-
-	void setPreRunBarriers(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
-	void run(RenderingContext& ctx);
+	RenderTargetHandle getColorRt(U idx) const
+	{
+		return m_colorRts[idx];
+	}
 
-	void setPostRunBarriers(RenderingContext& ctx);
+	RenderTargetHandle getDepthRt() const
+	{
+		return m_depthRt;
+	}
 
 private:
-	FramebufferPtr m_fb;
+	Array<RenderTargetDescription, GBUFFER_COLOR_ATTACHMENT_COUNT> m_colorRtDescrs;
+	RenderTargetDescription m_depthRtDescr;
+	FramebufferDescription m_fbDescr;
+
+	RenderingContext* m_ctx = nullptr;
+	Array<RenderTargetHandle, GBUFFER_COLOR_ATTACHMENT_COUNT> m_colorRts;
+	RenderTargetHandle m_depthRt;
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
-	/// Create a G buffer FBO
-	ANKI_USE_RESULT Error createRt();
+	// A RenderPassWorkCallback for G-buffer pass.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		GBuffer* self = scast<GBuffer*>(rgraphCtx.m_userData);
+		self->runInThread(*self->m_ctx, rgraphCtx);
+	}
+
+	void runInThread(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx) const;
 };
 /// @}
 

+ 233 - 157
src/anki/renderer/Indirect.cpp

@@ -40,7 +40,7 @@ struct Indirect::LightPassSpotLightUniforms
 };
 
 Indirect::Indirect(Renderer* r)
-	: RenderingPass(r)
+	: RendererObject(r)
 {
 }
 
@@ -66,9 +66,7 @@ Error Indirect::init(const ConfigSet& config)
 Error Indirect::initInternal(const ConfigSet& config)
 {
 	// Init cache entries
-	{
-		m_cacheEntries.create(getAllocator(), config.getNumber("r.indirect.maxSimultaneousProbeCount"));
-	}
+	m_cacheEntries.create(getAllocator(), config.getNumber("r.indirect.maxSimultaneousProbeCount"));
 
 	ANKI_CHECK(initGBuffer(config));
 	ANKI_CHECK(initLightShading(config));
@@ -94,12 +92,15 @@ Error Indirect::loadMesh(CString fname, BufferPtr& vert, BufferPtr& idx, U32& id
 	ANKI_CHECK(loader.load(fname));
 
 	PtrSize vertBuffSize = loader.getHeader().m_totalVerticesCount * sizeof(Vec3);
-	vert = getGrManager().newInstance<Buffer>(
-		vertBuffSize, BufferUsageBit::VERTEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferMapAccessBit::NONE);
+	vert = getGrManager().newInstance<Buffer>(BufferInitInfo(vertBuffSize,
+		BufferUsageBit::VERTEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
+		BufferMapAccessBit::NONE,
+		"IndirectMesh"));
 
-	idx = getGrManager().newInstance<Buffer>(loader.getIndexDataSize(),
+	idx = getGrManager().newInstance<Buffer>(BufferInitInfo(loader.getIndexDataSize(),
 		BufferUsageBit::INDEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
-		BufferMapAccessBit::NONE);
+		BufferMapAccessBit::NONE,
+		"IndirectMesh"));
 
 	// Upload data
 	CommandBufferInitInfo init;
@@ -142,46 +143,46 @@ Error Indirect::initGBuffer(const ConfigSet& config)
 {
 	m_gbuffer.m_tileSize = config.getNumber("r.indirect.reflectionResolution");
 
-	// Create attachments
+	// Create RT descriptions
 	{
-		TextureInitInfo texinit = m_r->create2DRenderTargetInitInfo(m_gbuffer.m_tileSize * 6,
+		RenderTargetDescription texinit = m_r->create2DRenderTargetDescription(m_gbuffer.m_tileSize * 6,
 			m_gbuffer.m_tileSize,
 			MS_COLOR_ATTACHMENT_PIXEL_FORMATS[0],
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			SamplingFilter::NEAREST, // Because we don't want the light pass to bleed to near faces
-			1,
-			"GI_gbuff");
+			"GI GBuffer");
 
-		// Create color attachments
+		// Create color RT descriptions
 		for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
 		{
 			texinit.m_format = MS_COLOR_ATTACHMENT_PIXEL_FORMATS[i];
-			m_gbuffer.m_colorRts[i] = m_r->createAndClearRenderTarget(texinit);
+			m_gbuffer.m_colorRtDescrs[i] = texinit;
+			m_gbuffer.m_colorRtDescrs[i].setName(StringAuto(getAllocator()).sprintf("GI GBuff Col #%u", i).toCString());
+			m_gbuffer.m_colorRtDescrs[i].bake();
 		}
 
-		// Create depth attachment
+		// Create depth RT
 		texinit.m_usage |= TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
 		texinit.m_format = GBUFFER_DEPTH_ATTACHMENT_PIXEL_FORMAT;
-		m_gbuffer.m_depthRt = m_r->createAndClearRenderTarget(texinit);
+		texinit.setName("GI GBuff Depth");
+		m_gbuffer.m_depthRtDescr = texinit;
+		m_gbuffer.m_depthRtDescr.bake();
 	}
 
-	// Create FB
+	// Create FB descr
 	{
-		FramebufferInitInfo fbInit("GI_gbuff");
-		fbInit.m_colorAttachmentCount = GBUFFER_COLOR_ATTACHMENT_COUNT;
+		m_gbuffer.m_fbDescr.m_colorAttachmentCount = GBUFFER_COLOR_ATTACHMENT_COUNT;
 
 		for(U j = 0; j < GBUFFER_COLOR_ATTACHMENT_COUNT; ++j)
 		{
-			fbInit.m_colorAttachments[j].m_texture = m_gbuffer.m_colorRts[j];
-			fbInit.m_colorAttachments[j].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+			m_gbuffer.m_fbDescr.m_colorAttachments[j].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
 		}
 
-		fbInit.m_depthStencilAttachment.m_texture = m_gbuffer.m_depthRt;
-		fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
-		fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
-		fbInit.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
+		m_gbuffer.m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+		m_gbuffer.m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
+		m_gbuffer.m_fbDescr.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
 
-		m_gbuffer.m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+		m_gbuffer.m_fbDescr.bake();
 	}
 
 	return Error::NONE;
@@ -200,8 +201,9 @@ Error Indirect::initLightShading(const ConfigSet& config)
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
 				| TextureUsageBit::GENERATE_MIPMAPS,
 			SamplingFilter::LINEAR,
-			m_lightShading.m_mipCount,
-			"GI_refl");
+			"GI refl");
+		texinit.m_mipmapsCount = m_lightShading.m_mipCount;
+		texinit.m_sampling.m_mipmapFilter = SamplingFilter::LINEAR;
 		texinit.m_type = TextureType::CUBE_ARRAY;
 		texinit.m_layerCount = m_cacheEntries.getSize();
 		texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
@@ -252,8 +254,7 @@ Error Indirect::initIrradiance(const ConfigSet& config)
 			LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			SamplingFilter::LINEAR,
-			1,
-			"GI_irr");
+			"GI irr");
 
 		texinit.m_layerCount = m_cacheEntries.getSize();
 		texinit.m_type = TextureType::CUBE_ARRAY;
@@ -288,21 +289,27 @@ void Indirect::initCacheEntry(U32 cacheEntryIdx)
 
 	for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
 	{
-		FramebufferInitInfo fbInit("GI_refl");
-		fbInit.m_colorAttachmentCount = 1;
-		fbInit.m_colorAttachments[0].m_texture = m_lightShading.m_cubeArr;
-		fbInit.m_colorAttachments[0].m_surface.m_layer = cacheEntryIdx;
-		fbInit.m_colorAttachments[0].m_surface.m_face = faceIdx;
-		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
-
-		ANKI_ASSERT(!cacheEntry.m_lightShadingFbs[faceIdx].isCreated());
-		cacheEntry.m_lightShadingFbs[faceIdx] = getGrManager().newInstance<Framebuffer>(fbInit);
-
-		fbInit.m_colorAttachments[0].m_texture = m_irradiance.m_cubeArr;
-		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+		// Light pass FB
+		{
+			FramebufferDescription& fbDescr = cacheEntry.m_lightShadingFbDescrs[faceIdx];
+			ANKI_ASSERT(!fbDescr.isBacked());
+			fbDescr.m_colorAttachmentCount = 1;
+			fbDescr.m_colorAttachments[0].m_surface.m_layer = cacheEntryIdx;
+			fbDescr.m_colorAttachments[0].m_surface.m_face = faceIdx;
+			fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
+			fbDescr.bake();
+		}
 
-		ANKI_ASSERT(!cacheEntry.m_irradianceFbs[faceIdx].isCreated());
-		cacheEntry.m_irradianceFbs[faceIdx] = getGrManager().newInstance<Framebuffer>(fbInit);
+		// Irradiance FB
+		{
+			FramebufferDescription& fbDescr = cacheEntry.m_irradianceFbDescrs[faceIdx];
+			ANKI_ASSERT(!fbDescr.isBacked());
+			fbDescr.m_colorAttachmentCount = 1;
+			fbDescr.m_colorAttachments[0].m_surface.m_layer = cacheEntryIdx;
+			fbDescr.m_colorAttachments[0].m_surface.m_face = faceIdx;
+			fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+			fbDescr.bake();
+		}
 	}
 }
 
@@ -407,18 +414,18 @@ void Indirect::prepareProbes(
 	}
 }
 
-void Indirect::runGBuffer(RenderingContext& rctx, const ReflectionProbeQueueElement& probe)
+void Indirect::runGBuffer(CommandBufferPtr& cmdb)
 {
-	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
-
-	cmdb->beginRenderPass(m_gbuffer.m_fb);
+	ANKI_ASSERT(m_ctx.m_probe);
+	ANKI_TRACE_SCOPED_EVENT(RENDER_IR);
+	const ReflectionProbeQueueElement& probe = *m_ctx.m_probe;
 
 	// For each face
 	for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
 	{
 		const U32 viewportX = faceIdx * m_gbuffer.m_tileSize;
-		cmdb->setViewport(viewportX, 0, viewportX + m_gbuffer.m_tileSize, m_gbuffer.m_tileSize);
-		cmdb->setScissor(viewportX, 0, viewportX + m_gbuffer.m_tileSize, m_gbuffer.m_tileSize);
+		cmdb->setViewport(viewportX, 0, m_gbuffer.m_tileSize, m_gbuffer.m_tileSize);
+		cmdb->setScissor(viewportX, 0, m_gbuffer.m_tileSize, m_gbuffer.m_tileSize);
 
 		/// Draw
 		ANKI_ASSERT(probe.m_renderQueues[faceIdx]);
@@ -432,29 +439,32 @@ void Indirect::runGBuffer(RenderingContext& rctx, const ReflectionProbeQueueElem
 			rqueue.m_renderables.getEnd());
 	}
 
-	cmdb->endRenderPass();
-
 	// Restore state
-	cmdb->setScissor(0, 0, MAX_U16, MAX_U16);
+	cmdb->setScissor(0, 0, MAX_U32, MAX_U32);
 }
 
-void Indirect::runLightShading(RenderingContext& rctx, const ReflectionProbeQueueElement& probe, CacheEntry& cacheEntry)
+void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 {
-	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
+	ANKI_ASSERT(faceIdx <= 6);
+	ANKI_TRACE_SCOPED_EVENT(RENDER_IR);
 
-	// Set common state
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	ANKI_ASSERT(m_ctx.m_probe);
+	const ReflectionProbeQueueElement& probe = *m_ctx.m_probe;
+
+	// Set common state for all lights
 	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
 	{
-		cmdb->bindTexture(0, i, m_gbuffer.m_colorRts[i]);
+		rgraphCtx.bindTexture(0, i, m_ctx.m_gbufferColorRts[i]);
 	}
-	cmdb->bindTexture(0, GBUFFER_COLOR_ATTACHMENT_COUNT, m_gbuffer.m_depthRt);
+	rgraphCtx.bindTexture(0, GBUFFER_COLOR_ATTACHMENT_COUNT, m_ctx.m_gbufferDepthRt);
 	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);
 	cmdb->setCullMode(FaceSelectionBit::FRONT);
 
-	// For each face
-	for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
+	// Render lights
 	{
 		ANKI_ASSERT(probe.m_renderQueues[faceIdx]);
 		const RenderQueue& rqueue = *probe.m_renderQueues[faceIdx];
@@ -462,9 +472,6 @@ void Indirect::runLightShading(RenderingContext& rctx, const ReflectionProbeQueu
 		const Mat4& vpMat = rqueue.m_viewProjectionMatrix;
 		const Mat4& vMat = rqueue.m_viewMatrix;
 
-		// Set per face state
-		cmdb->beginRenderPass(cacheEntry.m_lightShadingFbs[faceIdx]);
-
 		// Do point lights
 		cmdb->bindShaderProgram(m_lightShading.m_plightGrProg);
 		cmdb->bindVertexBuffer(0, m_lightShading.m_plightPositions, 0, sizeof(F32) * 3);
@@ -548,8 +555,6 @@ void Indirect::runLightShading(RenderingContext& rctx, const ReflectionProbeQueu
 
 			++splightEl;
 		}
-
-		cmdb->endRenderPass();
 	}
 
 	// Restore state
@@ -557,138 +562,209 @@ void Indirect::runLightShading(RenderingContext& rctx, const ReflectionProbeQueu
 	cmdb->setCullMode(FaceSelectionBit::BACK);
 }
 
-void Indirect::runIrradiance(RenderingContext& rctx, U32 cacheEntryIdx)
+void Indirect::runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 {
-	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
+	ANKI_ASSERT(faceIdx < 6);
+	ANKI_ASSERT(m_ctx.m_cacheEntryIdx < m_cacheEntries.getSize());
 
-	// Set common state
-	cmdb->bindShaderProgram(m_irradiance.m_grProg);
-	cmdb->bindTexture(0, 0, m_lightShading.m_cubeArr);
+	ANKI_TRACE_SCOPED_EVENT(RENDER_IR);
 
-	// For each face
-	for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
-	{
-		cmdb->beginRenderPass(m_cacheEntries[cacheEntryIdx].m_irradianceFbs[faceIdx]);
+	TexturePtr texToBind;
+	TextureUsageBit usage;
+	DepthStencilAspectBit aspect;
+	rgraphCtx.getRenderTargetState(m_ctx.m_lightShadingRt, texToBind, usage, aspect);
 
-		cmdb->setViewport(0, 0, m_irradiance.m_tileSize, m_irradiance.m_tileSize);
+	rgraphCtx.m_commandBuffer->generateMipmaps2d(texToBind, faceIdx, m_ctx.m_cacheEntryIdx);
+}
 
-		// Set uniforms
-		UVec4* faceIdxArrayIdx = allocateAndBindUniforms<UVec4*>(sizeof(UVec4), cmdb, 0, 0);
-		faceIdxArrayIdx->x() = faceIdx;
-		faceIdxArrayIdx->y() = cacheEntryIdx;
+void Indirect::runIrradiance(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
+{
+	ANKI_ASSERT(faceIdx < 6);
+	ANKI_TRACE_SCOPED_EVENT(RENDER_IR);
+	const U32 cacheEntryIdx = m_ctx.m_cacheEntryIdx;
+	ANKI_ASSERT(cacheEntryIdx < m_cacheEntries.getSize());
 
-		m_r->drawQuad(cmdb);
-		cmdb->endRenderPass();
-	}
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	// Set state
+	cmdb->bindShaderProgram(m_irradiance.m_grProg);
+	rgraphCtx.bindTexture(0, 0, m_ctx.m_lightShadingRt);
+	cmdb->setViewport(0, 0, m_irradiance.m_tileSize, m_irradiance.m_tileSize);
+
+	// Set uniforms
+	UVec4* faceIdxArrayIdx = allocateAndBindUniforms<UVec4*>(sizeof(UVec4), cmdb, 0, 0);
+	faceIdxArrayIdx->x() = faceIdx;
+	faceIdxArrayIdx->y() = cacheEntryIdx;
+
+	// Draw
+	drawQuad(cmdb);
 }
 
-void Indirect::run(RenderingContext& rctx)
+void Indirect::populateRenderGraph(RenderingContext& rctx)
 {
 	ANKI_TRACE_SCOPED_EVENT(RENDER_IR);
-	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
 
-	cmdb->informTextureCurrentUsage(m_lightShading.m_cubeArr, TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->informTextureCurrentUsage(m_irradiance.m_cubeArr, TextureUsageBit::SAMPLED_FRAGMENT);
+#if ANKI_EXTRA_CHECKS
+	m_ctx = {};
+#endif
+	RenderGraphDescription& rgraph = rctx.m_renderGraphDescr;
 
-	// Prepare the probes
+	// Prepare the probes and maybe get one to render this frame
 	ReflectionProbeQueueElement* probeToUpdate;
 	U32 probeToUpdateCacheEntryIdx;
 	prepareProbes(rctx, probeToUpdate, probeToUpdateCacheEntryIdx);
 
-	// Update a probe if needed
+	// Render a probe if needed
 	if(probeToUpdate)
 	{
-		if(!m_cacheEntries[probeToUpdateCacheEntryIdx].m_lightShadingFbs[0].isCreated())
+		m_ctx.m_cacheEntryIdx = probeToUpdateCacheEntryIdx;
+		m_ctx.m_probe = probeToUpdate;
+
+		if(!m_cacheEntries[probeToUpdateCacheEntryIdx].m_lightShadingFbDescrs[0].isBacked())
 		{
 			initCacheEntry(probeToUpdateCacheEntryIdx);
 		}
 
-		// Barriers
-		for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
+		// G-buffer pass
 		{
-			cmdb->setTextureSurfaceBarrier(m_gbuffer.m_colorRts[i],
-				TextureUsageBit::NONE,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-				TextureSurfaceInfo(0, 0, 0, 0));
-		}
-
-		cmdb->setTextureSurfaceBarrier(m_gbuffer.m_depthRt,
-			TextureUsageBit::NONE,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-			TextureSurfaceInfo(0, 0, 0, 0));
+			// RTs
+			Array<RenderTargetHandle, MAX_COLOR_ATTACHMENTS> rts;
+			for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
+			{
+				m_ctx.m_gbufferColorRts[i] = rgraph.newRenderTarget(m_gbuffer.m_colorRtDescrs[i]);
+				rts[i] = m_ctx.m_gbufferColorRts[i];
+			}
+			m_ctx.m_gbufferDepthRt = rgraph.newRenderTarget(m_gbuffer.m_depthRtDescr);
 
-		// Run g-buffer pass
-		runGBuffer(rctx, *probeToUpdate);
+			// Pass
+			GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("GI gbuff");
+			pass.setFramebufferInfo(m_gbuffer.m_fbDescr, rts, m_ctx.m_gbufferDepthRt);
+			pass.setWork(runGBufferCallback, this, 0);
 
-		// Barriers
-		for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
-		{
-			cmdb->setTextureSurfaceBarrier(m_gbuffer.m_colorRts[i],
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-				TextureUsageBit::SAMPLED_FRAGMENT,
-				TextureSurfaceInfo(0, 0, 0, 0));
+			for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
+			{
+				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});
 		}
 
-		cmdb->setTextureSurfaceBarrier(m_gbuffer.m_depthRt,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-			TextureUsageBit::SAMPLED_FRAGMENT,
-			TextureSurfaceInfo(0, 0, 0, 0));
-
-		for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
+		// Light shading passes
 		{
-			cmdb->setTextureSurfaceBarrier(m_lightShading.m_cubeArr,
-				TextureUsageBit::NONE,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-				TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
-		}
-
-		// Run light shading pass
-		runLightShading(rctx, *probeToUpdate, m_cacheEntries[probeToUpdateCacheEntryIdx]);
-
-		// Barriers
-		for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
-		{
-			cmdb->setTextureSurfaceBarrier(m_lightShading.m_cubeArr,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-				TextureUsageBit::GENERATE_MIPMAPS,
-				TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
+			Array<RenderPassWorkCallback, 6> callbacks = {{runLightShadingCallback<0>,
+				runLightShadingCallback<1>,
+				runLightShadingCallback<2>,
+				runLightShadingCallback<3>,
+				runLightShadingCallback<4>,
+				runLightShadingCallback<5>}};
+
+			// RT
+			m_ctx.m_lightShadingRt =
+				rgraph.importRenderTarget("GI light", m_lightShading.m_cubeArr, TextureUsageBit::SAMPLED_FRAGMENT);
+
+			// Passes
+			static const Array<CString, 6> passNames = {{"GI LightShad #0",
+				"GI LightShad #1",
+				"GI LightShad #2",
+				"GI LightShad #3",
+				"GI LightShad #4",
+				"GI LightShad #5"}};
+			for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
+			{
+				GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass(passNames[faceIdx]);
+				pass.setFramebufferInfo(m_cacheEntries[probeToUpdateCacheEntryIdx].m_lightShadingFbDescrs[faceIdx],
+					{{m_ctx.m_lightShadingRt}},
+					{});
+				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});
+
+				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});
+			}
 		}
 
-		// Run the mipmaping passes
-		for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
+		// Mipmapping "passes"
 		{
-			cmdb->generateMipmaps2d(m_lightShading.m_cubeArr, faceIdx, probeToUpdateCacheEntryIdx);
+			static const Array<RenderPassWorkCallback, 6> callbacks = {{runMipmappingOfLightShadingCallback<0>,
+				runMipmappingOfLightShadingCallback<1>,
+				runMipmappingOfLightShadingCallback<2>,
+				runMipmappingOfLightShadingCallback<3>,
+				runMipmappingOfLightShadingCallback<4>,
+				runMipmappingOfLightShadingCallback<5>}};
+
+			static const Array<CString, 6> passNames = {
+				{"GI Mip #0", "GI Mip #1", "GI Mip #2", "GI Mip #3", "GI Mip #4", "GI Mip #5"}};
+			for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
+			{
+				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});
+				}
+			}
 		}
 
-		// Barriers
-		for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
+		// Irradiance passes
 		{
-			for(U mip = 0; mip < m_lightShading.m_mipCount; ++mip)
+			static const Array<RenderPassWorkCallback, 6> callbacks = {{runIrradianceCallback<0>,
+				runIrradianceCallback<1>,
+				runIrradianceCallback<2>,
+				runIrradianceCallback<3>,
+				runIrradianceCallback<4>,
+				runIrradianceCallback<5>}};
+
+			// Rt
+			m_ctx.m_irradianceRt =
+				rgraph.importRenderTarget("GI irradiance", m_irradiance.m_cubeArr, TextureUsageBit::SAMPLED_FRAGMENT);
+
+			static const Array<CString, 6> passNames = {
+				{"GI Irr/ce #0", "GI Irr/ce #1", "GI Irr/ce #2", "GI Irr/ce #3", "GI Irr/ce #4", "GI Irr/ce #5"}};
+			for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
 			{
-				cmdb->setTextureSurfaceBarrier(m_lightShading.m_cubeArr,
-					TextureUsageBit::GENERATE_MIPMAPS,
-					TextureUsageBit::SAMPLED_FRAGMENT,
-					TextureSurfaceInfo(mip, 0, faceIdx, probeToUpdateCacheEntryIdx));
-			}
+				GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass(passNames[faceIdx]);
 
-			cmdb->setTextureSurfaceBarrier(m_irradiance.m_cubeArr,
-				TextureUsageBit::NONE,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-				TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
-		}
+				pass.setFramebufferInfo(m_cacheEntries[probeToUpdateCacheEntryIdx].m_irradianceFbDescrs[faceIdx],
+					{{m_ctx.m_irradianceRt}},
+					{});
 
-		// Run irradiance
-		runIrradiance(rctx, probeToUpdateCacheEntryIdx);
+				pass.setWork(callbacks[faceIdx], this, 0);
 
-		// Barriers
-		for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
-		{
-			cmdb->setTextureSurfaceBarrier(m_irradiance.m_cubeArr,
-				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-				TextureUsageBit::SAMPLED_FRAGMENT,
-				TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
+				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)});
+			}
 		}
 	}
+	else
+	{
+		// Just import
+
+		m_ctx.m_lightShadingRt =
+			rgraph.importRenderTarget("GI light", m_lightShading.m_cubeArr, TextureUsageBit::SAMPLED_FRAGMENT);
+		m_ctx.m_irradianceRt =
+			rgraph.importRenderTarget("GI irradiance", m_irradiance.m_cubeArr, TextureUsageBit::SAMPLED_FRAGMENT);
+	}
 }
 
 Bool Indirect::findBestCacheEntry(U64 probeUuid, U32& cacheEntryIdxAllocated, Bool& cacheEntryFound)

+ 65 - 19
src/anki/renderer/Indirect.h

@@ -6,7 +6,7 @@
 #pragma once
 
 #include <anki/renderer/Renderer.h>
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/renderer/Clusterer.h>
 #include <anki/resource/TextureResource.h>
 
@@ -17,7 +17,7 @@ namespace anki
 /// @{
 
 /// Probe reflections and irradiance.
-class Indirect : public RenderingPass
+class Indirect : public RendererObject
 {
 	friend class IrTask;
 
@@ -28,31 +28,32 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
-	void run(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
 	U getReflectionTextureMipmapCount() const
 	{
 		return m_lightShading.m_mipCount;
 	}
 
-	TexturePtr getIrradianceTexture() const
+	TexturePtr getIntegrationLut() const
 	{
-		return m_irradiance.m_cubeArr;
+		return m_integrationLut->getGrTexture();
 	}
 
-	TexturePtr getReflectionTexture() const
+	SamplerPtr getIntegrationLutSampler() const
 	{
-		return m_lightShading.m_cubeArr;
+		return m_integrationLutSampler;
 	}
 
-	TexturePtr getIntegrationLut() const
+	RenderTargetHandle getReflectionRt() const
 	{
-		return m_integrationLut->getGrTexture();
+		return m_ctx.m_lightShadingRt;
 	}
 
-	SamplerPtr getIntegrationLutSampler() const
+	RenderTargetHandle getIrradianceRt() const
 	{
-		return m_integrationLutSampler;
+		return m_ctx.m_irradianceRt;
 	}
 
 private:
@@ -64,9 +65,9 @@ private:
 	{
 	public:
 		U32 m_tileSize = 0;
-		Array<TexturePtr, GBUFFER_COLOR_ATTACHMENT_COUNT> m_colorRts;
-		TexturePtr m_depthRt;
-		FramebufferPtr m_fb;
+		Array<RenderTargetDescription, GBUFFER_COLOR_ATTACHMENT_COUNT> m_colorRtDescrs;
+		RenderTargetDescription m_depthRtDescr;
+		FramebufferDescription m_fbDescr;
 	} m_gbuffer; ///< G-buffer pass.
 
 	class
@@ -108,8 +109,8 @@ private:
 		U64 m_probeUuid;
 		Timestamp m_lastUsedTimestamp = 0; ///< When it was rendered.
 
-		Array<FramebufferPtr, 6> m_lightShadingFbs;
-		Array<FramebufferPtr, 6> m_irradianceFbs;
+		Array<FramebufferDescription, 6> m_lightShadingFbDescrs;
+		Array<FramebufferDescription, 6> m_irradianceFbDescrs;
 	};
 
 	DynamicArray<CacheEntry> m_cacheEntries;
@@ -119,6 +120,18 @@ private:
 	TextureResourcePtr m_integrationLut;
 	SamplerPtr m_integrationLutSampler;
 
+	class
+	{
+	public:
+		const ReflectionProbeQueueElement* m_probe = nullptr;
+		U32 m_cacheEntryIdx = MAX_U32;
+
+		Array<RenderTargetHandle, GBUFFER_COLOR_ATTACHMENT_COUNT> m_gbufferColorRts;
+		RenderTargetHandle m_gbufferDepthRt;
+		RenderTargetHandle m_lightShadingRt;
+		RenderTargetHandle m_irradianceRt;
+	} m_ctx; ///< Runtime context.
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initGBuffer(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initLightShading(const ConfigSet& cfg);
@@ -130,12 +143,45 @@ private:
 
 	void prepareProbes(
 		RenderingContext& ctx, ReflectionProbeQueueElement*& probeToUpdate, U32& probeToUpdateCacheEntryIdx);
-	void runGBuffer(RenderingContext& rctx, const ReflectionProbeQueueElement& probe);
-	void runLightShading(RenderingContext& rctx, const ReflectionProbeQueueElement& probe, CacheEntry& cacheEntry);
-	void runIrradiance(RenderingContext& rctx, U32 cacheEntryIdx);
 
 	/// Find or allocate a new cache entry.
 	Bool findBestCacheEntry(U64 probeUuid, U32& cacheEntryIdx, Bool& cacheEntryFound);
+
+	void runGBuffer(CommandBufferPtr& cmdb);
+	void runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
+	void runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
+	void runIrradiance(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
+
+	// A RenderPassWorkCallback for G-buffer pass
+	static void runGBufferCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		Indirect* const self = scast<Indirect*>(rgraphCtx.m_userData);
+		self->runGBuffer(rgraphCtx.m_commandBuffer);
+	}
+
+	// A RenderPassWorkCallback for the light shading pass into a single face.
+	template<U faceIdx>
+	static void runLightShadingCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		Indirect* const self = scast<Indirect*>(rgraphCtx.m_userData);
+		self->runLightShading(faceIdx, rgraphCtx);
+	}
+
+	// A RenderPassWorkCallback for the mipmapping of light shading result.
+	template<U faceIdx>
+	static void runMipmappingOfLightShadingCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		Indirect* const self = scast<Indirect*>(rgraphCtx.m_userData);
+		self->runMipmappingOfLightShading(faceIdx, rgraphCtx);
+	}
+
+	// A RenderPassWorkCallback for the irradiance calculation of a single cube face.
+	template<U faceIdx>
+	static void runIrradianceCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		Indirect* const self = scast<Indirect*>(rgraphCtx.m_userData);
+		self->runIrradiance(faceIdx, rgraphCtx);
+	}
 };
 /// @}
 

+ 45 - 106
src/anki/renderer/LensFlare.cpp

@@ -4,8 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/renderer/LensFlare.h>
-#include <anki/renderer/Bloom.h>
-#include <anki/renderer/GBuffer.h>
+#include <anki/renderer/DepthDownscale.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/misc/ConfigSet.h>
@@ -14,9 +13,8 @@
 namespace anki
 {
 
-class Sprite
+struct Sprite
 {
-public:
 	Vec2 m_pos; ///< Position in NDC
 	Vec2 m_scale; ///< Scale of the quad
 	Vec4 m_color;
@@ -26,7 +24,6 @@ public:
 
 LensFlare::~LensFlare()
 {
-	m_queries.destroy(getAllocator());
 }
 
 Error LensFlare::init(const ConfigSet& config)
@@ -79,141 +76,83 @@ Error LensFlare::initOcclusion(const ConfigSet& config)
 {
 	GrManager& gr = getGrManager();
 
-	m_queries.create(getAllocator(), m_maxFlares);
+	m_indirectBuff = gr.newInstance<Buffer>(BufferInitInfo(m_maxFlares * sizeof(DrawArraysIndirectInfo),
+		BufferUsageBit::INDIRECT | BufferUsageBit::STORAGE_COMPUTE_WRITE,
+		BufferMapAccessBit::NONE,
+		"LensFlares"));
 
-	m_queryResultBuff = gr.newInstance<Buffer>(m_maxFlares * sizeof(U32),
-		BufferUsageBit::STORAGE_COMPUTE_READ | BufferUsageBit::QUERY_RESULT,
-		BufferMapAccessBit::NONE);
+	ANKI_CHECK(
+		getResourceManager().loadResource("programs/LensFlareUpdateIndirectInfo.ankiprog", m_updateIndirectBuffProg));
 
-	m_indirectBuff = gr.newInstance<Buffer>(m_maxFlares * sizeof(DrawArraysIndirectInfo),
-		BufferUsageBit::INDIRECT | BufferUsageBit::STORAGE_COMPUTE_WRITE | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
-		BufferMapAccessBit::NONE);
+	ShaderProgramResourceConstantValueInitList<1> consts(m_updateIndirectBuffProg);
+	consts.add("IN_DEPTH_MAP_SIZE", Vec2(m_r->getWidth() / 2 / 2, m_r->getHeight() / 2 / 2));
 
-	ANKI_CHECK(getResourceManager().loadResource("programs/LensFlareOcclusionTest.ankiprog", m_occlusionProg));
 	const ShaderProgramResourceVariant* variant;
-	m_occlusionProg->getOrCreateVariant(variant);
-	m_occlusionGrProg = variant->getProgram();
-
-	ANKI_CHECK(
-		getResourceManager().loadResource("programs/LensFlareUpdateIndirectInfo.ankiprog", m_updateIndirectBuffProg));
-	m_updateIndirectBuffProg->getOrCreateVariant(variant);
+	m_updateIndirectBuffProg->getOrCreateVariant(consts.get(), variant);
 	m_updateIndirectBuffGrProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void LensFlare::resetOcclusionQueries(RenderingContext& ctx, CommandBufferPtr cmdb)
+void LensFlare::updateIndirectInfo(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
-	if(ctx.m_renderQueue->m_lensFlares.getSize() > m_maxFlares)
-	{
-		ANKI_R_LOGW("Visible flares exceed the limit. Increase lf.maxFlares");
-	}
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
+	ANKI_ASSERT(count > 0);
 
-	const U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
-	for(U i = 0; i < count; ++i)
-	{
-		if(!m_queries[i])
-		{
-			m_queries[i] = getGrManager().newInstance<OcclusionQuery>();
-		}
+	cmdb->bindShaderProgram(m_updateIndirectBuffGrProg);
 
-		cmdb->resetOcclusionQuery(m_queries[i]);
-	}
-}
-
-void LensFlare::runOcclusionTests(RenderingContext& ctx, CommandBufferPtr cmdb)
-{
-	const U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
-	Vec3* positions = nullptr;
-	const Vec3* initialPositions;
-	if(count)
-	{
-		// Setup MVP UBO
-		Mat4* mvp = allocateAndBindUniforms<Mat4*>(sizeof(Mat4), cmdb, 0, 0);
-		*mvp = ctx.m_renderQueue->m_viewProjectionMatrix;
-
-		// Alloc dyn mem
-		StagingGpuMemoryToken vertToken;
-		positions = static_cast<Vec3*>(m_r->getStagingGpuMemoryManager().allocateFrame(
-			sizeof(Vec3) * count, StagingGpuMemoryType::VERTEX, vertToken));
-		initialPositions = positions;
-
-		cmdb->bindVertexBuffer(0, vertToken.m_buffer, vertToken.m_offset, sizeof(Vec3));
-
-		// Setup state
-		cmdb->bindShaderProgram(m_occlusionGrProg);
-		cmdb->setVertexAttribute(0, 0, PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT), 0);
-		cmdb->setColorChannelWriteMask(0, ColorBit::NONE);
-		cmdb->setColorChannelWriteMask(1, ColorBit::NONE);
-		cmdb->setColorChannelWriteMask(2, ColorBit::NONE);
-		cmdb->setDepthWrite(false);
-	}
+	// Write flare info
+	Vec4* flarePositions = allocateAndBindStorage<Vec4*>(sizeof(Mat4) + count * sizeof(Vec4), cmdb, 0, 0);
+	*reinterpret_cast<Mat4*>(flarePositions) = ctx.m_viewProjMatJitter;
+	flarePositions += 4;
 
 	for(U i = 0; i < count; ++i)
 	{
-		*positions = ctx.m_renderQueue->m_lensFlares[i].m_worldPosition;
-
-		// Draw and query
-		cmdb->beginOcclusionQuery(m_queries[i]);
-		cmdb->drawArrays(PrimitiveTopology::POINTS, 1, 1, positions - initialPositions);
-		cmdb->endOcclusionQuery(m_queries[i]);
-
-		++positions;
+		*flarePositions = Vec4(ctx.m_renderQueue->m_lensFlares[i].m_worldPosition, 1.0f);
+		++flarePositions;
 	}
 
-	// Restore state
-	if(count)
-	{
-		cmdb->setColorChannelWriteMask(0, ColorBit::ALL);
-		cmdb->setColorChannelWriteMask(1, ColorBit::ALL);
-		cmdb->setColorChannelWriteMask(2, ColorBit::ALL);
-		cmdb->setDepthWrite(true);
-	}
+	rgraphCtx.bindStorageBuffer(0, 1, m_runCtx.m_indirectBuffHandle);
+	rgraphCtx.bindTexture(0, 0, m_r->getDepthDownscale().getQuarterColorRt());
+	cmdb->dispatchCompute(count, 1, 1);
 }
 
-void LensFlare::updateIndirectInfo(RenderingContext& ctx, CommandBufferPtr cmdb)
+void LensFlare::populateRenderGraph(RenderingContext& ctx)
 {
-	U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
-	if(count == 0)
+	if(ctx.m_renderQueue->m_lensFlares.getSize() == 0)
 	{
 		return;
 	}
 
-	// Write results to buffer
-	for(U i = 0; i < count; ++i)
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	// Import buffer
+	m_runCtx.m_indirectBuffHandle = rgraph.importBuffer("LensFl Indirect", m_indirectBuff, BufferUsageBit::NONE);
+
+	// Update the indirect buffer
 	{
-		cmdb->writeOcclusionQueryResultToBuffer(m_queries[i], sizeof(U32) * i, m_queryResultBuff);
-	}
+		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("LF Upd Ind/ct");
 
-	// Set barrier
-	cmdb->setBufferBarrier(m_queryResultBuff,
-		BufferUsageBit::QUERY_RESULT,
-		BufferUsageBit::STORAGE_COMPUTE_READ,
-		0,
-		sizeof(DrawArraysIndirectInfo) * count);
+		rpass.setWork(runUpdateIndirectCallback, this, 0);
 
-	// Update the indirect info
-	cmdb->bindShaderProgram(m_updateIndirectBuffGrProg);
-	cmdb->bindStorageBuffer(0, 0, m_queryResultBuff, 0, MAX_PTR_SIZE);
-	cmdb->bindStorageBuffer(0, 1, m_indirectBuff, 0, MAX_PTR_SIZE);
-	cmdb->dispatchCompute(count, 1, 1);
+		rpass.newConsumer({m_runCtx.m_indirectBuffHandle, BufferUsageBit::STORAGE_COMPUTE_WRITE});
+		rpass.newConsumer({m_r->getDepthDownscale().getQuarterColorRt(), TextureUsageBit::SAMPLED_COMPUTE});
 
-	// Set barrier
-	cmdb->setBufferBarrier(m_indirectBuff,
-		BufferUsageBit::STORAGE_COMPUTE_WRITE,
-		BufferUsageBit::INDIRECT,
-		0,
-		sizeof(DrawArraysIndirectInfo) * count);
+		rpass.newProducer({m_runCtx.m_indirectBuffHandle, BufferUsageBit::STORAGE_COMPUTE_WRITE});
+	}
 }
 
-void LensFlare::run(RenderingContext& ctx, CommandBufferPtr cmdb)
+void LensFlare::runDrawFlares(const RenderingContext& ctx, CommandBufferPtr& cmdb)
 {
-	const U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
-	if(count == 0)
+	if(ctx.m_renderQueue->m_lensFlares.getSize() == 0)
 	{
 		return;
 	}
 
+	const U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
+
 	cmdb->bindShaderProgram(m_realGrProg);
 	cmdb->setDepthWrite(false);
 	cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
@@ -257,7 +196,7 @@ void LensFlare::run(RenderingContext& ctx, CommandBufferPtr cmdb)
 
 		// Render
 		ANKI_ASSERT(flareEl.m_texture);
-		cmdb->bindTexture(0, 0, TexturePtr(flareEl.m_texture));
+		cmdb->bindTexture(0, 0, TexturePtr(flareEl.m_texture), TextureUsageBit::SAMPLED_FRAGMENT);
 
 		cmdb->drawArraysIndirect(
 			PrimitiveTopology::TRIANGLE_STRIP, 1, i * sizeof(DrawArraysIndirectInfo), m_indirectBuff);

+ 27 - 14
src/anki/renderer/LensFlare.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/Gr.h>
 #include <anki/resource/TextureResource.h>
 
@@ -16,11 +16,11 @@ namespace anki
 /// @{
 
 /// Lens flare rendering pass. Part of forward shading.
-class LensFlare : public RenderingPass
+class LensFlare : public RendererObject
 {
 anki_internal:
 	LensFlare(Renderer* r)
-		: RenderingPass(r)
+		: RendererObject(r)
 	{
 	}
 
@@ -28,25 +28,22 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& config);
 
-	void resetOcclusionQueries(RenderingContext& ctx, CommandBufferPtr cmdb);
+	void runDrawFlares(const RenderingContext& ctx, CommandBufferPtr& cmdb);
 
-	void runOcclusionTests(RenderingContext& ctx, CommandBufferPtr cmdb);
+	void populateRenderGraph(RenderingContext& ctx);
 
-	void updateIndirectInfo(RenderingContext& ctx, CommandBufferPtr cmdb);
-
-	void run(RenderingContext& ctx, CommandBufferPtr cmdb);
+	/// Get it to set a dependency.
+	RenderPassBufferHandle getIndirectDrawBuffer() const
+	{
+		return m_runCtx.m_indirectBuffHandle;
+	}
 
 private:
-	// Occlusion query
-	DynamicArray<OcclusionQueryPtr> m_queries;
-	BufferPtr m_queryResultBuff;
+	// Occlusion test
 	BufferPtr m_indirectBuff;
 	ShaderProgramResourcePtr m_updateIndirectBuffProg;
 	ShaderProgramPtr m_updateIndirectBuffGrProg;
 
-	ShaderProgramResourcePtr m_occlusionProg;
-	ShaderProgramPtr m_occlusionGrProg;
-
 	// Sprite billboards
 	ShaderProgramResourcePtr m_realProg;
 	ShaderProgramPtr m_realGrProg;
@@ -54,10 +51,26 @@ private:
 	U8 m_maxFlares;
 	U16 m_maxSprites;
 
+	class
+	{
+	public:
+		RenderingContext* m_ctx = nullptr;
+		RenderPassBufferHandle m_indirectBuffHandle;
+	} m_runCtx;
+
 	ANKI_USE_RESULT Error initSprite(const ConfigSet& config);
 	ANKI_USE_RESULT Error initOcclusion(const ConfigSet& config);
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
+
+	void updateIndirectInfo(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+
+	/// A RenderPassWorkCallback for updating the indirect info.
+	static void runUpdateIndirectCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		LensFlare* const self = scast<LensFlare*>(rgraphCtx.m_userData);
+		self->updateIndirectInfo(*self->m_runCtx.m_ctx, rgraphCtx);
+	}
 };
 /// @}
 

+ 25 - 36
src/anki/renderer/LightBin.cpp

@@ -349,7 +349,7 @@ LightBin::LightBin(const GenericMemoryPoolAllocator<U8>& alloc,
 	, m_clusterCount(clusterCountX * clusterCountY * clusterCountZ)
 	, m_threadPool(threadPool)
 	, m_stagingMem(stagingMem)
-	, m_barrier(threadPool->getThreadsCount())
+	, m_barrier(threadPool->getThreadCount())
 {
 	m_clusterer.init(alloc, clusterCountX, clusterCountY, clusterCountZ);
 }
@@ -366,16 +366,9 @@ Error LightBin::bin(const Mat4& viewMat,
 	StackAllocator<U8> frameAlloc,
 	U maxLightIndices,
 	Bool shadowsEnabled,
-	StagingGpuMemoryToken& pointLightsToken,
-	StagingGpuMemoryToken& spotLightsToken,
-	StagingGpuMemoryToken* probesToken,
-	StagingGpuMemoryToken& decalsToken,
-	StagingGpuMemoryToken& clustersToken,
-	StagingGpuMemoryToken& lightIndicesToken,
-	TexturePtr& diffuseDecalTexAtlas,
-	TexturePtr& normalRoughnessDecalTexAtlas)
+	LightBinOut& out)
 {
-	ANKI_TRACE_START_EVENT(RENDERER_LIGHT_BINNING);
+	ANKI_TRACE_SCOPED_EVENT(RENDERER_LIGHT_BINNING);
 
 	// Prepare the clusterer
 	ClustererPrepareInfo pinf;
@@ -409,7 +402,7 @@ Error LightBin::bin(const Mat4& viewMat,
 	if(visiblePointLightsCount)
 	{
 		ShaderPointLight* data = static_cast<ShaderPointLight*>(m_stagingMem->allocateFrame(
-			sizeof(ShaderPointLight) * visiblePointLightsCount, StagingGpuMemoryType::UNIFORM, pointLightsToken));
+			sizeof(ShaderPointLight) * visiblePointLightsCount, StagingGpuMemoryType::UNIFORM, out.m_pointLightsToken));
 
 		ctx.m_pointLights = WeakArray<ShaderPointLight>(data, visiblePointLightsCount);
 
@@ -418,13 +411,13 @@ Error LightBin::bin(const Mat4& viewMat,
 	}
 	else
 	{
-		pointLightsToken.markUnused();
+		out.m_pointLightsToken.markUnused();
 	}
 
 	if(visibleSpotLightsCount)
 	{
 		ShaderSpotLight* data = static_cast<ShaderSpotLight*>(m_stagingMem->allocateFrame(
-			sizeof(ShaderSpotLight) * visibleSpotLightsCount, StagingGpuMemoryType::UNIFORM, spotLightsToken));
+			sizeof(ShaderSpotLight) * visibleSpotLightsCount, StagingGpuMemoryType::UNIFORM, out.m_spotLightsToken));
 
 		ctx.m_spotLights = WeakArray<ShaderSpotLight>(data, visibleSpotLightsCount);
 
@@ -433,31 +426,28 @@ Error LightBin::bin(const Mat4& viewMat,
 	}
 	else
 	{
-		spotLightsToken.markUnused();
+		out.m_spotLightsToken.markUnused();
 	}
 
-	if(probesToken)
+	if(visibleProbeCount)
 	{
-		if(visibleProbeCount)
-		{
-			ShaderProbe* data = static_cast<ShaderProbe*>(m_stagingMem->allocateFrame(
-				sizeof(ShaderProbe) * visibleProbeCount, StagingGpuMemoryType::UNIFORM, *probesToken));
+		ShaderProbe* data = static_cast<ShaderProbe*>(m_stagingMem->allocateFrame(
+			sizeof(ShaderProbe) * visibleProbeCount, StagingGpuMemoryType::UNIFORM, out.m_probesToken));
 
-			ctx.m_probes = WeakArray<ShaderProbe>(data, visibleProbeCount);
+		ctx.m_probes = WeakArray<ShaderProbe>(data, visibleProbeCount);
 
-			ctx.m_vProbes =
-				WeakArray<const ReflectionProbeQueueElement>(rqueue.m_reflectionProbes.getBegin(), visibleProbeCount);
-		}
-		else
-		{
-			probesToken->markUnused();
-		}
+		ctx.m_vProbes =
+			WeakArray<const ReflectionProbeQueueElement>(rqueue.m_reflectionProbes.getBegin(), visibleProbeCount);
+	}
+	else
+	{
+		out.m_probesToken.markUnused();
 	}
 
 	if(visibleDecalCount)
 	{
 		ShaderDecal* data = static_cast<ShaderDecal*>(m_stagingMem->allocateFrame(
-			sizeof(ShaderDecal) * visibleDecalCount, StagingGpuMemoryType::UNIFORM, decalsToken));
+			sizeof(ShaderDecal) * visibleDecalCount, StagingGpuMemoryType::UNIFORM, out.m_decalsToken));
 
 		ctx.m_decals = WeakArray<ShaderDecal>(data, visibleDecalCount);
 
@@ -465,20 +455,20 @@ Error LightBin::bin(const Mat4& viewMat,
 	}
 	else
 	{
-		decalsToken.markUnused();
+		out.m_decalsToken.markUnused();
 	}
 
 	ctx.m_bin = this;
 
 	// Get mem for clusters
 	ShaderCluster* data = static_cast<ShaderCluster*>(m_stagingMem->allocateFrame(
-		sizeof(ShaderCluster) * m_clusterCount, StagingGpuMemoryType::STORAGE, clustersToken));
+		sizeof(ShaderCluster) * m_clusterCount, StagingGpuMemoryType::STORAGE, out.m_clustersToken));
 
 	ctx.m_clusters = WeakArray<ShaderCluster>(data, m_clusterCount);
 
 	// Allocate light IDs
-	U32* data2 = static_cast<U32*>(
-		m_stagingMem->allocateFrame(maxLightIndices * sizeof(U32), StagingGpuMemoryType::STORAGE, lightIndicesToken));
+	U32* data2 = static_cast<U32*>(m_stagingMem->allocateFrame(
+		maxLightIndices * sizeof(U32), StagingGpuMemoryType::STORAGE, out.m_lightIndicesToken));
 
 	ctx.m_lightIds = WeakArray<U32>(data2, maxLightIndices);
 
@@ -490,7 +480,7 @@ Error LightBin::bin(const Mat4& viewMat,
 	ctx.m_lightIdsCount.set(SIZE_IDX_COUNT);
 
 	// Fire the async job
-	for(U i = 0; i < m_threadPool->getThreadsCount(); i++)
+	for(U i = 0; i < m_threadPool->getThreadCount(); i++)
 	{
 		tasks[i].m_ctx = &ctx;
 
@@ -500,10 +490,9 @@ Error LightBin::bin(const Mat4& viewMat,
 	// Sync
 	ANKI_CHECK(m_threadPool->waitForAllThreadsToFinish());
 
-	diffuseDecalTexAtlas = ctx.m_diffDecalTexAtlas;
-	normalRoughnessDecalTexAtlas = ctx.m_normalRoughnessDecalTexAtlas;
+	out.m_diffDecalTex = ctx.m_diffDecalTexAtlas;
+	out.m_normRoughnessDecalTex = ctx.m_normalRoughnessDecalTexAtlas;
 
-	ANKI_TRACE_STOP_EVENT(RENDERER_LIGHT_BINNING);
 	return Error::NONE;
 }
 

+ 16 - 8
src/anki/renderer/LightBin.h

@@ -16,6 +16,21 @@ class LightBinContext;
 /// @addtogroup renderer
 /// @{
 
+/// @memberof LightBin
+class LightBinOut
+{
+public:
+	StagingGpuMemoryToken m_pointLightsToken;
+	StagingGpuMemoryToken m_spotLightsToken;
+	StagingGpuMemoryToken m_probesToken;
+	StagingGpuMemoryToken m_decalsToken;
+	StagingGpuMemoryToken m_clustersToken;
+	StagingGpuMemoryToken m_lightIndicesToken;
+
+	TexturePtr m_diffDecalTex;
+	TexturePtr m_normRoughnessDecalTex;
+};
+
 /// Bins lights and probes to clusters.
 class LightBin
 {
@@ -39,14 +54,7 @@ public:
 		StackAllocator<U8> frameAlloc,
 		U maxLightIndices,
 		Bool shadowsEnabled,
-		StagingGpuMemoryToken& pointLightsToken,
-		StagingGpuMemoryToken& spotLightsToken,
-		StagingGpuMemoryToken* probesToken,
-		StagingGpuMemoryToken& decalsToken,
-		StagingGpuMemoryToken& clustersToken,
-		StagingGpuMemoryToken& lightIndicesToken,
-		TexturePtr& diffuseDecalTexAtlas,
-		TexturePtr& normalRoughnessDecalTexAtlas);
+		LightBinOut& out);
 
 	const Clusterer& getClusterer() const
 	{

+ 70 - 82
src/anki/renderer/LightShading.cpp

@@ -12,15 +12,15 @@
 #include <anki/renderer/LightBin.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/ForwardShading.h>
+#include <anki/renderer/DepthDownscale.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
 
 namespace anki
 {
 
-class ShaderCommonUniforms
+struct ShaderCommonUniforms
 {
-public:
 	Vec4 m_projectionParams;
 	Vec4 m_rendererSizeTimePad1;
 	Vec4 m_nearFarClustererMagicPad1;
@@ -31,28 +31,14 @@ public:
 	Mat4 m_invProjMat;
 };
 
-enum class IsShaderVariantBit : U8
-{
-	P_LIGHTS = 1 << 0,
-	S_LIGHTS = 1 << 1,
-	DECALS = 1 << 2,
-	INDIRECT = 1 << 3,
-	P_LIGHTS_SHADOWS = 1 << 4,
-	S_LIGHTS_SHADOWS = 1 << 5
-};
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(IsShaderVariantBit, inline)
-
 LightShading::LightShading(Renderer* r)
-	: RenderingPass(r)
+	: RendererObject(r)
 {
 }
 
 LightShading::~LightShading()
 {
-	if(m_lightBin)
-	{
-		getAllocator().deleteInstance(m_lightBin);
-	}
+	getAllocator().deleteInstance(m_lightBin);
 }
 
 Error LightShading::init(const ConfigSet& config)
@@ -92,9 +78,7 @@ Error LightShading::initInternal(const ConfigSet& config)
 		&m_r->getThreadPool(),
 		&m_r->getStagingGpuMemoryManager());
 
-	//
 	// Load shaders and programs
-	//
 	ANKI_CHECK(getResourceManager().loadResource("programs/LightShading.ankiprog", m_prog));
 
 	ShaderProgramResourceConstantValueInitList<4> consts(m_prog);
@@ -105,22 +89,19 @@ Error LightShading::initInternal(const ConfigSet& config)
 
 	m_prog->getOrCreateVariant(consts.get(), m_progVariant);
 
-	//
-	// Create framebuffer
-	//
-	m_rt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
+	// Create RT descr
+	m_rtDescr = m_r->create2DRenderTargetDescription(m_r->getWidth(),
 		m_r->getHeight(),
 		LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
 		SamplingFilter::LINEAR,
-		1,
-		"lightp"));
+		"Light Shading");
+	m_rtDescr.bake();
 
-	FramebufferInitInfo fbInit("lightp");
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+	// Create FB descr
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_fbDescr.bake();
 
 	return Error::NONE;
 }
@@ -134,69 +115,62 @@ Error LightShading::binLights(RenderingContext& ctx)
 		ctx.m_renderQueue->m_viewProjectionMatrix,
 		ctx.m_renderQueue->m_cameraTransform,
 		*ctx.m_renderQueue,
-		getFrameAllocator(),
+		ctx.m_tempAllocator,
 		m_maxLightIds,
 		true,
-		ctx.m_lightShading.m_pointLightsToken,
-		ctx.m_lightShading.m_spotLightsToken,
-		&ctx.m_lightShading.m_probesToken,
-		ctx.m_lightShading.m_decalsToken,
-		ctx.m_lightShading.m_clustersToken,
-		ctx.m_lightShading.m_lightIndicesToken,
-		ctx.m_lightShading.m_diffDecalTex,
-		ctx.m_lightShading.m_normRoughnessDecalTex));
+		m_runCtx.m_resources));
 
 	return Error::NONE;
 }
 
-void LightShading::run(RenderingContext& ctx)
+void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	const LightShadingResources& rsrc = m_runCtx.m_resources;
 
-	cmdb->beginRenderPass(m_fb);
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 	cmdb->bindShaderProgram(m_progVariant->getProgram());
 
-	cmdb->bindTexture(1, 0, m_r->getGBuffer().m_rt0);
-	cmdb->bindTexture(1, 1, m_r->getGBuffer().m_rt1);
-	cmdb->bindTexture(1, 2, m_r->getGBuffer().m_rt2);
-	cmdb->bindTexture(1, 3, m_r->getGBuffer().m_depthRt, DepthStencilAspectBit::DEPTH);
-	cmdb->informTextureCurrentUsage(m_r->getSsao().getRt(), TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->bindTexture(1, 4, m_r->getSsao().getRt());
-
-	cmdb->bindTexture(0, 0, m_r->getShadowMapping().m_shadowAtlas);
-	cmdb->bindTexture(0, 1, m_r->getIndirect().getReflectionTexture());
-	cmdb->bindTexture(0, 2, m_r->getIndirect().getIrradianceTexture());
-	cmdb->bindTextureAndSampler(
-		0, 3, m_r->getIndirect().getIntegrationLut(), m_r->getIndirect().getIntegrationLutSampler());
+	rgraphCtx.bindTexture(1, 0, m_r->getGBuffer().getColorRt(0));
+	rgraphCtx.bindTexture(1, 1, m_r->getGBuffer().getColorRt(1));
+	rgraphCtx.bindTexture(1, 2, m_r->getGBuffer().getColorRt(2));
+	rgraphCtx.bindTexture(1, 3, m_r->getGBuffer().getDepthRt());
+	rgraphCtx.bindTexture(1, 4, m_r->getSsao().getRt());
+
+	rgraphCtx.bindTexture(0, 0, m_r->getShadowMapping().getShadowmapRt());
+	rgraphCtx.bindTexture(0, 1, m_r->getIndirect().getReflectionRt());
+	rgraphCtx.bindTexture(0, 2, m_r->getIndirect().getIrradianceRt());
+	cmdb->bindTextureAndSampler(0,
+		3,
+		m_r->getIndirect().getIntegrationLut(),
+		m_r->getIndirect().getIntegrationLutSampler(),
+		TextureUsageBit::SAMPLED_FRAGMENT);
 	cmdb->bindTexture(
-		0, 4, (ctx.m_lightShading.m_diffDecalTex) ? ctx.m_lightShading.m_diffDecalTex : m_r->getDummyTexture());
+		0, 4, (rsrc.m_diffDecalTex) ? rsrc.m_diffDecalTex : m_r->getDummyTexture(), TextureUsageBit::SAMPLED_FRAGMENT);
 	cmdb->bindTexture(0,
 		5,
-		(ctx.m_lightShading.m_normRoughnessDecalTex) ? ctx.m_lightShading.m_normRoughnessDecalTex
-													 : m_r->getDummyTexture());
+		(rsrc.m_normRoughnessDecalTex) ? rsrc.m_normRoughnessDecalTex : m_r->getDummyTexture(),
+		TextureUsageBit::SAMPLED_FRAGMENT);
 
-	bindUniforms(cmdb, 0, 0, ctx.m_lightShading.m_commonToken);
-	bindUniforms(cmdb, 0, 1, ctx.m_lightShading.m_pointLightsToken);
-	bindUniforms(cmdb, 0, 2, ctx.m_lightShading.m_spotLightsToken);
-	bindUniforms(cmdb, 0, 3, ctx.m_lightShading.m_probesToken);
-	bindUniforms(cmdb, 0, 4, ctx.m_lightShading.m_decalsToken);
+	bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
+	bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
+	bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
+	bindUniforms(cmdb, 0, 3, rsrc.m_probesToken);
+	bindUniforms(cmdb, 0, 4, rsrc.m_decalsToken);
 
-	bindStorage(cmdb, 0, 0, ctx.m_lightShading.m_clustersToken);
-	bindStorage(cmdb, 0, 1, ctx.m_lightShading.m_lightIndicesToken);
+	bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
+	bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
 
 	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 
 	// Apply the forward shading result
-	m_r->getForwardShading().drawUpscale(ctx);
-
-	cmdb->endRenderPass();
+	m_r->getForwardShading().drawUpscale(ctx, rgraphCtx);
 }
 
 void LightShading::updateCommonBlock(RenderingContext& ctx)
 {
-	ShaderCommonUniforms* blk =
-		allocateUniforms<ShaderCommonUniforms*>(sizeof(ShaderCommonUniforms), ctx.m_lightShading.m_commonToken);
+	ShaderCommonUniforms* blk = allocateUniforms<ShaderCommonUniforms*>(
+		sizeof(ShaderCommonUniforms), m_runCtx.m_resources.m_commonUniformsToken);
 
 	// Start writing
 	blk->m_projectionParams = ctx.m_unprojParams;
@@ -216,20 +190,34 @@ void LightShading::updateCommonBlock(RenderingContext& ctx)
 	blk->m_invProjMat = ctx.m_projMatJitter.getInverse();
 }
 
-void LightShading::setPreRunBarriers(RenderingContext& ctx)
+void LightShading::populateRenderGraph(RenderingContext& ctx)
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
-void LightShading::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
+	// Create RT
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+
+	// Create pass
+	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Light Shading");
+
+	pass.setWork(runCallback, this, 0);
+	pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rt}}, {});
+
+	pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	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->getSsao().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getIndirect().getReflectionRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getIndirect().getIrradianceRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+
+	pass.newConsumer({m_r->getDepthDownscale().getHalfDepthColorRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getForwardShading().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+
+	pass.newProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 }
 
 } // end namespace anki

+ 38 - 18
src/anki/renderer/LightShading.h

@@ -5,21 +5,26 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
+#include <anki/renderer/LightBin.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/resource/ShaderProgramResource.h>
 
 namespace anki
 {
 
-// Forward
-class LightBin;
-
 /// @addtogroup renderer
 /// @{
 
+/// @memberof LightShading
+class LightShadingResources : public LightBinOut
+{
+public:
+	StagingGpuMemoryToken m_commonUniformsToken;
+};
+
 /// Clustered deferred light pass.
-class LightShading : public RenderingPass
+class LightShading : public RendererObject
 {
 anki_internal:
 	LightShading(Renderer* r);
@@ -28,17 +33,13 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
 
-	ANKI_USE_RESULT Error binLights(RenderingContext& ctx);
+	void populateRenderGraph(RenderingContext& ctx);
 
-	void setPreRunBarriers(RenderingContext& ctx);
-
-	void run(RenderingContext& ctx);
-
-	void setPostRunBarriers(RenderingContext& ctx);
+	ANKI_USE_RESULT Error binLights(RenderingContext& ctx);
 
-	TexturePtr getRt() const
+	RenderTargetHandle getRt() const
 	{
-		return m_rt;
+		return m_runCtx.m_rt;
 	}
 
 	const LightBin& getLightBin() const
@@ -46,15 +47,17 @@ anki_internal:
 		return *m_lightBin;
 	}
 
-private:
-	/// The IS render target
-	TexturePtr m_rt;
+	const LightShadingResources& getResources() const
+	{
+		return m_runCtx.m_resources;
+	}
 
+private:
 	Array<U32, 3> m_clusterCounts = {{0, 0, 0}};
 	U32 m_clusterCount = 0;
 
-	/// The IS FBO
-	FramebufferPtr m_fb;
+	RenderTargetDescription m_rtDescr;
+	FramebufferDescription m_fbDescr;
 
 	// Light shaders
 	ShaderProgramResourcePtr m_prog;
@@ -67,10 +70,27 @@ private:
 	U32 m_maxLightIds;
 	/// @}
 
+	class
+	{
+	public:
+		RenderTargetHandle m_rt;
+		RenderingContext* m_ctx;
+		LightShadingResources m_resources;
+	} m_runCtx; ///< Run context.
+
 	/// Called by init
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
 	void updateCommonBlock(RenderingContext& ctx);
+
+	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+
+	/// A RenderPassWorkCallback for the light pass.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		LightShading* const self = scast<LightShading*>(rgraphCtx.m_userData);
+		self->run(*self->m_runCtx.m_ctx, rgraphCtx);
+	}
 };
 /// @}
 

+ 56 - 36
src/anki/renderer/MainRenderer.cpp

@@ -30,7 +30,7 @@ MainRenderer::~MainRenderer()
 	ANKI_R_LOGI("Destroying main renderer");
 }
 
-Error MainRenderer::create(ThreadPool* threadpool,
+Error MainRenderer::init(ThreadPool* threadpool,
 	ResourceManager* resources,
 	GrManager* gr,
 	StagingGpuMemoryManager* stagingMem,
@@ -44,15 +44,9 @@ Error MainRenderer::create(ThreadPool* threadpool,
 	m_alloc = HeapAllocator<U8>(allocCb, allocCbUserData);
 	m_frameAlloc = StackAllocator<U8>(allocCb, allocCbUserData, 1024 * 1024 * 10, 1.0);
 
-	// Init default FB
+	// Init renderer and manipulate the width/height
 	m_width = config.getNumber("width");
 	m_height = config.getNumber("height");
-	FramebufferInitInfo fbInit;
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_defaultFb = gr->newInstance<Framebuffer>(fbInit);
-
-	// Init renderer and manipulate the width/height
 	ConfigSet config2 = config;
 	m_renderingQuality = config.getNumber("r.renderingQuality");
 	UVec2 size(m_renderingQuality * F32(m_width), m_renderingQuality * F32(m_height));
@@ -77,6 +71,8 @@ Error MainRenderer::create(ThreadPool* threadpool,
 		ANKI_R_LOGI("The main renderer will have to blit the offscreen renderer's result");
 	}
 
+	m_rgraph = gr->newInstance<RenderGraph>();
+
 	ANKI_R_LOGI("Main renderer initialized. Rendering size %ux%u", m_width, m_height);
 
 	return Error::NONE;
@@ -84,63 +80,87 @@ Error MainRenderer::create(ThreadPool* threadpool,
 
 Error MainRenderer::render(RenderQueue& rqueue)
 {
-	ANKI_TRACE_START_EVENT(RENDER);
+	ANKI_TRACE_SCOPED_EVENT(RENDER);
 
 	// First thing, reset the temp mem pool
 	m_frameAlloc.getMemoryPool().reset();
 
-	// Create command buffers
-	GrManager& gr = m_r->getGrManager();
-	CommandBufferInitInfo cinf;
-	cinf.m_flags =
-		CommandBufferFlag::COMPUTE_WORK | CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::TRANSFER_WORK;
-	cinf.m_hints = m_cbInitHints;
-	CommandBufferPtr cmdb = gr.newInstance<CommandBuffer>(cinf);
-
-	cinf.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
-	CommandBufferPtr defaultFbCmdb = gr.newInstance<CommandBuffer>(cinf);
-
 	// Run renderer
 	RenderingContext ctx(m_frameAlloc);
 
 	if(m_rDrawToDefaultFb)
 	{
-		ctx.m_outFb = m_defaultFb;
 		ctx.m_outFbWidth = m_width;
 		ctx.m_outFbHeight = m_height;
 	}
 
-	ctx.m_commandBuffer = cmdb;
-	ctx.m_defaultFbCommandBuffer = defaultFbCmdb;
 	ctx.m_renderQueue = &rqueue;
 	ctx.m_unprojParams = ctx.m_renderQueue->m_projectionMatrix.extractPerspectiveUnprojectionParams();
-	ANKI_CHECK(m_r->render(ctx));
+	ANKI_CHECK(m_r->populateRenderGraph(ctx));
 
 	// Blit renderer's result to default FB if needed
 	if(!m_rDrawToDefaultFb)
 	{
-		defaultFbCmdb->beginRenderPass(m_defaultFb);
-		defaultFbCmdb->setViewport(0, 0, m_width, m_height);
+		GraphicsRenderPassDescription& pass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Final Blit");
 
-		defaultFbCmdb->bindShaderProgram(m_blitGrProg);
-		defaultFbCmdb->bindTexture(0, 0, m_r->getFinalComposite().getRt());
+		FramebufferDescription fbDescr;
+		fbDescr.setDefaultFramebuffer();
+		fbDescr.bake();
+		pass.setFramebufferInfo(fbDescr, {{}}, {});
+		pass.setWork(runCallback, this, 0);
 
-		m_r->drawQuad(defaultFbCmdb);
-		defaultFbCmdb->endRenderPass();
+		pass.newConsumer({m_r->getFinalComposite().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	}
 
-	// Flush the command buffers
-	cmdb->flush();
-	defaultFbCmdb->flush();
+	// Bake the render graph
+	m_rgraph->compileNewGraph(ctx.m_renderGraphDescr, m_frameAlloc);
 
-	// Set the hints
-	m_cbInitHints = cmdb->computeInitHints();
+	// Populate the 2nd level command buffers
+	class Task : public ThreadPoolTask
+	{
+	public:
+		RenderGraphPtr m_rgraph;
+
+		Error operator()(U32 taskId, PtrSize threadsCount)
+		{
+			m_rgraph->runSecondLevel(taskId);
+			return Error::NONE;
+		}
+	};
+
+	Task task;
+	task.m_rgraph = m_rgraph;
+	for(U i = 0; i < m_r->getThreadPool().getThreadCount(); ++i)
+	{
+		m_r->getThreadPool().assignNewTask(i, &task);
+	}
 
-	ANKI_TRACE_STOP_EVENT(RENDER);
+	ANKI_CHECK(m_r->getThreadPool().waitForAllThreadsToFinish());
+
+	// Populate 1st level command buffers
+	m_rgraph->run();
+
+	// Flush
+	m_rgraph->flush();
+
+	// Reset render pass for the next frame
+	m_rgraph->reset();
+	m_r->finalize(ctx);
 
 	return Error::NONE;
 }
 
+void MainRenderer::runBlit(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	cmdb->setViewport(0, 0, m_width, m_height);
+
+	cmdb->bindShaderProgram(m_blitGrProg);
+	rgraphCtx.bindTexture(0, 0, m_r->getFinalComposite().getRt());
+
+	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3, 1);
+}
+
 Dbg& MainRenderer::getDbg()
 {
 	return m_r->getDbg();

+ 11 - 4
src/anki/renderer/MainRenderer.h

@@ -29,7 +29,7 @@ public:
 
 	~MainRenderer();
 
-	ANKI_USE_RESULT Error create(ThreadPool* threadpool,
+	ANKI_USE_RESULT Error init(ThreadPool* threadpool,
 		ResourceManager* resources,
 		GrManager* gl,
 		StagingGpuMemoryManager* stagingMem,
@@ -64,14 +64,21 @@ private:
 	ShaderProgramResourcePtr m_blitProg;
 	ShaderProgramPtr m_blitGrProg;
 
-	FramebufferPtr m_defaultFb;
 	U32 m_width = 0; ///< Default FB size.
 	U32 m_height = 0; ///< Default FB size.
 
 	F32 m_renderingQuality = 1.0;
 
-	/// Optimize job chain
-	CommandBufferInitHints m_cbInitHints;
+	RenderGraphPtr m_rgraph;
+
+	void runBlit(RenderPassWorkContext& rgraphCtx);
+
+	// A RenderPassWorkCallback for blit pass.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		MainRenderer* const self = scast<MainRenderer*>(rgraphCtx.m_userData);
+		self->runBlit(rgraphCtx);
+	}
 };
 /// @}
 

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

@@ -141,7 +141,8 @@ class DecalQueueElement final
 public:
 	const void* m_userData;
 	RenderQueueDrawCallback m_drawCallback;
-	Texture* m_diffuseAtlas;
+	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;
 	Vec4 m_diffuseAtlasUv;
 	Vec4 m_normalRoughnessAtlasUv;

+ 60 - 267
src/anki/renderer/Renderer.cpp

@@ -27,14 +27,6 @@
 namespace anki
 {
 
-static Bool threadWillDoWork(PtrSize problemSize, U32 threadId, PtrSize threadCount)
-{
-	PtrSize start, end;
-	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
-
-	return start != end;
-}
-
 Renderer::Renderer()
 	: m_sceneDrawer(this)
 {
@@ -105,8 +97,10 @@ Error Renderer::initInternal(const ConfigSet& config)
 		m_dummyTex = getGrManager().newInstance<Texture>(texinit);
 	}
 
-	m_dummyBuff = getGrManager().newInstance<Buffer>(
-		getDummyBufferSize(), BufferUsageBit::UNIFORM_ALL | BufferUsageBit::STORAGE_ALL, BufferMapAccessBit::NONE);
+	m_dummyBuff = getGrManager().newInstance<Buffer>(BufferInitInfo(getDummyBufferSize(),
+		BufferUsageBit::UNIFORM_ALL | BufferUsageBit::STORAGE_ALL,
+		BufferMapAccessBit::NONE,
+		"Dummy"));
 
 	// Init the stages. Careful with the order!!!!!!!!!!
 	m_indirect.reset(m_alloc.newInstance<Indirect>(this));
@@ -164,8 +158,6 @@ Error Renderer::initInternal(const ConfigSet& config)
 
 	initJitteredMats();
 
-	m_rgraph = m_gr->newInstance<RenderGraph>();
-
 	return Error::NONE;
 }
 
@@ -226,12 +218,8 @@ void Renderer::initJitteredMats()
 	}
 }
 
-Error Renderer::render(RenderingContext& ctx)
+Error Renderer::populateRenderGraph(RenderingContext& ctx)
 {
-	m_rgraph->reset();
-
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
 	ctx.m_jitterMat = m_jitteredMats8x[m_frameCount & (8 - 1)];
 	ctx.m_projMatJitter = ctx.m_jitterMat * ctx.m_renderQueue->m_projectionMatrix;
 	ctx.m_viewProjMatJitter = ctx.m_projMatJitter * ctx.m_renderQueue->m_viewMatrix;
@@ -252,134 +240,38 @@ Error Renderer::render(RenderingContext& ctx)
 		m_resourcesDirty = false;
 	}
 
-	// Prepare SM. Do that first because it touches the render queue elements
-	m_shadowMapping->prepareBuildCommandBuffers(ctx);
-
-	// Run stages
-	m_indirect->run(ctx);
-
-	ANKI_CHECK(m_lightShading->binLights(ctx));
-	m_lensFlare->resetOcclusionQueries(ctx, cmdb);
-
-	ANKI_CHECK(buildCommandBuffers(ctx));
-
-	// Barriers
-	m_shadowMapping->setPreRunBarriers(ctx);
-	m_gbuffer->setPreRunBarriers(ctx);
-
-	// Passes & more
-	m_shadowMapping->run(ctx);
-	m_gbuffer->run(ctx);
-
-	// Barriers
-	m_gbuffer->setPostRunBarriers(ctx);
-	m_shadowMapping->setPostRunBarriers(ctx);
-	m_depth->m_hd.setPreRunBarriers(ctx);
-
-	// Passes
-	m_depth->m_hd.run(ctx);
-	m_lensFlare->updateIndirectInfo(ctx, cmdb);
-
-	// Barriers
-	m_depth->m_hd.setPostRunBarriers(ctx);
-	m_depth->m_qd.setPreRunBarriers(ctx);
-
-	// Passes
-	m_depth->m_qd.run(ctx);
-
-	// Barriers
-	m_depth->m_qd.setPostRunBarriers(ctx);
-	m_vol->m_main.setPreRunBarriers(ctx);
-	m_ssao->m_main.setPreRunBarriers(ctx);
-
-	// Passes
-	m_vol->m_main.run(ctx);
-	m_ssao->m_main.run(ctx);
-
-	// Barriers
-	m_vol->m_main.setPostRunBarriers(ctx);
-	m_vol->m_hblur.setPreRunBarriers(ctx);
-	m_ssao->m_main.setPostRunBarriers(ctx);
-	m_ssao->m_hblur.setPreRunBarriers(ctx);
-
-	// Passes
-	m_vol->m_hblur.run(ctx);
-	m_ssao->m_hblur.run(ctx);
-
-	// Barriers
-	m_vol->m_hblur.setPostRunBarriers(ctx);
-	m_vol->m_vblur.setPreRunBarriers(ctx);
-	m_ssao->m_hblur.setPostRunBarriers(ctx);
-	m_ssao->m_vblur.setPreRunBarriers(ctx);
-
-	// Passes
-	m_vol->m_vblur.run(ctx);
-	m_ssao->m_vblur.run(ctx);
-
-	// Barriers
-	m_vol->m_vblur.setPostRunBarriers(ctx);
-	m_ssao->m_vblur.setPostRunBarriers(ctx);
-	m_forwardShading->setPreRunBarriers(ctx);
-
-	// Passes
-	m_forwardShading->run(ctx);
-
-	// Barriers
-	m_forwardShading->setPostRunBarriers(ctx);
-	m_lightShading->setPreRunBarriers(ctx);
-
-	// Passes
-	m_lightShading->run(ctx);
-
-	// Barriers
-	m_lightShading->setPostRunBarriers(ctx);
-	m_temporalAA->setPreRunBarriers(ctx);
-
-	// Passes
-	m_temporalAA->run(ctx);
-
-	// Barriers
-	m_temporalAA->setPostRunBarriers(ctx);
-	m_downscale->setPreRunBarriers(ctx);
-
-	// Passes
-	m_downscale->run(ctx);
-
-	// Barriers
-	m_downscale->setPostRunBarriers(ctx);
-
-	// Passes
-	m_tonemapping->run(ctx);
-
-	// Barriers
-	m_bloom->m_extractExposure.setPreRunBarriers(ctx);
-
-	// Passes
-	m_bloom->m_extractExposure.run(ctx);
-
-	// Barriers
-	m_bloom->m_extractExposure.setPostRunBarriers(ctx);
-	m_bloom->m_upscale.setPreRunBarriers(ctx);
-
-	// Passes
-	m_bloom->m_upscale.run(ctx);
-
-	// Barriers
-	m_bloom->m_upscale.setPostRunBarriers(ctx);
+	// Populate render graph. WARNING Watch the order
+	m_shadowMapping->populateRenderGraph(ctx);
+	m_indirect->populateRenderGraph(ctx);
+	m_gbuffer->populateRenderGraph(ctx);
+	m_depth->populateRenderGraph(ctx);
+	m_vol->populateRenderGraph(ctx);
+	m_ssao->populateRenderGraph(ctx);
+	m_lensFlare->populateRenderGraph(ctx);
+	m_forwardShading->populateRenderGraph(ctx);
+	m_lightShading->populateRenderGraph(ctx);
+	m_temporalAA->populateRenderGraph(ctx);
+	m_downscale->populateRenderGraph(ctx);
+	m_tonemapping->populateRenderGraph(ctx);
+	m_bloom->populateRenderGraph(ctx);
 
 	if(m_dbg->getEnabled())
 	{
-		ANKI_CHECK(m_dbg->run(ctx));
+		m_dbg->populateRenderGraph(ctx);
 	}
 
-	// Passes
-	ANKI_CHECK(m_finalComposite->run(ctx));
+	m_finalComposite->populateRenderGraph(ctx);
+
+	ANKI_CHECK(m_lightShading->binLights(ctx));
+
+	return Error::NONE;
+}
 
+void Renderer::finalize(const RenderingContext& ctx)
+{
 	++m_frameCount;
 	m_prevViewProjMat = ctx.m_renderQueue->m_viewProjectionMatrix;
 	m_prevCamTransform = ctx.m_renderQueue->m_cameraTransform;
-
-	return Error::NONE;
 }
 
 Vec3 Renderer::unproject(
@@ -401,7 +293,7 @@ Vec3 Renderer::unproject(
 }
 
 TextureInitInfo Renderer::create2DRenderTargetInitInfo(
-	U32 w, U32 h, const PixelFormat& format, TextureUsageBit usage, SamplingFilter filter, U mipsCount, CString name)
+	U32 w, U32 h, const PixelFormat& format, TextureUsageBit usage, SamplingFilter filter, CString name)
 {
 	ANKI_ASSERT(!!(usage & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE));
 	TextureInitInfo init(name);
@@ -412,18 +304,33 @@ TextureInitInfo Renderer::create2DRenderTargetInitInfo(
 	init.m_layerCount = 1;
 	init.m_type = TextureType::_2D;
 	init.m_format = format;
-	init.m_mipmapsCount = mipsCount;
+	init.m_mipmapsCount = 1;
 	init.m_samples = 1;
 	init.m_usage = usage;
 	init.m_sampling.m_minMagFilter = filter;
-	if(mipsCount > 1)
-	{
-		init.m_sampling.m_mipmapFilter = filter;
-	}
-	else
-	{
-		init.m_sampling.m_mipmapFilter = SamplingFilter::BASE;
-	}
+	init.m_sampling.m_repeat = false;
+	init.m_sampling.m_anisotropyLevel = 0;
+
+	return init;
+}
+
+RenderTargetDescription Renderer::create2DRenderTargetDescription(
+	U32 w, U32 h, const PixelFormat& format, TextureUsageBit usage, SamplingFilter filter, CString name)
+{
+	ANKI_ASSERT(!!(usage & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE));
+	RenderTargetDescription init(name);
+
+	init.m_width = w;
+	init.m_height = h;
+	init.m_depth = 1;
+	init.m_layerCount = 1;
+	init.m_type = TextureType::_2D;
+	init.m_format = format;
+	init.m_mipmapsCount = 1;
+	init.m_samples = 1;
+	init.m_usage = usage;
+	init.m_sampling.m_minMagFilter = filter;
+	init.m_sampling.m_mipmapFilter = filter;
 	init.m_sampling.m_repeat = false;
 	init.m_sampling.m_anisotropyLevel = 0;
 
@@ -457,6 +364,8 @@ TexturePtr Renderer::createAndClearRenderTarget(const TextureInitInfo& inf, cons
 				TextureSurfaceInfo surf(mip, 0, face, layer);
 
 				FramebufferInitInfo fbInit;
+				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)
@@ -465,6 +374,8 @@ TexturePtr Renderer::createAndClearRenderTarget(const TextureInitInfo& inf, cons
 					fbInit.m_depthStencilAttachment.m_surface = surf;
 					fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH_STENCIL;
 					fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
+
+					dsUsage = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
 				}
 				else
 				{
@@ -474,13 +385,15 @@ TexturePtr Renderer::createAndClearRenderTarget(const TextureInitInfo& inf, cons
 					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;
 				}
 				FramebufferPtr fb = m_gr->newInstance<Framebuffer>(fbInit);
 
 				cmdb->setTextureSurfaceBarrier(
 					tex, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, surf);
 
-				cmdb->beginRenderPass(fb);
+				cmdb->beginRenderPass(fb, colUsage, dsUsage);
 				cmdb->endRenderPass();
 
 				if(!!inf.m_initialUsage)
@@ -497,124 +410,4 @@ TexturePtr Renderer::createAndClearRenderTarget(const TextureInitInfo& inf, cons
 	return tex;
 }
 
-void Renderer::buildCommandBuffersInternal(RenderingContext& ctx, U32 threadId, PtrSize threadCount)
-{
-	// G-Buffer pass
-	//
-	m_gbuffer->buildCommandBuffers(ctx, threadId, threadCount);
-
-	// Append to the last MS's cmdb the occlusion tests
-	if(ctx.m_gbuffer.m_lastThreadWithWork == threadId)
-	{
-		m_lensFlare->runOcclusionTests(ctx, ctx.m_gbuffer.m_commandBuffers[threadId]);
-	}
-
-	if(ctx.m_gbuffer.m_commandBuffers[threadId])
-	{
-		ctx.m_gbuffer.m_commandBuffers[threadId]->flush();
-	}
-
-	// SM
-	//
-	m_shadowMapping->buildCommandBuffers(ctx, threadId, threadCount);
-
-	// FS
-	//
-	m_forwardShading->buildCommandBuffers(ctx, threadId, threadCount);
-
-	// Append to the last FB's cmdb the other passes
-	if(ctx.m_forwardShading.m_lastThreadWithWork == threadId)
-	{
-		m_lensFlare->run(ctx, ctx.m_forwardShading.m_commandBuffers[threadId]);
-		m_forwardShading->drawVolumetric(ctx, ctx.m_forwardShading.m_commandBuffers[threadId]);
-	}
-	else if(threadId == threadCount - 1 && ctx.m_forwardShading.m_lastThreadWithWork == MAX_U32)
-	{
-		// There is no FS work. Create a cmdb just for LF & VOL
-
-		CommandBufferInitInfo init;
-		init.m_flags =
-			CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::SMALL_BATCH;
-		init.m_framebuffer = m_forwardShading->getFramebuffer();
-		CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(init);
-
-		// Inform on textures
-		cmdb->informTextureSurfaceCurrentUsage(m_forwardShading->getRt(),
-			TextureSurfaceInfo(0, 0, 0, 0),
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
-		cmdb->informTextureSurfaceCurrentUsage(
-			m_depth->m_hd.m_colorRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
-		cmdb->informTextureSurfaceCurrentUsage(m_depth->m_hd.m_depthRt,
-			TextureSurfaceInfo(0, 0, 0, 0),
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
-
-		cmdb->setViewport(0, 0, m_forwardShading->getWidth(), m_forwardShading->getHeight());
-
-		m_lensFlare->run(ctx, cmdb);
-		m_forwardShading->drawVolumetric(ctx, cmdb);
-
-		ctx.m_forwardShading.m_commandBuffers[threadId] = cmdb;
-	}
-
-	if(ctx.m_forwardShading.m_commandBuffers[threadId])
-	{
-		ctx.m_forwardShading.m_commandBuffers[threadId]->flush();
-	}
-}
-
-Error Renderer::buildCommandBuffers(RenderingContext& ctx)
-{
-	ANKI_TRACE_SCOPED_EVENT(RENDERER_COMMAND_BUFFER_BUILDING);
-	ThreadPool& threadPool = getThreadPool();
-
-	// Find the last jobs for MS and FS
-	U32 lastMsJob = MAX_U32;
-	U32 lastFsJob = MAX_U32;
-	const U threadCount = threadPool.getThreadsCount();
-	for(U i = threadCount - 1; i != 0; --i)
-	{
-		const U gbuffProblemSize =
-			ctx.m_renderQueue->m_renderables.getSize() + ctx.m_renderQueue->m_earlyZRenderables.getSize();
-		if(threadWillDoWork(gbuffProblemSize, i, threadCount) && lastMsJob == MAX_U32)
-		{
-			lastMsJob = i;
-		}
-
-		if(threadWillDoWork(ctx.m_renderQueue->m_forwardShadingRenderables.getSize(), i, threadCount)
-			&& lastFsJob == MAX_U32)
-		{
-			lastFsJob = i;
-		}
-	}
-
-	ctx.m_gbuffer.m_lastThreadWithWork = lastMsJob;
-	ctx.m_forwardShading.m_lastThreadWithWork = lastFsJob;
-
-	// Build
-	class Task : public ThreadPoolTask
-	{
-	public:
-		Renderer* m_r ANKI_DBG_NULLIFY;
-		RenderingContext* m_ctx ANKI_DBG_NULLIFY;
-
-		Error operator()(U32 threadId, PtrSize threadCount)
-		{
-			m_r->buildCommandBuffersInternal(*m_ctx, threadId, threadCount);
-			return Error::NONE;
-		}
-	};
-
-	Task task;
-	task.m_r = this;
-	task.m_ctx = &ctx;
-	for(U i = 0; i < threadPool.getThreadsCount(); i++)
-	{
-		threadPool.assignNewTask(i, &task);
-	}
-
-	ANKI_CHECK(threadPool.waitForAllThreadsToFinish());
-
-	return Error::NONE;
-}
-
 } // end namespace anki

+ 24 - 76
src/anki/renderer/Renderer.h

@@ -30,8 +30,12 @@ class StagingGpuMemoryManager;
 class RenderingContext
 {
 public:
+	StackAllocator<U8> m_tempAllocator;
+
 	RenderQueue* m_renderQueue ANKI_DBG_NULLIFY;
 
+	RenderGraphDescription m_renderGraphDescr;
+
 	// Extra matrices
 	Mat4 m_projMatJitter;
 	Mat4 m_viewProjMatJitter;
@@ -41,58 +45,12 @@ public:
 
 	Vec4 m_unprojParams;
 
-	CommandBufferPtr m_commandBuffer; ///< Primary command buffer.
-	CommandBufferPtr m_defaultFbCommandBuffer; ///< The default framebuffer renderpass is in a separate cmdb.
-
-	StackAllocator<U8> m_tempAllocator;
-
-	class GBuffer
-	{
-	public:
-		Array<CommandBufferPtr, ThreadPool::MAX_THREADS> m_commandBuffers;
-		U32 m_lastThreadWithWork = 0;
-	} m_gbuffer;
-
-	class LensFlare
-	{
-	public:
-		DynamicArrayAuto<OcclusionQueryPtr> m_queriesToTest;
-
-		LensFlare(const StackAllocator<U8>& alloc)
-			: m_queriesToTest(alloc)
-		{
-		}
-	} m_lensFlare;
-
-	class LightShading
-	{
-	public:
-		StagingGpuMemoryToken m_commonToken;
-		StagingGpuMemoryToken m_pointLightsToken;
-		StagingGpuMemoryToken m_spotLightsToken;
-		StagingGpuMemoryToken m_probesToken;
-		StagingGpuMemoryToken m_decalsToken;
-		StagingGpuMemoryToken m_clustersToken;
-		StagingGpuMemoryToken m_lightIndicesToken;
-
-		TexturePtr m_diffDecalTex;
-		TexturePtr m_normRoughnessDecalTex;
-	} m_lightShading;
-
-	class ForwardShading
-	{
-	public:
-		Array<CommandBufferPtr, ThreadPool::MAX_THREADS> m_commandBuffers;
-		U32 m_lastThreadWithWork = 0;
-	} m_forwardShading;
-
-	FramebufferPtr m_outFb;
 	U32 m_outFbWidth = 0;
 	U32 m_outFbHeight = 0;
 
 	RenderingContext(const StackAllocator<U8>& alloc)
 		: m_tempAllocator(alloc)
-		, m_lensFlare(alloc)
+		, m_renderGraphDescr(alloc)
 	{
 	}
 };
@@ -176,6 +134,16 @@ public:
 		return *m_downscale;
 	}
 
+	LensFlare& getLensFlare()
+	{
+		return *m_lensFlare;
+	}
+
+	const LensFlare& getLensFlare() const
+	{
+		return *m_lensFlare;
+	}
+
 	U32 getWidth() const
 	{
 		return m_width;
@@ -203,7 +171,9 @@ public:
 		Bool willDrawToDefaultFbo);
 
 	/// This function does all the rendering stages and produces a final result.
-	ANKI_USE_RESULT Error render(RenderingContext& ctx);
+	ANKI_USE_RESULT Error populateRenderGraph(RenderingContext& ctx);
+
+	void finalize(const RenderingContext& ctx);
 
 anki_internal:
 	U64 getFrameCount() const
@@ -235,17 +205,6 @@ anki_internal:
 	static Vec3 unproject(
 		const Vec3& windowCoords, const Mat4& modelViewMat, const Mat4& projectionMat, const int view[4]);
 
-	/// Draws a quad. Actually it draws 2 triangles because OpenGL will no longer support quads
-	static void drawQuad(CommandBufferPtr& cmdb)
-	{
-		drawQuadInstanced(cmdb, 1);
-	}
-
-	static void drawQuadInstanced(CommandBufferPtr& cmdb, U32 primitiveCount)
-	{
-		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3, primitiveCount);
-	}
-
 	/// Get the LOD given the distance of an object from the camera
 	U calculateLod(F32 distance) const
 	{
@@ -265,13 +224,12 @@ anki_internal:
 	}
 
 	/// Create the init info for a 2D texture that will be used as a render target.
-	ANKI_USE_RESULT TextureInitInfo create2DRenderTargetInitInfo(U32 w,
-		U32 h,
-		const PixelFormat& format,
-		TextureUsageBit usage,
-		SamplingFilter filter,
-		U mipsCount = 1,
-		CString name = {});
+	ANKI_USE_RESULT TextureInitInfo create2DRenderTargetInitInfo(
+		U32 w, U32 h, const PixelFormat& format, TextureUsageBit usage, SamplingFilter filter, CString name = {});
+
+	/// Create the init info for a 2D texture that will be used as a render target.
+	ANKI_USE_RESULT RenderTargetDescription create2DRenderTargetDescription(
+		U32 w, U32 h, const PixelFormat& format, TextureUsageBit usage, SamplingFilter filter, CString name = {});
 
 	ANKI_USE_RESULT TexturePtr createAndClearRenderTarget(
 		const TextureInitInfo& inf, const ClearValue& clearVal = ClearValue());
@@ -352,11 +310,6 @@ anki_internal:
 		return m_linearSampler;
 	}
 
-	RenderGraphPtr getRenderGraph() const
-	{
-		return m_rgraph;
-	}
-
 private:
 	ThreadPool* m_threadpool = nullptr;
 	ResourceManager* m_resources = nullptr;
@@ -413,13 +366,8 @@ private:
 	SamplerPtr m_nearestSampler;
 	SamplerPtr m_linearSampler;
 
-	RenderGraphPtr m_rgraph;
-
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
-	ANKI_USE_RESULT Error buildCommandBuffers(RenderingContext& ctx);
-	void buildCommandBuffersInternal(RenderingContext& ctx, U32 threadId, PtrSize threadCount);
-
 	void initJitteredMats();
 };
 /// @}

+ 24 - 13
src/anki/renderer/RenderingPass.cpp → src/anki/renderer/RendererObject.cpp

@@ -3,44 +3,39 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/util/Enum.h>
 
 namespace anki
 {
 
-GrManager& RenderingPass::getGrManager()
+GrManager& RendererObject::getGrManager()
 {
 	return m_r->getGrManager();
 }
 
-const GrManager& RenderingPass::getGrManager() const
+const GrManager& RendererObject::getGrManager() const
 {
 	return m_r->getGrManager();
 }
 
-HeapAllocator<U8> RenderingPass::getAllocator() const
+HeapAllocator<U8> RendererObject::getAllocator() const
 {
 	return m_r->getAllocator();
 }
 
-StackAllocator<U8> RenderingPass::getFrameAllocator() const
-{
-	return m_r->getFrameAllocator();
-}
-
-ResourceManager& RenderingPass::getResourceManager()
+ResourceManager& RendererObject::getResourceManager()
 {
 	return m_r->getResourceManager();
 }
 
-void* RenderingPass::allocateFrameStagingMemory(PtrSize size, StagingGpuMemoryType usage, StagingGpuMemoryToken& token)
+void* RendererObject::allocateFrameStagingMemory(PtrSize size, StagingGpuMemoryType usage, StagingGpuMemoryToken& token)
 {
 	return m_r->getStagingGpuMemoryManager().allocateFrame(size, usage, token);
 }
 
-void RenderingPass::bindUniforms(CommandBufferPtr& cmdb, U set, U binding, const StagingGpuMemoryToken& token) const
+void RendererObject::bindUniforms(CommandBufferPtr& cmdb, U set, U binding, const StagingGpuMemoryToken& token) const
 {
 	if(token && !token.isUnused())
 	{
@@ -52,7 +47,7 @@ void RenderingPass::bindUniforms(CommandBufferPtr& cmdb, U set, U binding, const
 	}
 }
 
-void RenderingPass::bindStorage(CommandBufferPtr& cmdb, U set, U binding, const StagingGpuMemoryToken& token) const
+void RendererObject::bindStorage(CommandBufferPtr& cmdb, U set, U binding, const StagingGpuMemoryToken& token) const
 {
 	if(token && !token.isUnused())
 	{
@@ -64,4 +59,20 @@ void RenderingPass::bindStorage(CommandBufferPtr& cmdb, U set, U binding, const
 	}
 }
 
+U32 RendererObject::computeNumberOfSecondLevelCommandBuffers(U32 drawcallCount) const
+{
+	const U drawcallsPerThread = drawcallCount / m_r->getThreadPool().getThreadCount();
+	U secondLevelCmdbCount;
+	if(drawcallsPerThread < MIN_DRAWCALLS_PER_2ND_LEVEL_COMMAND_BUFFER)
+	{
+		secondLevelCmdbCount = max<U>(1u, drawcallCount / MIN_DRAWCALLS_PER_2ND_LEVEL_COMMAND_BUFFER);
+	}
+	else
+	{
+		secondLevelCmdbCount = m_r->getThreadPool().getThreadCount();
+	}
+
+	return secondLevelCmdbCount;
+}
+
 } // end namespace anki

+ 29 - 14
src/anki/renderer/RenderingPass.h → src/anki/renderer/RendererObject.h

@@ -23,22 +23,38 @@ class ConfigSet;
 /// @addtogroup renderer
 /// @{
 
-/// Rendering pass
-class RenderingPass
+/// Renderer object.
+class RendererObject
 {
 anki_internal:
-	RenderingPass(Renderer* r)
+	RendererObject(Renderer* r)
 		: m_r(r)
 	{
 	}
 
-	~RenderingPass()
+	~RendererObject()
 	{
 	}
 
 	HeapAllocator<U8> getAllocator() const;
 
-	StackAllocator<U8> getFrameAllocator() const;
+protected:
+	Renderer* m_r; ///< Know your father
+
+	GrManager& getGrManager();
+	const GrManager& getGrManager() const;
+
+	ResourceManager& getResourceManager();
+
+	void* allocateFrameStagingMemory(PtrSize size, StagingGpuMemoryType usage, StagingGpuMemoryToken& token);
+
+	U32 computeNumberOfSecondLevelCommandBuffers(U32 drawcallCount) const;
+
+	/// Used in fullscreen quad draws.
+	static void drawQuad(CommandBufferPtr& cmdb)
+	{
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3, 1);
+	}
 
 	template<typename TPtr>
 	TPtr allocateUniforms(PtrSize size, StagingGpuMemoryToken& token)
@@ -65,15 +81,14 @@ anki_internal:
 
 	void bindStorage(CommandBufferPtr& cmdb, U set, U binding, const StagingGpuMemoryToken& token) const;
 
-protected:
-	Renderer* m_r; ///< Know your father
-
-	GrManager& getGrManager();
-	const GrManager& getGrManager() const;
-
-	ResourceManager& getResourceManager();
-
-	void* allocateFrameStagingMemory(PtrSize size, StagingGpuMemoryType usage, StagingGpuMemoryToken& token);
+	template<typename TPtr>
+	TPtr allocateAndBindStorage(PtrSize size, CommandBufferPtr& cmdb, U set, U binding)
+	{
+		StagingGpuMemoryToken token;
+		TPtr ptr = allocateStorage<TPtr>(size, token);
+		bindStorage(cmdb, set, binding, token);
+		return ptr;
+	}
 };
 /// @}
 

+ 0 - 60
src/anki/renderer/ScreenSpaceLensFlare.cpp

@@ -1,60 +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/renderer/ScreenSpaceLensFlare.h>
-#include <anki/renderer/Renderer.h>
-#include <anki/renderer/Bloom.h>
-#include <anki/misc/ConfigSet.h>
-
-namespace anki
-{
-
-Error ScreenSpaceLensFlare::init(const ConfigSet& config)
-{
-	ANKI_R_LOGI("Initializing screen space lens flare");
-
-	Error err = initInternal(config);
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to init screen space lens flare");
-	}
-
-	return err;
-}
-
-Error ScreenSpaceLensFlare::initInternal(const ConfigSet& config)
-{
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/LensDirt.ankitex", m_lensDirtTex));
-
-	ANKI_CHECK(getResourceManager().loadResource("programs/ScreenSpaceLensFlare.ankiprog", m_prog));
-
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add(
-		"INPUT_TEX_SIZE", UVec2(m_r->getBloom().m_extractExposure.m_width, m_r->getBloom().m_extractExposure.m_height));
-
-	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(consts.get(), variant);
-	m_grProg = variant->getProgram();
-
-	return Error::NONE;
-}
-
-void ScreenSpaceLensFlare::run(RenderingContext& ctx)
-{
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	// Draw to the SSLF FB
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
-	cmdb->bindTexture(0, 0, m_r->getBloom().m_extractExposure.m_rt);
-	cmdb->bindTexture(0, 1, m_lensDirtTex->getGrTexture());
-
-	m_r->drawQuad(cmdb);
-
-	// Retore state
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
-}
-
-} // end namespace anki

+ 0 - 37
src/anki/renderer/ScreenSpaceLensFlare.h

@@ -1,37 +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
-
-#pragma once
-
-#include <anki/renderer/RenderingPass.h>
-
-namespace anki
-{
-
-/// @addtogroup renderer
-/// @{
-
-/// Screen space lens flare pass.
-class ScreenSpaceLensFlare : public RenderingPass
-{
-anki_internal:
-	ScreenSpaceLensFlare(Renderer* r)
-		: RenderingPass(r)
-	{
-	}
-
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-	void run(RenderingContext& ctx);
-
-private:
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-	TextureResourcePtr m_lensDirtTex;
-
-	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
-};
-/// @}
-
-} // end namespace anki

+ 94 - 132
src/anki/renderer/ShadowMapping.cpp

@@ -47,20 +47,20 @@ Error ShadowMapping::initScratch(const ConfigSet& cfg)
 		m_scratchTileCount = cfg.getNumber("r.shadowMapping.scratchTileCount");
 		m_scratchTileResolution = cfg.getNumber("r.shadowMapping.resolution");
 
-		m_scratchRt = m_r->createAndClearRenderTarget(
-			m_r->create2DRenderTargetInitInfo(m_scratchTileResolution * m_scratchTileCount,
-				m_scratchTileResolution,
-				SHADOW_DEPTH_PIXEL_FORMAT,
-				TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-				SamplingFilter::LINEAR,
-				1,
-				"scratch_smap"));
-
-		FramebufferInitInfo fbInit("scratch_smap");
-		fbInit.m_depthStencilAttachment.m_texture = m_scratchRt;
-		fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
-		fbInit.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
-		m_scratchFb = getGrManager().newInstance<Framebuffer>(fbInit);
+		// RT
+		m_scratchRtDescr = m_r->create2DRenderTargetDescription(m_scratchTileResolution * m_scratchTileCount,
+			m_scratchTileResolution,
+			SHADOW_DEPTH_PIXEL_FORMAT,
+			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
+			SamplingFilter::LINEAR,
+			"Scratch ShadMap");
+		m_scratchRtDescr.bake();
+
+		// FB
+		m_scratchFbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
+		m_scratchFbDescr.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
+		m_scratchFbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+		m_scratchFbDescr.bake();
 	}
 
 	return Error::NONE;
@@ -74,23 +74,22 @@ Error ShadowMapping::initEsm(const ConfigSet& cfg)
 		m_tileCountPerRowOrColumn = cfg.getNumber("r.shadowMapping.tileCountPerRowOrColumn");
 		m_atlasResolution = m_tileResolution * m_tileCountPerRowOrColumn;
 
+		// RT
 		TextureInitInfo texinit = m_r->create2DRenderTargetInitInfo(m_atlasResolution,
 			m_atlasResolution,
 			SHADOW_COLOR_PIXEL_FORMAT,
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			SamplingFilter::LINEAR,
-			1,
 			"esm");
 		texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
 		ClearValue clearVal;
 		clearVal.m_colorf[0] = 1.0f;
-		m_shadowAtlas = m_r->createAndClearRenderTarget(texinit, clearVal);
+		m_esmAtlas = m_r->createAndClearRenderTarget(texinit, clearVal);
 
-		FramebufferInitInfo fbInit("esm");
-		fbInit.m_colorAttachments[0].m_texture = m_shadowAtlas;
-		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::LOAD;
-		fbInit.m_colorAttachmentCount = 1;
-		m_esmFb = getGrManager().newInstance<Framebuffer>(fbInit);
+		// FB
+		m_esmFbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::LOAD;
+		m_esmFbDescr.m_colorAttachmentCount = 1;
+		m_esmFbDescr.bake();
 	}
 
 	// Tiles
@@ -111,8 +110,8 @@ Error ShadowMapping::initEsm(const ConfigSet& cfg)
 
 				tile.m_viewport[0] = x * m_tileResolution;
 				tile.m_viewport[1] = y * m_tileResolution;
-				tile.m_viewport[2] = tile.m_viewport[0] + m_tileResolution;
-				tile.m_viewport[3] = tile.m_viewport[1] + m_tileResolution;
+				tile.m_viewport[2] = m_tileResolution;
+				tile.m_viewport[3] = m_tileResolution;
 			}
 		}
 
@@ -144,101 +143,51 @@ Error ShadowMapping::initInternal(const ConfigSet& cfg)
 	return Error::NONE;
 }
 
-void ShadowMapping::run(RenderingContext& ctx)
+void ShadowMapping::runEsm(RenderPassWorkContext& rgraphCtx)
 {
+	ANKI_ASSERT(m_esmResolveWorkItems.getSize());
 	ANKI_TRACE_SCOPED_EVENT(RENDER_SM);
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	if(m_scratchWorkItems.getSize())
-	{
-		// Run the scratch pass
-		ANKI_ASSERT(m_freeScratchTiles < m_scratchTileCount);
-		cmdb->beginRenderPass(m_scratchFb,
-			0,
-			0,
-			(m_scratchTileCount - m_freeScratchTiles) * m_scratchTileResolution,
-			m_scratchTileResolution);
-
-		for(U tid = 0; tid < m_r->getThreadPool().getThreadsCount(); ++tid)
-		{
-			cmdb->pushSecondLevelCommandBuffer(m_scratchSecondLevelCmdbs[tid]);
-			m_scratchSecondLevelCmdbs[tid].reset(nullptr);
-		}
 
-		cmdb->endRenderPass();
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
-		// Barriers
-		cmdb->setTextureSurfaceBarrier(m_scratchRt,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-			TextureUsageBit::SAMPLED_FRAGMENT,
-			TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->setTextureSurfaceBarrier(m_shadowAtlas,
-			TextureUsageBit::SAMPLED_FRAGMENT,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			TextureSurfaceInfo(0, 0, 0, 0));
+	cmdb->bindShaderProgram(m_esmResolveGrProg);
+	rgraphCtx.bindTexture(0, 0, m_scratchRt);
 
-		// ESM pass
-		cmdb->bindShaderProgram(m_esmResolveGrProg);
-		cmdb->bindTexture(0, 0, m_scratchRt);
-		cmdb->beginRenderPass(m_esmFb);
+	for(const EsmResolveWorkItem& workItem : m_esmResolveWorkItems)
+	{
+		ANKI_TRACE_INC_COUNTER(RENDERER_SHADOW_PASSES, 1);
 
-		for(const EsmResolveWorkItem& workItem : m_esmResolveWorkItems)
-		{
-			ANKI_TRACE_INC_COUNTER(RENDERER_SHADOW_PASSES, 1);
-
-			cmdb->setViewport(workItem.m_viewportOut[0],
-				workItem.m_viewportOut[1],
-				workItem.m_viewportOut[2],
-				workItem.m_viewportOut[3]);
-			cmdb->setScissor(workItem.m_viewportOut[0],
-				workItem.m_viewportOut[1],
-				workItem.m_viewportOut[2],
-				workItem.m_viewportOut[3]);
-
-			Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4) * 2, cmdb, 0, 0);
-			unis[0] = Vec4(workItem.m_cameraNear, workItem.m_cameraFar, 0.0f, 0.0f);
-			unis[1] = workItem.m_uvIn;
-
-			m_r->drawQuad(cmdb);
-		}
+		cmdb->setViewport(
+			workItem.m_viewportOut[0], workItem.m_viewportOut[1], workItem.m_viewportOut[2], workItem.m_viewportOut[3]);
+		cmdb->setScissor(
+			workItem.m_viewportOut[0], workItem.m_viewportOut[1], workItem.m_viewportOut[2], workItem.m_viewportOut[3]);
 
-		cmdb->endRenderPass();
+		Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4) * 2, cmdb, 0, 0);
+		unis[0] = Vec4(workItem.m_cameraNear, workItem.m_cameraFar, 0.0f, 0.0f);
+		unis[1] = workItem.m_uvIn;
 
-		// Restore GR state
-		cmdb->setScissor(0, 0, MAX_U16, MAX_U16);
+		drawQuad(cmdb);
 	}
+
+	// Restore GR state
+	cmdb->setScissor(0, 0, MAX_U32, MAX_U32);
 }
 
-void ShadowMapping::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount)
+void ShadowMapping::runShadowMapping(RenderPassWorkContext& rgraphCtx)
 {
+	ANKI_ASSERT(m_scratchWorkItems.getSize());
 	ANKI_TRACE_SCOPED_EVENT(RENDER_SM);
 
-	CommandBufferPtr cmdb;
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	const U threadIdx = rgraphCtx.m_currentSecondLevelCommandBufferIndex;
 
 	for(ScratchBufferWorkItem& work : m_scratchWorkItems)
 	{
-		if(work.m_threadPoolTaskIdx != threadId)
+		if(work.m_threadPoolTaskIdx != threadIdx)
 		{
 			continue;
 		}
 
-		// Lazily create the command buffer
-		if(!cmdb.isCreated())
-		{
-			CommandBufferInitInfo cinf;
-			cinf.m_flags = CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::GRAPHICS_WORK;
-			cinf.m_framebuffer = m_scratchFb;
-			if(work.m_renderableElementCount < COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS)
-			{
-				cinf.m_flags |= CommandBufferFlag::SMALL_BATCH;
-			}
-			cmdb = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
-
-			// Inform on Rts
-			cmdb->informTextureSurfaceCurrentUsage(
-				m_scratchRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
-		}
-
 		// Set state
 		cmdb->setViewport(work.m_viewport[0], work.m_viewport[1], work.m_viewport[2], work.m_viewport[3]);
 		cmdb->setScissor(work.m_viewport[0], work.m_viewport[1], work.m_viewport[2], work.m_viewport[3]);
@@ -251,43 +200,59 @@ void ShadowMapping::buildCommandBuffers(RenderingContext& ctx, U threadId, U thr
 			work.m_renderQueue->m_renderables.getBegin() + work.m_firstRenderableElement
 				+ work.m_renderableElementCount);
 	}
-
-	if(cmdb.isCreated())
-	{
-		cmdb->flush();
-		m_scratchSecondLevelCmdbs[threadId] = cmdb;
-	}
 }
 
-void ShadowMapping::setPreRunBarriers(RenderingContext& ctx)
+void ShadowMapping::populateRenderGraph(RenderingContext& ctx)
 {
 	ANKI_TRACE_SCOPED_EVENT(RENDER_SM);
 
+	// First process the lights
+	U32 threadCountForScratchPass = 0;
+	processLights(ctx, threadCountForScratchPass);
+
+	// Build the render graph
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	if(m_scratchWorkItems.getSize())
 	{
-		CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-		cmdb->setTextureSurfaceBarrier(m_scratchRt,
-			TextureUsageBit::NONE,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-			TextureSurfaceInfo(0, 0, 0, 0));
-	}
-}
+		// Will have to create render passes
 
-void ShadowMapping::setPostRunBarriers(RenderingContext& ctx)
-{
-	ANKI_TRACE_SCOPED_EVENT(RENDER_SM);
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+		// Scratch pass
+		{
+			// Compute render area
+			const U32 minx = 0, miny = 0, height = m_scratchTileResolution;
+			const U32 width = m_scratchTileResolution * m_scratchWorkItems.getSize();
+
+			GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SM scratch");
+
+			m_scratchRt = rgraph.newRenderTarget(m_scratchRtDescr);
+			pass.setFramebufferInfo(m_scratchFbDescr, {}, m_scratchRt, minx, miny, width, height);
+			ANKI_ASSERT(
+				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});
+		}
 
-	if(m_scratchWorkItems.getSize())
-	{
-		cmdb->setTextureSurfaceBarrier(m_shadowAtlas,
-			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			TextureUsageBit::SAMPLED_FRAGMENT,
-			TextureSurfaceInfo(0, 0, 0, 0));
+		// ESM pass
+		{
+			GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("ESM");
+
+			m_esmRt = rgraph.importRenderTarget("ESM", m_esmAtlas, TextureUsageBit::SAMPLED_FRAGMENT);
+			pass.setFramebufferInfo(m_esmFbDescr, {{m_esmRt}}, {});
+			pass.setWork(runEsmCallback, this, 0);
+
+			pass.newConsumer({m_scratchRt, TextureUsageBit::SAMPLED_FRAGMENT, DepthStencilAspectBit::DEPTH});
+			pass.newConsumer({m_esmRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+			pass.newProducer({m_esmRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		}
 	}
 	else
 	{
-		cmdb->informTextureCurrentUsage(m_shadowAtlas, TextureUsageBit::SAMPLED_FRAGMENT);
+		// No need for shadowmapping passes, just import the ESM atlas
+		m_esmRt = rgraph.importRenderTarget("ESM", m_esmAtlas, TextureUsageBit::SAMPLED_FRAGMENT);
 	}
 }
 
@@ -311,10 +276,8 @@ Mat4 ShadowMapping::createSpotLightTextureMatrix(const Tile& tile)
 		1.0);
 }
 
-void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
+void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScratchPass)
 {
-	ANKI_TRACE_SCOPED_EVENT(RENDER_SM);
-
 	// Reset stuff
 	m_freeScratchTiles = m_scratchTileCount;
 
@@ -431,7 +394,7 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 		}
 		else
 		{
-			// Doesn't have renderables or the allocation failed, won't be shadow caster
+			// Doesn't have renderables or the allocation failed, won't be a shadow caster
 			light->m_shadowRenderQueue = nullptr;
 		}
 	}
@@ -444,7 +407,8 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 		U lightToRenderDrawcallCount = lightToRender->m_drawcallCount;
 		const LightToRenderToScratchInfo* lightToRenderEnd = lightsToRender.getEnd();
 
-		const U threadCount = m_r->getThreadPool().getThreadsCount();
+		const U threadCount = computeNumberOfSecondLevelCommandBuffers(drawcallCount);
+		threadCountForScratchPass = threadCount;
 		for(U taskId = 0; taskId < threadCount; ++taskId)
 		{
 			PtrSize start, end;
@@ -452,6 +416,8 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 
 			// While there are drawcalls in this task emit new work items
 			U taskDrawcallCount = end - start;
+			ANKI_ASSERT(taskDrawcallCount > 0 && "Because we used computeNumberOfSecondLevelCommandBuffers()");
+
 			while(taskDrawcallCount)
 			{
 				ANKI_ASSERT(lightToRender != lightToRenderEnd);
@@ -499,15 +465,11 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 			ANKI_ASSERT(esmItems && itemSize && itemStorageSize);
 			m_esmResolveWorkItems = WeakArray<EsmResolveWorkItem>(esmItems, itemSize);
 		}
-
-		m_scratchSecondLevelCmdbs =
-			WeakArray<CommandBufferPtr>(ctx.m_tempAllocator.newArray<CommandBufferPtr>(threadCount), threadCount);
 	}
 	else
 	{
 		m_scratchWorkItems = WeakArray<ScratchBufferWorkItem>();
 		m_esmResolveWorkItems = WeakArray<EsmResolveWorkItem>();
-		m_scratchSecondLevelCmdbs = WeakArray<CommandBufferPtr>();
 	}
 }
 
@@ -523,7 +485,7 @@ void ShadowMapping::newScratchAndEsmResloveRenderWorkItems(U32 tileIdx,
 		Array<U32, 4> viewport;
 		viewport[0] = scratchTileIdx * m_scratchTileResolution;
 		viewport[1] = 0;
-		viewport[2] = viewport[0] + m_scratchTileResolution;
+		viewport[2] = m_scratchTileResolution;
 		viewport[3] = m_scratchTileResolution;
 
 		LightToRenderToScratchInfo toRender = {

+ 37 - 18
src/anki/renderer/ShadowMapping.h

@@ -5,10 +5,9 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/Gr.h>
 #include <anki/resource/TextureResource.h>
-#include <anki/util/Array.h>
 
 namespace anki
 {
@@ -17,13 +16,11 @@ namespace anki
 /// @{
 
 /// Shadowmapping pass
-class ShadowMapping : public RenderingPass
+class ShadowMapping : public RendererObject
 {
 anki_internal:
-	TexturePtr m_shadowAtlas; ///< ESM texture atlas.
-
 	ShadowMapping(Renderer* r)
-		: RenderingPass(r)
+		: RendererObject(r)
 	{
 	}
 
@@ -31,15 +28,13 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
 
-	void prepareBuildCommandBuffers(RenderingContext& ctx);
-
-	void buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount);
-
-	void setPreRunBarriers(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
-	void run(RenderingContext& ctx);
-
-	void setPostRunBarriers(RenderingContext& ctx);
+	RenderTargetHandle getShadowmapRt() const
+	{
+		return m_esmRt;
+	}
 
 private:
 	/// @name ESM stuff
@@ -58,6 +53,7 @@ private:
 		Array<U32, 4> m_viewport;
 	};
 
+	/// A HashMap key.
 	class TileKey
 	{
 	public:
@@ -70,7 +66,10 @@ private:
 		}
 	};
 
-	FramebufferPtr m_esmFb;
+	FramebufferDescription m_esmFbDescr; ///< The FB for ESM
+	TexturePtr m_esmAtlas; ///< ESM texture atlas.
+	RenderTargetHandle m_esmRt;
+
 	U32 m_tileResolution = 0; ///< Tile resolution.
 	U32 m_atlasResolution = 0; ///< Atlas size is (m_atlasResolution, m_atlasResolution)
 	U32 m_tileCountPerRowOrColumn = 0;
@@ -97,12 +96,22 @@ private:
 	ANKI_USE_RESULT Error initEsm(const ConfigSet& cfg);
 
 	static Mat4 createSpotLightTextureMatrix(const Tile& tile);
+
+	/// A RenderPassWorkCallback for ESM
+	static void runEsmCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		scast<ShadowMapping*>(rgraphCtx.m_userData)->runEsm(rgraphCtx);
+	}
+
+	void runEsm(RenderPassWorkContext& rgraphCtx);
 	/// @}
 
 	/// @name Scratch buffer stuff
 	/// @{
-	TexturePtr m_scratchRt; ///< Size of the RT is (m_scratchTileSize * m_scratchTileCount, m_scratchTileSize).
-	FramebufferPtr m_scratchFb;
+	RenderTargetHandle m_scratchRt; ///< Size of the RT is (m_scratchTileSize * m_scratchTileCount, m_scratchTileSize).
+	FramebufferDescription m_scratchFbDescr; ///< FB info.
+	RenderTargetDescription m_scratchRtDescr; ///< Render target.
+
 	U32 m_scratchTileCount = 0;
 	U32 m_scratchTileResolution = 0;
 	U32 m_freeScratchTiles = 0;
@@ -120,9 +129,16 @@ private:
 	struct LightToRenderToScratchInfo;
 
 	WeakArray<ScratchBufferWorkItem> m_scratchWorkItems;
-	WeakArray<CommandBufferPtr> m_scratchSecondLevelCmdbs;
 
 	ANKI_USE_RESULT Error initScratch(const ConfigSet& cfg);
+
+	/// A RenderPassWorkCallback for shadow passes.
+	static void runShadowmappingCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		scast<ShadowMapping*>(rgraphCtx.m_userData)->runShadowMapping(rgraphCtx);
+	}
+
+	void runShadowMapping(RenderPassWorkContext& rgraphCtx);
 	/// @}
 
 	/// @name Misc & common
@@ -144,6 +160,9 @@ private:
 		DynamicArrayAuto<EsmResolveWorkItem>& esmResolveWorkItem,
 		U32& drawcallCount) const;
 
+	/// Iterate lights and create work items.
+	void processLights(RenderingContext& ctx, U32& threadCountForScratchPass);
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
 	/// @}
 };

+ 148 - 145
src/anki/renderer/Ssao.cpp

@@ -16,172 +16,68 @@ namespace anki
 
 const PixelFormat Ssao::RT_PIXEL_FORMAT(ComponentFormat::R8, TransformFormat::UNORM);
 
-Error SsaoMain::init(const ConfigSet& config)
+Ssao::~Ssao()
+{
+}
+
+Error Ssao::initMain(const ConfigSet& config)
 {
 	// Noise
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_main.m_noiseTex));
 
 	// Shader
-	ANKI_CHECK(getResourceManager().loadResource("programs/Ssao.ankiprog", m_prog));
+	ANKI_CHECK(getResourceManager().loadResource("programs/Ssao.ankiprog", m_main.m_prog));
 
-	ShaderProgramResourceConstantValueInitList<6> consts(m_prog);
-	consts.add("NOISE_MAP_SIZE", U32(m_noiseTex->getWidth()))
-		.add("FB_SIZE", UVec2(m_ssao->m_width, m_ssao->m_height))
+	ShaderProgramResourceConstantValueInitList<6> consts(m_main.m_prog);
+	consts.add("NOISE_MAP_SIZE", U32(m_main.m_noiseTex->getWidth()))
+		.add("FB_SIZE", UVec2(m_width, m_height))
 		.add("RADIUS", 3.0f)
 		.add("BIAS", 0.0f)
 		.add("STRENGTH", 2.0f)
 		.add("HISTORY_FEEDBACK", 1.0f / 4.0f);
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(consts.get(), variant);
-	m_grProg = variant->getProgram();
+	m_main.m_prog->getOrCreateVariant(consts.get(), variant);
+	m_main.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void SsaoMain::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void SsaoMain::run(RenderingContext& ctx)
-{
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->beginRenderPass(m_ssao->m_fb[m_r->getFrameCount() & 1]);
-	cmdb->setViewport(0, 0, m_ssao->m_width, m_ssao->m_height);
-	cmdb->bindShaderProgram(m_grProg);
-
-	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_colorRt);
-	cmdb->bindTextureAndSampler(0, 1, m_r->getGBuffer().m_rt2, m_r->getLinearSampler());
-	cmdb->bindTexture(0, 2, m_noiseTex->getGrTexture());
-	cmdb->informTextureCurrentUsage(m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1], TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->bindTexture(0, 3, m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1]);
-
-	struct Unis
-	{
-		Vec4 m_unprojectionParams;
-		Vec4 m_projectionMat;
-		Vec4 m_noiseLayerPad3;
-		Mat4 m_prevViewProjMatMulInvViewProjMat;
-	};
-
-	Unis* unis = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 0);
-	const Mat4& pmat = ctx.m_renderQueue->m_projectionMatrix;
-	unis->m_unprojectionParams = ctx.m_unprojParams;
-	unis->m_projectionMat = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
-	unis->m_noiseLayerPad3 = Vec4(m_r->getFrameCount() % m_noiseTex->getLayerCount(), 0.0, 0.0, 0.0);
-	unis->m_prevViewProjMatMulInvViewProjMat =
-		ctx.m_prevViewProjMat * ctx.m_renderQueue->m_viewProjectionMatrix.getInverse();
-
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-void SsaoMain::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-Error SsaoHBlur::init(const ConfigSet& config)
+Error Ssao::initHBlur(const ConfigSet& config)
 {
 	// shader
-	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/DepthAwareBlur.ankiprog", m_prog));
+	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/DepthAwareBlur.ankiprog", m_hblur.m_prog));
 
-	ShaderProgramResourceMutationInitList<3> mutators(m_prog);
+	ShaderProgramResourceMutationInitList<3> mutators(m_hblur.m_prog);
 	mutators.add("HORIZONTAL", 1).add("KERNEL_SIZE", 9).add("COLOR_COMPONENTS", 1);
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add("TEXTURE_SIZE", UVec2(m_ssao->m_width, m_ssao->m_height));
+	ShaderProgramResourceConstantValueInitList<1> consts(m_hblur.m_prog);
+	consts.add("TEXTURE_SIZE", UVec2(m_width, m_height));
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
+	m_hblur.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
 
-	m_grProg = variant->getProgram();
+	m_hblur.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void SsaoHBlur::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void SsaoHBlur::run(RenderingContext& ctx)
-{
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->setViewport(0, 0, m_ssao->m_width, m_ssao->m_height);
-	cmdb->beginRenderPass(m_ssao->m_fb[(m_r->getFrameCount() + 1) & 1]);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTexture(0, 0, m_ssao->m_rt[m_r->getFrameCount() & 1]);
-	cmdb->bindTexture(0, 1, m_r->getDepthDownscale().m_qd.m_colorRt);
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-void SsaoHBlur::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-Error SsaoVBlur::init(const ConfigSet& config)
+Error Ssao::initVBlur(const ConfigSet& config)
 {
 	// shader
-	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/DepthAwareBlur.ankiprog", m_prog));
+	ANKI_CHECK(m_r->getResourceManager().loadResource("programs/DepthAwareBlur.ankiprog", m_vblur.m_prog));
 
-	ShaderProgramResourceMutationInitList<3> mutators(m_prog);
+	ShaderProgramResourceMutationInitList<3> mutators(m_vblur.m_prog);
 	mutators.add("HORIZONTAL", 0).add("KERNEL_SIZE", 9).add("COLOR_COMPONENTS", 1);
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add("TEXTURE_SIZE", UVec2(m_ssao->m_width, m_ssao->m_height));
+	ShaderProgramResourceConstantValueInitList<1> consts(m_vblur.m_prog);
+	consts.add("TEXTURE_SIZE", UVec2(m_width, m_height));
 
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
+	m_vblur.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
 
-	m_grProg = variant->getProgram();
+	m_vblur.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
 
-void SsaoVBlur::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
-void SsaoVBlur::run(RenderingContext& ctx)
-{
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->setViewport(0, 0, m_ssao->m_width, m_ssao->m_height);
-	cmdb->beginRenderPass(m_ssao->m_fb[m_r->getFrameCount() & 1]);
-	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTexture(0, 0, m_ssao->m_rt[(m_r->getFrameCount() + 1) & 1]);
-	cmdb->bindTexture(0, 1, m_r->getDepthDownscale().m_qd.m_colorRt);
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
-
-void SsaoVBlur::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_ssao->m_rt[m_r->getFrameCount() & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
-}
-
 Error Ssao::init(const ConfigSet& config)
 {
 	m_width = m_r->getWidth() / SSAO_FRACTION;
@@ -192,32 +88,32 @@ Error Ssao::init(const ConfigSet& config)
 	for(U i = 0; i < 2; ++i)
 	{
 		// RT
-		m_rt[i] = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_width,
+		TextureInitInfo texinit = m_r->create2DRenderTargetInitInfo(m_width,
 			m_height,
 			Ssao::RT_PIXEL_FORMAT,
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::CLEAR,
 			SamplingFilter::LINEAR,
-			1,
-			"ssaomain"));
-
-		// FB
-		FramebufferInitInfo fbInit("ssaomain");
-		fbInit.m_colorAttachmentCount = 1;
-		fbInit.m_colorAttachments[0].m_texture = m_rt[i];
-		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-		m_fb[i] = getGrManager().newInstance<Framebuffer>(fbInit);
+			"ssaomain");
+		texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
+
+		m_rtTextures[i] = m_r->createAndClearRenderTarget(texinit);
 	}
 
-	Error err = m_main.init(config);
+	// FB descr
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_fbDescr.bake();
+
+	Error err = initMain(config);
 
 	if(!err)
 	{
-		err = m_hblur.init(config);
+		err = initHBlur(config);
 	}
 
 	if(!err)
 	{
-		err = m_vblur.init(config);
+		err = initVBlur(config);
 	}
 
 	if(err)
@@ -228,9 +124,116 @@ Error Ssao::init(const ConfigSet& config)
 	return err;
 }
 
-TexturePtr Ssao::getRt() const
+void Ssao::runMain(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	cmdb->setViewport(0, 0, m_width, m_height);
+	cmdb->bindShaderProgram(m_main.m_grProg);
+
+	rgraphCtx.bindTexture(0, 0, m_r->getDepthDownscale().getQuarterColorRt());
+	rgraphCtx.bindTextureAndSampler(0, 1, m_r->getGBuffer().getColorRt(2), m_r->getLinearSampler());
+	cmdb->bindTexture(0, 2, m_main.m_noiseTex->getGrTexture(), TextureUsageBit::SAMPLED_FRAGMENT);
+	rgraphCtx.bindTexture(0, 3, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1]);
+
+	struct Unis
+	{
+		Vec4 m_unprojectionParams;
+		Vec4 m_projectionMat;
+		Vec4 m_noiseLayerPad3;
+		Mat4 m_prevViewProjMatMulInvViewProjMat;
+	};
+
+	Unis* unis = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 0);
+	const Mat4& pmat = ctx.m_renderQueue->m_projectionMatrix;
+	unis->m_unprojectionParams = ctx.m_unprojParams;
+	unis->m_projectionMat = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
+	unis->m_noiseLayerPad3 = Vec4(m_r->getFrameCount() % m_main.m_noiseTex->getLayerCount(), 0.0, 0.0, 0.0);
+	unis->m_prevViewProjMatMulInvViewProjMat =
+		ctx.m_prevViewProjMat * ctx.m_renderQueue->m_viewProjectionMatrix.getInverse();
+
+	drawQuad(cmdb);
+}
+
+void Ssao::runHBlur(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	cmdb->setViewport(0, 0, m_width, m_height);
+	cmdb->bindShaderProgram(m_hblur.m_grProg);
+	rgraphCtx.bindTexture(0, 0, m_runCtx.m_rts[m_r->getFrameCount() & 1]);
+	rgraphCtx.bindTexture(0, 1, m_r->getDepthDownscale().getQuarterColorRt());
+	drawQuad(cmdb);
+}
+
+void Ssao::runVBlur(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	cmdb->setViewport(0, 0, m_width, m_height);
+	cmdb->bindShaderProgram(m_vblur.m_grProg);
+	rgraphCtx.bindTexture(0, 0, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1]);
+	rgraphCtx.bindTexture(0, 1, m_r->getDepthDownscale().getQuarterColorRt());
+	drawQuad(cmdb);
+}
+
+void Ssao::populateRenderGraph(RenderingContext& ctx)
+{
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	// Create RTs
+	const U rtToRenderIdx = m_r->getFrameCount() & 1;
+	m_runCtx.m_rts[rtToRenderIdx] =
+		rgraph.importRenderTarget("SSAO #1", m_rtTextures[rtToRenderIdx], TextureUsageBit::NONE);
+	const U rtToReadIdx = !rtToRenderIdx;
+	m_runCtx.m_rts[rtToReadIdx] =
+		rgraph.importRenderTarget("SSAO #2", m_rtTextures[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT);
+
+	// Create main render pass
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SSAO main");
+
+		pass.setWork(runMainCallback, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToRenderIdx]}}, {});
+
+		pass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({m_r->getDepthDownscale().getQuarterColorRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newProducer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	// Create HBlur pass
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SSAO hblur");
+
+		pass.setWork(runHBlurCallback, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToReadIdx]}}, {});
+
+		pass.newConsumer({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({m_r->getDepthDownscale().getQuarterColorRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newProducer({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	// Create VBlur pass
+	{
+		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SSAO vblur");
+
+		pass.setWork(runVBlurCallback, this, 0);
+		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToRenderIdx]}}, {});
+
+		pass.newConsumer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({m_r->getDepthDownscale().getQuarterColorRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newProducer({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+}
+
+RenderTargetHandle Ssao::getRt() const
 {
-	return m_rt[m_r->getFrameCount() & 1];
+	return m_runCtx.m_rts[m_r->getFrameCount() & 1];
 }
 
 } // end namespace anki

+ 58 - 91
src/anki/renderer/Ssao.h

@@ -5,132 +5,99 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/Gr.h>
-#include <anki/core/Timestamp.h>
 
 namespace anki
 {
 
-// Forward
-class Ssao;
-
 /// @addtogroup renderer
 /// @{
 
 /// Screen space ambient occlusion pass
-class SsaoMain : public RenderingPass
+class Ssao : public RendererObject
 {
-	friend class SsaoVBlur;
-	friend class SsaoHBlur;
-
 anki_internal:
-	SsaoMain(Renderer* r, Ssao* ssao)
-		: RenderingPass(r)
-		, m_ssao(ssao)
+	static const PixelFormat RT_PIXEL_FORMAT;
+
+	Ssao(Renderer* r)
+		: RendererObject(r)
 	{
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
+	~Ssao();
 
-	void setPreRunBarriers(RenderingContext& ctx);
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
 
-	void run(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
-	void setPostRunBarriers(RenderingContext& ctx);
+	RenderTargetHandle getRt() const;
 
 private:
-	Ssao* m_ssao;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-	TextureResourcePtr m_noiseTex;
-};
-
-/// Screen space ambient occlusion blur pass.
-class SsaoHBlur : public RenderingPass
-{
-	friend class SsaoVBlur;
+	U32 m_width, m_height;
 
-anki_internal:
-	SsaoHBlur(Renderer* r, Ssao* ssao)
-		: RenderingPass(r)
-		, m_ssao(ssao)
+	class
 	{
-	}
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+		TextureResourcePtr m_noiseTex;
+	} m_main; ///< Main noisy pass.
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_hblur; ///< Horizontal blur.
 
-	void setPreRunBarriers(RenderingContext& ctx);
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_vblur; ///< Vertical blur.
 
-	void run(RenderingContext& ctx);
+	class
+	{
+	public:
+		Array<RenderTargetHandle, 2> m_rts;
+		const RenderingContext* m_ctx = nullptr;
+	} m_runCtx; ///< Runtime context.
 
-	void setPostRunBarriers(RenderingContext& ctx);
+	Array<TexturePtr, 2> m_rtTextures;
+	FramebufferDescription m_fbDescr;
 
-private:
-	Ssao* m_ssao;
+	ANKI_USE_RESULT Error initMain(const ConfigSet& set);
+	ANKI_USE_RESULT Error initVBlur(const ConfigSet& set);
+	ANKI_USE_RESULT Error initHBlur(const ConfigSet& set);
 
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
+	void runMain(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+	void runHBlur(RenderPassWorkContext& rgraphCtx);
+	void runVBlur(RenderPassWorkContext& rgraphCtx);
 
-/// Screen space ambient occlusion blur pass.
-class SsaoVBlur : public RenderingPass
-{
-anki_internal:
-	SsaoVBlur(Renderer* r, Ssao* ssao)
-		: RenderingPass(r)
-		, m_ssao(ssao)
+	/// A RenderPassWorkCallback for SSAO main pass.
+	static void runMainCallback(RenderPassWorkContext& rgraphCtx)
 	{
+		Ssao* const self = scast<Ssao*>(rgraphCtx.m_userData);
+		self->runMain(*self->m_runCtx.m_ctx, rgraphCtx);
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	void setPreRunBarriers(RenderingContext& ctx);
-
-	void run(RenderingContext& ctx);
-
-	void setPostRunBarriers(RenderingContext& ctx);
-
-private:
-	Ssao* m_ssao;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
-
-/// Screen space ambient occlusion pass
-class Ssao : public RenderingPass
-{
-	friend class SsaoMain;
-	friend class SsaoHBlur;
-	friend class SsaoVBlur;
-
-anki_internal:
-	static const PixelFormat RT_PIXEL_FORMAT;
-
-	SsaoMain m_main;
-	SsaoHBlur m_hblur;
-	SsaoVBlur m_vblur;
-
-	Ssao(Renderer* r)
-		: RenderingPass(r)
-		, m_main(r, this)
-		, m_hblur(r, this)
-		, m_vblur(r, this)
+	/// A RenderPassWorkCallback for SSAO HBlur.
+	static void runHBlurCallback(RenderPassWorkContext& rgraphCtx)
 	{
+		Ssao* const self = scast<Ssao*>(rgraphCtx.m_userData);
+		self->runHBlur(rgraphCtx);
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	TexturePtr getRt() const;
-
-private:
-	U32 m_width, m_height;
-
-	Array<TexturePtr, 2> m_rt;
-	Array<FramebufferPtr, 2> m_fb;
+	/// A RenderPassWorkCallback for SSAO VBlur.
+	static void runVBlurCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		Ssao* const self = scast<Ssao*>(rgraphCtx.m_userData);
+		self->runVBlur(rgraphCtx);
+	}
 };
 /// @}
 

+ 36 - 37
src/anki/renderer/TemporalAA.cpp

@@ -13,7 +13,7 @@ namespace anki
 {
 
 TemporalAA::TemporalAA(Renderer* r)
-	: RenderingPass(r)
+	: RendererObject(r)
 {
 }
 
@@ -43,64 +43,63 @@ Error TemporalAA::initInternal(const ConfigSet& config)
 
 	for(U i = 0; i < 2; ++i)
 	{
-		m_rts[i] = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
+		m_rtTextures[i] = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
 			m_r->getHeight(),
 			LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			SamplingFilter::LINEAR,
-			1,
-			"taa"));
-
-		FramebufferInitInfo fbInit("taa");
-		fbInit.m_colorAttachmentCount = 1;
-		fbInit.m_colorAttachments[0].m_texture = m_rts[i];
-		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-		m_fbs[i] = getGrManager().newInstance<Framebuffer>(fbInit);
+			"TemporalAA"));
 	}
 
-	return Error::NONE;
-}
+	m_fbDescr.m_colorAttachmentCount = 1;
+	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_fbDescr.bake();
 
-void TemporalAA::setPreRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rts[m_r->getFrameCount() & 1],
-		TextureUsageBit::NONE,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureSurfaceInfo(0, 0, 0, 0));
+	return Error::NONE;
 }
 
-void TemporalAA::run(RenderingContext& ctx)
+void TemporalAA::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
-	cmdb->beginRenderPass(m_fbs[m_r->getFrameCount() & 1]);
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
 	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindTextureAndSampler(0, 0, m_r->getGBuffer().m_depthRt, m_r->getLinearSampler());
-	cmdb->bindTextureAndSampler(0, 1, m_r->getLightShading().getRt(), m_r->getLinearSampler());
-	cmdb->informTextureCurrentUsage(m_rts[(m_r->getFrameCount() + 1) & 1], TextureUsageBit::SAMPLED_FRAGMENT);
-	cmdb->bindTextureAndSampler(0, 2, m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
-	cmdb->bindStorageBuffer(0, 0, m_r->getTonemapping().m_luminanceBuff, 0, MAX_PTR_SIZE);
+	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());
 
 	Mat4* unis = allocateAndBindUniforms<Mat4*>(sizeof(Mat4), cmdb, 0, 0);
 	*unis = ctx.m_jitterMat * ctx.m_prevViewProjMat * ctx.m_viewProjMatJitter.getInverse();
 
-	m_r->drawQuad(cmdb);
-	cmdb->endRenderPass();
-}
+	rgraphCtx.bindUniformBuffer(0, 1, m_r->getTonemapping().getAverageLuminanceBuffer());
 
-void TemporalAA::setPostRunBarriers(RenderingContext& ctx)
-{
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rts[m_r->getFrameCount() & 1],
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSurfaceInfo(0, 0, 0, 0));
+	drawQuad(cmdb);
 }
 
-TexturePtr TemporalAA::getRt() const
+void TemporalAA::populateRenderGraph(RenderingContext& ctx)
 {
-	return m_rts[m_r->getFrameCount() & 1];
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	// Import RTs
+	m_runCtx.m_historyRt = rgraph.importRenderTarget(
+		"TemporalAA hist", m_rtTextures[(m_r->getFrameCount() + 1) & 1], TextureUsageBit::SAMPLED_FRAGMENT);
+	m_runCtx.m_renderRt =
+		rgraph.importRenderTarget("TemporalAA", m_rtTextures[m_r->getFrameCount() & 1], TextureUsageBit::NONE);
+
+	// Create pass
+	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("TemporalAA");
+
+	pass.setWork(runCallback, this, 0);
+	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->getLightShading().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_runCtx.m_historyRt, TextureUsageBit::SAMPLED_FRAGMENT});
+
+	pass.newProducer({m_runCtx.m_renderRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 }
 
 } // end namespace anki

+ 27 - 9
src/anki/renderer/TemporalAA.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 
 namespace anki
 {
@@ -14,29 +14,47 @@ namespace anki
 /// @{
 
 /// Temporal AA resolve.
-class TemporalAA : public RenderingPass
+class TemporalAA : public RendererObject
 {
 public:
 	TemporalAA(Renderer* r);
 
 	~TemporalAA();
 
-	TexturePtr getRt() const;
-
 	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
+	void populateRenderGraph(RenderingContext& ctx);
+
+	RenderTargetHandle getRt() const
+	{
+		return m_runCtx.m_renderRt;
+	}
 
 private:
-	Array<TexturePtr, 2> m_rts;
-	Array<FramebufferPtr, 2> m_fbs;
+	Array<TexturePtr, 2> m_rtTextures;
+	FramebufferDescription m_fbDescr;
 
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
 
+	class
+	{
+	public:
+		RenderingContext* m_ctx = nullptr;
+		RenderTargetHandle m_renderRt;
+		RenderTargetHandle m_historyRt;
+	} m_runCtx;
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
+
+	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+
+	/// A RenderPassWorkCallback for the AA pass.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		TemporalAA* const self = scast<TemporalAA*>(rgraphCtx.m_userData);
+		self->run(*self->m_runCtx.m_ctx, rgraphCtx);
+	}
 };
 /// @}
 

+ 26 - 6
src/anki/renderer/Tonemapping.cpp

@@ -38,9 +38,10 @@ Error Tonemapping::initInternal(const ConfigSet& initializer)
 	m_grProg = variant->getProgram();
 
 	// Create buffer
-	m_luminanceBuff = getGrManager().newInstance<Buffer>(sizeof(Vec4),
+	m_luminanceBuff = getGrManager().newInstance<Buffer>(BufferInitInfo(sizeof(Vec4),
 		BufferUsageBit::STORAGE_ALL | BufferUsageBit::UNIFORM_ALL | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
-		BufferMapAccessBit::NONE);
+		BufferMapAccessBit::NONE,
+		"AvgLum"));
 
 	CommandBufferInitInfo cmdbinit;
 	cmdbinit.m_flags = CommandBufferFlag::SMALL_BATCH | CommandBufferFlag::TRANSFER_WORK;
@@ -61,14 +62,33 @@ Error Tonemapping::initInternal(const ConfigSet& initializer)
 	return Error::NONE;
 }
 
-void Tonemapping::run(RenderingContext& ctx)
+void Tonemapping::run(RenderPassWorkContext& rgraphCtx)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
 	cmdb->bindShaderProgram(m_grProg);
-	cmdb->bindStorageBuffer(0, 0, m_luminanceBuff, 0, MAX_PTR_SIZE);
-	cmdb->bindTexture(0, 0, m_r->getDownscaleBlur().getPassTexture(m_rtIdx));
+	rgraphCtx.bindStorageBuffer(0, 0, m_runCtx.m_buffHandle);
+	rgraphCtx.bindTexture(0, 0, m_r->getDownscaleBlur().getPassRt(m_rtIdx));
 
 	cmdb->dispatchCompute(1, 1, 1);
 }
 
+void Tonemapping::populateRenderGraph(RenderingContext& ctx)
+{
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	// Create buffer
+	m_runCtx.m_buffHandle = rgraph.importBuffer("Avg lum", m_luminanceBuff, BufferUsageBit::NONE);
+
+	// Create the pass
+	ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("Avg lum");
+
+	pass.setWork(runCallback, this, 0);
+
+	pass.newConsumer({m_runCtx.m_buffHandle, BufferUsageBit::STORAGE_COMPUTE_READ_WRITE});
+	pass.newConsumer({m_r->getDownscaleBlur().getPassRt(m_rtIdx), TextureUsageBit::SAMPLED_COMPUTE});
+
+	pass.newProducer({m_runCtx.m_buffHandle, BufferUsageBit::STORAGE_COMPUTE_READ_WRITE});
+}
+
 } // end namespace anki

+ 27 - 6
src/anki/renderer/Tonemapping.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 
 namespace anki
 {
@@ -14,26 +14,47 @@ namespace anki
 /// @{
 
 /// Tonemapping.
-class Tonemapping : public RenderingPass
+class Tonemapping : public RendererObject
 {
 anki_internal:
-	BufferPtr m_luminanceBuff;
-
 	Tonemapping(Renderer* r)
-		: RenderingPass(r)
+		: RendererObject(r)
 	{
 	}
 
 	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
-	void run(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
+
+	RenderPassBufferHandle getAverageLuminanceBuffer() const
+	{
+		return m_runCtx.m_buffHandle;
+	}
 
 private:
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
 	U8 m_rtIdx;
 
+	BufferPtr m_luminanceBuff;
+
+	class
+	{
+	public:
+		RenderPassBufferHandle m_buffHandle;
+	} m_runCtx;
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
+
+	void run(RenderPassWorkContext& rgraphCtx);
+
+	/// A RenderPassWorkCallback to run the compute pass.
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		Tonemapping* const self = scast<Tonemapping*>(rgraphCtx.m_userData);
+		self->run(rgraphCtx);
+	}
 };
 /// @}
 

+ 153 - 186
src/anki/renderer/Volumetric.cpp

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

+ 60 - 113
src/anki/renderer/Volumetric.h

@@ -5,161 +5,108 @@
 
 #pragma once
 
-#include <anki/renderer/RenderingPass.h>
+#include <anki/renderer/RendererObject.h>
 
 namespace anki
 {
 
-// Forward
-class Volumetric;
-
 /// @addtogroup renderer
 /// @{
 
-/// Volumetic main pass.
-class VolumetricMain : public RenderingPass
+/// Volumetric effects.
+class Volumetric : public RendererObject
 {
-	friend class Volumetric;
-	friend class VolumetricHBlur;
-	friend class VolumetricVBlur;
+public:
+	void setFogParticleColor(const Vec3& col)
+	{
+		m_main.m_fogParticleColor = col;
+	}
 
 anki_internal:
-	VolumetricMain(Renderer* r, Volumetric* vol)
-		: RenderingPass(r)
-		, m_vol(vol)
+
+	Volumetric(Renderer* r)
+		: RendererObject(r)
 	{
 	}
 
-	~VolumetricMain()
+	~Volumetric()
 	{
 	}
 
 	ANKI_USE_RESULT Error init(const ConfigSet& config);
 
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
 
-	TexturePtr getRt() const;
+	RenderTargetHandle getRt() const;
 
 private:
-	Volumetric* m_vol;
-
-	Vec3 m_fogParticleColor = Vec3(1.0);
-	Mat3x4 m_prevCameraRot = Mat3x4::getIdentity();
-
-	Array<TexturePtr, 2> m_rt; ///< vRT
-	Array<FramebufferPtr, 2> m_fb;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-
-	TextureResourcePtr m_noiseTex;
-};
-
-/// Volumetric blur pass.
-class VolumetricHBlur : public RenderingPass
-{
-	friend class Volumetric;
-	friend class VolumetricVBlur;
-
-anki_internal:
-	VolumetricHBlur(Renderer* r, Volumetric* vol)
-		: RenderingPass(r)
-		, m_vol(vol)
-	{
-	}
+	U32 m_width = 0, m_height = 0;
 
-	~VolumetricHBlur()
+	class
 	{
-	}
-
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
+	public:
+		Vec3 m_fogParticleColor = Vec3(1.0);
+		Mat3x4 m_prevCameraRot = Mat3x4::getIdentity();
 
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
 
-private:
-	Volumetric* m_vol;
-
-	TexturePtr m_rt;
-	FramebufferPtr m_fb;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
+		TextureResourcePtr m_noiseTex;
+	} m_main; ///< Main noisy pass.
 
-/// Volumetric blur pass.
-class VolumetricVBlur : public RenderingPass
-{
-	friend class Volumetric;
-
-anki_internal:
-	VolumetricVBlur(Renderer* r, Volumetric* vol)
-		: RenderingPass(r)
-		, m_vol(vol)
+	class
 	{
-	}
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_hblur; ///< Horizontal blur.
 
-	~VolumetricVBlur()
+	class
 	{
-	}
-
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_vblur; ///< Vertical blur.
 
-	void setPreRunBarriers(RenderingContext& ctx);
-	void run(RenderingContext& ctx);
-	void setPostRunBarriers(RenderingContext& ctx);
-
-private:
-	Volumetric* m_vol;
-
-	Array<FramebufferPtr, 2> m_fb;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-};
+	class
+	{
+	public:
+		Array<RenderTargetHandle, 2> m_rts;
+		const RenderingContext* m_ctx = nullptr;
+	} m_runCtx; ///< Runtime context.
 
-/// Volumetric effects.
-class Volumetric : public RenderingPass
-{
-	friend class VolumetricMain;
-	friend class VolumetricHBlur;
-	friend class VolumetricVBlur;
+	Array<TexturePtr, 2> m_rtTextures;
+	FramebufferDescription m_fbDescr;
 
-public:
-	void setFogParticleColor(const Vec3& col)
-	{
-		m_main.m_fogParticleColor = col;
-	}
+	ANKI_USE_RESULT Error initMain(const ConfigSet& set);
+	ANKI_USE_RESULT Error initVBlur(const ConfigSet& set);
+	ANKI_USE_RESULT Error initHBlur(const ConfigSet& set);
 
-anki_internal:
-	VolumetricMain m_main;
-	VolumetricHBlur m_hblur;
-	VolumetricVBlur m_vblur;
+	void runMain(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+	void runHBlur(RenderPassWorkContext& rgraphCtx);
+	void runVBlur(RenderPassWorkContext& rgraphCtx);
 
-	Volumetric(Renderer* r)
-		: RenderingPass(r)
-		, m_main(r, this)
-		, m_hblur(r, this)
-		, m_vblur(r, this)
+	/// A RenderPassWorkCallback for SSAO main pass.
+	static void runMainCallback(RenderPassWorkContext& rgraphCtx)
 	{
+		Volumetric* const self = scast<Volumetric*>(rgraphCtx.m_userData);
+		self->runMain(*self->m_runCtx.m_ctx, rgraphCtx);
 	}
 
-	~Volumetric()
+	/// A RenderPassWorkCallback for SSAO HBlur.
+	static void runHBlurCallback(RenderPassWorkContext& rgraphCtx)
 	{
+		Volumetric* const self = scast<Volumetric*>(rgraphCtx.m_userData);
+		self->runHBlur(rgraphCtx);
 	}
 
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	TexturePtr getRt() const
+	/// A RenderPassWorkCallback for SSAO VBlur.
+	static void runVBlurCallback(RenderPassWorkContext& rgraphCtx)
 	{
-		return m_main.getRt();
+		Volumetric* const self = scast<Volumetric*>(rgraphCtx.m_userData);
+		self->runVBlur(rgraphCtx);
 	}
-
-private:
-	U32 m_width = 0, m_height = 0;
 };
 /// @}
 

+ 6 - 4
src/anki/resource/Mesh.cpp

@@ -102,13 +102,15 @@ Error Mesh::load(const ResourceFilename& filename, Bool async)
 	// Allocate the buffers
 	GrManager& gr = getManager().getGrManager();
 
-	m_vertBuff = gr.newInstance<Buffer>(loader.getVertexDataSize(),
+	m_vertBuff = gr.newInstance<Buffer>(BufferInitInfo(loader.getVertexDataSize(),
 		BufferUsageBit::VERTEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION | BufferUsageBit::FILL,
-		BufferMapAccessBit::NONE);
+		BufferMapAccessBit::NONE,
+		"MeshVert"));
 
-	m_indicesBuff = gr.newInstance<Buffer>(loader.getIndexDataSize(),
+	m_indicesBuff = gr.newInstance<Buffer>(BufferInitInfo(loader.getIndexDataSize(),
 		BufferUsageBit::INDEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION | BufferUsageBit::FILL,
-		BufferMapAccessBit::NONE);
+		BufferMapAccessBit::NONE,
+		"MeshIdx"));
 
 	// Clear them
 	CommandBufferInitInfo cmdbinit;

+ 3 - 2
src/anki/resource/ResourceManager.cpp

@@ -30,7 +30,7 @@ ResourceManager::~ResourceManager()
 {
 	m_cacheDir.destroy(m_alloc);
 	m_alloc.deleteInstance(m_asyncLoader);
-	m_transferGpuAlloc.destroy();
+	m_alloc.deleteInstance(m_transferGpuAlloc);
 }
 
 Error ResourceManager::init(ResourceManagerInitInfo& init)
@@ -63,7 +63,8 @@ Error ResourceManager::init(ResourceManagerInitInfo& init)
 	m_asyncLoader = m_alloc.newInstance<AsyncLoader>();
 	m_asyncLoader->init(m_alloc);
 
-	ANKI_CHECK(m_transferGpuAlloc.init(init.m_config->getNumber("rsrc.transferScratchMemorySize"), m_gr, m_alloc));
+	m_transferGpuAlloc = m_alloc.newInstance<TransferGpuAllocator>();
+	ANKI_CHECK(m_transferGpuAlloc->init(init.m_config->getNumber("rsrc.transferScratchMemorySize"), m_gr, m_alloc));
 
 	return Error::NONE;
 }

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini