Browse Source

Add the SVGF variance calculation

Panagiotis Christopoulos Charitos 4 years ago
parent
commit
43d35dd4fc

+ 25 - 16
AnKi/Renderer/RtShadows.cpp

@@ -212,27 +212,36 @@ void RtShadows::run(RenderPassWorkContext& rgraphCtx)
 
 	cmdb->bindShaderProgram(m_rtLibraryGrProg);
 
-	cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearRepeat);
+	bindUniforms(cmdb, 0, 0, ctx.m_lightShadingUniformsToken);
 
-	rgraphCtx.bindImage(0, 1, m_runCtx.m_renderRt, TextureSubresourceInfo());
+	bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
+	bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
+	rgraphCtx.bindColorTexture(0, 3, m_r->getShadowMapping().getShadowmapRt());
 
-	rgraphCtx.bindColorTexture(0, 2, m_runCtx.m_historyAndFinalRt);
-	cmdb->bindSampler(0, 3, m_r->getSamplers().m_trilinearClamp);
-	cmdb->bindSampler(0, 4, m_r->getSamplers().m_nearestNearestClamp);
-	rgraphCtx.bindTexture(0, 5, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
-	rgraphCtx.bindColorTexture(0, 6, m_r->getMotionVectors().getMotionVectorsRt());
-	rgraphCtx.bindColorTexture(0, 7, m_r->getMotionVectors().getRejectionFactorRt());
-	rgraphCtx.bindColorTexture(0, 8, m_r->getGBuffer().getColorRt(2));
-	rgraphCtx.bindAccelerationStructure(0, 9, m_r->getAccelerationStructureBuilder().getAccelerationStructureHandle());
+	bindStorage(cmdb, 0, 4, rsrc.m_clustersToken);
+	bindStorage(cmdb, 0, 5, rsrc.m_indicesToken);
 
-	bindUniforms(cmdb, 0, 10, ctx.m_lightShadingUniformsToken);
+	cmdb->bindSampler(0, 6, m_r->getSamplers().m_trilinearRepeat);
 
-	bindUniforms(cmdb, 0, 11, rsrc.m_pointLightsToken);
-	bindUniforms(cmdb, 0, 12, rsrc.m_spotLightsToken);
-	rgraphCtx.bindColorTexture(0, 13, m_r->getShadowMapping().getShadowmapRt());
+	rgraphCtx.bindImage(0, 7, m_runCtx.m_renderRt, TextureSubresourceInfo());
 
-	bindStorage(cmdb, 0, 14, rsrc.m_clustersToken);
-	bindStorage(cmdb, 0, 15, rsrc.m_indicesToken);
+	rgraphCtx.bindColorTexture(0, 8, m_runCtx.m_historyAndFinalRt);
+	cmdb->bindSampler(0, 9, m_r->getSamplers().m_trilinearClamp);
+	cmdb->bindSampler(0, 10, m_r->getSamplers().m_nearestNearestClamp);
+	rgraphCtx.bindTexture(0, 11, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
+	rgraphCtx.bindColorTexture(0, 12, m_r->getMotionVectors().getMotionVectorsRt());
+	rgraphCtx.bindColorTexture(0, 13, m_r->getMotionVectors().getRejectionFactorRt());
+	rgraphCtx.bindColorTexture(0, 14, m_r->getGBuffer().getColorRt(2));
+	rgraphCtx.bindAccelerationStructure(0, 15, m_r->getAccelerationStructureBuilder().getAccelerationStructureHandle());
+
+	if(!m_useSvgf)
+	{
+		// Bind something random
+		rgraphCtx.bindColorTexture(0, 16, m_r->getMotionVectors().getMotionVectorsRt());
+		rgraphCtx.bindImage(0, 17, m_runCtx.m_renderRt, TextureSubresourceInfo());
+		rgraphCtx.bindColorTexture(0, 18, m_r->getMotionVectors().getMotionVectorsRt());
+		rgraphCtx.bindImage(0, 19, m_runCtx.m_renderRt, TextureSubresourceInfo());
+	}
 
 	cmdb->bindAllBindless(1);
 

+ 8 - 0
AnKi/Shaders/BilateralFilter.glsl

@@ -23,6 +23,14 @@ struct BilateralConfig
 	F32 m_roughnessWeight;
 };
 
+void initConfig(out BilateralConfig cfg)
+{
+	cfg.m_depthWeight = 0.0;
+	cfg.m_normalWeight = 0.0;
+	cfg.m_planeWeight = 0.0;
+	cfg.m_roughnessWeight = 0.0;
+}
+
 // https://cs.dartmouth.edu/~wjarosz/publications/mara17towards.html
 F32 calculateBilateralWeight(BilateralSample center, BilateralSample tap, BilateralConfig config)
 {

+ 7 - 10
AnKi/Shaders/RtShadows.glsl

@@ -8,6 +8,8 @@
 #include <AnKi/Shaders/Include/RtShadows.h>
 #include <AnKi/Shaders/Pack.glsl>
 
+const F32 RT_SHADOWS_MAX_HISTORY_LENGTH = 31.0;
+
 UVec4 packRtShadows(F32 shadowFactors[MAX_RT_SHADOW_LAYERS])
 {
 	const U32 a = newPackUnorm4x8(Vec4(shadowFactors[0], shadowFactors[1], shadowFactors[2], shadowFactors[3]));
@@ -15,13 +17,6 @@ UVec4 packRtShadows(F32 shadowFactors[MAX_RT_SHADOW_LAYERS])
 	return UVec4(a, b, 0, 0);
 }
 
-UVec4 packRtShadows(F32 shadowFactors[MAX_RT_SHADOW_LAYERS], U32 temporalHistory)
-{
-	UVec4 packed = packRtShadows(shadowFactors);
-	packed.z = temporalHistory;
-	return packed;
-}
-
 void unpackRtShadows(UVec4 packed, out F32 shadowFactors[MAX_RT_SHADOW_LAYERS])
 {
 	const Vec4 a = newUnpackUnorm4x8(packed.x);
@@ -36,8 +31,10 @@ void unpackRtShadows(UVec4 packed, out F32 shadowFactors[MAX_RT_SHADOW_LAYERS])
 	shadowFactors[7] = b[3];
 }
 
-void unpackRtShadows(UVec4 packed, out F32 shadowFactors[MAX_RT_SHADOW_LAYERS], out U32 temporalHistory)
+void zeroRtShadowLayers(out F32 shadowFactors[MAX_RT_SHADOW_LAYERS])
 {
-	unpackRtShadows(packed, shadowFactors);
-	temporalHistory = packed.z;
+	ANKI_UNROLL for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
+	{
+		shadowFactors[i] = 0.0;
+	}
 }

+ 1 - 0
AnKi/Shaders/RtShadowsDenoise.ankiprog

@@ -71,6 +71,7 @@ void main()
 		crnt.m_normal = unpackNormalFromGBuffer(texelFetch(u_gbuffer2Tex, unormalizedUvs, 0));
 
 		BilateralConfig config;
+		initConfig(config);
 		const Vec3 weights = normalize(Vec3(0.0, 1.0, 1.0));
 		config.m_depthWeight = weights.x;
 		config.m_normalWeight = weights.y;

+ 1 - 1
AnKi/Shaders/RtShadowsHit.ankiprog

@@ -17,7 +17,7 @@ layout(shaderRecordEXT, scalar) buffer b_model
 	ModelGpuDescriptor u_modelDescriptor;
 };
 
-layout(set = 0, binding = 0) uniform sampler u_sampler;
+layout(set = 0, binding = 6) uniform sampler u_sampler;
 
 ANKI_BINDLESS_SET(1);
 #endif

+ 75 - 64
AnKi/Shaders/RtShadowsRayGen.ankiprog

@@ -14,23 +14,29 @@
 #include <AnKi/Shaders/Pack.glsl>
 #include <AnKi/Shaders/RtShadows.glsl>
 
-layout(set = 0, binding = 0) uniform sampler u_trilinearRepeatSampler; // Used by the hit shaders
-layout(set = 0, binding = 1) uniform uimage2D u_outImg;
-layout(set = 0, binding = 2) uniform utexture2D u_historyRt;
-layout(set = 0, binding = 3) uniform sampler u_linearAnyClampSampler;
-layout(set = 0, binding = 4) uniform sampler u_nearestAnyClampSampler;
-layout(set = 0, binding = 5) uniform texture2D u_depthRt;
-layout(set = 0, binding = 6) uniform texture2D u_motionVectorsRt;
-layout(set = 0, binding = 7) uniform texture2D u_motionVectorsRejectionRt;
-layout(set = 0, binding = 8) uniform texture2D u_normalRt;
-layout(set = 0, binding = 9) uniform accelerationStructureEXT u_tlas;
-
 #define LIGHT_SET 0
-#define LIGHT_COMMON_UNIS_BINDING 10
-#define LIGHT_LIGHTS_BINDING 11
-#define LIGHT_CLUSTERS_BINDING 14
+#define LIGHT_COMMON_UNIS_BINDING 0
+#define LIGHT_LIGHTS_BINDING 1
+#define LIGHT_CLUSTERS_BINDING 4
 #include <AnKi/Shaders/ClusteredShadingCommon.glsl>
 
+layout(set = 0, binding = 6) uniform sampler u_trilinearRepeatSampler; // Used by the hit shaders
+layout(set = 0, binding = 7) uniform uimage2D u_shadowsImage;
+layout(set = 0, binding = 8) uniform utexture2D u_historyShadowsTex;
+layout(set = 0, binding = 9) uniform sampler u_linearAnyClampSampler;
+layout(set = 0, binding = 10) uniform sampler u_nearestAnyClampSampler;
+layout(set = 0, binding = 11) uniform texture2D u_depthRt;
+layout(set = 0, binding = 12) uniform texture2D u_motionVectorsRt;
+layout(set = 0, binding = 13) uniform texture2D u_motionVectorsRejectionRt;
+layout(set = 0, binding = 14) uniform texture2D u_normalRt;
+layout(set = 0, binding = 15) uniform accelerationStructureEXT u_tlas;
+#if SVGF
+layout(set = 0, binding = 16) uniform texture2D u_prevMomentsTex;
+layout(set = 0, binding = 17) uniform image2D u_momentsImage;
+layout(set = 0, binding = 18) uniform texture2D u_prevHistoryLengthTex;
+layout(set = 0, binding = 19) uniform image2D u_historyLengthImage;
+#endif
+
 ANKI_BINDLESS_SET(1); // Used by the hit shaders
 
 layout(push_constant, std430) uniform b_pc
@@ -67,7 +73,14 @@ void main()
 
 	if(depth == 1.0)
 	{
-		imageStore(u_outImg, IVec2(gl_LaunchIDEXT.xy), UVec4(0));
+		imageStore(u_shadowsImage, IVec2(gl_LaunchIDEXT.xy), UVec4(0));
+#if SVGF
+		imageStore(u_momentsImage, IVec2(gl_LaunchIDEXT.xy), Vec4(0.0));
+
+		// Set to max history length because this pixel won't need any processing from further compute stages
+		imageStore(u_historyLengthImage, IVec2(gl_LaunchIDEXT.xy),
+				   Vec4(RT_SHADOWS_MAX_HISTORY_LENGTH / RT_SHADOWS_MAX_HISTORY_LENGTH, 0.0, 0.0, 0.0));
+#endif
 		return;
 	}
 
@@ -80,10 +93,7 @@ void main()
 	U32 idxOffset = u_clusters[clusterIdx];
 
 	F32 shadowFactors[MAX_RT_SHADOW_LAYERS];
-	ANKI_UNROLL for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
-	{
-		shadowFactors[i] = 0.0;
-	}
+	zeroRtShadowLayers(shadowFactors);
 
 	// Get a random factor
 	const UVec3 random = rand3DPCG16(UVec3(gl_LaunchIDEXT.xy, u_lightingUniforms.m_frameCount));
@@ -143,60 +153,61 @@ void main()
 		}
 	}
 
