Browse Source

Implement checkerboard SSGI

Panagiotis Christopoulos Charitos 5 years ago
parent
commit
3963648a00

+ 24 - 30
shaders/Ssgi.ankiprog

@@ -3,13 +3,13 @@
 // Code licensed under the BSD License.
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
-// if VARIANT==0 then the checkerboard pattern is (render on 'v'):
+// The VARIANT points to the fragment in the quad that will be processed:
 // -----
 // -----
-// |v| |
-// | |v|
+// |3|2|
+// |0|1|
 // -----
 // -----
 
 
-#pragma anki mutator VARIANT 0 1
+#pragma anki mutator VARIANT 0 1 2 3
 
 
 #pragma anki start comp
 #pragma anki start comp
 #include <shaders/SsRaymarching.glsl>
 #include <shaders/SsRaymarching.glsl>
@@ -37,16 +37,20 @@ layout(set = 0, binding = 6) uniform texture2D u_historyTex;
 void main()
 void main()
 {
 {
 	// Compute a global invocation ID that takes the checkerboard pattern into account
 	// Compute a global invocation ID that takes the checkerboard pattern into account
-	IVec2 fixedGlobalInvocationId = IVec2(gl_GlobalInvocationID.xy);
-	fixedGlobalInvocationId.x *= 2;
+	UVec2 fixedGlobalInvocationId = IVec2(gl_GlobalInvocationID.xy);
+	fixedGlobalInvocationId *= 2;
 #if VARIANT == 0
 #if VARIANT == 0
-	fixedGlobalInvocationId.x += ((fixedGlobalInvocationId.y + 1) & 1);
+	// Nothing
+#elif VARIANT == 1
+	fixedGlobalInvocationId.x += 1;
+#elif VARIANT == 2
+	fixedGlobalInvocationId += 1;
 #else
 #else
-	fixedGlobalInvocationId.x += ((fixedGlobalInvocationId.y + 0) & 1);
+	fixedGlobalInvocationId.y += 1;
 #endif
 #endif
 
 
-	if(fixedGlobalInvocationId.x >= I32(u_unis.m_framebufferSize.x)
-		|| fixedGlobalInvocationId.y >= I32(u_unis.m_framebufferSize.y))
+	if(fixedGlobalInvocationId.x >= u_unis.m_framebufferSize.x
+		|| fixedGlobalInvocationId.y >= u_unis.m_framebufferSize.y)
 	{
 	{
 		// Skip threads outside the writable image
 		// Skip threads outside the writable image
 		return;
 		return;
@@ -67,7 +71,7 @@ void main()
 	const Vec3 viewPos = viewPos4.xyz / viewPos4.w;
 	const Vec3 viewPos = viewPos4.xyz / viewPos4.w;
 
 
 	// Get a random point inside the hemisphere. Use hemisphereSampleCos to avoid perpendicular vecs to viewNormal
 	// Get a random point inside the hemisphere. Use hemisphereSampleCos to avoid perpendicular vecs to viewNormal
-	const UVec2 random = rand3DPCG16(UVec3(fixedGlobalInvocationId, u_unis.m_frameCount)).xy;
+	const UVec2 random = rand3DPCG16(UVec3(gl_GlobalInvocationID.xy, u_unis.m_frameCount)).xy;
 	const Vec2 randomCircle = hammersleyRandom16(0, 0xFFFFu, random);
 	const Vec2 randomCircle = hammersleyRandom16(0, 0xFFFFu, random);
 	const Vec3 randomHemisphere = rotationFromDirection(viewNormal) * hemisphereSampleCos(randomCircle);
 	const Vec3 randomHemisphere = rotationFromDirection(viewNormal) * hemisphereSampleCos(randomCircle);
 
 
@@ -104,7 +108,7 @@ void main()
 	}
 	}
 
 
 	// Read the light buffer
 	// Read the light buffer
-	Vec4 outColor;
+	Vec3 outColor;
 	ANKI_BRANCH if(hitAttenuation > 0.0)
 	ANKI_BRANCH if(hitAttenuation > 0.0)
 	{
 	{
 		// Reproject the UV because you are reading the previous frame
 		// Reproject the UV because you are reading the previous frame
@@ -112,10 +116,9 @@ void main()
 		hitPoint.xy = NDC_TO_UV(v4.xy / v4.w);
 		hitPoint.xy = NDC_TO_UV(v4.xy / v4.w);
 
 
 		// Read the light buffer
 		// Read the light buffer
-		outColor.rgb = textureLod(u_lightBufferRt, u_trilinearClampSampler, hitPoint.xy, 100.0).rgb;
-		outColor.rgb = clamp(outColor.rgb, 0.0, FLT_MAX); // Fix the value just in case
-		outColor.rgb *= hitAttenuation;
-		outColor.a = 1.0 - hitAttenuation;
+		outColor = textureLod(u_lightBufferRt, u_trilinearClampSampler, hitPoint.xy, 100.0).rgb;
+		outColor = clamp(outColor, 0.0, FLT_MAX); // Fix the value just in case
+		outColor *= hitAttenuation;
 
 
 		// Compute a new normal based on the new hit point
 		// Compute a new normal based on the new hit point
 		const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, hitPoint.xy, 0.0).r;
 		const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, hitPoint.xy, 0.0).r;
@@ -125,31 +128,22 @@ void main()
 
 
 		// Modulate
 		// Modulate
 		const F32 NoL = max(0.0, dot(randomHemisphere, newViewNormal));
 		const F32 NoL = max(0.0, dot(randomHemisphere, newViewNormal));
-		outColor.xyz *= NoL;
-		outColor.xyz *= PI / 2.0;
+		outColor *= NoL;
+		outColor *= PI;
 	}
 	}
 	else
 	else
 	{
 	{
-		outColor = Vec4(0.0, 0.0, 0.0, 1.0);
+		outColor = Vec3(0.0, 0.0, 0.0);
 	}
 	}
 
 
 	// Blend with history
 	// Blend with history
 	const Vec4 v4 = u_unis.m_prevViewProjMatMulInvViewProjMat * Vec4(ndc, depth, 1.0);
 	const Vec4 v4 = u_unis.m_prevViewProjMatMulInvViewProjMat * Vec4(ndc, depth, 1.0);
 	const Vec2 historyUv = NDC_TO_UV(v4.xy / v4.w);
 	const Vec2 historyUv = NDC_TO_UV(v4.xy / v4.w);
