소스 검색

Merging IR and IS

Panagiotis Christopoulos Charitos 9 년 전
부모
커밋
1bd2f13bd1

+ 1 - 1
include/anki/gr/Common.h

@@ -68,7 +68,7 @@ const U MAX_VERTEX_ATTRIBUTES = 8;
 const U MAX_COLOR_ATTACHMENTS = 4;
 const U MAX_MIPMAPS = 16;
 const U MAX_TEXTURE_LAYERS = 32;
-const U MAX_TEXTURE_BINDINGS = 8;
+const U MAX_TEXTURE_BINDINGS = 10;
 const U MAX_UNIFORM_BUFFER_BINDINGS = 1;
 const U MAX_STORAGE_BUFFER_BINDINGS = 8;
 const U MAX_ATOMIC_BUFFER_BINDINGS = 1;

+ 11 - 20
include/anki/renderer/Ir.h

@@ -36,29 +36,29 @@ anki_internal:
 
 	ANKI_USE_RESULT Error run(RenderingContext& ctx);
 
-	DynamicBufferToken getProbesToken() const
+	U getCubemapArrayMipmapCount() const
 	{
-		return m_probesToken;
+		return m_cubemapArrMipCount;
 	}
 
-	DynamicBufferToken getProbeIndicesToken() const
+	TexturePtr getIrradianceTexture() const
 	{
-		return m_indicesToken;
+		return m_irradianceCubemapArr;
 	}
 
-	DynamicBufferToken getClustersToken() const
+	TexturePtr getReflectionTexture() const
 	{
-		return m_clustersToken;
+		return m_envCubemapArr;
 	}
 
-	U getCubemapArrayMipmapCount() const
+	TexturePtr getIntegrationLut() const
 	{
-		return m_cubemapArrMipCount;
+		return m_integrationLut->getGrTexture();
 	}
 
-	ResourceGroupPtr getResourceGroup() const
+	SamplerPtr getIntegrationLutSampler() const
 	{
-		return m_rcGroup;
+		return m_integrationLutSampler;
 	}
 
 private:
@@ -77,7 +77,6 @@ private:
 	U16 m_cubemapArrSize = 0;
 	U16 m_fbSize = 0;
 	DArray<CacheEntry> m_cacheEntries;
-	Barrier m_barrier;
 
 	// Irradiance
 	TexturePtr m_irradianceCubemapArr;
@@ -89,20 +88,12 @@ private:
 	TextureResourcePtr m_integrationLut;
 	SamplerPtr m_integrationLutSampler;
 
-	// Tokens
-	DynamicBufferToken m_probesToken;
-	DynamicBufferToken m_clustersToken;
-	DynamicBufferToken m_indicesToken;
-
-	ResourceGroupPtr m_rcGroup;
-
 	ANKI_USE_RESULT Error initIrradiance();
 
 	/// Bin probes in clusters.
 	void binProbes(U32 threadId, PtrSize threadsCount, IrRunContext& ctx);
 
-	ANKI_USE_RESULT Error writeProbeAndRender(
-		RenderingContext& ctx, SceneNode& node, IrShaderReflectionProbe& probe);
+	ANKI_USE_RESULT Error tryRender(RenderingContext& ctx, SceneNode& node);
 
 	void binProbe(U probeIdx, IrRunContext& ctx, IrTaskContext& task) const;
 

+ 7 - 4
include/anki/renderer/Is.h

@@ -29,6 +29,7 @@ class SpatialComponent;
 class FrustumComponent;
 class TaskCommonData;
 class ClustererTestResult;
+class SceneNode;
 
 /// @addtogroup renderer
 /// @{
@@ -101,6 +102,7 @@ private:
 	DynamicBufferToken m_sLightsToken;
 	DynamicBufferToken m_clustersToken;
 	DynamicBufferToken m_lightIdsToken;
+	DynamicBufferToken m_probesToken;
 
 	ResourceGroupPtr m_rcGroup;
 
@@ -119,10 +121,6 @@ private:
 
 	/// @name Limits
 	/// @{
-	U16 m_maxPointLights;
-	U32 m_maxSpotLights;
-	U32 m_maxSpotTexLights;
-
 	U32 m_maxLightIds;
 	/// @}
 
@@ -161,6 +159,11 @@ private:
 		U lightType,
 		TaskCommonData& task,
 		ClustererTestResult& testResult);
+
+	void writeAndBinProbe(const FrustumComponent& camFrc,
+		const SceneNode& node,
+		TaskCommonData& task,
+		ClustererTestResult& testResult);
 };
 
 /// @}

+ 0 - 1
include/anki/scene/LensFlareComponent.h

@@ -100,7 +100,6 @@ public:
 
 private:
 	TextureResourcePtr m_tex; ///< Array of textures.
-	U8 m_flareCount = 0; ///< Cache the flare count.
 
 	Vec4 m_colorMul = Vec4(1.0); ///< Color multiplier.
 

+ 13 - 0
include/anki/scene/ReflectionProbeComponent.h

@@ -59,10 +59,23 @@ public:
 		m_markedForRendering = render;
 	}
 
+	void setTextureArrayIndex(U idx)
+	{
+		m_textureArrayIndex = idx;
+	}
+
+	U getTextureArrayIndex() const
+	{
+		ANKI_ASSERT(m_textureArrayIndex < MAX_U16);
+		return m_textureArrayIndex;
+	}
+
 private:
 	Vec4 m_pos = Vec4(0.0);
 	F32 m_radius = 0.0;
 	Bool8 m_markedForRendering = false;
+
+	U16 m_textureArrayIndex = MAX_U16; ///< Used by the renderer
 };
 /// @}
 

+ 1 - 1
shaders/Common.glsl

@@ -31,7 +31,7 @@ const float PI = 3.14159265358979323846;
 // Binding
 #define UBO_BINDING(slot_, binding_) binding = slot_ * 1 + binding_
 #define SS_BINDING(slot_, binding_) binding = slot_ * 8 + binding_
-#define TEX_BINDING(slot_, binding_) binding = slot_ * 8 + binding_
+#define TEX_BINDING(slot_, binding_) binding = slot_ * 10 + binding_
 #define ATOMIC_BINDING(slot_, binding_) binding = slot_ * 1 + binding_
 
 // Common locations

+ 10 - 9
shaders/FsCommonFrag.glsl

@@ -143,8 +143,8 @@ vec3 computeLightColor(vec3 diffCol)
 
 	// Find the cluster and then the light counts
 	uint lightOffset;
-	uint pointLightsCount;
-	uint spotLightsCount;
+	uint pointLightCount;
+	uint spotLightCount;
 	{
 		uint clusterIdx = computeClusterIndexUsingCustomFragCoord(
 			u_lightingUniforms.nearFarClustererMagicPad1.x,
@@ -154,15 +154,16 @@ vec3 computeLightColor(vec3 diffCol)
 			u_lightingUniforms.tileCountPad1.y,
 			gl_FragCoord.xy * 2.0);
 
-		uint cluster = u_clusters[clusterIdx];
-
-		lightOffset = cluster >> 16u;
-		pointLightsCount = (cluster >> 8u) & 0xFFu;
-		spotLightsCount = cluster & 0xFFu;
+		uint probeCount;
+		getClusterInfo(clusterIdx,
+			lightOffset,
+			pointLightCount,
+			spotLightCount,
+			probeCount);
 	}
 
 	// Point lights
-	for(uint i = 0U; i < pointLightsCount; ++i)
+	for(uint i = 0U; i < pointLightCount; ++i)
 	{
 		uint lightId = u_lightIndices[lightOffset++];
 		PointLight light = u_pointLights[lightId];
@@ -189,7 +190,7 @@ vec3 computeLightColor(vec3 diffCol)
 	}
 
 	// Spot lights