-	// Do temporal accumulation
-#if SVGF
-	// Gives the number of frames with temporal stability
-	U32 temporalHistory;
-#endif
+	// Get history info
+	const Vec2 historyUv = uv + textureLod(u_motionVectorsRt, u_linearAnyClampSampler, uv, 0.0).xy;
+	const F32 historyRejectionFactor = textureLod(u_motionVectorsRejectionRt, u_linearAnyClampSampler, uv, 0.0).x;
+	const F32 nominalBlendFactor = 0.1;
+	const F32 blendFactor = mix(nominalBlendFactor, 1.0, historyRejectionFactor);
+
+	// Read the history. Use nearest sampler because it's an integer texture
+	const UVec4 packedhistory = textureLod(u_historyShadowsTex, u_nearestAnyClampSampler, historyUv, 0.0);
+	F32 history[MAX_RT_SHADOW_LAYERS];
+	unpackRtShadows(packedhistory, history);
 
+	// Blend with history
+	for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
 	{
-		const Vec2 historyUv = uv + textureLod(u_motionVectorsRt, u_linearAnyClampSampler, uv, 0.0).rg;
-		const F32 rejectionFactor = textureLod(u_motionVectorsRejectionRt, u_linearAnyClampSampler, uv, 0.0).r;
+		const F32 lerp = min(1.0, u_unis.historyRejectFactor[i] + blendFactor);
+		shadowFactors[i] = mix(history[i], shadowFactors[i], lerp);
+	}
+
+	// Store the 1st image
+	const UVec4 packed = packRtShadows(shadowFactors);
+	imageStore(u_shadowsImage, IVec2(gl_LaunchIDEXT.xy), packed);
 