-	const Vec4 history = textureLod(u_historyTex, u_trilinearClampSampler, historyUv, 0.0);
+	const Vec3 history = textureLod(u_historyTex, u_trilinearClampSampler, historyUv, 0.0).rgb;
 	outColor = mix(history, outColor, 0.15);
 	outColor = mix(history, outColor, 0.15);
 
 
 	// Store
 	// Store
-	imageStore(out_img, fixedGlobalInvocationId, outColor);
-
-	IVec2 adjacentGlobalInvocationId = IVec2(gl_GlobalInvocationID.xy);
-	adjacentGlobalInvocationId.x *= 2;
-#if VARIANT == 0
-	adjacentGlobalInvocationId.x += ((adjacentGlobalInvocationId.y + 0) & 1);
-#else
-	adjacentGlobalInvocationId.x += ((adjacentGlobalInvocationId.y + 1) & 1);
-#endif
-	imageStore(out_img, adjacentGlobalInvocationId, outColor * 1.0);
+	imageStore(out_img, IVec2(gl_GlobalInvocationID.xy), Vec4(outColor, 1.0));
 }
 }
 
 
 #pragma anki end
 #pragma anki end

+ 3 - 3
shaders/SsgiDenoise.ankiprog

@@ -81,9 +81,9 @@ void main()
 #endif
 #endif
 
 
 	Vec2 inUvOffset = Vec2(0.0);
 	Vec2 inUvOffset = Vec2(0.0);
-	inUvOffset.X_OR_Y = 1.5 * IN_TEXEL_SIZE.X_OR_Y;
+	inUvOffset.X_OR_Y = 1.0 * IN_TEXEL_SIZE.X_OR_Y;
 	Vec2 depthUvOffset = Vec2(0.0);
 	Vec2 depthUvOffset = Vec2(0.0);
-	depthUvOffset.X_OR_Y = 1.5 * DEPTH_TEXEL_SIZE.X_OR_Y;
+	depthUvOffset.X_OR_Y = 2.0 * DEPTH_TEXEL_SIZE.X_OR_Y;
 
 
 	ANKI_UNROLL for(U32 i = 0u; i < (SAMPLE_COUNT - 1u) / 2u; ++i)
 	ANKI_UNROLL for(U32 i = 0u; i < (SAMPLE_COUNT - 1u) / 2u; ++i)
 	{
 	{
@@ -91,7 +91,7 @@ void main()
 		sampleTex(inUv - inUvOffset, depthUv - depthUvOffset, refDepth, color, weight);
 		sampleTex(inUv - inUvOffset, depthUv - depthUvOffset, refDepth, color, weight);
 
 
 		inUvOffset.X_OR_Y += IN_TEXEL_SIZE.X_OR_Y;
 		inUvOffset.X_OR_Y += IN_TEXEL_SIZE.X_OR_Y;
-		depthUvOffset.X_OR_Y += DEPTH_TEXEL_SIZE.X_OR_Y;
+		depthUvOffset.X_OR_Y += 2.0 * DEPTH_TEXEL_SIZE.X_OR_Y;
 	}
 	}
 
 
 	color /= weight;
 	color /= weight;

+ 122 - 0
shaders/SsgiReconstruct.ankiprog