-	for(uint i = 0U; i < spotLightsCount; ++i)
+	for(uint i = 0U; i < spotLightCount; ++i)
 	{
 		uint lightId = u_lightIndices[lightOffset++];
 		SpotLight light = u_spotLights[lightId];

+ 0 - 160
shaders/ImageReflections.glsl

@@ -1,160 +0,0 @@
-// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-// Contains resources and functions for image reflections
-
-#ifndef ANKI_SHADERS_IMAGE_REFLECTIONS_GLSL
-#define ANKI_SHADERS_IMAGE_REFLECTIONS_GLSL
-
-#include "shaders/Clusterer.glsl"
-
-// Representation of a reflection probe
-struct ReflectionProbe
-{
-	// Position of the prove in view space. Radius of probe squared
-	vec4 positionRadiusSq;
-
-	// Slice in u_reflectionsTex vector.
-	vec4 cubemapIndexPad3;
-};
-
-layout(std140,
-	row_major,
-	SS_BINDING(IMAGE_REFLECTIONS_SET,
-		   IMAGE_REFLECTIONS_FIRST_SS_BINDING)) readonly buffer _irs1
-{
-	mat3 u_invViewRotation;
-	vec4 u_nearClusterMagicPad2;
-	ReflectionProbe u_reflectionProbes[];
-};
-
-layout(std430,
-	row_major,
-	SS_BINDING(IMAGE_REFLECTIONS_SET,
-		   IMAGE_REFLECTIONS_FIRST_SS_BINDING + 1)) readonly buffer _irs2
-{
-	uint u_reflectionProbeIndices[];
-};
-
-layout(std430,
-	row_major,
-	SS_BINDING(IMAGE_REFLECTIONS_SET,
-		   IMAGE_REFLECTIONS_FIRST_SS_BINDING + 2)) readonly buffer _irs3
-{
-	uvec2 u_reflectionClusters[];
-};
-
-layout(TEX_BINDING(IMAGE_REFLECTIONS_SET,
-	IMAGE_REFLECTIONS_TEX_BINDING)) uniform samplerCubeArray u_reflectionsTex;
-
-layout(TEX_BINDING(IMAGE_REFLECTIONS_SET,
-	IMAGE_REFLECTIONS_TEX_BINDING
-		+ 1)) uniform samplerCubeArray u_irradianceTex;
-
-layout(TEX_BINDING(IMAGE_REFLECTIONS_SET,
-	IMAGE_REFLECTIONS_TEX_BINDING + 2)) uniform sampler2D u_integrationLut;
-
-//==============================================================================
-// Compute the cubemap texture lookup vector given the reflection vector (r)
-// the radius squared of the probe (R2) and the frag pos in sphere space (f)
-vec3 computeCubemapVecAccurate(in vec3 r, in float R2, in vec3 f)
-{
-	// Compute the collision of the r to the inner part of the sphere
-	// From now on we work on the sphere's space
-
-	// Project the center of the sphere (it's zero now since we are in sphere
-	// space) in ray "f,r"
-	vec3 p = f - r * dot(f, r);
-
-	// The collision to the sphere is point x where x = p + T * r
-	// Because of the pythagorean theorem: R^2 = dot(p, p) + dot(T * r, T * r)
-	// solving for T, T = R / |p|
-	// then x becomes x = sqrt(R^2 - dot(p, p)) * r + p;
-	float pp = dot(p, p);
-	pp = min(pp, R2);
-	float sq = sqrt(R2 - pp);
-	vec3 x = p + sq * r;
-
-	// Rotate UV to move it to world space
-	vec3 uv = u_invViewRotation * x;
-
-	return uv;
-}
-
-//==============================================================================
-// Cheap version of computeCubemapVecAccurate
-vec3 computeCubemapVecCheap(in vec3 r, in float R2, in vec3 f)
-{
-	return u_invViewRotation * r;
-}
-
-//==============================================================================
-void readIndirectInternal(in uint clusterIndex,
-	in vec3 posVSpace,
-	in vec3 r,
-	in vec3 n,
-	in float lod,
-	out vec3 specIndirect,
-	out vec3 diffIndirect)
-{
-	specIndirect = vec3(0.0);
-	diffIndirect = vec3(0.0);
-
-	// Check proxy
-	uvec2 cluster = u_reflectionClusters[clusterIndex];
-	uint indexOffset = cluster[0];
-	uint indexCount = cluster[1];
-	for(uint i = 0; i < indexCount; ++i)
-	{
-		uint probeIndex = u_reflectionProbeIndices[indexOffset++];
-		ReflectionProbe probe = u_reflectionProbes[probeIndex];
-
-		float R2 = probe.positionRadiusSq.w;
-		vec3 center = probe.positionRadiusSq.xyz;
-
-		// Get distance from the center of the probe
-		vec3 f = posVSpace - center;
-
-		// Cubemap UV in view space
-		vec3 uv = computeCubemapVecAccurate(r, R2, f);
-
-		// Read!
-		float cubemapIndex = probe.cubemapIndexPad3.x;
-		vec3 c = textureLod(u_reflectionsTex, vec4(uv, cubemapIndex), lod).rgb;
-
-		// Combine (lerp) with previous color
-		float d = dot(f, f);
-		float factor = d / R2;
-		factor = min(factor, 1.0);
-		specIndirect = mix(c, specIndirect, factor);
-		// Same as: specIndirect = c * (1.0 - factor) + specIndirect * factor
-
-		// Do the same for diffuse
-		uv = computeCubemapVecCheap(n, R2, f);
-		vec3 id = texture(u_irradianceTex, vec4(uv, cubemapIndex)).rgb;
-		diffIndirect = mix(id, diffIndirect, factor);
-	}
-}
-
-//==============================================================================
-void readIndirect(in vec3 posVSpace,
-	in vec3 r,
-	in vec3 n,
-	in float lod,
-	out vec3 specIndirect,
-	out vec3 diffIndirect)
-{
-	uint clusterIdx =
-		computeClusterIndexUsingFragCoord(u_nearClusterMagicPad2.x,
-			u_nearClusterMagicPad2.y,
-			posVSpace.z,
-			TILE_COUNT_X,
-			TILE_COUNT_Y);
-
-	readIndirectInternal(
-		clusterIdx, posVSpace, r, n, lod, specIndirect, diffIndirect);
-}
-
-#endif

+ 20 - 20
shaders/IsLp.frag.glsl

@@ -29,15 +29,6 @@ layout(location = 0) out vec3 out_color;
 
 const uint TILE_COUNT = TILE_COUNT_X * TILE_COUNT_Y;
 
-#if INDIRECT_ENABLED
-#define IMAGE_REFLECTIONS_SET 1
-#define IMAGE_REFLECTIONS_FIRST_SS_BINDING 0
-#define IMAGE_REFLECTIONS_TEX_BINDING 0
-#include "shaders/ImageReflections.glsl"
-#undef IMAGE_REFLECTIONS_SET
-#undef IMAGE_REFLECTIONS_FIRST_SS_BINDING
-#endif
-
 //==============================================================================
 // Return frag pos in view space
 vec3 getFragPosVSpace()
@@ -117,17 +108,19 @@ void main()
 		TILE_COUNT_X,
 		TILE_COUNT_Y);
 
-	uint cluster = u_clusters[clusterIdx];
-	uint lightOffset = cluster >> 16u;
-	uint pointLightsCount = (cluster >> 8u) & 0xFFu;
-	uint spotLightsCount = cluster & 0xFFu;
+	uint lightOffset;
+	uint pointLightCount;
+	uint spotLightCount;
+	uint probeCount;
+	getClusterInfo(
+		clusterIdx, lightOffset, pointLightCount, spotLightCount, probeCount);
 
 	// Shadowpass sample count
 	uint shadowSampleCount =
 		computeShadowSampleCount(SHADOW_SAMPLE_COUNT, fragPos.z);
 
 	// Point lights
-	for(uint i = 0U; i < pointLightsCount; ++i)
+	for(uint i = 0U; i < pointLightCount; ++i)
 	{
 		uint lightId = u_lightIndices[lightOffset++];
 		PointLight light = u_pointLights[lightId];
@@ -147,7 +140,7 @@ void main()
 	}
 
 	// Spot lights