-		// Use nearest because it's an integer texture
-		const UVec4 history2 = textureLod(u_historyRt, u_nearestAnyClampSampler, historyUv, 0.0);
-		F32 history[MAX_RT_SHADOW_LAYERS];
 #if SVGF
-		unpackRtShadows(history2, history, temporalHistory);
-#else
-		unpackRtShadows(history2, history);
-#endif
+	// Compute the moments
+	Vec2 moments = Vec2(0.0);
+	ANKI_UNROLL for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
+	{
+		moments.x += shadowFactors[i];
+	}
+	moments.y = moments.x * moments.x;
 
-		// Compute blend factors
-		const F32 nominalBlendFactor = 0.1;
-		const F32 blendFactor = mix(nominalBlendFactor, 1.0, rejectionFactor);
+	// Blend the moments
+	const Vec2 prevMoments = textureLod(u_prevMomentsTex, u_nearestAnyClampSampler, historyUv, 0.0).xy;
+	moments = mix(prevMoments, moments, blendFactor);
 
-		// Blend with history
-#if SVGF
-		F32 maxLerp = 0.0;
-#endif
-		for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
-		{
-			const F32 lerp = min(1.0, u_unis.historyRejectFactor[i] + blendFactor);
-			shadowFactors[i] = mix(history[i], shadowFactors[i], lerp);
-#if SVGF
-			maxLerp = max(maxLerp, lerp);
-#endif
-		}
+	// Store the moments
+	imageStore(u_momentsImage, IVec2(gl_LaunchIDEXT.xy), Vec4(moments, 0.0, 0.0));
 