@@ -0,0 +1,122 @@
+// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+// The VARIANT points to the master fragment in the quad:
+// -----
+// |3|2|
+// |0|1|
+// -----
+
+#pragma anki mutator VARIANT 0 1 2 3
+
+ANKI_SPECIALIZATION_CONSTANT_UVEC2(FB_SIZE, 0, UVec2(1));
+
+#pragma anki start comp
+
+#include <shaders/Common.glsl>
+
+const UVec2 WORKGROUP_SIZE = UVec2(8u, 8u);
+layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
+
+layout(set = 0, binding = 0) uniform sampler u_linearAnyClampSampler;
+layout(set = 0, binding = 1) uniform texture2D u_inTex;
+layout(set = 0, binding = 2) uniform texture2D u_depthTex;
+layout(set = 0, binding = 3) writeonly uniform image2D u_outImg;
+
+shared Vec3 s_colors[WORKGROUP_SIZE.y][WORKGROUP_SIZE.x];
+shared Vec4 s_depths[WORKGROUP_SIZE.y][WORKGROUP_SIZE.x];
+
+F32 computeDepthWeights(F32 refDepth, F32 depth)
+{
+	const F32 diff = abs(refDepth - depth);
+	const F32 weight = 1.0 - pow(diff, 0.5);
+	return weight;
+}
+
+void reconstruct(IVec2 storeCoord, F32 depthRef, Vec4 colorAndDepth0, Vec4 colorAndDepth1)
+{
+	F32 weight = computeDepthWeights(depthRef, colorAndDepth0.w);
+	Vec3 col = colorAndDepth0.rgb * weight;
+	F32 weightSum = weight;
+
+	weight = computeDepthWeights(depthRef, colorAndDepth1.w);
+	col += colorAndDepth1.rgb * weight;
+	weightSum += weight;
+
+	col /= weightSum;
+
+	imageStore(u_outImg, storeCoord, Vec4(col, 0.0));
+}
+
+void reconstructAll(Vec4 depthRefs, Vec3 masterColor)
+{
+	const IVec2 localInvocationId = IVec2(gl_LocalInvocationID.xy);
+
+#if VARIANT == 0
+	const IVec2 masterStoreCoord = IVec2(gl_GlobalInvocationID.xy * 2);
+	const IVec2 slaveRelativeCoords[3] = IVec2[](IVec2(1, 0), IVec2(1, 1), IVec2(0, 1));
+	const U32 masterDrefIdx = 3;
+	const U32 slaveDrefIdx[3] = U32[](2, 1, 0);
+#elif VARIANT == 1
+	const IVec2 masterStoreCoord = IVec2(gl_GlobalInvocationID.xy * 2) + IVec2(1, 0);
+	const IVec2 slaveRelativeCoords[3] = IVec2[](IVec2(-1, 0), IVec2(0, 1), IVec2(-1, 1));
+	const U32 masterDrefIdx = 2;
+	const U32 slaveDrefIdx[3] = U32[](3, 1, 0);
+#elif VARIANT == 2
+	const IVec2 masterStoreCoord = IVec2(gl_GlobalInvocationID.xy * 2) + IVec2(1, 1);
+	const IVec2 slaveRelativeCoords[3] = IVec2[](IVec2(-1, -1), IVec2(0, -1), IVec2(-1, 0));
+	const U32 masterDrefIdx = 1;
+	const U32 slaveDrefIdx[3] = U32[](3, 2, 0);
+#else
+	const IVec2 masterStoreCoord = IVec2(gl_GlobalInvocationID.xy * 2) + IVec2(0, 1);
+	const IVec2 slaveRelativeCoords[3] = IVec2[](IVec2(0, -1), IVec2(-1, 1), IVec2(0, 1));
+	const U32 masterDrefIdx = 0;
+	const U32 slaveDrefIdx[3] = U32[](3, 2, 1);
+#endif
+
+	const Vec4 masterColorAndDepth = Vec4(masterColor, depthRefs[masterDrefIdx]);
+	imageStore(u_outImg, masterStoreCoord, Vec4(masterColor, 0.0));
+
+	ANKI_UNROLL for(U32 i = 0; i < 3; ++i)
+	{
+		const IVec2 sharedCoord =
+			clamp(localInvocationId + slaveRelativeCoords[i], IVec2(0), IVec2(WORKGROUP_SIZE) - 1);
+		const Vec3 masterColor2 = s_colors[sharedCoord.y][sharedCoord.x];
+		const F32 masterDepth2 = s_depths[sharedCoord.y][sharedCoord.x][masterDrefIdx];
+		const IVec2 storeCoord = masterStoreCoord + slaveRelativeCoords[i];
+		reconstruct(storeCoord, depthRefs[slaveDrefIdx[i]], masterColorAndDepth, Vec4(masterColor2, masterDepth2));
+	}
+}
+
+void main()
+{
+	const UVec2 IN_TEXTURE_SIZE = FB_SIZE / 2;
+	ANKI_BRANCH if(gl_GlobalInvocationID.x >= IN_TEXTURE_SIZE.x || gl_GlobalInvocationID.y >= IN_TEXTURE_SIZE.y)
+	{
+		// Out of bounds
+		s_colors[gl_LocalInvocationID.y][gl_LocalInvocationID.x] = Vec3(0.0);
+		s_depths[gl_LocalInvocationID.y][gl_LocalInvocationID.x] = Vec4(1000.0); // High value so it has low weight
+		return;
+	}
+
+	const Vec2 inTexelSize = 1.0 / Vec2(IN_TEXTURE_SIZE);
+	const Vec2 fbTexelSize = 1.0 / Vec2(FB_SIZE);
+
+	const Vec2 inUv = (Vec2(gl_GlobalInvocationID.xy) + 0.5) / Vec2(IN_TEXTURE_SIZE);
+	const Vec2 fbUv = (Vec2(gl_GlobalInvocationID.xy) * 2.0 + 1.0) / Vec2(FB_SIZE);
+
+	const Vec3 color = textureLod(u_inTex, u_linearAnyClampSampler, inUv, 0.0).rgb;
+	const Vec4 depthRefs = textureGather(sampler2D(u_depthTex, u_linearAnyClampSampler), fbUv, 0);
+
+	s_colors[gl_LocalInvocationID.y][gl_LocalInvocationID.x] = color;
+	s_depths[gl_LocalInvocationID.y][gl_LocalInvocationID.x] = depthRefs;
+
+	memoryBarrierShared();
+	barrier();
+
+	reconstructAll(depthRefs, color);
+}
+
+#pragma anki end

+ 0 - 171
shaders/SsgiRecontruct.ankiprog