-	for(uint i = 0U; i < spotLightsCount; ++i)
+	for(uint i = 0U; i < spotLightCount; ++i)
 	{
 		uint lightId = u_lightIndices[lightOffset++];
 		SpotLight light = u_spotLights[lightId];
@@ -179,7 +172,14 @@ void main()
 	float reflLod = float(IR_MIPMAP_COUNT) * gbuffer.roughness;
 
 	vec3 specIndirect, diffIndirect;
-	readIndirect(fragPos, r, normal, reflLod, specIndirect, diffIndirect);
+	readIndirect(lightOffset,
+		probeCount,
+		fragPos,
+		r,
+		normal,
+		reflLod,
+		specIndirect,
+		diffIndirect);
 
 	diffIndirect *= gbuffer.diffuse;
 
@@ -193,18 +193,18 @@ void main()
 
 // out_color = diffCol;
 #if 0
-	if(pointLightsCount == 0)
+	if(pointLightCount == 0)
 	{
 	}
-	else if(pointLightsCount == 1)
+	else if(pointLightCount == 1)
 	{
 		out_color += vec3(0.0, 1.0, 0.0);
 	}
-	else if(pointLightsCount == 2)
+	else if(pointLightCount == 2)
 	{
 		out_color += vec3(0.0, 0.0, 1.0);
 	}
-	else if(pointLightsCount == 3)
+	else if(pointLightCount == 3)
 	{
 		out_color += vec3(1.0, 0.0, 1.0);
 	}

+ 95 - 0
shaders/LightFunctions.glsl

@@ -15,6 +15,21 @@ const float OMNI_LIGHT_FRUSTUM_NEAR_PLANE = 0.1 / 4.0;
 
 const uint SHADOW_SAMPLE_COUNT = 16;
 
+//==============================================================================
+// Get element count attached in a cluster
+void getClusterInfo(in uint clusterIdx,
+	out uint indexOffset,
+	out uint pointLightCount,
+	out uint spotLightCount,
+	out uint probeCount)
+{
+	uint cluster = u_clusters[clusterIdx];
+	indexOffset = cluster >> 16u;
+	probeCount = (cluster >> 8u) & 0xFu;
+	pointLightCount = (cluster >> 4u) & 0xFu;
+	spotLightCount = cluster & 0xFu;
+}
+
 //==============================================================================
 float computeAttenuationFactor(float lightRadius, vec3 frag2Light)
 {
@@ -170,4 +185,84 @@ float computeShadowFactorOmni(vec3 frag2Light, float layer, float radius)
 	return shadowFactor;
 }
 
+//==============================================================================
+// Compute the cubemap texture lookup vector given the reflection vector (r)
+// the radius squared of the probe (R2) and the frag pos in sphere space (f)
+vec3 computeCubemapVecAccurate(in vec3 r, in float R2, in vec3 f)
+{
+	// Compute the collision of the r to the inner part of the sphere
+	// From now on we work on the sphere's space
+
+	// Project the center of the sphere (it's zero now since we are in sphere
+	// space) in ray "f,r"
+	vec3 p = f - r * dot(f, r);
+
+	// The collision to the sphere is point x where x = p + T * r
+	// Because of the pythagorean theorem: R^2 = dot(p, p) + dot(T * r, T * r)
+	// solving for T, T = R / |p|
+	// then x becomes x = sqrt(R^2 - dot(p, p)) * r + p;
+	float pp = dot(p, p);
+	pp = min(pp, R2);
+	float sq = sqrt(R2 - pp);
+	vec3 x = p + sq * r;
+
+	// Rotate UV to move it to world space
+	vec3 uv = u_lightingUniforms.invViewRotation * x;
+
+	return uv;
+}
+
+//==============================================================================
+// Cheap version of computeCubemapVecAccurate
+vec3 computeCubemapVecCheap(in vec3 r, in float R2, in vec3 f)
+{
+	return u_lightingUniforms.invViewRotation * r;
+}
+
+//==============================================================================
+void readIndirect(in uint indexOffset,
+	in uint probeCount,
+	in vec3 posVSpace,
+	in vec3 r,
+	in vec3 n,
+	in float lod,
+	out vec3 specIndirect,
+	out vec3 diffIndirect)
+{
+	specIndirect = vec3(0.0);
+	diffIndirect = vec3(0.0);
+
+	// Check proxy
+	for(uint i = 0; i < probeCount; ++i)
+	{
+		uint probeIndex = u_lightIndices[indexOffset++];
+		ReflectionProbe probe = u_reflectionProbes[probeIndex];
+
+		float R2 = probe.positionRadiusSq.w;
+		vec3 center = probe.positionRadiusSq.xyz;
+
+		// Get distance from the center of the probe
+		vec3 f = posVSpace - center;
+
+		// Cubemap UV in view space
+		vec3 uv = computeCubemapVecAccurate(r, R2, f);
+
+		// Read!
+		float cubemapIndex = probe.cubemapIndexPad3.x;
+		vec3 c = textureLod(u_reflectionsTex, vec4(uv, cubemapIndex), lod).rgb;
+
+		// Combine (lerp) with previous color
+		float d = dot(f, f);
+		float factor = d / R2;
+		factor = min(factor, 1.0);
+		specIndirect = mix(c, specIndirect, factor);
+		// Same as: specIndirect = c * (1.0 - factor) + specIndirect * factor
+
+		// Do the same for diffuse
+		uv = computeCubemapVecCheap(n, R2, f);
+		vec3 id = texture(u_irradianceTex, vec4(uv, cubemapIndex)).rgb;
+		diffIndirect = mix(id, diffIndirect, factor);
+	}
+}
+
 #endif

+ 41 - 14
shaders/LightResources.glsl

@@ -16,18 +16,10 @@ struct LightingUniforms
 	vec4 rendererSizeTimePad1;
 	vec4 nearFarClustererMagicPad1;
 	mat4 viewMat;
+	mat3 invViewRotation;
 	uvec4 tileCountPad1;
 };
 
-layout(std140,
-	row_major,
-	SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING)) readonly buffer _s0
-{
-	LightingUniforms u_lightingUniforms;
-};
-
-#ifdef FRAGMENT_SHADER
-
 // Point light
 struct PointLight
 {
@@ -36,11 +28,6 @@ struct PointLight
 	vec4 specularColorTexId; // xyz: spec color, w: diffuse tex ID
 };
 
-layout(std140, SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING + 1)) readonly buffer _s1
-{
-	PointLight u_pointLights[];
-};
-
 // Spot light
 struct SpotLight
 {
@@ -52,6 +39,30 @@ struct SpotLight
 	mat4 texProjectionMat;
 };
 
+// Representation of a reflection probe
+struct ReflectionProbe
+{
+	// Position of the prove in view space. Radius of probe squared
+	vec4 positionRadiusSq;
+
+	// Slice in u_reflectionsTex vector.
+	vec4 cubemapIndexPad3;
+};
+
+layout(std140,
+	row_major,
+	SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING)) readonly buffer _s0
+{
+	LightingUniforms u_lightingUniforms;
+};
+
+#ifdef FRAGMENT_SHADER
+
+layout(std140, SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING + 1)) readonly buffer _s1
+{
+	PointLight u_pointLights[];
+};
+
 layout(SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING + 2),
 	std140,
 	row_major) readonly buffer _s2
@@ -69,11 +80,27 @@ layout(std430, SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING + 4)) readonly buffer _s4
 	uint u_lightIndices[];
 };
 
+layout(std140,
+	row_major,
+	SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING + 5)) readonly buffer _s5
+{
+	ReflectionProbe u_reflectionProbes[];
+};
+
 layout(TEX_BINDING(LIGHT_SET,
 	LIGHT_TEX_BINDING)) uniform highp sampler2DArrayShadow u_spotMapArr;
 layout(TEX_BINDING(LIGHT_SET,
 	LIGHT_TEX_BINDING + 1)) uniform highp samplerCubeArrayShadow u_omniMapArr;
 