-#if SVGF
-		if(maxLerp == 1.0)
-		{
-			temporalHistory = 1; // Rejected the history of one layer, reset the temporal history
-		}
-		else
-		{
-			++temporalHistory; // Sample seems stable, inc it's temporal history
-		}
-#endif
+	// Compute the temporal history (Gives the number of frames with temporal stability)
+	F32 historyLength;
+	if(historyRejectionFactor >= 0.9)
+	{
+		// Rejection factor too high, reset the temporal history for all layers
+		historyLength = 1.0 / RT_SHADOWS_MAX_HISTORY_LENGTH;
 	}
+	else
+	{
+		// Sample seems stable, increment it's temporal history
 
-	// Store
-#if SVGF
-	const UVec4 packed = packRtShadows(shadowFactors, temporalHistory);
-#else
-	const UVec4 packed = packRtShadows(shadowFactors);
+		historyLength = textureLod(u_prevHistoryLengthTex, u_nearestAnyClampSampler, historyUv, 0.0).r;
+		historyLength += 1.0 / RT_SHADOWS_MAX_HISTORY_LENGTH;
+	}
+
+	// Store temporal history
+	imageStore(u_historyLengthImage, IVec2(gl_LaunchIDEXT.xy), Vec4(historyLength));
 #endif
-	imageStore(u_outImg, IVec2(gl_LaunchIDEXT.xy), packed);
 }
 #pragma anki end