@@ -1,171 +0,0 @@
-// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma anki mutator VARIANT 0 1 2 3
-
-ANKI_SPECIALIZATION_CONSTANT_UVEC2(FB_SIZE, 0, UVec2(1));
-
-#pragma anki start comp
-
-#include <shaders/Common.glsl>
-
-const UVec2 WORKGROUP_SIZE = UVec2(8u, 8u);
-layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
-
-layout(set = 0, binding = 0) uniform sampler u_linearAnyClampSampler;
-layout(set = 0, binding = 1) uniform texture2D u_inTex;
-layout(set = 0, binding = 2) uniform texture2D u_depthTex;
-layout(set = 0, binding = 3) writeonly uniform image2D u_outImg;
-
-shared Vec3 s_colors[WORKGROUP_SIZE.y + 2][WORKGROUP_SIZE.x + 2];
-shared Vec4 s_depths[WORKGROUP_SIZE.y + 2][WORKGROUP_SIZE.x + 2];
-
-Vec3 computeDepthWeights(F32 refDepth, Vec3 depths)
-{
-	const Vec3 diffs = abs(refDepth - depths);
-	const Vec3 weights = 1.0 - pow(diffs, Vec3(0.5));
-	return weights;
-}
-
-void reconstruct(IVec2 storeCoord, F32 depthRef, Vec4 colorAndDepth0, Vec4 colorAndDepth1, Vec4 colorAndDepth2)
-{
-	const Vec3 weights = computeDepthWeights(depthRef, Vec3(colorAndDepth0.w, colorAndDepth1.w, colorAndDepth2.w));
-	const F32 weightSum = weights[0] + weights[1] + weights[2];
-
-	Vec3 col = colorAndDepth0.rgb * weights[0];
-	col += colorAndDepth1.rgb * weights[1];
-	col += colorAndDepth2.rgb * weights[2];
-	col /= weightSum;
-
-	imageStore(u_outImg, storeCoord, Vec4(col, 0.0));
-}
-
-void main()
-{
-	const UVec2 IN_TEXTURE_SIZE = FB_SIZE / 2;
-	ANKI_BRANCH if(gl_GlobalInvocationID.x >= IN_TEXTURE_SIZE.x || gl_GlobalInvocationID.y >= IN_TEXTURE_SIZE.y)
-	{
-		// Out of bounds
-		return;
-	}
-
-	const Vec2 inTexelSize = 1.0 / Vec2(IN_TEXTURE_SIZE);
-	const Vec2 fbTexelSize = 1.0 / Vec2(FB_SIZE);
-
-	// Compute UVs
-	const Vec2 inUv = (Vec2(gl_GlobalInvocationID.xy) + 0.5) / Vec2(IN_TEXTURE_SIZE);
-
-#if VARIANT == 0
-	const Vec2 fbOffset = Vec2(0.0, 0.0);
-#elif VARIANT == 1
-	const Vec2 fbOffset = Vec2(1.0, 0.0);
-#elif VARIANT == 1
-	const Vec2 fbOffset = Vec2(1.0, 1.0);
-#else
-	const Vec2 fbOffset = Vec2(0.0, 1.0);
-#endif
-	const Vec2 fbUv = (Vec2(gl_GlobalInvocationID.xy) * 2.0 + fbOffset + 0.5) / Vec2(FB_SIZE);
-
-	// Read the color and store it
-	const Vec3 centerColor = textureLod(u_inTex, u_linearAnyClampSampler, inUv, 0.0).rgb;
-	s_colors[gl_LocalInvocationID.y + 1][gl_LocalInvocationID.x + 1] = centerColor;
-
-	if(gl_LocalInvocationID.x == 0)
-	{
-		const Vec3 c = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, 0.0, IVec2(-1, 0)).rgb;
-		s_colors[gl_LocalInvocationID.y + 1][0] = c;
-	}
-	else if(gl_LocalInvocationID.x == WORKGROUP_SIZE.x - 1)
-	{
-		const Vec3 c = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, 0.0, IVec2(1, 0)).rgb;
-		s_colors[gl_LocalInvocationID.y + 1][WORKGROUP_SIZE.x + 1] = c;
-	}
-
-	if(gl_LocalInvocationID.y == 0)
-	{
-		const Vec3 c = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, 0.0, IVec2(-1, 0)).rgb;
-		s_colors[0][gl_LocalInvocationID.x + 1] = c;
-	}
-	else if(gl_LocalInvocationID.y == WORKGROUP_SIZE.y - 1)
-	{
-		const Vec3 c = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, 0.0, IVec2(-1, 0)).rgb;
-		s_colors[0][gl_LocalInvocationID.x + 1] = c;
-	}
-
-	// Read the texels in and around the current cell
-	const Vec3 centerColor = textureLod(u_inTex, u_linearAnyClampSampler, inUv, 0.0).rgb;
-
-	const Vec3 leftColor = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, IVec2(-1, 0) 0.0).rgb;
-	const Vec3 rightColor = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, IVec2(1, 0) 0.0).rgb;
-	const Vec3 topColor = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, IVec2(0, 1) 0.0).rgb;
-	const Vec3 bottomColor = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, IVec2(0, -1) 0.0).rgb;
-
-	const Vec3 leftTopColor = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, IVec2(-1, 1) 0.0).rgb;
-	const Vec3 rightTopColor = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, IVec2(1, 1) 0.0).rgb;
-	const Vec3 leftBottomColor = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, IVec2(-1, -1) 0.0).rgb;
-	const Vec3 rightBottomColor = textureLodOffset(u_inTex, u_linearAnyClampSampler, inUv, IVec2(1, -1) 0.0).rgb;
-
-	// Also the depths around
-	const F32 leftDepth = textureLodOffset(u_depthTex, u_linearAnyClampSampler, fbUv, 0.0, IVec2(-2, 0)).r;
-	const F32 rightDepth = textureLodOffset(u_depthTex, u_linearAnyClampSampler, fbUv, 0.0, IVec2(2, 0)).r;
-	const F32 topDepth = textureLodOffset(u_depthTex, u_linearAnyClampSampler, fbUv, 0.0, IVec2(0, 2)).r;
-	const F32 bottomDepth = textureLodOffset(u_depthTex, u_linearAnyClampSampler, fbUv, 0.0, IVec2(0, -2)).r;
-
-	const F32 leftTopDepth = textureLodOffset(u_depthTex, u_linearAnyClampSampler, fbUv, 0.0, IVec2(-2, 2)).r;
-	const F32 rightTopDepth = textureLodOffset(u_depthTex, u_linearAnyClampSampler, fbUv, 0.0, IVec2(2, 2)).r;
-	const F32 leftBottomDepth = textureLodOffset(u_depthTex, u_linearAnyClampSampler, fbUv, 0.0, IVec2(-2, -2)).r;
-	const F32 rightBottomDepth = textureLodOffset(u_depthTex, u_linearAnyClampSampler, fbUv, 0.0, IVec2(2, -2)).r;
-
-	// Combine them
-	const Vec4 leftColorAndDepth = Vec4(leftColor, leftDepth);
-	const Vec4 rightColorAndDepth = Vec4(rightColor, rightDepth);
-	const Vec4 topColorAndDepth = Vec4(topColor, topDepth);
-	const Vec4 bottomColorAndDepth = Vec4(topColor, topDepth);
-
-	// Get the depth references of the current 2x2 cell
-	const Vec4 depthRefs = textureGather(sampler2D(u_depthTex, u_linearAnyClampSampler),
-		(Vec2(gl_GlobalInvocationID.xy) * 2.0 + 1.0) / Vec2(FB_SIZE),
-		0);
-	const F32 bottomLeftDref = depthRefs.w;
-	const F32 bottomRightDref = depthRefs.z;
-	const F32 topRightDref = depthRefs.y;
-	const F32 topLeftDref = depthRefs.x;
-
-	//     +---+
-	//     |3|2|
-	//     +---+  top
-	//     |0|1|
-	// +---+---+---+
-	// |3|2|3|2|3|2|
-	// +---+---+---+ left, center, right
-	// |0|1|0|1|0|1|
-	// +---+---+---+
-	//     |3|2|
-	//     +---+ bottom
-	//     |0|1|
-	//     +---+
-#if VARIANT == 0
-	imageStore(u_outImg, fbCoord, Vec4(centerColor, 0.0));
-	const Vec4 centerColorAndDepth = Vec4(centerColor, bottomLeftDref);
-
-	reconstruct(fbCoord + IVec2(1, 0), bottomRightDref, centerColorAndDepth, bottomColorAndDepth, rightColorAndDepth);
-	reconstruct(fbCoord + IVec2(1, 1), topRightDref, centerColorAndDepth, topColorAndDepth, rightColorAndDepth);
-	reconstruct(fbCoord + IVec2(0, 1), topLeftDref, centerColorAndDepth, topColorAndDepth, leftColorAndDepth);
-#elif VARIANT == 1
-	imageStore(u_outImg, fbCoord, Vec4(centerColor, 0.0));
-	const Vec4 centerColorAndDepth = Vec4(centerColor, bottomRightDref);
-
-	reconstruct(fbCoord + IVec2(-1, 0), bottomLeftDref, centerColorAndDepth, bottomColorAndDepth, leftColorAndDepth);
-	reconstruct(fbCoord + IVec2(0, 1), topRightDref, centerColorAndDepth, topColorAndDepth, rightColorAndDepth);
-	reconstruct(fbCoord + IVec2(-1, 1), topLeftDref, centerColorAndDepth, topColorAndDepth, leftColorAndDepth);
-#elif VARIANT == 2
-	imageStore(u_outImg, fbCoord, Vec4(centerColor, 0.0));
-	const Vec4 centerColorAndDepth = Vec4(centerColor, topRightDref);
-
-	reconstruct(fbCoord + IVec2(-1, -1), bottomLeftDref, centerColorAndDepth, bottomColorAndDepth, leftColorAndDepth);
-#endif
-}
-
-#pragma anki end