+layout(TEX_BINDING(LIGHT_SET,
+	LIGHT_TEX_BINDING + 2)) uniform samplerCubeArray u_reflectionsTex;
+
+layout(TEX_BINDING(
+	LIGHT_SET, LIGHT_TEX_BINDING + 3)) uniform samplerCubeArray u_irradianceTex;
+
+layout(TEX_BINDING(
+	LIGHT_SET, LIGHT_TEX_BINDING + 4)) uniform sampler2D u_integrationLut;
+
 #endif // FRAGMENT_SHADER
 
 #endif

+ 15 - 0
shaders/Pps.frag.glsl

@@ -135,6 +135,21 @@ vec3 fog(vec3 colorIn, vec2 uv)
 	return colorIn * (1.0 - t) + u_uniforms.fogColorFogFactor.rgb * t;
 }
 
+//==============================================================================
+vec3 dither(in vec3 col)
+{
+	const float c0 = 16.0;
+
+	vec3 vDither = vec3(dot(vec2(171.0, 231.0), gl_FragCoord.xy));
+	vDither.rgb = fract(vDither.rgb / vec3(103.0, 71.0, 97.0));
+
+	col = col * (255.0 / c0) + vDither.rgb;
+	col = floor(col) / 255.0;
+	col *= c0;
+
+	return col;
+}
+
 //==============================================================================
 void main()
 {

+ 0 - 104
shaders/Refl.frag.glsl

@@ -1,104 +0,0 @@
-// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include "shaders/Pack.glsl"
-
-// Common
-layout(TEX_BINDING(0, 0)) uniform sampler2D u_depthRt;
-layout(TEX_BINDING(0, 1)) uniform sampler2D u_msRt0;
-layout(TEX_BINDING(0, 2)) uniform sampler2D u_msRt1;
-layout(TEX_BINDING(0, 3)) uniform sampler2D u_msRt2;
-
-layout(std140, UBO_BINDING(0, 0)) uniform u0_
-{
-	vec4 u_projectionParams;
-	mat4 u_projectionMat;
-};
-
-// SSLR
-#if SSLR_ENABLED
-layout(TEX_BINDING(0, 4)) uniform sampler2D u_isRt;
-#include "shaders/Sslr.glsl"
-#endif
-
-// IR
-#if IR_ENABLED
-#define IMAGE_REFLECTIONS_SET 1
-#define IMAGE_REFLECTIONS_FIRST_SS_BINDING 0
-#define IMAGE_REFLECTIONS_TEX_BINDING 0
-#include "shaders/ImageReflections.glsl"
-#undef IMAGE_REFLECTIONS_SET
-#undef IMAGE_REFLECTIONS_FIRST_SS_BINDING
-#endif
-
-// In/out
-layout(location = 0) in vec2 in_texCoord;
-layout(location = 0) out vec3 out_indirectColor;
-layout(location = 1) out vec4 out_rt2;
-
-void main()
-{
-	//
-	// Decode the G-buffer
-	//
-	float depth = textureLod(u_depthRt, in_texCoord, 0.0).r;
-	vec3 posVSpace;
-	posVSpace.z = u_projectionParams.z / (u_projectionParams.w + depth);
-	posVSpace.xy =
-		(2.0 * in_texCoord - 1.0) * u_projectionParams.xy * posVSpace.z;
-
-	GbufferInfo gbuffer;
-	readGBuffer(u_msRt0, u_msRt1, u_msRt2, in_texCoord, 0.0, gbuffer);
-
-	// Compute relflection vector
-	vec3 eye = normalize(posVSpace);
-	vec3 r = reflect(eye, gbuffer.normal);
-
-	out_indirectColor = vec3(0.0);
-
-//
-// SSLR
-//
-#if SSLR_ENABLED
-	float sslrContribution;
-
-	// Don't bother for very rough surfaces
-	if(gbuffer.roughness > SSLR_START_ROUGHNESS)
-	{
-		sslrContribution = 1.0;
-		out_color = vec3(1.0, 0.0, 1.0);
-	}
-	else
-	{
-		sslrContribution = 0.0;
-	}
-#else
-	const float sslrContribution = 0.0;
-#endif
-
-//
-// IR
-//
-#if IR_ENABLED
-	float reflLod = float(IR_MIPMAP_COUNT) * gbuffer.roughness;
-
-	vec3 specIndirect, diffIndirect;
-	readIndirect(
-		posVSpace, r, gbuffer.normal, reflLod, specIndirect, diffIndirect);
-
-	diffIndirect *= gbuffer.diffuse;
-
-	// Finalize the indirect specular
-	float ndotv = dot(gbuffer.normal, -eye);
-	vec2 envBRDF = texture(u_integrationLut, vec2(gbuffer.roughness, ndotv)).xy;
-	specIndirect = specIndirect * (gbuffer.specular * envBRDF.x + envBRDF.y);
-
-	// Finalize
-	out_indirectColor = diffIndirect + specIndirect;
-#endif
-
-	out_rt2 = vec4(gbuffer.normal, 0.0);
-	gl_FragDepth = depth;
-}

+ 1 - 2
src/gr/vulkan/Common.cpp

@@ -5,7 +5,7 @@
 
 #include <anki/gr/vulkan/Common.h>
 
-namespace anki 
+namespace anki
 {
 
 //==============================================================================
@@ -46,4 +46,3 @@ VkCompareOp convertCompareOp(CompareOperation ak)
 }
 
 } // end namespace anki
-

+ 3 - 372
src/renderer/Ir.cpp