+ 137 - 0
AnKi/Shaders/RtShadowsSvgfVariance.ankiprog

@@ -0,0 +1,137 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+ANKI_SPECIALIZATION_CONSTANT_UVEC2(FB_SIZE, 0, UVec2(1));
+
+#pragma anki start comp
+
+#include <AnKi/Shaders/RtShadows.glsl>
+#include <AnKi/Shaders/BilateralFilter.glsl>
+
+layout(set = 0, binding = 0) uniform sampler u_nearestAnyClampSampler;
+layout(set = 0, binding = 1) uniform sampler u_linearAnyClampSampler;
+layout(set = 0, binding = 2) uniform utexture2D u_shadowsTex;
+layout(set = 0, binding = 3) uniform texture2D u_momentsTex;
+layout(set = 0, binding = 4) uniform texture2D u_historyLengthTex;
+layout(set = 0, binding = 5) uniform texture2D u_depthTex;
+layout(set = 0, binding = 6) uniform texture2D u_gbuffer2Tex;
+
+layout(set = 0, binding = 7) uniform uimage2D u_shadowsImage;
+layout(set = 0, binding = 8) uniform image2D u_varianceImage;
+
+layout(std430, push_constant, row_major) uniform b_pc
+{
+	Mat4 u_invViewProjMat;
+};
+
+Vec3 unproject(Vec2 uv, F32 depth)
+{
+	const Vec4 worldPos4 = u_invViewProjMat * Vec4(UV_TO_NDC(uv), depth, 1.0);
+	const Vec3 worldPos = worldPos4.xyz / worldPos4.w;
+	return worldPos;
+}
+
+void main()
+{
+	ANKI_BRANCH if(gl_GlobalInvocationID.x >= FB_SIZE.x || gl_GlobalInvocationID.y >= FB_SIZE.y)
+	{
+		// Out of bounds
+		return;
+	}
+
+	const Vec2 uv = (Vec2(gl_GlobalInvocationID.xy) + 0.5) / Vec2(FB_SIZE);
+
+	const F32 historyLength = textureLod(u_historyLengthTex, u_nearestAnyClampSampler, uv, 0.0).r;
+
+	UVec4 outPackedShadowLayers;
+	F32 outVariance;
+
+	if(historyLength < 4.0 / RT_SHADOWS_MAX_HISTORY_LENGTH)
+	{
+		// It's been stable less than 4 frames, need to do some work
+
+		const I32 radius = 1;
+		const Vec2 texelSize = 1.0 / Vec2(FB_SIZE);
+
+		// Set the reference sample
+		BilateralSample ref;
+		ref.m_depth = textureLod(u_depthTex, u_linearAnyClampSampler, uv, 0.0).r;
+		ref.m_position = unproject(uv, ref.m_depth);
+		ref.m_normal = readNormalFromGBuffer(u_gbuffer2Tex, u_linearAnyClampSampler, uv);
+
+		// Init the sums
+		Vec2 sumMoments = Vec2(0.0);
+		F32 sumWeight = 0.0;
+		F32 sumShadowLayers[MAX_RT_SHADOW_LAYERS];
+		zeroRtShadowLayers(sumShadowLayers);
+
+		// Iterate
+		for(I32 offsetx = -radius; offsetx <= radius; offsetx++)
+		{
+			for(I32 offsety = -radius; offsety <= radius; offsety++)
+			{
+				const Vec2 sampleUv = uv + Vec2(offsetx, offsety) * texelSize;
+
+				// Set the current sample
+				BilateralSample crnt;
+				crnt.m_depth = textureLod(u_depthTex, u_linearAnyClampSampler, sampleUv, 0.0).r;
+				crnt.m_position = unproject(sampleUv, crnt.m_depth);
+				crnt.m_normal =
+					unpackNormalFromGBuffer(textureLod(u_gbuffer2Tex, u_linearAnyClampSampler, sampleUv, 0.0));
+
+				// Do bilateral
+				BilateralConfig config;
+				initConfig(config);
+				const Vec3 weights = normalize(Vec3(0.0, 0.0, 1.0));
+				config.m_depthWeight = weights.x;
+				config.m_normalWeight = weights.y;
+				config.m_planeWeight = weights.z;
+				const F32 w = calculateBilateralWeight(crnt, ref, config);
+
+				// Sum
+				const Vec2 moments = textureLod(u_momentsTex, u_nearestAnyClampSampler, sampleUv, 0.0).xy;
+				sumMoments += moments * w;
+
+				F32 shadowLayers[MAX_RT_SHADOW_LAYERS];
+				unpackRtShadows(textureLod(u_shadowsTex, u_nearestAnyClampSampler, sampleUv, 0.0), shadowLayers);
+				ANKI_UNROLL for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
+				{
+					sumShadowLayers[i] += shadowLayers[i] * w;
+				}
+
+				sumWeight += w;
+			}
+		}
+
+		sumWeight = max(EPSILON, sumWeight);
+
+		ANKI_UNROLL for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
+		{
+			sumShadowLayers[i] /= sumWeight;
+		}
+		sumMoments /= sumWeight;
+
+		outPackedShadowLayers = packRtShadows(sumShadowLayers);
+
+		outVariance = max(0.0, sumMoments.y - sumMoments.x * sumMoments.x);
+
+		// Give the variance a boost for the first frames
+		outVariance *= 4.0 / (historyLength * RT_SHADOWS_MAX_HISTORY_LENGTH);
+	}
+	else
+	{
+		// Stable for more that 4 frames, passthrough
+
+		outPackedShadowLayers = textureLod(u_shadowsTex, u_nearestAnyClampSampler, uv, 0.0);
+
+		const Vec2 moments = textureLod(u_momentsTex, u_nearestAnyClampSampler, uv, 0.0).xy;
+		outVariance = max(0.0, moments.y - moments.x * moments.x);
+	}
+
+	// Store
+	imageStore(u_shadowsImage, IVec2(gl_GlobalInvocationID), outPackedShadowLayers);
+	imageStore(u_varianceImage, IVec2(gl_GlobalInvocationID), Vec4(outVariance, 0.0, 0.0, 0.0));
+}
+#pragma anki end