+ 2 - 2
src/anki/core/ConfigDefs.h

@@ -8,8 +8,8 @@ ANKI_CONFIG_OPTION(core_storagePerFrameMemorySize, 16_MB, 1_MB, 1_GB)
 ANKI_CONFIG_OPTION(core_vertexPerFrameMemorySize, 10_MB, 1_MB, 1_GB)
 ANKI_CONFIG_OPTION(core_vertexPerFrameMemorySize, 10_MB, 1_MB, 1_GB)
 ANKI_CONFIG_OPTION(core_textureBufferPerFrameMemorySize, 1_MB, 1_MB, 1_GB)
 ANKI_CONFIG_OPTION(core_textureBufferPerFrameMemorySize, 1_MB, 1_MB, 1_GB)
 
 
-ANKI_CONFIG_OPTION(width, 1280, 16, 16 * 1024, "Width")
-ANKI_CONFIG_OPTION(height, 768, 16, 16 * 1024, "Height")
+ANKI_CONFIG_OPTION(width, 1920, 16, 16 * 1024, "Width")
+ANKI_CONFIG_OPTION(height, 1080, 16, 16 * 1024, "Height")
 ANKI_CONFIG_OPTION(core_mainThreadCount, max(2u, getCpuCoresCount() / 2u), 2u, 1024u)
 ANKI_CONFIG_OPTION(core_mainThreadCount, max(2u, getCpuCoresCount() / 2u), 2u, 1024u)
 ANKI_CONFIG_OPTION(core_displayStats, 0, 0, 1)
 ANKI_CONFIG_OPTION(core_displayStats, 0, 0, 1)
 ANKI_CONFIG_OPTION(core_clearCaches, 0, 0, 1)
 ANKI_CONFIG_OPTION(core_clearCaches, 0, 0, 1)

+ 108 - 55
src/anki/renderer/Ssgi.cpp