@@ -16,135 +16,9 @@
 namespace anki
 {
 
-//==============================================================================
-// Misc                                                                        =
-//==============================================================================
-
-struct IrShaderReflectionProbe
-{
-	Vec3 m_pos;
-	F32 m_radiusSq;
-	F32 m_cubemapIndex;
-	U32 _m_pading[3];
-};
-
-struct IrShaderCluster
-{
-	U32 m_indexOffset;
-	U32 m_probeCount;
-};
-
-static const U MAX_PROBES_PER_CLUSTER = 16;
-
-/// Store the probe radius for sorting the indices.
-class ClusterDataIndex
-{
-public:
-	U32 m_index = 0;
-	F32 m_probeRadius = 0.0;
-};
-
-class IrClusterData
-{
-public:
-	Atomic<U32> m_probeCount = {0};
-	Array<ClusterDataIndex, MAX_PROBES_PER_CLUSTER> m_probeIds;
-
-	Bool operator==(const IrClusterData& b) const
-	{
-		const U probeCount = m_probeCount.load() % MAX_PROBES_PER_CLUSTER;
-		const U bProbeCount = b.m_probeCount.load() % MAX_PROBES_PER_CLUSTER;
-
-		if(probeCount != bProbeCount)
-		{
-			return false;
-		}
-
-		if(probeCount > 0)
-		{
-			if(memcmp(&m_probeIds[0],
-				   &b.m_probeIds[0],
-				   sizeof(m_probeIds[0]) * probeCount)
-				!= 0)
-			{
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-	/// Sort the indices from the smallest probe to the biggest.
-	void sort()
-	{
-		const U probeCount = m_probeCount.load() % MAX_PROBES_PER_CLUSTER;
-		if(probeCount > 1)
-		{
-			std::sort(m_probeIds.getBegin(),
-				m_probeIds.getBegin() + probeCount,
-				[](const ClusterDataIndex& a, const ClusterDataIndex& b) {
-					ANKI_ASSERT(a.m_probeRadius > 0.0 && b.m_probeRadius > 0.0);
-					return a.m_probeRadius < b.m_probeRadius;
-				});
-		}
-	}
-};
-
-/// Context for the whole run.
-class IrRunContext
-{
-public:
-	Ir* m_ir ANKI_DBG_NULLIFY_PTR;
-
-	DArray<IrClusterData> m_clusterData;
-	SArray<IrShaderCluster> m_clusters;
-	SArray<U32> m_indices;
-	Atomic<U32> m_indexCount = {0};
-	VisibilityTestResults* m_visRez ANKI_DBG_NULLIFY_PTR;
-
-	/// An atomic that will help allocating the index buffer
-	Atomic<U32> m_probeIndicesAllocate = {0};
-	/// Same as m_probeIndicesAllocate
-	Atomic<U32> m_clustersAllocate = {0};
-
-	StackAllocator<U8> m_alloc;
-
-	~IrRunContext()
-	{
-		// Deallocate. Watch the order
-		m_clusterData.destroy(m_alloc);
-	}
-};
-
-/// Thread specific context.
-class IrTaskContext
-{
-public:
-	ClustererTestResult m_clustererTestResult;
-	SceneNode* m_node ANKI_DBG_NULLIFY_PTR;
-};
-
-/// Write the lights to the GPU buffers.
-class IrTask : public ThreadPool::Task
-{
-public:
-	IrRunContext* m_ctx ANKI_DBG_NULLIFY_PTR;
-
-	Error operator()(U32 threadId, PtrSize threadsCount) override
-	{
-		m_ctx->m_ir->binProbes(threadId, threadsCount, *m_ctx);
-		return ErrorCode::NONE;
-	}
-};
-
-//==============================================================================
-// Ir                                                                          =
-//==============================================================================
-
 //==============================================================================
 Ir::Ir(Renderer* r)
 	: RenderingPass(r)
-	, m_barrier(r->getThreadPool().getThreadsCount())
 {
 }
 
@@ -236,20 +110,6 @@ Error Ir::init(const ConfigSet& config)
 	sinit.m_repeat = false;
 	m_integrationLutSampler = getGrManager().newInstance<Sampler>(sinit);
 
-	// Init the resource group
-	ResourceGroupInitInfo rcInit;
-	rcInit.m_textures[0].m_texture = m_envCubemapArr;
-	rcInit.m_textures[1].m_texture = m_irradianceCubemapArr;
-
-	rcInit.m_textures[2].m_texture = m_integrationLut->getGrTexture();
-	rcInit.m_textures[2].m_sampler = m_integrationLutSampler;
-
-	rcInit.m_storageBuffers[0].m_dynamic = true;
-	rcInit.m_storageBuffers[1].m_dynamic = true;
-	rcInit.m_storageBuffers[2].m_dynamic = true;
-
-	m_rcGroup = getGrManager().newInstance<ResourceGroup>(rcInit);
-
 	return ErrorCode::NONE;
 }
 
@@ -298,46 +158,6 @@ Error Ir::run(RenderingContext& rctx)
 		ANKI_LOGW("Increase the ir.cubemapTextureArraySize");
 	}
 
-	//
-	// Perform some initialization
-	//
-	IrRunContext ctx;
-
-	ctx.m_visRez = &visRez;
-	ctx.m_ir = this;
-	ctx.m_alloc = getFrameAllocator();
-
-	// Allocate temp CPU mem
-	ctx.m_clusterData.create(
-		getFrameAllocator(), m_r->getClusterer().getClusterCount());
-
-	//
-	// Render and populate probes GPU mem
-	//
-
-	// Probes GPU mem
-	void* data = getGrManager().allocateFrameHostVisibleMemory(
-		sizeof(IrShaderReflectionProbe)
-				* visRez.getCount(VisibilityGroupType::REFLECTION_PROBES)
-			+ sizeof(Mat3x4)
-			+ sizeof(Vec4),
-		BufferUsage::STORAGE,
-		m_probesToken);
-
-	Mat3x4* invViewRotation = static_cast<Mat3x4*>(data);
-	*invViewRotation =
-		Mat3x4(frc.getViewMatrix().getInverse().getRotationPart());
-
-	Vec4* nearClusterDivisor = reinterpret_cast<Vec4*>(invViewRotation + 1);
-	nearClusterDivisor->x() = frc.getFrustum().getNear();
-	nearClusterDivisor->y() = m_r->getClusterer().getShaderMagicValue();
-	nearClusterDivisor->z() = 0.0;
-	nearClusterDivisor->w() = 0.0;
-
-	SArray<IrShaderReflectionProbe> probes(
-		reinterpret_cast<IrShaderReflectionProbe*>(nearClusterDivisor + 1),
-		visRez.getCount(VisibilityGroupType::REFLECTION_PROBES));
-
 	// Render some of the probes
 	const VisibleNode* it =
 		visRez.getBegin(VisibilityGroupType::REFLECTION_PROBES);
@@ -348,7 +168,7 @@ Error Ir::run(RenderingContext& rctx)
 	while(it != end)
 	{
 		// Write and render probe
-		ANKI_CHECK(writeProbeAndRender(rctx, *it->m_node, probes[probeIdx]));
+		ANKI_CHECK(tryRender(rctx, *it->m_node));
 
 		++it;
 		++probeIdx;
@@ -356,126 +176,14 @@ Error Ir::run(RenderingContext& rctx)
 	ANKI_ASSERT(
 		probeIdx == visRez.getCount(VisibilityGroupType::REFLECTION_PROBES));
 
-	//
-	// Start the jobs that can run in parallel
-	//
-	ThreadPool& threadPool = m_r->getThreadPool();
-	Array<IrTask, ThreadPool::MAX_THREADS> tasks;
-	for(U i = 0; i < threadPool.getThreadsCount(); i++)
-	{
-		tasks[i].m_ctx = &ctx;
-		threadPool.assignNewTask(i, &tasks[i]);
-	}
-
-	// Sync
-	ANKI_CHECK(threadPool.waitForAllThreadsToFinish());
-
 	// Bye
 	ANKI_TRACE_STOP_EVENT(RENDER_IR);
 	return ErrorCode::NONE;
 }
 
 //==============================================================================
-void Ir::binProbes(U32 threadId, PtrSize threadsCount, IrRunContext& ctx)
+Error Ir::tryRender(RenderingContext& ctx, SceneNode& node)
 {
-	ANKI_TRACE_START_EVENT(RENDER_IR);
-	IrTaskContext task;
-
-	//
-	// Bin the probes
-	//
-
-	PtrSize start, end;
-	ThreadPool::Task::choseStartEnd(threadId,
-		threadsCount,
-		ctx.m_visRez->getCount(VisibilityGroupType::REFLECTION_PROBES),
-		start,
-		end);
-
-	// Init clusterer test result for this thread
-	if(start < end)
-	{
-		m_r->getClusterer().initTestResults(
-			getFrameAllocator(), task.m_clustererTestResult);
-	}
-
-	for(auto i = start; i < end; i++)
-	{
-		VisibleNode* vnode =
-			ctx.m_visRez->getBegin(VisibilityGroupType::REFLECTION_PROBES) + i;
-		SceneNode& node = *vnode->m_node;
-
-		task.m_node = &node;
-
-		// Bin it to temp clusters
-		binProbe(i, ctx, task);
-	}
-
-	//
-	// Write the clusters
-	//
-
-	// Allocate the cluster buffer. First come first served
-	U who = ctx.m_clustersAllocate.fetchAdd(1);
-	if(who == 0)
-	{
-		void* mem = getGrManager().allocateFrameHostVisibleMemory(
-			m_r->getClusterer().getClusterCount() * sizeof(IrShaderCluster),
-			BufferUsage::STORAGE,
-			m_clustersToken);
-
-		ctx.m_clusters =
-			SArray<IrShaderCluster>(static_cast<IrShaderCluster*>(mem),
-				m_r->getClusterer().getClusterCount());
-	}
-
-	// Use the same trick to allocate the indices
-	ANKI_TRACE_STOP_EVENT(RENDER_IR);
-	m_barrier.wait();
-	ANKI_TRACE_START_EVENT(RENDER_IR);
-
-	who = ctx.m_probeIndicesAllocate.fetchAdd(1);
-	if(who == 0)
-	{
-		// Set it to zero in order to reuse it
-		U indexCount = ctx.m_indexCount.exchange(0);
-		if(indexCount > 0)
-		{
-			void* mem = getGrManager().allocateFrameHostVisibleMemory(
-				indexCount * sizeof(U32), BufferUsage::STORAGE, m_indicesToken);
-
-			ctx.m_indices = SArray<U32>(static_cast<U32*>(mem), indexCount);
-		}
-		else
-		{
-			m_indicesToken.markUnused();
-		}
-	}
-
-	// Sync
-	ANKI_TRACE_STOP_EVENT(RENDER_IR);
-	m_barrier.wait();
-	ANKI_TRACE_START_EVENT(RENDER_IR);
-
-	ThreadPool::Task::choseStartEnd(threadId,
-		threadsCount,
-		m_r->getClusterer().getClusterCount(),
-		start,
-		end);
-
-	for(auto i = start; i < end; i++)
-	{
-		Bool hasPrevCluster = (i != start);
-		writeIndicesAndCluster(i, hasPrevCluster, ctx);
-	}
-	ANKI_TRACE_STOP_EVENT(RENDER_IR);
-}
-
-//==============================================================================
-Error Ir::writeProbeAndRender(
-	RenderingContext& ctx, SceneNode& node, IrShaderReflectionProbe& probe)
-{
-	const FrustumComponent& frc = *ctx.m_frustumComponent;
 	ReflectionProbeComponent& reflc =
 		node.getComponent<ReflectionProbeComponent>();
 
@@ -484,9 +192,7 @@ Error Ir::writeProbeAndRender(
 	findCacheEntry(node, entry, render);
 
 	// Write shader var
-	probe.m_pos = (frc.getViewMatrix() * reflc.getPosition().xyz1()).xyz();
-	probe.m_radiusSq = reflc.getRadius() * reflc.getRadius();
-	probe.m_cubemapIndex = entry;
+	reflc.setTextureArrayIndex(entry);
 
 	if(reflc.getMarkedForRendering())
 	{
@@ -503,81 +209,6 @@ Error Ir::writeProbeAndRender(
 	return ErrorCode::NONE;
 }
 
-//==============================================================================
-void Ir::binProbe(U probeIdx, IrRunContext& ctx, IrTaskContext& task) const
-{
-	const SpatialComponent& sp = task.m_node->getComponent<SpatialComponent>();
-	const ReflectionProbeComponent& reflc =
-		task.m_node->getComponent<ReflectionProbeComponent>();
-
-	// Perform the expensive tests
-	m_r->getClusterer().bin(sp.getSpatialCollisionShape(),
-		sp.getAabb(),
-		task.m_clustererTestResult);
-
-	// Bin to the correct tiles
-	auto it = task.m_clustererTestResult.getClustersBegin();
-	auto end = task.m_clustererTestResult.getClustersEnd();
-	for(; it != end; ++it)
-	{
-		U x = (*it)[0];
-		U y = (*it)[1];
-		U z = (*it)[2];
-
-		U i = m_r->getClusterer().getClusterCountX()
-				* (z * m_r->getClusterer().getClusterCountY() + y)
-			+ x;
-
-		auto& cluster = ctx.m_clusterData[i];
-
-		i = cluster.m_probeCount.fetchAdd(1) % MAX_PROBES_PER_CLUSTER;
-		cluster.m_probeIds[i].m_index = probeIdx;
-		cluster.m_probeIds[i].m_probeRadius = reflc.getRadius();
-	}
-
-	ctx.m_indexCount.fetchAdd(task.m_clustererTestResult.getClusterCount());
-}
-
-//==============================================================================
-void Ir::writeIndicesAndCluster(
-	U clusterIdx, Bool hasPrevCluster, IrRunContext& ctx)
-{
-	IrClusterData& cdata = ctx.m_clusterData[clusterIdx];
-	IrShaderCluster& cluster = ctx.m_clusters[clusterIdx];
-
-	const U probeCount = cdata.m_probeCount.load() % MAX_PROBES_PER_CLUSTER;
-	if(probeCount > 0)
-	{
-		// Sort to satisfy the probe hierarchy
-		cdata.sort();
-
-		// Check if the cdata is the same for the previous
-		if(hasPrevCluster && cdata == ctx.m_clusterData[clusterIdx - 1])
-		{
-			// Same data
-			cluster = ctx.m_clusters[clusterIdx - 1];
-		}
-		else
-		{
-			// Have to store the indices
-			U idx = ctx.m_indexCount.fetchAdd(probeCount);
-
-			cluster.m_indexOffset = idx;
-			cluster.m_probeCount = probeCount;
-			for(U j = 0; j < probeCount; ++j)
-			{
-				ctx.m_indices[idx] = cdata.m_probeIds[j].m_index;
-				++idx;
-			}
-		}
-	}
-	else
-	{
-		cluster.m_indexOffset = 0;
-		cluster.m_probeCount = 0;
-	}
-}
-
 //==============================================================================
 Error Ir::renderReflection(RenderingContext& ctx,
 	SceneNode& node,

+ 167 - 63
src/renderer/Is.cpp

@@ -11,6 +11,7 @@
 #include <anki/renderer/Ir.h>
 #include <anki/scene/Camera.h>
 #include <anki/scene/Light.h>
+#include <anki/scene/ReflectionProbeComponent.h>
 #include <anki/scene/Visibility.h>
 #include <anki/core/Trace.h>
 #include <anki/util/Logger.h>
@@ -29,8 +30,9 @@ namespace anki
 
 struct ShaderCluster
 {
-	/// If m_combo = 0xFFFFAABB then FFFF is the light index offset, AA the
-	/// number of point lights and BB the number of spot lights
+	/// If m_combo = 0xFFFF?CAB then FFFF is the light index offset, A the
+	/// number of point lights and B the number of spot lights, C the number
+	/// of probes
 	U32 m_combo;
 };
 
@@ -52,6 +54,14 @@ struct ShaderSpotLight : ShaderLight
 	Mat4 m_texProjectionMat; ///< Texture projection matrix
 };
 
+struct ShaderProbe
+{
+	Vec3 m_pos;
+	F32 m_radiusSq;
+	F32 m_cubemapIndex;
+	U32 _m_pading[3];
+};
+
 struct ShaderCommonUniforms
 {
 	Vec4 m_projectionParams;
@@ -59,6 +69,7 @@ struct ShaderCommonUniforms
 	Vec4 m_rendererSizeTimePad1;
 	Vec4 m_nearFarClustererMagicPad1;
 	Mat4 m_viewMat;
+	Mat3x4 m_invViewRotation;
 	UVec4 m_tileCount;
 };
 
@@ -66,41 +77,66 @@ using Lid = U32; ///< Light ID
 static const U MAX_TYPED_LIGHTS_PER_CLUSTER = 16;
 static const F32 INVALID_TEXTURE_INDEX = 128.0;
 
+/// Store the probe radius for sorting the indices.
+class ClusterDataIndex
+{
+public:
+	U32 m_index = 0;
+	F32 m_probeRadius = 0.0;
+};
+static_assert(sizeof(ClusterDataIndex) == sizeof(U32) * 2, "Because we memcmp");
+
 class ClusterData
 {
 public:
-	Atomic<U32> m_pointCount;
-	Atomic<U32> m_spotCount;
+	Atomic<U32> m_pointCount = {0};
+	Atomic<U32> m_spotCount = {0};
+	Atomic<U32> m_probeCount = {0};
 
 	Array<Lid, MAX_TYPED_LIGHTS_PER_CLUSTER> m_pointIds;
 	Array<Lid, MAX_TYPED_LIGHTS_PER_CLUSTER> m_spotIds;
+	Array<ClusterDataIndex, MAX_TYPED_LIGHTS_PER_CLUSTER> m_probeIds;
 
 	void sortLightIds()
 	{
 		const U pointCount = m_pointCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
-		const U spotCount = m_spotCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
-
 		if(pointCount > 1)
 		{
 			std::sort(&m_pointIds[0], &m_pointIds[0] + pointCount);
 		}
 
+		const U spotCount = m_spotCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
 		if(spotCount > 1)
 		{
 			std::sort(&m_spotIds[0], &m_spotIds[0] + spotCount);
 		}
+
+		const U probeCount = m_probeCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
+		if(probeCount > 1)
+		{
+			std::sort(m_probeIds.getBegin(),
+				m_probeIds.getBegin() + probeCount,
+				[](const ClusterDataIndex& a, const ClusterDataIndex& b) {
+					ANKI_ASSERT(a.m_probeRadius > 0.0 && b.m_probeRadius > 0.0);
+					return a.m_probeRadius < b.m_probeRadius;
+				});
+		}
 	}
 
 	Bool operator==(const ClusterData& b) const
 	{
 		const U pointCount = m_pointCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
 		const U spotCount = m_spotCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
+		const U probeCount = m_probeCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
 		const U pointCount2 =
 			b.m_pointCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
 		const U spotCount2 =
 			b.m_spotCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
+		const U probeCount2 =
+			b.m_probeCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
 
-		if(pointCount != pointCount2 || spotCount != spotCount2)
+		if(pointCount != pointCount2 || spotCount != spotCount2
+			|| probeCount != probeCount2)
 		{
 			return false;
 		}
@@ -124,6 +160,17 @@ public:
 			}
 		}
 
+		if(probeCount > 0)
+		{
+			if(memcmp(&m_probeIds[0],
+				   &b.m_probeIds[0],
+				   sizeof(b.m_probeIds[0]) * probeCount)
+				!= 0)
+			{
+				return false;
+			}
+		}
+
 		return true;
 	}
 };
@@ -140,12 +187,14 @@ public:
 	// To fill the light buffers
 	SArray<ShaderPointLight> m_pointLights;
 	SArray<ShaderSpotLight> m_spotLights;
+	SArray<ShaderProbe> m_probes;
 
 	SArray<Lid> m_lightIds;
 	SArray<ShaderCluster> m_clusters;
 
 	Atomic<U32> m_pointLightsCount = {0};
 	Atomic<U32> m_spotLightsCount = {0};
+	Atomic<U32> m_probeCount = {0};
 
 	// To fill the tile buffers
 	DArrayAuto<ClusterData> m_tempClusters;
@@ -156,6 +205,7 @@ public:
 	// Misc
 	SArray<VisibleNode> m_vPointLights;
 	SArray<VisibleNode> m_vSpotLights;
+	SArray<VisibleNode> m_vProbes;
 
 	Is* m_is = nullptr;
 };
@@ -214,15 +264,6 @@ Error Is::init(const ConfigSet& config)
 Error Is::initInternal(const ConfigSet& config)
 {
 	m_groundLightEnabled = config.getNumber("is.groundLightEnabled");
-	m_maxPointLights = config.getNumber("is.maxPointLights");
-	m_maxSpotLights = config.getNumber("is.maxSpotLights");
-	m_maxSpotTexLights = config.getNumber("is.maxSpotTexLights");
-
-	if(m_maxPointLights < 1 || m_maxSpotLights < 1 || m_maxSpotTexLights < 1)
-	{
-		ANKI_LOGE("Incorrect number of max lights");
-		return ErrorCode::USER_DATA;
-	}
 
 	m_maxLightIds = config.getNumber("is.maxLightsPerCluster");
 
@@ -244,8 +285,6 @@ Error Is::initInternal(const ConfigSet& config)
 				"#define CLUSTER_COUNT %u\n"
 				"#define RENDERER_WIDTH %u\n"
 				"#define RENDERER_HEIGHT %u\n"
-				"#define MAX_POINT_LIGHTS %u\n"
-				"#define MAX_SPOT_LIGHTS %u\n"
 				"#define MAX_LIGHT_INDICES %u\n"
 				"#define GROUND_LIGHT %u\n"
 				"#define POISSON %u\n"
@@ -256,8 +295,6 @@ Error Is::initInternal(const ConfigSet& config)
 		m_r->getClusterCount(),
 		m_r->getWidth(),
 		m_r->getHeight(),
-		m_maxPointLights,
-		m_maxSpotLights,
 		m_maxLightIds,
 		m_groundLightEnabled,
 		m_r->getSmEnabled() ? m_r->getSm().getPoissonEnabled() : 0,
@@ -316,11 +353,22 @@ Error Is::initInternal(const ConfigSet& config)
 			init.m_textures[5].m_texture = m_r->getSm().getOmniTextureArray();
 		}
 
+		if(m_r->getIrEnabled())
+		{
+			init.m_textures[6].m_texture = m_r->getIr().getReflectionTexture();
+			init.m_textures[7].m_texture = m_r->getIr().getIrradianceTexture();
+
+			init.m_textures[8].m_texture = m_r->getIr().getIntegrationLut();
+			init.m_textures[8].m_sampler =
+				m_r->getIr().getIntegrationLutSampler();
+		}
+
 		init.m_storageBuffers[0].m_dynamic = true;
 		init.m_storageBuffers[1].m_dynamic = true;
 		init.m_storageBuffers[2].m_dynamic = true;
 		init.m_storageBuffers[3].m_dynamic = true;
 		init.m_storageBuffers[4].m_dynamic = true;
+		init.m_storageBuffers[5].m_dynamic = true;
 
 		m_rcGroup = getGrManager().newInstance<ResourceGroup>(init);
 	}
@@ -352,9 +400,7 @@ Error Is::lightPass(RenderingContext& ctx)
 	//
 	U visiblePointLightsCount = vi.getCount(VisibilityGroupType::LIGHTS_POINT);
 	U visibleSpotLightsCount = vi.getCount(VisibilityGroupType::LIGHTS_SPOT);
-
-	visiblePointLightsCount = min<U>(visiblePointLightsCount, m_maxPointLights);
-	visibleSpotLightsCount = min<U>(visibleSpotLightsCount, m_maxSpotLights);
+	U visibleProbeCount = vi.getCount(VisibilityGroupType::REFLECTION_PROBES);
 
 	ANKI_TRACE_INC_COUNTER(
 		RENDERER_LIGHTS, visiblePointLightsCount + visibleSpotLightsCount);
@@ -376,6 +422,10 @@ Error Is::lightPass(RenderingContext& ctx)
 
 		taskData.m_pointLights =
 			SArray<ShaderPointLight>(data, visiblePointLightsCount);
+
+		taskData.m_vPointLights =
+			SArray<VisibleNode>(vi.getBegin(VisibilityGroupType::LIGHTS_POINT),
+				visiblePointLightsCount);
 	}
 	else
 	{
@@ -392,24 +442,33 @@ Error Is::lightPass(RenderingContext& ctx)
 
 		taskData.m_spotLights =
 			SArray<ShaderSpotLight>(data, visibleSpotLightsCount);
+
+		taskData.m_vSpotLights =
+			SArray<VisibleNode>(vi.getBegin(VisibilityGroupType::LIGHTS_SPOT),
+				visibleSpotLightsCount);
 	}
 	else
 	{
 		m_sLightsToken.markUnused();
 	}
 