+ 1 - 1
AnKi/Shaders/SsgiDenoise.ankiprog

@@ -59,11 +59,11 @@ void sampleTex(Vec2 colorUv, Vec2 fullUv, BilateralSample ref, inout Vec3 col, i
 	crnt.m_normal = readNormal(fullUv);
 
 	BilateralConfig config;
+	initConfig(config);
 	const Vec3 weights = normalize(Vec3(0.0, 1.0, 1.0));
 	config.m_depthWeight = weights.x;
 	config.m_normalWeight = weights.y;
 	config.m_planeWeight = weights.z;
-	config.m_roughnessWeight = 0.0;
 
 	const F32 w = calculateBilateralWeight(crnt, ref, config);
 	col += color * w;

+ 10 - 8
Docs/CodeStyle.md

@@ -62,11 +62,6 @@ All **template arguments** should start with `T`.
 
 	template<typename TSomething, typename TOther, U32 T_SOME_CONST>
 
-The **source files** are always PascalCase.
-
-	ThreadHive.h
-	MyShaderProgram.ankiprog
-
 All **function and method names** should form a sentence with at least one verb.
 
 	doSomething(...);
@@ -100,14 +95,20 @@ Whatever the PEP 8 guide proposes.
 Naming conventions for files and directories
 ============================================
 
-Filenames and directories should be PascalCase. The extensions of the files are lowercase.
+**Filenames and directories should be PascalCase**. The extensions of the files are lowercase.
+
+	Source/ThreadHive.h
+
+	Shaders/MyShaderProgram.ankiprog
+
+	Some/Path/Script.py
 
 C++ rules
 =========
 
 **Always use strongly typed enums**. If you need to relax the rules use the `ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS` macro.
 
-	enum class MyEnum : uint {...};
+	enum class MyEnum : U32 {...};
 	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(MyEnum);
 
 **Never use `typedef`** to define types. Use `using` instead.
@@ -123,7 +124,8 @@ C++ rules
 
 **Always use `constexpr`** when applicable. Never use defines for constants.
 
-	constexpr uint SOME_CONST = ...;
+	constexpr uint SOME_CONST = ...; // YES!!
+	#define SOME_CONST (...)         // NO
 
 **Never use `struct`** and always use `class` instead. Since it's difficult to come up with rules on when to use struct over class always use class and be done with it.