@@ -14,6 +14,9 @@
 namespace anki
 namespace anki
 {
 {
 
 
+static constexpr U32 WRITE = 0;
+static constexpr U32 READ = 1;
+
 Ssgi::~Ssgi()
 Ssgi::~Ssgi()
 {
 {
 }
 }
@@ -32,56 +35,78 @@ Error Ssgi::initInternal(const ConfigSet& cfg)
 {
 {
 	const U32 width = m_r->getWidth();
 	const U32 width = m_r->getWidth();
 	const U32 height = m_r->getHeight();
 	const U32 height = m_r->getHeight();
-	ANKI_R_LOGI("Initializing SSGI pass (%ux%u)", width, height);
+	ANKI_ASSERT((width % 2) == 0 && (height % 2) == 0 && "The algorithms won't work");
+	ANKI_R_LOGI("Initializing SSGI pass");
 	m_main.m_maxSteps = cfg.getNumberU32("r_ssgiMaxSteps");
 	m_main.m_maxSteps = cfg.getNumberU32("r_ssgiMaxSteps");
 	m_main.m_depthLod = min(cfg.getNumberU32("r_ssgiDepthLod"), m_r->getDepthDownscale().getMipmapCount() - 1);
 	m_main.m_depthLod = min(cfg.getNumberU32("r_ssgiDepthLod"), m_r->getDepthDownscale().getMipmapCount() - 1);
 	m_main.m_firstStepPixels = 32;
 	m_main.m_firstStepPixels = 32;
 
 
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseRgb816x16.png", m_main.m_noiseTex));
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseRgb816x16.png", m_main.m_noiseTex));
 
 
-	// Create RTs
-	TextureInitInfo texinit = m_r->create2DRenderTargetInitInfo(width,
-		height,
-		Format::R16G16B16A16_SFLOAT,
-		TextureUsageBit::IMAGE_COMPUTE_WRITE | TextureUsageBit::SAMPLED_ALL,
-		"SSGI");
-	texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
-	m_main.m_rts[0] = m_r->createAndClearRenderTarget(texinit);
-	m_main.m_rts[1] = m_r->createAndClearRenderTarget(texinit);
-
-	// Create main shaders
+	// Init main
 	{
 	{
+		m_main.m_rtDescr =
+			m_r->create2DRenderTargetDescription(width / 2, height / 2, Format::B10G11R11_UFLOAT_PACK32, "SSGI_tmp");
+		m_main.m_rtDescr.bake();
+
 		ANKI_CHECK(getResourceManager().loadResource("shaders/Ssgi.ankiprog", m_main.m_prog));
 		ANKI_CHECK(getResourceManager().loadResource("shaders/Ssgi.ankiprog", m_main.m_prog));
 
 
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_main.m_prog);
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_main.m_prog);
-		variantInitInfo.addMutation("VARIANT", 0);
 
 
-		const ShaderProgramResourceVariant* variant;
-		m_main.m_prog->getOrCreateVariant(variantInitInfo, variant);
-		m_main.m_grProg[0] = variant->getProgram();
+		for(U32 i = 0; i < 4; ++i)
+		{
+			variantInitInfo.addMutation("VARIANT", i);
 
 
-		variantInitInfo.addMutation("VARIANT", 1);
-		m_main.m_prog->getOrCreateVariant(variantInitInfo, variant);
-		m_main.m_grProg[1] = variant->getProgram();
+			const ShaderProgramResourceVariant* variant;
+			m_main.m_prog->getOrCreateVariant(variantInitInfo, variant);
+			m_main.m_grProg[i] = variant->getProgram();
+		}
 	}
 	}
 
 
 	// Init denoise
 	// Init denoise
 	{
 	{
-		ANKI_CHECK(getResourceManager().loadResource("shaders/DepthAwareBlurCompute.ankiprog", m_denoise.m_prog));
+		ANKI_CHECK(getResourceManager().loadResource("shaders/SsgiDenoise.ankiprog", m_denoise.m_prog));
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_denoise.m_prog);
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_denoise.m_prog);
 		const ShaderProgramResourceVariant* variant;
 		const ShaderProgramResourceVariant* variant;
 
 
-		variantInitInfo.addConstant("TEXTURE_SIZE", UVec2(m_r->getWidth(), m_r->getHeight()));
+		variantInitInfo.addConstant("IN_TEXTURE_SIZE", UVec2(width / 2, height / 2));
 
 
-		variantInitInfo.addMutation("SAMPLE_COUNT", 15);
-		variantInitInfo.addMutation("COLOR_COMPONENTS", 4);
-		variantInitInfo.addMutation("ORIENTATION", 0);
-		m_denoise.m_prog->getOrCreateVariant(variantInitInfo, variant);
-		m_denoise.m_grProg[0] = variant->getProgram();
+		for(U32 i = 0; i < 4; ++i)
+		{
+			variantInitInfo.addMutation("VARIANT", i);
+
+			variantInitInfo.addMutation("SAMPLE_COUNT", 11);
+			variantInitInfo.addMutation("ORIENTATION", 0);
+			m_denoise.m_prog->getOrCreateVariant(variantInitInfo, variant);
+			m_denoise.m_grProg[0][i] = variant->getProgram();
 
 
-		variantInitInfo.addMutation("ORIENTATION", 1);
-		m_denoise.m_prog->getOrCreateVariant(variantInitInfo, variant);
-		m_denoise.m_grProg[1] = variant->getProgram();
+			variantInitInfo.addMutation("SAMPLE_COUNT", 15);
+			variantInitInfo.addMutation("ORIENTATION", 1);
+			m_denoise.m_prog->getOrCreateVariant(variantInitInfo, variant);
+			m_denoise.m_grProg[1][i] = variant->getProgram();
+		}
+	}
+
+	// Init reconstruction
+	{
+		ANKI_CHECK(getResourceManager().loadResource("shaders/SsgiReconstruct.ankiprog", m_recontruction.m_prog));
+		ShaderProgramResourceVariantInitInfo variantInitInfo(m_recontruction.m_prog);
+		variantInitInfo.addConstant("FB_SIZE", UVec2(m_r->getWidth(), m_r->getHeight()));
+		const ShaderProgramResourceVariant* variant;
+
+		for(U32 i = 0; i < 4; ++i)
+		{
+			variantInitInfo.addMutation("VARIANT", i);
+			m_recontruction.m_prog->getOrCreateVariant(variantInitInfo, variant);
+			m_recontruction.m_grProg[i] = variant->getProgram();
+		}
+
+		const TextureInitInfo initInfo = m_r->create2DRenderTargetInitInfo(width,
+			height,
+			Format::B10G11R11_UFLOAT_PACK32,
+			TextureUsageBit::SAMPLED_ALL | TextureUsageBit::IMAGE_COMPUTE_WRITE,
+			"SSGI");
+		m_recontruction.m_rt = m_r->createAndClearRenderTarget(initInfo);
 	}
 	}
 
 
 	return Error::NONE;
 	return Error::NONE;