-	if(visiblePointLightsCount)
+	if(m_r->getIrEnabled() && visibleProbeCount)
 	{
-		taskData.m_vPointLights = std::move(
-			SArray<VisibleNode>(vi.getBegin(VisibilityGroupType::LIGHTS_POINT),
-				visiblePointLightsCount));
-	}
+		ShaderProbe* data = static_cast<ShaderProbe*>(
+			getGrManager().allocateFrameHostVisibleMemory(
+				sizeof(ShaderProbe) * visibleProbeCount,
+				BufferUsage::STORAGE,
+				m_probesToken));
 
-	if(visibleSpotLightsCount)
+		taskData.m_probes = SArray<ShaderProbe>(data, visibleProbeCount);
+
+		taskData.m_vProbes = SArray<VisibleNode>(
+			vi.getBegin(VisibilityGroupType::REFLECTION_PROBES),
+			visibleProbeCount);
+	}
+	else
 	{
-		taskData.m_vSpotLights = std::move(
-			SArray<VisibleNode>(vi.getBegin(VisibilityGroupType::LIGHTS_SPOT),
-				visibleSpotLightsCount));
+		m_probesToken.markUnused();
 	}
 
 	taskData.m_is = this;
@@ -480,10 +539,7 @@ void Is::binLights(U32 threadId, PtrSize threadsCount, TaskCommonData& task)
 		SpatialComponent& sp = snode.getComponent<SpatialComponent>();
 
 		I pos = writePointLight(light, move, camfrc, task);
-		if(pos != -1)
-		{
-			binLight(sp, pos, 0, task, testResult);
-		}
+		binLight(sp, pos, 0, task, testResult);
 	}
 
 	ThreadPool::Task::choseStartEnd(
@@ -497,10 +553,15 @@ void Is::binLights(U32 threadId, PtrSize threadsCount, TaskCommonData& task)
 		const FrustumComponent* frc = snode.tryGetComponent<FrustumComponent>();
 
 		I pos = writeSpotLight(light, move, frc, cammove, camfrc, task);
-		if(pos != -1)
-		{
-			binLight(sp, pos, 1, task, testResult);
-		}
+		binLight(sp, pos, 1, task, testResult);
+	}
+
+	ThreadPool::Task::choseStartEnd(
+		threadId, threadsCount, task.m_vProbes.getSize(), start, end);
+	for(auto i = start; i < end; i++)
+	{
+		SceneNode& snode = *task.m_vProbes[i].m_node;
+		writeAndBinProbe(camfrc, snode, task, testResult);
 	}
 
 	//
@@ -513,7 +574,7 @@ void Is::binLights(U32 threadId, PtrSize threadsCount, TaskCommonData& task)
 	ThreadPool::Task::choseStartEnd(
 		threadId, threadsCount, clusterCount, start, end);
 