@@ -91,22 +116,21 @@ void Ssgi::populateRenderGraph(RenderingContext& ctx)
 {
 {
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	m_runCtx.m_ctx = &ctx;
 	m_runCtx.m_ctx = &ctx;
-	m_main.m_writeRtIdx = (m_main.m_writeRtIdx + 1) % 2;
 
 
 	// Main pass
 	// Main pass
 	{
 	{
 		// Create RTs
 		// Create RTs
-		if(ANKI_LIKELY(m_main.m_rtImportedOnce))
+		if(ANKI_LIKELY(m_recontruction.m_rtImportedOnce))
 		{
 		{
-			m_runCtx.m_rts[0] = rgraph.importRenderTarget(m_main.m_rts[0]);
-			m_runCtx.m_rts[1] = rgraph.importRenderTarget(m_main.m_rts[1]);
+			m_runCtx.m_finalRt = rgraph.importRenderTarget(m_recontruction.m_rt);
 		}
 		}
 		else
 		else
 		{
 		{
-			m_runCtx.m_rts[0] = rgraph.importRenderTarget(m_main.m_rts[0], TextureUsageBit::SAMPLED_FRAGMENT);
-			m_runCtx.m_rts[1] = rgraph.importRenderTarget(m_main.m_rts[1], TextureUsageBit::SAMPLED_FRAGMENT);
-			m_main.m_rtImportedOnce = true;
+			m_runCtx.m_finalRt = rgraph.importRenderTarget(m_recontruction.m_rt, TextureUsageBit::SAMPLED_FRAGMENT);
+			m_recontruction.m_rtImportedOnce = true;
 		}
 		}
+		m_runCtx.m_intermediateRts[WRITE] = rgraph.newRenderTarget(m_main.m_rtDescr);
+		m_runCtx.m_intermediateRts[READ] = rgraph.newRenderTarget(m_main.m_rtDescr);
 
 
 		// Create pass
 		// Create pass
 		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("SSGI");
 		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("SSGI");
@@ -115,8 +139,8 @@ void Ssgi::populateRenderGraph(RenderingContext& ctx)
 			this,
 			this,
 			0);
 			0);
 
 
-		rpass.newDependency({m_runCtx.m_rts[m_main.m_writeRtIdx], TextureUsageBit::IMAGE_COMPUTE_WRITE});
-		rpass.newDependency({m_runCtx.m_rts[!m_main.m_writeRtIdx], TextureUsageBit::SAMPLED_COMPUTE});
+		rpass.newDependency({m_runCtx.m_intermediateRts[WRITE], TextureUsageBit::IMAGE_COMPUTE_WRITE});
+		rpass.newDependency({m_runCtx.m_finalRt, TextureUsageBit::SAMPLED_COMPUTE});
 
 
 		TextureSubresourceInfo hizSubresource;
 		TextureSubresourceInfo hizSubresource;
 		hizSubresource.m_firstMipmap = m_main.m_depthLod;
 		hizSubresource.m_firstMipmap = m_main.m_depthLod;
@@ -127,10 +151,10 @@ void Ssgi::populateRenderGraph(RenderingContext& ctx)
 
 
 	// Blur vertical
 	// Blur vertical
 	{
 	{
-		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("SSGIBlurV");
+		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("SSGI_blur_v");
 
 
-		rpass.newDependency({m_runCtx.m_rts[m_main.m_writeRtIdx], TextureUsageBit::SAMPLED_COMPUTE});
-		rpass.newDependency({m_runCtx.m_rts[!m_main.m_writeRtIdx], TextureUsageBit::IMAGE_COMPUTE_WRITE});
+		rpass.newDependency({m_runCtx.m_intermediateRts[WRITE], TextureUsageBit::SAMPLED_COMPUTE});
+		rpass.newDependency({m_runCtx.m_intermediateRts[READ], TextureUsageBit::IMAGE_COMPUTE_WRITE});
 		rpass.newDependency({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_COMPUTE});
 		rpass.newDependency({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_COMPUTE});
 
 
 		rpass.setWork(
 		rpass.setWork(
@@ -141,10 +165,10 @@ void Ssgi::populateRenderGraph(RenderingContext& ctx)
 
 
 	// Blur horizontal
 	// Blur horizontal
 	{
 	{
-		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("SSGIBlurH");
+		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("SSGI_blur_h");
 
 
-		rpass.newDependency({m_runCtx.m_rts[!m_main.m_writeRtIdx], TextureUsageBit::SAMPLED_COMPUTE});
-		rpass.newDependency({m_runCtx.m_rts[m_main.m_writeRtIdx], TextureUsageBit::IMAGE_COMPUTE_WRITE});
+		rpass.newDependency({m_runCtx.m_intermediateRts[READ], TextureUsageBit::SAMPLED_COMPUTE});
+		rpass.newDependency({m_runCtx.m_intermediateRts[WRITE], TextureUsageBit::IMAGE_COMPUTE_WRITE});
 		rpass.newDependency({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_COMPUTE});
 		rpass.newDependency({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_COMPUTE});
 
 
 		rpass.setWork(
 		rpass.setWork(
@@ -152,15 +176,30 @@ void Ssgi::populateRenderGraph(RenderingContext& ctx)
 			this,
 			this,
 			0);
 			0);
 	}
 	}
+
+	// Reconstruction
+	{
+		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("SSGI_recon");
+
+		rpass.newDependency({m_runCtx.m_intermediateRts[WRITE], TextureUsageBit::SAMPLED_COMPUTE});
+		rpass.newDependency({m_runCtx.m_finalRt, TextureUsageBit::IMAGE_COMPUTE_WRITE});
+
+		rpass.setWork(
+			[](RenderPassWorkContext& rgraphCtx) {
+				static_cast<Ssgi*>(rgraphCtx.m_userData)->runRecontruct(rgraphCtx);
+			},
+			this,
+			0);
+	}
 }
 }
 
 
 void Ssgi::run(RenderPassWorkContext& rgraphCtx)
 void Ssgi::run(RenderPassWorkContext& rgraphCtx)
 {
 {
 	RenderingContext& ctx = *m_runCtx.m_ctx;
 	RenderingContext& ctx = *m_runCtx.m_ctx;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	cmdb->bindShaderProgram(m_main.m_grProg[m_r->getFrameCount() & 1u]);
+	cmdb->bindShaderProgram(m_main.m_grProg[m_r->getFrameCount() % 4]);
 
 
-	rgraphCtx.bindImage(0, 0, m_runCtx.m_rts[m_main.m_writeRtIdx], TextureSubresourceInfo());
+	rgraphCtx.bindImage(0, 0, m_runCtx.m_intermediateRts[WRITE], TextureSubresourceInfo());
 
 
 	// Bind uniforms
 	// Bind uniforms
 	SsgiUniforms* unis = allocateAndBindUniforms<SsgiUniforms*>(sizeof(SsgiUniforms), cmdb, 0, 1);
 	SsgiUniforms* unis = allocateAndBindUniforms<SsgiUniforms*>(sizeof(SsgiUniforms), cmdb, 0, 1);
@@ -184,36 +223,50 @@ void Ssgi::run(RenderPassWorkContext& rgraphCtx)
 	rgraphCtx.bindTexture(0, 4, m_r->getDepthDownscale().getHiZRt(), hizSubresource);
 	rgraphCtx.bindTexture(0, 4, m_r->getDepthDownscale().getHiZRt(), hizSubresource);
 
 
 	rgraphCtx.bindColorTexture(0, 5, m_r->getDownscaleBlur().getRt());
 	rgraphCtx.bindColorTexture(0, 5, m_r->getDownscaleBlur().getRt());
-	rgraphCtx.bindColorTexture(0, 6, m_runCtx.m_rts[!m_main.m_writeRtIdx]);
+	rgraphCtx.bindColorTexture(0, 6, m_runCtx.m_finalRt);
 
 
 	// Dispatch
 	// Dispatch
-	dispatchPPCompute(cmdb, 16, 16, m_r->getWidth() / 2, m_r->getHeight());
+	dispatchPPCompute(cmdb, 16, 16, m_r->getWidth() / 2, m_r->getHeight() / 2);
 }
 }
 
 
 void Ssgi::runVBlur(RenderPassWorkContext& rgraphCtx)
 void Ssgi::runVBlur(RenderPassWorkContext& rgraphCtx)
 {
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	cmdb->bindShaderProgram(m_denoise.m_grProg[0]);
+	cmdb->bindShaderProgram(m_denoise.m_grProg[0][m_r->getFrameCount() % 4]);
 
 
 	cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
 	cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
-	rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_rts[m_main.m_writeRtIdx]);
+	rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_intermediateRts[WRITE]);
 	rgraphCtx.bindTexture(0, 2, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
 	rgraphCtx.bindTexture(0, 2, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
 
 
-	rgraphCtx.bindImage(0, 3, m_runCtx.m_rts[!m_main.m_writeRtIdx], TextureSubresourceInfo());
+	rgraphCtx.bindImage(0, 3, m_runCtx.m_intermediateRts[READ], TextureSubresourceInfo());
 
 
-	dispatchPPCompute(cmdb, 8, 8, m_r->getWidth(), m_r->getHeight());
+	dispatchPPCompute(cmdb, 8, 8, m_r->getWidth() / 2, m_r->getHeight() / 2);
 }
 }
 
 
 void Ssgi::runHBlur(RenderPassWorkContext& rgraphCtx)
 void Ssgi::runHBlur(RenderPassWorkContext& rgraphCtx)
 {
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	cmdb->bindShaderProgram(m_denoise.m_grProg[1]);
+	cmdb->bindShaderProgram(m_denoise.m_grProg[1][m_r->getFrameCount() % 4]);
+
+	cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
+	rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_intermediateRts[READ]);
+	rgraphCtx.bindTexture(0, 2, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
+
+	rgraphCtx.bindImage(0, 3, m_runCtx.m_intermediateRts[WRITE], TextureSubresourceInfo());
+
+	dispatchPPCompute(cmdb, 8, 8, m_r->getWidth() / 2, m_r->getHeight() / 2);
+}
+
+void Ssgi::runRecontruct(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	cmdb->bindShaderProgram(m_recontruction.m_grProg[m_r->getFrameCount() % 4]);
 
 
 	cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
 	cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
-	rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_rts[!m_main.m_writeRtIdx]);
+	rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_intermediateRts[WRITE]);
 	rgraphCtx.bindTexture(0, 2, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
 	rgraphCtx.bindTexture(0, 2, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
 
 
-	rgraphCtx.bindImage(0, 3, m_runCtx.m_rts[m_main.m_writeRtIdx], TextureSubresourceInfo());
+	rgraphCtx.bindImage(0, 3, m_runCtx.m_finalRt, TextureSubresourceInfo());
 
 
 	dispatchPPCompute(cmdb, 8, 8, m_r->getWidth(), m_r->getHeight());
 	dispatchPPCompute(cmdb, 8, 8, m_r->getWidth(), m_r->getHeight());
 }
 }

+ 18 - 9
src/anki/renderer/Ssgi.h

@@ -34,13 +34,13 @@ public:
 
 
 	RenderTargetHandle getRt() const
 	RenderTargetHandle getRt() const
 	{
 	{
-		return m_runCtx.m_rts[m_main.m_writeRtIdx];
+		return m_runCtx.m_finalRt;
 	}
 	}
 
 
 	void getDebugRenderTarget(CString rtName, RenderTargetHandle& handle) const override
 	void getDebugRenderTarget(CString rtName, RenderTargetHandle& handle) const override
 	{
 	{
 		ANKI_ASSERT(rtName == "SSGI");
 		ANKI_ASSERT(rtName == "SSGI");
-		handle = m_runCtx.m_rts[m_main.m_writeRtIdx];
+		handle = m_runCtx.m_finalRt;
 	}
 	}
 
 
 private:
 private:
@@ -48,28 +48,36 @@ private:
 	{
 	{
 	public:
 	public:
 		ShaderProgramResourcePtr m_prog;
 		ShaderProgramResourcePtr m_prog;
-		Array<ShaderProgramPtr, 2> m_grProg;
-		Array<TexturePtr, 2> m_rts;
+		Array<ShaderProgramPtr, 4> m_grProg;
+		RenderTargetDescription m_rtDescr;
 		TextureResourcePtr m_noiseTex;
 		TextureResourcePtr m_noiseTex;
 		U32 m_maxSteps = 32;
 		U32 m_maxSteps = 32;
 		U32 m_firstStepPixels = 16;
 		U32 m_firstStepPixels = 16;
 		U32 m_depthLod = 0;
 		U32 m_depthLod = 0;
-		Bool m_rtImportedOnce = false;
-		U8 m_writeRtIdx = 1;
 	} m_main;
 	} m_main;
 
 
 	class
 	class
 	{
 	{
 	public:
 	public:
 		ShaderProgramResourcePtr m_prog;
 		ShaderProgramResourcePtr m_prog;
-		Array<ShaderProgramPtr, 2> m_grProg;
+		Array2d<ShaderProgramPtr, 2, 4> m_grProg;
 	} m_denoise;
 	} m_denoise;
 
 
 	class
 	class
 	{
 	{
 	public:
 	public:
-		Array<RenderTargetHandle, 2> m_rts;
-		RenderingContext* m_ctx ANKI_DEBUG_CODE(= nullptr);
+		TexturePtr m_rt;
+		ShaderProgramResourcePtr m_prog;
+		Array<ShaderProgramPtr, 4> m_grProg;
+		Bool m_rtImportedOnce = false;
+	} m_recontruction;
+
+	class
+	{
+	public:
+		Array<RenderTargetHandle, 2> m_intermediateRts;
+		RenderTargetHandle m_finalRt;
+		RenderingContext* m_ctx = nullptr;
 	} m_runCtx;
 	} m_runCtx;
 
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
@@ -77,6 +85,7 @@ private:
 	void run(RenderPassWorkContext& rgraphCtx);
 	void run(RenderPassWorkContext& rgraphCtx);
 	void runVBlur(RenderPassWorkContext& rgraphCtx);
 	void runVBlur(RenderPassWorkContext& rgraphCtx);
 	void runHBlur(RenderPassWorkContext& rgraphCtx);
 	void runHBlur(RenderPassWorkContext& rgraphCtx);
+	void runRecontruct(RenderPassWorkContext& rgraphCtx);
 };
 };
 /// @}
 /// @}