-	// Run per tile
+	// Run per cluster
 	for(U i = start; i < end; ++i)
 	{
 		auto& cluster = task.m_tempClusters[i];
@@ -522,7 +583,9 @@ void Is::binLights(U32 threadId, PtrSize threadsCount, TaskCommonData& task)
 			cluster.m_pointCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
 		const U countS =
 			cluster.m_spotCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
-		const U count = countP + countS;
+		const U countProbe =
+			cluster.m_probeCount.load() % MAX_TYPED_LIGHTS_PER_CLUSTER;
+		const U count = countP + countS + countProbe;
 
 		auto& c = task.m_clusters[i];
 		c.m_combo = 0;
@@ -557,8 +620,8 @@ void Is::binLights(U32 threadId, PtrSize threadsCount, TaskCommonData& task)
 
 			if(countP > 0)
 			{
-				ANKI_ASSERT(countP <= 0xFF);
-				c.m_combo |= countP << 8;
+				ANKI_ASSERT(countP <= 0xF);
+				c.m_combo |= countP << 4;
 				memcpy(&task.m_lightIds[offset],
 					&cluster.m_pointIds[0],
 					countP * sizeof(Lid));
@@ -566,12 +629,24 @@ void Is::binLights(U32 threadId, PtrSize threadsCount, TaskCommonData& task)
 
 			if(countS > 0)
 			{
-				ANKI_ASSERT(countS <= 0xFF);
+				ANKI_ASSERT(countS <= 0xF);
 				c.m_combo |= countS;
 				memcpy(&task.m_lightIds[offset + countP],
 					&cluster.m_spotIds[0],
 					countS * sizeof(Lid));
 			}
+
+			if(countProbe > 0)
+			{
+				ANKI_ASSERT(countProbe <= 0xF);
+				c.m_combo |= countProbe << 8;
+
+				for(U i = 0; i < countProbe; ++i)
+				{
+					task.m_lightIds[offset + countP + countS + i] =
+						cluster.m_probeIds[i].m_index;
+				}
+			}
 		}
 		else
 		{
@@ -588,10 +663,6 @@ I Is::writePointLight(const LightComponent& lightc,
 {
 	// Get GPU light
 	I i = task.m_pointLightsCount.fetchAdd(1);
-	if(ANKI_UNLIKELY(i >= I(m_maxPointLights)))
-	{
-		return -1;
-	}
 
 	ShaderPointLight& slight = task.m_pointLights[i];
 
@@ -625,10 +696,6 @@ I Is::writeSpotLight(const LightComponent& lightc,
 	TaskCommonData& task)
 {
 	I i = task.m_spotLightsCount.fetchAdd(1);
-	if(ANKI_UNLIKELY(i >= I(m_maxSpotTexLights)))
-	{
-		return -1;
-	}
 
 	ShaderSpotLight& light = task.m_spotLights[i];
 	F32 shadowmapIndex = INVALID_TEXTURE_INDEX;
@@ -725,6 +792,49 @@ void Is::binLight(SpatialComponent& sp,
 	}
 }
 
+//==============================================================================
+void Is::writeAndBinProbe(const FrustumComponent& camFrc,
+	const SceneNode& node,
+	TaskCommonData& task,
+	ClustererTestResult& testResult)
+{
+	const ReflectionProbeComponent& reflc =
+		node.getComponent<ReflectionProbeComponent>();
+	const SpatialComponent& sp = node.getComponent<SpatialComponent>();
+
+	// Write it
+	ShaderProbe probe;
+	probe.m_pos = (camFrc.getViewMatrix() * reflc.getPosition().xyz1()).xyz();
+	probe.m_radiusSq = reflc.getRadius() * reflc.getRadius();
+	probe.m_cubemapIndex = reflc.getTextureArrayIndex();
+
+	U idx = task.m_probeCount.fetchAdd(1);
+	task.m_probes[idx] = probe;
+
+	// Bin it
+	m_r->getClusterer().bin(
+		sp.getSpatialCollisionShape(), sp.getAabb(), testResult);
+
+	auto it = testResult.getClustersBegin();
+	auto end = testResult.getClustersEnd();
+	for(; it != end; ++it)
+	{
+		U x = (*it)[0];
+		U y = (*it)[1];
+		U z = (*it)[2];
+
+		U i = m_r->getClusterer().getClusterCountX()
+				* (z * m_r->getClusterer().getClusterCountY() + y)
+			+ x;
+
+		auto& cluster = task.m_tempClusters[i];
+
+		i = cluster.m_probeCount.fetchAdd(1) % MAX_TYPED_LIGHTS_PER_CLUSTER;
+		cluster.m_probeIds[i].m_index = idx;
+		cluster.m_probeIds[i].m_probeRadius = reflc.getRadius();
+	}
+}
+
 //==============================================================================
 void Is::setState(CommandBufferPtr& cmdb)
 {
@@ -738,18 +848,9 @@ void Is::setState(CommandBufferPtr& cmdb)
 	dyn.m_storageBuffers[2] = m_sLightsToken;
 	dyn.m_storageBuffers[3] = m_clustersToken;
 	dyn.m_storageBuffers[4] = m_lightIdsToken;
+	dyn.m_storageBuffers[5] = m_probesToken;
 
 	cmdb->bindResourceGroup(m_rcGroup, 0, &dyn);
-
-	if(m_r->getIrEnabled())
-	{
-		DynamicBufferInfo dyn;
-		dyn.m_storageBuffers[0] = m_r->getIr().getProbesToken();
-		dyn.m_storageBuffers[1] = m_r->getIr().getProbeIndicesToken();
-		dyn.m_storageBuffers[2] = m_r->getIr().getClustersToken();
-
-		cmdb->bindResourceGroup(m_r->getIr().getResourceGroup(), 1, &dyn);
-	}
 }
 
 //==============================================================================
@@ -777,6 +878,9 @@ void Is::updateCommonBlock(const FrustumComponent& fr)
 		m_r->getClusterer().getShaderMagicValue(),
 		0.0);
 
+	blk->m_invViewRotation =
+		Mat3x4(fr.getViewMatrix().getInverse().getRotationPart());
+
 	blk->m_rendererSizeTimePad1 = Vec4(
 		m_r->getWidth(), m_r->getHeight(), HighRezTimer::getCurrentTime(), 0.0);
 

+ 1 - 1
src/renderer/Sm.cpp

@@ -300,7 +300,7 @@ Error Sm::doOmniLight(SceneNode& light,
 				cinf.m_secondLevel = true;
 				cinf.m_framebuffer = fbs[frCount];
 				cmdbs[frCount] =
-					getGrManager().newInstance<CommandBuffer>(cinf);
+					m_r->getGrManager().newInstance<CommandBuffer>(cinf);
 				cmdbs[frCount]->setViewport(0, 0, m_resolution, m_resolution);
 				cmdbs[frCount]->setPolygonOffset(1.0, 2.0);