Browse Source

Re-design the shader programs and change the shader binary

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
a24df59c88
100 changed files with 1686 additions and 3642 deletions
  1. 1 0
      AnKi/Gr/Common.h
  2. 16 28
      AnKi/Importer/GltfImporterMaterial.cpp
  3. 6 1
      AnKi/Renderer/RtShadows.cpp
  4. 1 3
      AnKi/Renderer/VolumetricFog.cpp
  5. 0 1
      AnKi/Renderer/VolumetricFog.h
  6. 1 2
      AnKi/Renderer/VolumetricLightingAccumulation.cpp
  7. 0 1
      AnKi/Renderer/VolumetricLightingAccumulation.h
  8. 161 347
      AnKi/Resource/MaterialResource.cpp
  9. 19 57
      AnKi/Resource/MaterialResource.h
  10. 85 247
      AnKi/Resource/ShaderProgramResource.cpp
  11. 38 186
      AnKi/Resource/ShaderProgramResource.h
  12. 266 297
      AnKi/Resource/ShaderProgramResourceSystem.cpp
  13. 19 7
      AnKi/Resource/ShaderProgramResourceSystem.h
  14. 4 2
      AnKi/Scene/Components/ModelComponent.cpp
  15. 17 1
      AnKi/ShaderCompiler/Common.h
  16. 11 7
      AnKi/ShaderCompiler/Dxc.cpp
  17. 2 1
      AnKi/ShaderCompiler/Dxc.h
  18. 8 8
      AnKi/ShaderCompiler/RadeonGpuAnalyzer.cpp
  19. 65 355
      AnKi/ShaderCompiler/ShaderProgramBinary.h
  20. 26 105
      AnKi/ShaderCompiler/ShaderProgramBinary.xml
  21. 135 833
      AnKi/ShaderCompiler/ShaderProgramCompiler.cpp
  22. 17 55
      AnKi/ShaderCompiler/ShaderProgramCompiler.h
  23. 76 234
      AnKi/ShaderCompiler/ShaderProgramDump.cpp
  24. 1 1
      AnKi/ShaderCompiler/ShaderProgramDump.h
  25. 199 392
      AnKi/ShaderCompiler/ShaderProgramParser.cpp
  26. 64 90
      AnKi/ShaderCompiler/ShaderProgramParser.h
  27. 2 2
      AnKi/Shaders/ApplyIrradianceToReflection.ankiprog
  28. 2 2
      AnKi/Shaders/BlitCompute.ankiprog
  29. 4 4
      AnKi/Shaders/BlitRaster.ankiprog
  30. 2 2
      AnKi/Shaders/BloomCompute.ankiprog
  31. 4 4
      AnKi/Shaders/BloomRaster.ankiprog
  32. 2 2
      AnKi/Shaders/BloomUpscaleCompute.ankiprog
  33. 4 4
      AnKi/Shaders/BloomUpscaleRaster.ankiprog
  34. 2 2
      AnKi/Shaders/ClearTextureCompute.ankiprog
  35. 2 2
      AnKi/Shaders/ClusterBinning.ankiprog
  36. 2 2
      AnKi/Shaders/ClusterBinningPackVisibles.ankiprog
  37. 2 2
      AnKi/Shaders/ClusterBinningSetup.ankiprog
  38. 4 4
      AnKi/Shaders/DbgBillboard.ankiprog
  39. 4 4
      AnKi/Shaders/DbgRenderables.ankiprog
  40. 2 2
      AnKi/Shaders/DepthAwareBlurCompute.ankiprog
  41. 4 4
      AnKi/Shaders/DepthAwareBlurRaster.ankiprog
  42. 2 2
      AnKi/Shaders/DepthDownscaleCompute.ankiprog
  43. 4 4
      AnKi/Shaders/DepthDownscaleRaster.ankiprog
  44. 2 2
      AnKi/Shaders/DownscaleBlurCompute.ankiprog
  45. 4 4
      AnKi/Shaders/DownscaleBlurRaster.ankiprog
  46. 4 4
      AnKi/Shaders/DrawerStats.ankiprog
  47. 4 4
      AnKi/Shaders/FinalComposite.ankiprog
  48. 5 8
      AnKi/Shaders/ForwardShadingFog.ankiprog
  49. 6 8
      AnKi/Shaders/ForwardShadingGenericTransparent.ankiprog
  50. 6 8
      AnKi/Shaders/ForwardShadingParticles.ankiprog
  51. 2 2
      AnKi/Shaders/FsrCompute.ankiprog
  52. 4 4
      AnKi/Shaders/FsrRaster.ankiprog
  53. 227 143
      AnKi/Shaders/GBufferGeneric.ankiprog
  54. 5 8
      AnKi/Shaders/GBufferGpuParticles.ankiprog
  55. 4 4
      AnKi/Shaders/GBufferPost.ankiprog
  56. 2 2
      AnKi/Shaders/GpuParticlesSimulation.ankiprog
  57. 2 2
      AnKi/Shaders/GpuSceneMicroPatching.ankiprog
  58. 2 2
      AnKi/Shaders/GpuVisibility.ankiprog
  59. 4 4
      AnKi/Shaders/GpuVisibilityAccelerationStructures.ankiprog
  60. 2 2
      AnKi/Shaders/GpuVisibilityAccelerationStructuresZeroRemainingInstances.ankiprog
  61. 2 2
      AnKi/Shaders/GpuVisibilityNonRenderables.ankiprog
  62. 2 2
      AnKi/Shaders/HzbGenPyramid.ankiprog
  63. 2 2
      AnKi/Shaders/HzbMaxDepth.ankiprog
  64. 4 4
      AnKi/Shaders/HzbMaxDepthProject.ankiprog
  65. 0 5
      AnKi/Shaders/Include/MaterialTypes.h
  66. 2 2
      AnKi/Shaders/IndirectDiffuseCompute.ankiprog
  67. 2 2
      AnKi/Shaders/IndirectDiffuseDenoiseCompute.ankiprog
  68. 4 4
      AnKi/Shaders/IndirectDiffuseDenoiseRaster.ankiprog
  69. 4 4
      AnKi/Shaders/IndirectDiffuseRaster.ankiprog
  70. 2 2
      AnKi/Shaders/IndirectDiffuseVrsSriGeneration.ankiprog
  71. 2 2
      AnKi/Shaders/IndirectSpecularCompute.ankiprog
  72. 4 4
      AnKi/Shaders/IndirectSpecularRaster.ankiprog
  73. 6 0
      AnKi/Shaders/Intellisense.hlsl
  74. 2 2
      AnKi/Shaders/IrradianceDice.ankiprog
  75. 4 4
      AnKi/Shaders/LensFlareSprite.ankiprog
  76. 2 2
      AnKi/Shaders/LensFlareUpdateIndirectInfo.ankiprog
  77. 4 4
      AnKi/Shaders/LightShading.ankiprog
  78. 4 4
      AnKi/Shaders/LightShadingApplyFog.ankiprog
  79. 4 4
      AnKi/Shaders/LightShadingApplyIndirect.ankiprog
  80. 4 4
      AnKi/Shaders/LightShadingSkybox.ankiprog
  81. 2 2
      AnKi/Shaders/MotionVectorsCompute.ankiprog
  82. 4 4
      AnKi/Shaders/MotionVectorsRaster.ankiprog
  83. 2 2
      AnKi/Shaders/RtShadowsDenoise.ankiprog
  84. 6 10
      AnKi/Shaders/RtShadowsHit.ankiprog
  85. 3 5
      AnKi/Shaders/RtShadowsMiss.ankiprog
  86. 2 4
      AnKi/Shaders/RtShadowsRayGen.ankiprog
  87. 2 2
      AnKi/Shaders/RtShadowsSbtBuild.ankiprog
  88. 2 2
      AnKi/Shaders/RtShadowsSetupSbtBuild.ankiprog
  89. 2 2
      AnKi/Shaders/RtShadowsSvgfAtrous.ankiprog
  90. 2 2
      AnKi/Shaders/RtShadowsSvgfVariance.ankiprog
  91. 2 2
      AnKi/Shaders/RtShadowsUpscale.ankiprog
  92. 4 4
      AnKi/Shaders/ShadowMappingClearDepth.ankiprog
  93. 2 2
      AnKi/Shaders/ShadowMappingVetVisibility.ankiprog
  94. 2 2
      AnKi/Shaders/ShadowmapsResolveCompute.ankiprog
  95. 4 4
      AnKi/Shaders/ShadowmapsResolveRaster.ankiprog
  96. 2 2
      AnKi/Shaders/TemporalAACompute.ankiprog
  97. 4 4
      AnKi/Shaders/TemporalAARaster.ankiprog
  98. 2 2
      AnKi/Shaders/TonemapCompute.ankiprog
  99. 4 4
      AnKi/Shaders/TonemapRaster.ankiprog
  100. 2 2
      AnKi/Shaders/TonemappingAverageLuminance.ankiprog

+ 1 - 0
AnKi/Gr/Common.h

@@ -584,6 +584,7 @@ enum class ShaderTypeBit : U16
 	kAllLegacyGeometry = kVertex | kTessellationControl | kTessellationEvaluation | kGeometry,
 	kAllModernGeometry = kTask | kMesh,
 	kAllRayTracing = kRayGen | kAnyHit | kClosestHit | kMiss | kIntersection | kCallable,
+	kAllHit = kAnyHit | kClosestHit,
 	kAll = kAllGraphics | kCompute | kAllRayTracing,
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderTypeBit)

+ 16 - 28
AnKi/Importer/GltfImporterMaterial.cpp

@@ -14,21 +14,18 @@ namespace anki {
 
 inline constexpr const Char* kMaterialTemplate = R"(<!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<shaderPrograms>
-		<shaderProgram name="GBufferGeneric">
-			<mutation>
-				<mutator name="DIFFUSE_TEX" value="%diffTexMutator%"/>
-				<mutator name="SPECULAR_TEX" value="%specTexMutator%"/>
-				<mutator name="ROUGHNESS_TEX" value="%roughnessTexMutator%"/>
-				<mutator name="METAL_TEX" value="%metalTexMutator%"/>
-				<mutator name="NORMAL_TEX" value="%normalTexMutator%"/>
-				<mutator name="PARALLAX" value="%parallaxMutator%"/>
-				<mutator name="EMISSIVE_TEX" value="%emissiveTexMutator%"/>
-				<mutator name="ALPHA_TEST" value="%alphaTestMutator%"/>
-			</mutation>
-		</shaderProgram>
-		%rayTracing%
-	</shaderPrograms>
+	<shaderProgram name="GBufferGeneric">
+		<mutation>
+			<mutator name="DIFFUSE_TEX" value="%diffTexMutator%"/>
+			<mutator name="SPECULAR_TEX" value="%specTexMutator%"/>
+			<mutator name="ROUGHNESS_TEX" value="%roughnessTexMutator%"/>
+			<mutator name="METAL_TEX" value="%metalTexMutator%"/>
+			<mutator name="NORMAL_TEX" value="%normalTexMutator%"/>
+			<mutator name="PARALLAX" value="%parallaxMutator%"/>
+			<mutator name="EMISSIVE_TEX" value="%emissiveTexMutator%"/>
+			<mutator name="ALPHA_TEST" value="%alphaTestMutator%"/>
+		</mutation>
+	</shaderProgram>
 
 	<inputs>
 		%parallaxInput%
@@ -44,14 +41,6 @@ inline constexpr const Char* kMaterialTemplate = R"(<!-- This file is auto gener
 </material>
 )";
 
-inline constexpr const Char* kRtMaterialTemplate = R"(
-		<shaderProgram name="RtShadowsHit">
-			<mutation>
-				<mutator name="ALPHA_TEXTURE" value="%rtAlphaTestMutator%"/>
-			</mutation>
-		</shaderProgram>
-)";
-
 static ImporterString getTextureUri(const cgltf_texture_view& view)
 {
 	ANKI_ASSERT(view.texture);
@@ -161,6 +150,10 @@ Error GltfImporter::writeMaterial(const cgltf_material& mtl, Bool writeRayTracin
 
 Error GltfImporter::writeMaterialInternal(const cgltf_material& mtl, Bool writeRayTracing) const
 {
+	if(!writeRayTracing)
+	{
+		ANKI_IMPORTER_LOGW("Skipping ray tracing is ignored");
+	}
 	ImporterString fname;
 	fname.sprintf("%s%s", m_outDir.cstr(), computeMaterialResourceFilename(mtl).cstr());
 	ANKI_IMPORTER_LOGV("Importing material %s", fname.cstr());
@@ -179,11 +172,6 @@ Error GltfImporter::writeMaterialInternal(const cgltf_material& mtl, Bool writeR
 	xml += "\n";
 	xml += kMaterialTemplate;
 
-	if(writeRayTracing)
-	{
-		xml.replaceAll("%rayTracing%", kRtMaterialTemplate);
-	}
-
 	// Diffuse
 	Bool alphaTested = false;
 	if(mtl.pbr_metallic_roughness.base_color_texture.texture)

+ 6 - 1
AnKi/Renderer/RtShadows.cpp

@@ -58,6 +58,7 @@ Error RtShadows::initInternal()
 
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_rayGenProg);
 		variantInitInfo.addMutation("RAYS_PER_PIXEL", g_rtShadowsRaysPerPixelCVar.get());
+		variantInitInfo.requestTechnique("RtShadows");
 
 		const ShaderProgramResourceVariant* variant;
 		m_rayGenProg->getOrCreateVariant(variantInitInfo, variant);
@@ -68,8 +69,12 @@ Error RtShadows::initInternal()
 	// Miss prog
 	{
 		ANKI_CHECK(ResourceManager::getSingleton().loadResource("ShaderBinaries/RtShadowsMiss.ankiprogbin", m_missProg));
+
+		ShaderProgramResourceVariantInitInfo variantInitInfo(m_missProg);
+		variantInitInfo.requestTechnique("RtShadows");
+
 		const ShaderProgramResourceVariant* variant;
-		m_missProg->getOrCreateVariant(variant);
+		m_missProg->getOrCreateVariant(variantInitInfo, variant);
 		m_missShaderGroupIdx = variant->getShaderGroupHandleIndex();
 	}
 

+ 1 - 3
AnKi/Renderer/VolumetricFog.cpp

@@ -35,8 +35,6 @@ Error VolumetricFog::init()
 	const ShaderProgramResourceVariant* variant;
 	m_prog->getOrCreateVariant(variantInitInfo, variant);
 	m_grProg.reset(&variant->getProgram());
-	m_workgroupSize[0] = variant->getWorkgroupSizes()[0];
-	m_workgroupSize[1] = variant->getWorkgroupSizes()[1];
 
 	// RT descr
 	m_rtDescr = getRenderer().create2DRenderTargetDescription(m_volumeSize[0], m_volumeSize[1], Format::kR16G16B16A16_Sfloat, "Fog");
@@ -84,7 +82,7 @@ void VolumetricFog::populateRenderGraph(RenderingContext& ctx)
 
 		cmdb.setPushConstants(&regs, sizeof(regs));
 
-		dispatchPPCompute(cmdb, m_workgroupSize[0], m_workgroupSize[1], m_volumeSize[0], m_volumeSize[1]);
+		dispatchPPCompute(cmdb, 8, 8, m_volumeSize[0], m_volumeSize[1]);
 	});
 }
 

+ 0 - 1
AnKi/Renderer/VolumetricFog.h

@@ -45,7 +45,6 @@ private:
 
 	U32 m_finalZSplit = 0;
 
-	Array<U32, 2> m_workgroupSize = {};
 	Array<U32, 3> m_volumeSize;
 
 	class

+ 1 - 2
AnKi/Renderer/VolumetricLightingAccumulation.cpp

@@ -52,7 +52,6 @@ Error VolumetricLightingAccumulation::init()
 	const ShaderProgramResourceVariant* variant;
 	m_prog->getOrCreateVariant(variantInitInfo, variant);
 	m_grProg.reset(&variant->getProgram());
-	m_workgroupSize = variant->getWorkgroupSizes();
 
 	// Create RTs
 	TextureInitInfo texinit = getRenderer().create2DRenderTargetInitInfo(m_volumeSize[0], m_volumeSize[1], Format::kR16G16B16A16_Sfloat,
@@ -151,7 +150,7 @@ void VolumetricLightingAccumulation::populateRenderGraph(RenderingContext& ctx)
 		unis.m_maxZSplitsToProcessf = F32(m_finalZSplit + 1);
 		cmdb.setPushConstants(&unis, sizeof(unis));
 
-		dispatchPPCompute(cmdb, m_workgroupSize[0], m_workgroupSize[1], m_workgroupSize[2], m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]);
+		dispatchPPCompute(cmdb, 8, 8, 8, m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]);
 	});
 }
 

+ 0 - 1
AnKi/Renderer/VolumetricLightingAccumulation.h

@@ -46,7 +46,6 @@ private:
 
 	U32 m_finalZSplit = 0;
 
-	Array<U32, 3> m_workgroupSize = {};
 	Array<U32, 3> m_volumeSize;
 
 	class

+ 161 - 347
AnKi/Resource/MaterialResource.cpp

@@ -10,7 +10,7 @@
 
 namespace anki {
 
-inline constexpr Array<CString, U32(BuiltinMutatorId::kCount)> kBuiltinMutatorNames = {{"NONE", "ANKI_TECHNIQUE", "ANKI_BONES", "ANKI_VELOCITY"}};
+inline constexpr Array<CString, U32(BuiltinMutatorId::kCount)> kBuiltinMutatorNames = {{"NONE", "ANKI_BONES", "ANKI_VELOCITY"}};
 
 inline constexpr Array<CString, U(RenderingTechnique::kCount)> kTechniqueNames = {{"GBuffer", "Depth", "Forward", "RtShadow"}};
 
@@ -56,6 +56,19 @@ public:
 
 } // namespace
 
+static Bool mutatorValueExists(const ShaderProgramBinaryMutator& m, MutatorValue val)
+{
+	for(MutatorValue v : m.m_values)
+	{
+		if(v == val)
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
 MaterialVariable::MaterialVariable()
 {
 	m_Mat4 = Mat4::getZero();
@@ -65,59 +78,8 @@ MaterialVariable::~MaterialVariable()
 {
 }
 
-class MaterialResource::Program
-{
-public:
-	ShaderProgramResourcePtr m_prog;
-
-	mutable Array4d<MaterialVariant, U(RenderingTechnique::kCount), 2, 2, 2> m_variantMatrix;
-	mutable RWMutex m_variantMatrixMtx;
-
-	ResourceDynamicArray<PartialMutation> m_partialMutation; ///< Only with the non-builtins.
-
-	U32 m_presentBuildinMutators = 0;
-	U32 m_localUniformsStructIdx = 0; ///< Struct index in the program binary.
-
-	U8 m_lodCount = 1;
-
-	Program() = default;
-
-	Program(const Program&) = delete; // Non-copyable
-
-	Program(Program&& b)
-	{
-		*this = std::move(b);
-	}
-
-	Program& operator=(const Program& b) = delete; // Non-copyable
-
-	Program& operator=(Program&& b)
-	{
-		m_prog = std::move(b.m_prog);
-		for(RenderingTechnique t : EnumIterable<RenderingTechnique>())
-		{
-			for(U32 skin = 0; skin < 2; ++skin)
-			{
-				for(U32 vel = 0; vel < 2; ++vel)
-				{
-					for(U32 mesh = 0; mesh < 2; ++mesh)
-					{
-						m_variantMatrix[t][skin][vel][mesh] = std::move(b.m_variantMatrix[t][skin][vel][mesh]);
-					}
-				}
-			}
-		}
-		m_partialMutation = std::move(b.m_partialMutation);
-		m_presentBuildinMutators = b.m_presentBuildinMutators;
-		m_localUniformsStructIdx = b.m_localUniformsStructIdx;
-		m_lodCount = b.m_lodCount;
-		return *this;
-	}
-};
-
 MaterialResource::MaterialResource()
 {
-	memset(m_techniqueToProgram.getBegin(), 0xFF, m_techniqueToProgram.getSizeInBytes());
 }
 
 MaterialResource::~MaterialResource()
@@ -149,15 +111,9 @@ Error MaterialResource::load(const ResourceFilename& filename, Bool async)
 	ANKI_CHECK(doc.getChildElement("material", rootEl));
 
 	// <shaderPrograms>
-	XmlElement shaderProgramsEl;
-	ANKI_CHECK(rootEl.getChildElement("shaderPrograms", shaderProgramsEl));
 	XmlElement shaderProgramEl;
-	ANKI_CHECK(shaderProgramsEl.getChildElement("shaderProgram", shaderProgramEl));
-	do
-	{
-		ANKI_CHECK(parseShaderProgram(shaderProgramEl, async));
-		ANKI_CHECK(shaderProgramEl.getNextSiblingElement("shaderProgram", shaderProgramEl));
-	} while(shaderProgramEl);
+	ANKI_CHECK(rootEl.getChildElement("shaderProgram", shaderProgramEl));
+	ANKI_CHECK(parseShaderProgram(shaderProgramEl, async));
 
 	ANKI_ASSERT(!!m_techniquesMask);
 
@@ -177,8 +133,19 @@ Error MaterialResource::load(const ResourceFilename& filename, Bool async)
 
 	if(varsSet.getSetBitCount() != m_vars.getSize())
 	{
-		ANKI_RESOURCE_LOGE("Forgot to set a default value in %u input variables", U32(m_vars.getSize() - varsSet.getSetBitCount()));
-		return Error::kUserData;
+		ANKI_RESOURCE_LOGV("Material doesn't contain default value for %u input variables", U32(m_vars.getSize() - varsSet.getSetBitCount()));
+
+		// Remove unreferenced variables
+		ResourceDynamicArray<MaterialVariable> newVars;
+		for(U32 i = 0; i < m_vars.getSize(); ++i)
+		{
+			if(varsSet.get(i))
+			{
+				newVars.emplaceBack(std::move(m_vars[i]));
+			}
+		}
+
+		m_vars = std::move(newVars);
 	}
 
 	prefillLocalUniforms();
@@ -192,235 +159,58 @@ Error MaterialResource::parseShaderProgram(XmlElement shaderProgramEl, Bool asyn
 	CString shaderName;
 	ANKI_CHECK(shaderProgramEl.getAttributeText("name", shaderName));
 
-	if(!GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled && shaderName.find("Rt") == 0)
-	{
-		// Skip RT programs when RT is disabled
-		return Error::kNone;
-	}
-
 	ResourceString fname;
 	fname.sprintf("ShaderBinaries/%s.ankiprogbin", shaderName.cstr());
 
-	Program& prog = *m_programs.emplaceBack();
-	ANKI_CHECK(ResourceManager::getSingleton().loadResource(fname, prog.m_prog, async));
-
-	// <mutation>
-	XmlElement mutatorsEl;
-	ANKI_CHECK(shaderProgramEl.getChildElementOptional("mutation", mutatorsEl));
-	if(mutatorsEl)
-	{
-		ANKI_CHECK(parseMutators(mutatorsEl, prog));
-	}
-
-	// And find the builtin mutators
-	ANKI_CHECK(findBuiltinMutators(prog));
-
-	// Create the vars
-	ANKI_CHECK(createVars(prog));
+	ANKI_CHECK(ResourceManager::getSingleton().loadResource(fname, m_prog, async));
 
-	return Error::kNone;
-}
-
-Error MaterialResource::createVars(Program& prog)
-{
-	const ShaderProgramBinary& binary = prog.m_prog->getBinary();
-
-	// Find struct
-	const ShaderProgramBinaryStruct* localUniformsStruct = nullptr;
-	for(const ShaderProgramBinaryStruct& strct : binary.m_structs)
+	// Find present techniques
+	for(const ShaderProgramBinaryTechnique& t : m_prog->getBinary().m_techniques)
 	{
-		if(CString(strct.m_name.getBegin()) == "AnKiLocalConstants")
+		if(t.m_name.getBegin() == CString("GBuffer"))
 		{
-			localUniformsStruct = &strct;
-			break;
+			m_techniquesMask |= RenderingTechniqueBit::kGBuffer;
 		}
-
-		++prog.m_localUniformsStructIdx;
-	}
-
-	if(localUniformsStruct == nullptr)
-	{
-		prog.m_localUniformsStructIdx = kMaxU32;
-	}
-
-	// Iterate all members of the local uniforms struct to add its members
-	U32 offsetof = 0;
-	for(U32 i = 0; localUniformsStruct && i < localUniformsStruct->m_members.getSize(); ++i)
-	{
-		const ShaderProgramBinaryStructMember& member = localUniformsStruct->m_members[i];
-		const CString memberName = member.m_name.getBegin();
-
-		// Check if it needs to be added
-		Bool addIt = false;
-		if(member.m_dependentMutator == kMaxU32)
+		else if(t.m_name.getBegin() == CString("Shadows"))
 		{
-			addIt = true;
+			m_techniquesMask |= RenderingTechniqueBit::kDepth;
 		}
-		else
+		else if(t.m_name.getBegin() == CString("RtShadows"))
 		{
-			Bool found = false;
-			for(const PartialMutation& m : prog.m_partialMutation)
-			{
-				if(m.m_mutator->m_name == binary.m_mutators[member.m_dependentMutator].m_name.getBegin())
-				{
-					if(m.m_value == member.m_dependentMutatorValue)
-					{
-						addIt = true;
-					}
-					found = true;
-					break;
-				}
-			}
-
-			if(!found)
+			if(GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled)
 			{
-				ANKI_RESOURCE_LOGE("Incorrect combination of member variable %s and dependent mutator %s", memberName.cstr(),
-								   binary.m_mutators[member.m_dependentMutator].m_name.getBegin());
-				return Error::kUserData;
+				m_techniquesMask |= RenderingTechniqueBit::kRtShadow;
 			}
 		}
-
-		if(addIt)
+		else if(t.m_name.getBegin() == CString("Forward"))
 		{
-			MaterialVariable* var = tryFindVariable(memberName);
-			if(var)
-			{
-				if(var->m_dataType != member.m_type || var->m_offsetInLocalUniforms != offsetof)
-				{
-					ANKI_RESOURCE_LOGE("Member variable doesn't match between techniques: %s", memberName.cstr());
-					return Error::kUserData;
-				}
-			}
-			else
-			{
-				// Check that there are no other vars that overlap with the current var. This could happen if
-				// different programs have different signature for AnKiLocalConstants
-				for(const MaterialVariable& otherVar : m_vars)
-				{
-					if(!otherVar.isUniform())
-					{
-						continue;
-					}
-
-					const U32 aVarOffset = otherVar.m_offsetInLocalUniforms;
-					const U32 aVarEnd = aVarOffset + getShaderVariableDataTypeInfo(otherVar.m_dataType).m_size;
-					const U32 bVarOffset = offsetof;
-					const U32 bVarEnd = bVarOffset + getShaderVariableDataTypeInfo(member.m_type).m_size;
-
-					if((aVarOffset <= bVarOffset && aVarEnd > bVarOffset) || (bVarOffset <= aVarOffset && bVarEnd > aVarOffset))
-					{
-						ANKI_RESOURCE_LOGE("Member %s in AnKiLocalConstants overlaps with %s. Check your shaders", memberName.cstr(),
-										   otherVar.m_name.cstr());
-						return Error::kUserData;
-					}
-				}
-
-				// All good, add it
-				var = m_vars.emplaceBack();
-				var->m_name = memberName;
-				var->m_offsetInLocalUniforms = offsetof;
-				var->m_dataType = member.m_type;
-
-				offsetof += getShaderVariableDataTypeInfo(member.m_type).m_size;
-			}
+			m_techniquesMask |= RenderingTechniqueBit::kForward;
+		}
+		else
+		{
+			ANKI_RESOURCE_LOGE("Found unneeded technique in the shader: %s", t.m_name.getBegin());
+			return Error::kUserData;
 		}
 	}
 
-	m_localUniformsSize = max(offsetof, m_localUniformsSize);
-
-	// Iterate all variants of builtin mutators to gather the opaques
-	ShaderProgramResourceVariantInitInfo initInfo(prog.m_prog);
-
-	for(const PartialMutation& m : prog.m_partialMutation)
-	{
-		initInfo.addMutation(m.m_mutator->m_name, m.m_value);
-	}
-
-	Array<const ShaderProgramResourceMutator*, U(BuiltinMutatorId::kCount)> mutatorPtrs = {};
-	for(BuiltinMutatorId id : EnumIterable<BuiltinMutatorId>())
+	// <mutation>
+	XmlElement mutatorsEl;
+	ANKI_CHECK(shaderProgramEl.getChildElementOptional("mutation", mutatorsEl));
+	if(mutatorsEl)
 	{
-		mutatorPtrs[id] = prog.m_prog->tryFindMutator(kBuiltinMutatorNames[id]);
+		ANKI_CHECK(parseMutators(mutatorsEl));
 	}
 
-#define ANKI_LOOP(builtIn) \
-	for(U32 i = 0; i < ((mutatorPtrs[BuiltinMutatorId::builtIn]) ? mutatorPtrs[BuiltinMutatorId::builtIn]->m_values.getSize() : 1); ++i) \
-	{ \
-		if(mutatorPtrs[BuiltinMutatorId::builtIn]) \
-		{ \
-			initInfo.addMutation(mutatorPtrs[BuiltinMutatorId::builtIn]->m_name, mutatorPtrs[BuiltinMutatorId::builtIn]->m_values[i]); \
-		}
-
-#define ANKI_LOOP_END() }
-
-	ANKI_LOOP(kTechnique)
-	ANKI_LOOP(kBones)
-	ANKI_LOOP(kVelocity)
-	{
-		const ShaderProgramResourceVariant* variant;
-		prog.m_prog->getOrCreateVariant(initInfo, variant);
-		if(!variant)
-		{
-			// Skipped variant
-			continue;
-		}
-
-		// Add opaque vars
-		for(const ShaderProgramBinaryOpaqueInstance& instance : variant->getBinaryVariant().m_opaques)
-		{
-			const ShaderProgramBinaryOpaque& opaque = binary.m_opaques[instance.m_index];
-			if(opaque.m_type == ShaderVariableDataType::kSampler)
-			{
-				continue;
-			}
-
-			const CString opaqueName = opaque.m_name.getBegin();
-			MaterialVariable* var = tryFindVariable(opaqueName);
-
-			if(var)
-			{
-				if(var->m_dataType != opaque.m_type || var->m_opaqueBinding != opaque.m_binding)
-				{
-					ANKI_RESOURCE_LOGE("Opaque variable doesn't match between techniques: %s", opaqueName.cstr());
-					return Error::kUserData;
-				}
-			}
-			else
-			{
-				// Check that there are no other opaque with the same binding
-				for(const MaterialVariable& otherVar : m_vars)
-				{
-					if(!otherVar.isBoundableTexture())
-					{
-						continue;
-					}
-
-					if(otherVar.m_opaqueBinding == opaque.m_binding)
-					{
-						ANKI_RESOURCE_LOGE("Opaque variable %s has the same binding as %s. Check your shaders", otherVar.m_name.cstr(),
-										   opaqueName.cstr());
-						return Error::kUserData;
-					}
-				}
-
-				// All good, add it
-				var = m_vars.emplaceBack();
-				var->m_name = opaqueName;
-				var->m_opaqueBinding = opaque.m_binding;
-				var->m_dataType = opaque.m_type;
-			}
-		}
-	}
-	ANKI_LOOP_END()
-	ANKI_LOOP_END()
-	ANKI_LOOP_END()
+	// And find the builtin mutators
+	ANKI_CHECK(findBuiltinMutators());
 
-#undef ANKI_LOOP
-#undef ANKI_LOOP_END
+	// Create the vars
+	ANKI_CHECK(createVars());
 
 	return Error::kNone;
 }
 
-Error MaterialResource::parseMutators(XmlElement mutatorsEl, Program& prog)
+Error MaterialResource::parseMutators(XmlElement mutatorsEl)
 {
 	XmlElement mutatorEl;
 	ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
@@ -429,12 +219,12 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl, Program& prog)
 	ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
 	++mutatorCount;
 	ANKI_ASSERT(mutatorCount > 0);
-	prog.m_partialMutation.resize(mutatorCount);
+	m_partialMutation.resize(mutatorCount);
 	mutatorCount = 0;
 
 	do
 	{
-		PartialMutation& pmutation = prog.m_partialMutation[mutatorCount];
+		PartialMutation& pmutation = m_partialMutation[mutatorCount];
 
 		// name
 		CString mutatorName;
@@ -469,7 +259,7 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl, Program& prog)
 		ANKI_CHECK(mutatorEl.getAttributeNumber("value", pmutation.m_value));
 
 		// Find mutator
-		pmutation.m_mutator = prog.m_prog->tryFindMutator(mutatorName);
+		pmutation.m_mutator = m_prog->tryFindMutator(mutatorName);
 
 		if(!pmutation.m_mutator)
 		{
@@ -477,7 +267,7 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl, Program& prog)
 			return Error::kUserData;
 		}
 
-		if(!pmutation.m_mutator->valueExists(pmutation.m_value))
+		if(!mutatorValueExists(*pmutation.m_mutator, pmutation.m_value))
 		{
 			ANKI_RESOURCE_LOGE("Value %d is not part of the mutator %s", pmutation.m_value, mutatorName.cstr());
 			return Error::kUserData;
@@ -488,55 +278,18 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl, Program& prog)
 		ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
 	} while(mutatorEl);
 
-	ANKI_ASSERT(mutatorCount == prog.m_partialMutation.getSize());
+	ANKI_ASSERT(mutatorCount == m_partialMutation.getSize());
 
 	return Error::kNone;
 }
 
-Error MaterialResource::findBuiltinMutators(Program& prog)
+Error MaterialResource::findBuiltinMutators()
 {
 	U builtinMutatorCount = 0;
 
-	// ANKI_TECHNIQUE
-	CString techniqueMutatorName = kBuiltinMutatorNames[BuiltinMutatorId::kTechnique];
-	const ShaderProgramResourceMutator* techniqueMutator = prog.m_prog->tryFindMutator(techniqueMutatorName);
-	if(techniqueMutator)
-	{
-		for(U32 i = 0; i < techniqueMutator->m_values.getSize(); ++i)
-		{
-			const MutatorValue mvalue = techniqueMutator->m_values[i];
-			if(mvalue >= MutatorValue(RenderingTechnique::kCount) || mvalue < MutatorValue(RenderingTechnique::kFirst))
-			{
-				ANKI_RESOURCE_LOGE("Mutator %s has a wrong value %d", techniqueMutatorName.cstr(), mvalue);
-				return Error::kUserData;
-			}
-
-			const RenderingTechnique techniqueId = RenderingTechnique(mvalue);
-			const PtrSize progIdx = &prog - m_programs.getBegin();
-			ANKI_ASSERT(progIdx < m_programs.getSize());
-			m_techniqueToProgram[techniqueId] = U8(progIdx);
-
-			const RenderingTechniqueBit mask = RenderingTechniqueBit(1 << techniqueId);
-			if(!!(m_techniquesMask & mask))
-			{
-				ANKI_RESOURCE_LOGE("The %s technique appeared more than once", kTechniqueNames[mvalue].cstr());
-				return Error::kUserData;
-			}
-			m_techniquesMask |= mask;
-		}
-
-		++builtinMutatorCount;
-		prog.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::kTechnique);
-	}
-	else
-	{
-		ANKI_RESOURCE_LOGE("Mutator %s should be present in every shader program referenced by a material", techniqueMutatorName.cstr());
-		return Error::kUserData;
-	}
-
 	// ANKI_BONES
 	CString bonesMutatorName = kBuiltinMutatorNames[BuiltinMutatorId::kBones];
-	const ShaderProgramResourceMutator* bonesMutator = prog.m_prog->tryFindMutator(bonesMutatorName);
+	const ShaderProgramBinaryMutator* bonesMutator = m_prog->tryFindMutator(bonesMutatorName);
 	if(bonesMutator)
 	{
 		if(bonesMutator->m_values.getSize() != 2)
@@ -557,12 +310,12 @@ Error MaterialResource::findBuiltinMutators(Program& prog)
 		++builtinMutatorCount;
 
 		m_supportsSkinning = true;
-		prog.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::kBones);
+		m_presentBuildinMutatorMask |= U32(1 << BuiltinMutatorId::kBones);
 	}
 
 	// VELOCITY
 	CString velocityMutatorName = kBuiltinMutatorNames[BuiltinMutatorId::kVelocity];
-	const ShaderProgramResourceMutator* velocityMutator = prog.m_prog->tryFindMutator(velocityMutatorName);
+	const ShaderProgramBinaryMutator* velocityMutator = m_prog->tryFindMutator(velocityMutatorName);
 	if(velocityMutator)
 	{
 		if(velocityMutator->m_values.getSize() != 2)
@@ -581,10 +334,10 @@ Error MaterialResource::findBuiltinMutators(Program& prog)
 		}
 
 		++builtinMutatorCount;
-		prog.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::kVelocity);
+		m_presentBuildinMutatorMask |= U32(1 << BuiltinMutatorId::kVelocity);
 	}
 
-	if(prog.m_partialMutation.getSize() + builtinMutatorCount != prog.m_prog->getMutators().getSize())
+	if(m_partialMutation.getSize() + builtinMutatorCount != m_prog->getBinary().m_mutators.getSize())
 	{
 		ANKI_RESOURCE_LOGE("Some mutatators are unacounted for");
 		return Error::kUserData;
@@ -593,6 +346,39 @@ Error MaterialResource::findBuiltinMutators(Program& prog)
 	return Error::kNone;
 }
 
+Error MaterialResource::createVars()
+{
+	const ShaderProgramBinary& binary = m_prog->getBinary();
+
+	// Find struct
+	const ShaderProgramBinaryStruct* localUniformsStruct = nullptr;
+	for(const ShaderProgramBinaryStruct& strct : binary.m_structs)
+	{
+		if(CString(strct.m_name.getBegin()) == "AnKiLocalConstants")
+		{
+			localUniformsStruct = &strct;
+			break;
+		}
+	}
+
+	// Create vars
+	for(U32 i = 0; localUniformsStruct && i < localUniformsStruct->m_members.getSize(); ++i)
+	{
+		const ShaderProgramBinaryStructMember& member = localUniformsStruct->m_members[i];
+		const CString memberName = member.m_name.getBegin();
+
+		MaterialVariable& var = *m_vars.emplaceBack();
+		zeroMemory(var);
+		var.m_name = memberName;
+		var.m_dataType = member.m_type;
+		var.m_offsetInLocalUniforms = member.m_offset;
+	}
+
+	m_localUniformsSize = (localUniformsStruct) ? localUniformsStruct->m_size : 0;
+
+	return Error::kNone;
+}
+
 Error MaterialResource::parseInput(XmlElement inputEl, Bool async, BitSet<128>& varsSet)
 {
 	// Get var name
@@ -617,15 +403,7 @@ Error MaterialResource::parseInput(XmlElement inputEl, Bool async, BitSet<128>&
 	varsSet.set(idx);
 
 	// Set the value
-	if(foundVar->isBoundableTexture())
-	{
-		CString texfname;
-		ANKI_CHECK(inputEl.getAttributeText("value", texfname));
-		ANKI_CHECK(ResourceManager::getSingleton().loadResource(texfname, foundVar->m_image, async));
-
-		m_textures.emplaceBack(&foundVar->m_image->getTexture());
-	}
-	else if(foundVar->m_dataType == ShaderVariableDataType::kU32)
+	if(foundVar->m_dataType == ShaderVariableDataType::kU32)
 	{
 		// U32 is a bit special. It might be a number or a bindless texture
 
@@ -686,11 +464,6 @@ void MaterialResource::prefillLocalUniforms()
 
 	for(const MaterialVariable& var : m_vars)
 	{
-		if(!var.isUniform())
-		{
-			continue;
-		}
-
 		switch(var.m_dataType)
 		{
 #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \
@@ -710,11 +483,9 @@ void MaterialResource::prefillLocalUniforms()
 const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey& key_) const
 {
 	RenderingKey key = key_;
-	ANKI_ASSERT(m_techniqueToProgram[key.getRenderingTechnique()] != kMaxU8);
-	const Program& prog = m_programs[m_techniqueToProgram[key.getRenderingTechnique()]];
 
 	// Sanitize the key
-	if(!(prog.m_presentBuildinMutators & U32(BuiltinMutatorId::kVelocity)) && key.getVelocity())
+	if(!(m_presentBuildinMutatorMask & U32(BuiltinMutatorId::kVelocity)) && key.getVelocity())
 	{
 		// Particles set their own velocity
 		key.setVelocity(false);
@@ -731,23 +502,26 @@ const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey&
 		key.setMeshShaders(false);
 	}
 
-	ANKI_ASSERT(!key.getSkinned() || !!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::kBones)));
-	ANKI_ASSERT(!key.getVelocity() || !!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::kVelocity)));
+	ANKI_ASSERT(!key.getSkinned() || !!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kBones)));
+	ANKI_ASSERT(!key.getVelocity() || !!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kVelocity)));
 
-	MaterialVariant& variant = prog.m_variantMatrix[key.getRenderingTechnique()][key.getSkinned()][key.getVelocity()][key.getMeshShaders()];
+	MaterialVariant& variant = m_variantMatrix[key.getRenderingTechnique()][key.getSkinned()][key.getVelocity()][key.getMeshShaders()];
 
 	// Check if it's initialized
 	{
-		RLockGuard<RWMutex> lock(prog.m_variantMatrixMtx);
+		RLockGuard<RWMutex> lock(m_variantMatrixMtx);
 		if(variant.m_prog.isCreated()) [[likely]]
 		{
-			ANKI_ASSERT(key.getMeshShaders() == !!(variant.m_prog->getShaderTypes() & ShaderTypeBit::kAllModernGeometry));
+			if(!(RenderingTechniqueBit(1 << key.getRenderingTechnique()) & RenderingTechniqueBit::kAllRt))
+			{
+				ANKI_ASSERT(key.getMeshShaders() == !!(variant.m_prog->getShaderTypes() & ShaderTypeBit::kAllModernGeometry));
+			}
 			return variant;
 		}
 	}
 
 	// Not initialized, init it
-	WLockGuard<RWMutex> lock(prog.m_variantMatrixMtx);
+	WLockGuard<RWMutex> lock(m_variantMatrixMtx);
 
 	// Check again
 	if(variant.m_prog.isCreated())
@@ -755,29 +529,66 @@ const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey&
 		return variant;
 	}
 
-	ShaderProgramResourceVariantInitInfo initInfo(prog.m_prog);
+	ShaderProgramResourceVariantInitInfo initInfo(m_prog);
 
-	for(const PartialMutation& m : prog.m_partialMutation)
+	for(const PartialMutation& m : m_partialMutation)
 	{
-		initInfo.addMutation(m.m_mutator->m_name, m.m_value);
+		initInfo.addMutation(m.m_mutator->m_name.getBegin(), m.m_value);
 	}
 
-	initInfo.addMutation(kBuiltinMutatorNames[BuiltinMutatorId::kTechnique], MutatorValue(key.getRenderingTechnique()));
-
-	if(!!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::kBones)))
+	if(!!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kBones)))
 	{
 		initInfo.addMutation(kBuiltinMutatorNames[BuiltinMutatorId::kBones], MutatorValue(key.getSkinned()));
 	}
 
-	if(!!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::kVelocity)))
+	if(!!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kVelocity)))
 	{
 		initInfo.addMutation(kBuiltinMutatorNames[BuiltinMutatorId::kVelocity], MutatorValue(key.getVelocity()));
 	}
 
-	initInfo.requestMeshShaders(key.getMeshShaders());
+	switch(key.getRenderingTechnique())
+	{
+	case RenderingTechnique::kGBuffer:
+		if(key.getMeshShaders())
+		{
+			initInfo.requestShaderTypes(ShaderTypeBit::kAllModernGeometry | ShaderTypeBit::kFragment);
+		}
+		else
+		{
+			initInfo.requestShaderTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kFragment);
+		}
+		initInfo.requestTechnique("GBuffer");
+		break;
+	case RenderingTechnique::kDepth:
+		if(key.getMeshShaders())
+		{
+			initInfo.requestShaderTypes(ShaderTypeBit::kAllModernGeometry | ShaderTypeBit::kFragment);
+		}
+		else
+		{
+			initInfo.requestShaderTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kFragment);
+		}
+		initInfo.requestTechnique("Shadows");
+		break;
+	case RenderingTechnique::kForward:
+		if(key.getMeshShaders())
+		{
+			initInfo.requestShaderTypes(ShaderTypeBit::kAllModernGeometry | ShaderTypeBit::kFragment);
+		}
+		else
+		{
+			initInfo.requestShaderTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kFragment);
+		}
+		initInfo.requestTechnique("Forward");
+		break;
+	case RenderingTechnique::kRtShadow:
+		initInfo.requestShaderTypes(ShaderTypeBit::kAllHit);
+		initInfo.requestTechnique("RtShadows");
+		break;
+	}
 
 	const ShaderProgramResourceVariant* progVariant = nullptr;
-	prog.m_prog->getOrCreateVariant(initInfo, progVariant);
+	m_prog->getOrCreateVariant(initInfo, progVariant);
 
 	if(!progVariant)
 	{
@@ -785,12 +596,15 @@ const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey&
 	}
 
 	variant.m_prog.reset(&progVariant->getProgram());
-	ANKI_ASSERT(key.getMeshShaders() == !!(variant.m_prog->getShaderTypes() & ShaderTypeBit::kAllModernGeometry));
 
 	if(!!(RenderingTechniqueBit(1 << key.getRenderingTechnique()) & RenderingTechniqueBit::kAllRt))
 	{
 		variant.m_rtShaderGroupHandleIndex = progVariant->getShaderGroupHandleIndex();
 	}
+	else
+	{
+		ANKI_ASSERT(key.getMeshShaders() == !!(variant.m_prog->getShaderTypes() & ShaderTypeBit::kAllModernGeometry));
+	}
 
 	return variant;
 }

+ 19 - 57
AnKi/Resource/MaterialResource.h

@@ -25,7 +25,6 @@ class XmlElement;
 enum class BuiltinMutatorId : U8
 {
 	kNone = 0,
-	kTechnique,
 	kBones,
 	kVelocity,
 
@@ -58,7 +57,6 @@ public:
 	{
 		m_name = std::move(b.m_name);
 		m_offsetInLocalUniforms = b.m_offsetInLocalUniforms;
-		m_opaqueBinding = b.m_opaqueBinding;
 		m_dataType = b.m_dataType;
 		m_Mat4 = b.m_Mat4;
 		m_image = std::move(b.m_image);
@@ -73,34 +71,12 @@ public:
 	template<typename T>
 	const T& getValue() const;
 
-	Bool isBoundableTexture() const
-	{
-		return m_dataType >= ShaderVariableDataType::kTextureFirst && m_dataType <= ShaderVariableDataType::kTextureLast;
-	}
-
-	Bool isBindlessTexture() const
-	{
-		return m_dataType == ShaderVariableDataType::kU32 && m_image.get();
-	}
-
-	Bool isUniform() const
-	{
-		return !isBoundableTexture();
-	}
-
 	ShaderVariableDataType getDataType() const
 	{
 		ANKI_ASSERT(m_dataType != ShaderVariableDataType::kNone);
 		return m_dataType;
 	}
 
-	/// Get the binding of a texture or a sampler type of material variable.
-	U32 getTextureBinding() const
-	{
-		ANKI_ASSERT(m_opaqueBinding != kMaxU32 && isBoundableTexture());
-		return m_opaqueBinding;
-	}
-
 	U32 getOffsetInLocalUniforms() const
 	{
 		ANKI_ASSERT(m_offsetInLocalUniforms != kMaxU32);
@@ -110,7 +86,6 @@ public:
 protected:
 	ResourceString m_name;
 	U32 m_offsetInLocalUniforms = kMaxU32;
-	U32 m_opaqueBinding = kMaxU32; ///< Binding for textures and samplers.
 	ShaderVariableDataType m_dataType = ShaderVariableDataType::kNone;
 
 	/// Values
@@ -193,25 +168,17 @@ private:
 /// Material XML file format:
 /// @code
 ///	<material [shadows="0|1"]>
-/// 	<shaderPrograms>
-///			<shaderProgram name="name of the shader">
-///				[<mutation>
-///					<mutator name="str" value="value"/>
-///				</mutation>]
-///			</shaderProgram>
-///
-///			[<shaderProgram ...>
-///				...
-///			</shaderProgram>]
-/// 	</shaderPrograms>
+/// 	<shaderProgram name="name of the shader" />
+///			[<mutation>
+///				<mutator name="str" value="value"/>
+///			</mutation>]
+///		</shaderProgram>
 ///
 ///		[<inputs>
-///			<input name="name in AnKiMaterialUniforms struct or opaque type" value="value(s)"/> (1)
+///			<input name="name in AnKiMaterialConstants struct" value="value(s)"/>
 ///		</inputs>]
 ///	</material>
 /// @endcode
-///
-/// (1): Only for non-builtins.
 class MaterialResource : public ResourceObject
 {
 public:
@@ -243,12 +210,6 @@ public:
 		return m_techniquesMask;
 	}
 
-	/// Get all GPU resources of this material. Will be used for GPU refcounting.
-	ConstWeakArray<TexturePtr> getAllTextures() const
-	{
-		return m_textures;
-	}
-
 	/// @note It's thread-safe.
 	const MaterialVariant& getOrCreateVariant(const RenderingKey& key) const;
 
@@ -262,31 +223,32 @@ private:
 	class PartialMutation
 	{
 	public:
-		const ShaderProgramResourceMutator* m_mutator;
+		const ShaderProgramBinaryMutator* m_mutator;
 		MutatorValue m_value;
 	};
 
-	class Program;
+	ShaderProgramResourcePtr m_prog;
 
-	ResourceDynamicArray<Program> m_programs;
+	mutable Array4d<MaterialVariant, U(RenderingTechnique::kCount), 2, 2, 2> m_variantMatrix; ///< [technique][skinned][vel][meshShader]
+	mutable RWMutex m_variantMatrixMtx;
 
-	Array<U8, U(RenderingTechnique::kCount)> m_techniqueToProgram;
-	RenderingTechniqueBit m_techniquesMask = RenderingTechniqueBit::kNone;
+	ResourceDynamicArray<PartialMutation> m_partialMutation; ///< Only with the non-builtins.
 
 	ResourceDynamicArray<MaterialVariable> m_vars;
 
-	Bool m_supportsSkinning = false;
-
-	ResourceDynamicArray<TexturePtr> m_textures;
-
 	void* m_prefilledLocalUniforms = nullptr;
 	U32 m_localUniformsSize = 0;
 
-	Error parseMutators(XmlElement mutatorsEl, Program& prog);
+	U32 m_presentBuildinMutatorMask = 0;
+
+	Bool m_supportsSkinning = false;
+	RenderingTechniqueBit m_techniquesMask = RenderingTechniqueBit::kNone;
+
+	Error parseMutators(XmlElement mutatorsEl);
 	Error parseShaderProgram(XmlElement techniqueEl, Bool async);
 	Error parseInput(XmlElement inputEl, Bool async, BitSet<128>& varsSet);
-	Error findBuiltinMutators(Program& prog);
-	Error createVars(Program& prog);
+	Error findBuiltinMutators();
+	Error createVars();
 	void prefillLocalUniforms();
 
 	const MaterialVariable* tryFindVariableInternal(CString name) const;

+ 85 - 247
AnKi/Resource/ShaderProgramResource.cpp

@@ -23,7 +23,6 @@ ShaderProgramResourceVariant::~ShaderProgramResourceVariant()
 }
 
 ShaderProgramResource::ShaderProgramResource()
-	: m_binary(&ResourceMemoryPool::getSingleton())
 {
 }
 
@@ -34,6 +33,8 @@ ShaderProgramResource::~ShaderProgramResource()
 		ShaderProgramResourceVariant* variant = &(*it);
 		deleteInstance(ResourceMemoryPool::getSingleton(), variant);
 	}
+
+	ResourceMemoryPool::getSingleton().free(m_binary);
 }
 
 Error ShaderProgramResource::load(const ResourceFilename& filename, [[maybe_unused]] Bool async)
@@ -41,182 +42,77 @@ Error ShaderProgramResource::load(const ResourceFilename& filename, [[maybe_unus
 	// Load the binary
 	ResourceFilePtr file;
 	ANKI_CHECK(openFile(filename, file));
-	ANKI_CHECK(m_binary.deserializeFromAnyFile(*file));
-	const ShaderProgramBinary& binary = m_binary.getBinary();
+	ANKI_CHECK(deserializeShaderProgramBinaryFromAnyFile(*file, m_binary, ResourceMemoryPool::getSingleton()));
 
-	// Create the mutators
-	if(binary.m_mutators.getSize() > 0)
-	{
-		m_mutators.resize(binary.m_mutators.getSize());
+	return Error::kNone;
+}
 
-		for(U32 i = 0; i < binary.m_mutators.getSize(); ++i)
-		{
-			m_mutators[i].m_name = binary.m_mutators[i].m_name.getBegin();
-			ANKI_ASSERT(m_mutators[i].m_name.getLength() > 0);
-			m_mutators[i].m_values = binary.m_mutators[i].m_values;
-		}
-	}
+void ShaderProgramResource::getOrCreateVariant(const ShaderProgramResourceVariantInitInfo& info_, const ShaderProgramResourceVariant*& variant) const
+{
+	ShaderProgramResourceVariantInitInfo info = info_;
+
+	// Sanity checks
+	ANKI_ASSERT(info.m_setMutators.getSetBitCount() == m_binary->m_mutators.getSize());
 
-	// Create the constants
-	for(const ShaderProgramBinaryConstant& c : binary.m_constants)
+	// Find the technique
+	const CString techniqueName = info.m_techniqueName.getBegin();
+	U32 techniqueIdx = kMaxU32;
+	for(U32 i = 0; i < m_binary->m_techniques.getSize(); ++i)
 	{
-		U32 componentIdx;
-		U32 componentCount;
-		CString name;
-		ANKI_CHECK(parseConst(c.m_name.getBegin(), componentIdx, componentCount, name));
-
-		// Do the mapping
-		ConstMapping mapping;
-		mapping.m_component = componentIdx;
-		if(componentIdx > 0)
+		if(m_binary->m_techniques[i].m_name.getBegin() == techniqueName)
 		{
-			const ShaderProgramResourceConstant* other = tryFindConstant(name);
-			ANKI_ASSERT(other);
-			mapping.m_constsIdx = U32(other - m_consts.getBegin());
-		}
-		else
-		{
-			mapping.m_constsIdx = m_consts.getSize();
+			techniqueIdx = i;
+			break;
 		}
+	}
+	ANKI_ASSERT(techniqueIdx != kMaxU32 && "Technique not found");
+	const ShaderProgramBinaryTechnique& technique = m_binary->m_techniques[techniqueIdx];
 
-		m_constBinaryMapping.emplaceBack(mapping);
+	// Find the shader stages
+	if(info.m_shaderTypes == ShaderTypeBit::kNone)
+	{
+		// Try to guess the shader types
 
-		// Skip if const is there
-		if(componentIdx > 0)
+		if(technique.m_shaderTypes == ShaderTypeBit::kCompute)
 		{
-			continue;
+			// Only compute
 		}
-
-		// Create new one
-		ShaderProgramResourceConstant& in = *m_consts.emplaceBack();
-		in.m_name = name;
-		in.m_index = m_consts.getSize() - 1;
-
-		if(componentCount == 1)
+		else if((technique.m_shaderTypes & ~(ShaderTypeBit::kAllLegacyGeometry | ShaderTypeBit::kFragment)) == ShaderTypeBit::kNone)
 		{
-			in.m_dataType = c.m_type;
+			// Only legacy geometry
 		}
-		else if(componentCount == 2)
+		else if((technique.m_shaderTypes & ~(ShaderTypeBit::kAllModernGeometry | ShaderTypeBit::kFragment)) == ShaderTypeBit::kNone)
 		{
-			if(c.m_type == ShaderVariableDataType::kU32)
-			{
-				in.m_dataType = ShaderVariableDataType::kUVec2;
-			}
-			else if(c.m_type == ShaderVariableDataType::kI32)
-			{
-				in.m_dataType = ShaderVariableDataType::kIVec2;
-			}
-			else
-			{
-				ANKI_ASSERT(c.m_type == ShaderVariableDataType::kF32);
-				in.m_dataType = ShaderVariableDataType::kVec2;
-			}
+			// Only modern geometry
 		}
-		else if(componentCount == 3)
+		else if((technique.m_shaderTypes & ~ShaderTypeBit::kAllHit) == ShaderTypeBit::kNone)
 		{
-			if(c.m_type == ShaderVariableDataType::kU32)
-			{
-				in.m_dataType = ShaderVariableDataType::kUVec3;
-			}
-			else if(c.m_type == ShaderVariableDataType::kI32)
-			{
-				in.m_dataType = ShaderVariableDataType::kIVec3;
-			}
-			else
-			{
-				ANKI_ASSERT(c.m_type == ShaderVariableDataType::kF32);
-				in.m_dataType = ShaderVariableDataType::kVec3;
-			}
+			// Only hit
 		}
-		else if(componentCount == 4)
+		else if(technique.m_shaderTypes == ShaderTypeBit::kMiss)
 		{
-			if(c.m_type == ShaderVariableDataType::kU32)
-			{
-				in.m_dataType = ShaderVariableDataType::kUVec4;
-			}
-			else if(c.m_type == ShaderVariableDataType::kI32)
-			{
-				in.m_dataType = ShaderVariableDataType::kIVec4;
-			}
-			else
-			{
-				ANKI_ASSERT(c.m_type == ShaderVariableDataType::kF32);
-				in.m_dataType = ShaderVariableDataType::kVec4;
-			}
+			// Only miss
 		}
-		else
+		else if(technique.m_shaderTypes == ShaderTypeBit::kRayGen)
 		{
-			ANKI_ASSERT(0);
+			// Only ray gen
 		}
-	}
-
-	m_shaderStages = binary.m_presentShaderTypes;
-
-	// Do some RT checks
-	if(!!(m_shaderStages & ShaderTypeBit::kAllRayTracing))
-	{
-		if(m_shaderStages != (ShaderTypeBit::kAnyHit | ShaderTypeBit::kClosestHit) && m_shaderStages != ShaderTypeBit::kMiss
-		   && m_shaderStages != ShaderTypeBit::kRayGen)
+		else
 		{
-			ANKI_RESOURCE_LOGE("Any and closest hit shaders shouldn't coexist with other stages. Miss can't coexist with other stages. Raygen can't "
-							   "coexist with other stages as well");
-			return Error::kUserData;
+			ANKI_ASSERT(!"Can't figure out a sensible default");
 		}
-	}
-
-	return Error::kNone;
-}
 
-Error ShaderProgramResource::parseConst(CString constName, U32& componentIdx, U32& componentCount, CString& name)
-{
-	const CString prefixName = "_anki_const_";
-	const PtrSize prefix = constName.find(prefixName);
-	if(prefix != 0)
-	{
-		// Simple name
-		componentIdx = 0;
-		componentCount = 1;
-		name = constName;
-		return Error::kNone;
+		info.m_shaderTypes = technique.m_shaderTypes;
 	}
 
-	Array<char, 2> number;
-	number[0] = constName[prefixName.getLength()];
-	number[1] = '\0';
-	ANKI_CHECK(CString(number.getBegin()).toNumber(componentIdx));
-
-	number[0] = constName[prefixName.getLength() + 2];
-	ANKI_CHECK(CString(number.getBegin()).toNumber(componentCount));
-
-	name = constName.getBegin() + prefixName.getLength() + 4;
-
-	return Error::kNone;
-}
-
-void ShaderProgramResource::getOrCreateVariant(const ShaderProgramResourceVariantInitInfo& info, const ShaderProgramResourceVariant*& variant) const
-{
-	// Sanity checks
-	ANKI_ASSERT(info.m_setMutators.getSetBitCount() == m_mutators.getSize());
-	ANKI_ASSERT(info.m_setConstants.getSetBitCount() == m_consts.getSize());
-	if(info.m_meshShaders)
-	{
-		ANKI_ASSERT(!!(m_shaderStages & ShaderTypeBit::kAllModernGeometry));
-	}
-	else if(!!(m_shaderStages & ShaderTypeBit::kAllGraphics))
-	{
-		ANKI_ASSERT(!!(m_shaderStages & ShaderTypeBit::kAllLegacyGeometry));
-	}
+	ANKI_ASSERT((info.m_shaderTypes & technique.m_shaderTypes) == info.m_shaderTypes);
 
 	// Compute variant hash
-	U64 hash = 0xBAD << U32(info.m_meshShaders);
-	if(m_mutators.getSize())
-	{
-		hash = appendHash(info.m_mutation.getBegin(), m_mutators.getSize() * sizeof(info.m_mutation[0]), hash);
-	}
-
-	if(m_consts.getSize())
+	U64 hash = computeHash(&info.m_shaderTypes, sizeof(info.m_shaderTypes));
+	hash = appendHash(techniqueName.cstr(), techniqueName.getLength(), hash);
+	if(m_binary->m_mutators.getSize())
 	{
-		hash = appendHash(info.m_constantValues.getBegin(), m_consts.getSize() * sizeof(info.m_constantValues[0]), hash);
+		hash = appendHash(info.m_mutation.getBegin(), m_binary->m_mutators.getSize() * sizeof(info.m_mutation[0]), hash);
 	}
 
 	// Check if the variant is in the cache
@@ -228,7 +124,10 @@ void ShaderProgramResource::getOrCreateVariant(const ShaderProgramResourceVarian
 		{
 			// Done
 			variant = *it;
-			ANKI_ASSERT(!info.m_meshShaders || !!(variant->m_prog->getShaderTypes() & ShaderTypeBit::kAllModernGeometry));
+			if(!!(info.m_shaderTypes & ShaderTypeBit::kAllGraphics))
+			{
+				ANKI_ASSERT(variant->m_prog->getShaderTypes() == info.m_shaderTypes);
+			}
 			return;
 		}
 	}
@@ -246,30 +145,31 @@ void ShaderProgramResource::getOrCreateVariant(const ShaderProgramResourceVarian
 	}
 
 	// Create
-	ShaderProgramResourceVariant* v = createNewVariant(info);
+	ShaderProgramResourceVariant* v = createNewVariant(info, techniqueIdx);
 	if(v)
 	{
 		m_variants.emplace(hash, v);
 	}
 	variant = v;
-	ANKI_ASSERT(!info.m_meshShaders || !!(variant->m_prog->getShaderTypes() & ShaderTypeBit::kAllModernGeometry));
+	if(!!(info.m_shaderTypes & ShaderTypeBit::kAllGraphics))
+	{
+		ANKI_ASSERT(variant->m_prog->getShaderTypes() == info.m_shaderTypes);
+	}
 }
 
-ShaderProgramResourceVariant* ShaderProgramResource::createNewVariant(const ShaderProgramResourceVariantInitInfo& info) const
+ShaderProgramResourceVariant* ShaderProgramResource::createNewVariant(const ShaderProgramResourceVariantInitInfo& info, U32 techniqueIdx) const
 {
-	const ShaderProgramBinary& binary = m_binary.getBinary();
-
 	// Get the binary program variant
 	const ShaderProgramBinaryVariant* binaryVariant = nullptr;
 	U64 mutationHash = 0;
-	if(m_mutators.getSize())
+	if(m_binary->m_mutators.getSize())
 	{
 		// Create the mutation hash
-		mutationHash = computeHash(info.m_mutation.getBegin(), m_mutators.getSize() * sizeof(info.m_mutation[0]));
+		mutationHash = computeHash(info.m_mutation.getBegin(), m_binary->m_mutators.getSize() * sizeof(info.m_mutation[0]));
 
 		// Search for the mutation in the binary
 		// TODO optimize the search
-		for(const ShaderProgramBinaryMutation& mutation : binary.m_mutations)
+		for(const ShaderProgramBinaryMutation& mutation : m_binary->m_mutations)
 		{
 			if(mutation.m_hash == mutationHash)
 			{
@@ -279,111 +179,34 @@ ShaderProgramResourceVariant* ShaderProgramResource::createNewVariant(const Shad
 					return nullptr;
 				}
 
-				binaryVariant = &binary.m_variants[mutation.m_variantIndex];
+				binaryVariant = &m_binary->m_variants[mutation.m_variantIndex];
 				break;
 			}
 		}
 	}
 	else
 	{
-		ANKI_ASSERT(binary.m_variants.getSize() == 1);
-		binaryVariant = &binary.m_variants[0];
+		ANKI_ASSERT(m_binary->m_variants.getSize() == 1);
+		binaryVariant = &m_binary->m_variants[0];
 	}
 	ANKI_ASSERT(binaryVariant);
 	ShaderProgramResourceVariant* variant = newInstance<ShaderProgramResourceVariant>(ResourceMemoryPool::getSingleton());
-	variant->m_binaryVariant = binaryVariant;
-
-	// Set the constant values
-	Array<ShaderSpecializationConstValue, 64> constValues;
-	U32 constValueCount = 0;
-	for(const ShaderProgramBinaryConstantInstance& instance : binaryVariant->m_constants)
-	{
-		const ShaderProgramBinaryConstant& c = binary.m_constants[instance.m_index];
-		const U32 inputIdx = m_constBinaryMapping[instance.m_index].m_constsIdx;
-		const U32 component = m_constBinaryMapping[instance.m_index].m_component;
-
-		// Get value
-		const ShaderProgramResourceConstantValue* value = nullptr;
-		for(U32 i = 0; i < m_consts.getSize(); ++i)
-		{
-			if(info.m_constantValues[i].m_constantIndex == inputIdx)
-			{
-				value = &info.m_constantValues[i];
-				break;
-			}
-		}
-		ANKI_ASSERT(value && "Forgot to set the value of a constant");
-
-		constValues[constValueCount].m_constantId = c.m_constantId;
-		constValues[constValueCount].m_dataType = c.m_type;
-		constValues[constValueCount].m_int = value->m_ivec4[component];
-		++constValueCount;
-	}
-
-	// Get the workgroup sizes
-	if(!!(m_shaderStages & ShaderTypeBit::kCompute))
-	{
-		for(U32 i = 0; i < 3; ++i)
-		{
-			if(binaryVariant->m_workgroupSizes[i] != kMaxU32)
-			{
-				// Size didn't come from specialization const
-				variant->m_workgroupSizes[i] = binaryVariant->m_workgroupSizes[i];
-			}
-			else
-			{
-				// Size is specialization const
-
-				ANKI_ASSERT(binaryVariant->m_workgroupSizesConstants[i] != kMaxU32);
-
-				const U32 binaryConstIdx = binaryVariant->m_workgroupSizesConstants[i];
-				const U32 constIdx = m_constBinaryMapping[binaryConstIdx].m_constsIdx;
-				const U32 component = m_constBinaryMapping[binaryConstIdx].m_component;
-				[[maybe_unused]] const Const& c = m_consts[constIdx];
-				ANKI_ASSERT(c.m_dataType == ShaderVariableDataType::kU32 || c.m_dataType == ShaderVariableDataType::kUVec2
-							|| c.m_dataType == ShaderVariableDataType::kUVec3 || c.m_dataType == ShaderVariableDataType::kUVec4);
-
-				// Find the value
-				for(U32 i = 0; i < m_consts.getSize(); ++i)
-				{
-					if(info.m_constantValues[i].m_constantIndex == constIdx)
-					{
-						const I32 value = info.m_constantValues[i].m_ivec4[component];
-						ANKI_ASSERT(value > 0);
-
-						variant->m_workgroupSizes[i] = U32(value);
-						break;
-					}
-				}
-			}
-
-			ANKI_ASSERT(variant->m_workgroupSizes[i] != kMaxU32);
-		}
-	}
 
 	// Time to init the shaders
-	if(!!(m_shaderStages & (ShaderTypeBit::kAllGraphics | ShaderTypeBit::kCompute)))
+	if(!!(info.m_shaderTypes & (ShaderTypeBit::kAllGraphics | ShaderTypeBit::kCompute)))
 	{
-		const ShaderTypeBit stages =
-			m_shaderStages & ((info.m_meshShaders) ? ~ShaderTypeBit::kAllLegacyGeometry : ~ShaderTypeBit::kAllModernGeometry);
-
 		// Create the program name
 		String progName;
 		getFilepathFilename(getFilename(), progName);
-		char* cprogName = const_cast<char*>(progName.cstr());
-		if(progName.getLength() > kMaxGrObjectNameLength)
-		{
-			cprogName[kMaxGrObjectNameLength] = '\0';
-		}
 
-		ShaderProgramInitInfo progInf(cprogName);
+		ShaderProgramInitInfo progInf(progName);
 		Array<ShaderPtr, U32(ShaderType::kCount)> shaderRefs; // Just for refcounting
-		for(ShaderType shaderType : EnumBitsIterable<ShaderType, ShaderTypeBit>(stages))
+		for(ShaderType shaderType : EnumBitsIterable<ShaderType, ShaderTypeBit>(info.m_shaderTypes))
 		{
-			ShaderInitInfo inf(cprogName);
+			const ResourceString shaderName = (progName + "_" + m_binary->m_techniques[techniqueIdx].m_name.getBegin()).cstr();
+			ShaderInitInfo inf(shaderName);
 			inf.m_shaderType = shaderType;
-			inf.m_binary = binary.m_codeBlocks[binaryVariant->m_codeBlockIndices[shaderType]].m_binary;
-			inf.m_constValues.setArray((constValueCount) ? constValues.getBegin() : nullptr, constValueCount);
+			inf.m_binary = m_binary->m_codeBlocks[binaryVariant->m_techniqueCodeBlocks[techniqueIdx].m_codeBlockIndices[shaderType]].m_binary;
 			ShaderPtr shader = GrManager::getSingleton().newShader(inf);
 			shaderRefs[shaderType] = shader;
 
@@ -407,10 +230,10 @@ ShaderProgramResourceVariant* ShaderProgramResource::createNewVariant(const Shad
 	}
 	else
 	{
-		ANKI_ASSERT(!!(m_shaderStages & ShaderTypeBit::kAllRayTracing));
+		ANKI_ASSERT(!!(info.m_shaderTypes & ShaderTypeBit::kAllRayTracing));
 
 		// Find the library
-		CString libName = &binary.m_libraryName[0];
+		const CString libName = m_binary->m_techniques[techniqueIdx].m_name.getBegin();
 		ANKI_ASSERT(libName.getLength() > 0);
 
 		const ShaderProgramResourceSystem& progSystem = ResourceManager::getSingleton().getShaderProgramResourceSystem();
@@ -427,8 +250,23 @@ ShaderProgramResourceVariant* ShaderProgramResource::createNewVariant(const Shad
 
 		variant->m_prog = foundLib->getShaderProgram();
 
+		RayTracingShaderGroupType groupType;
+		if(!!(info.m_shaderTypes & ShaderTypeBit::kRayGen))
+		{
+			groupType = RayTracingShaderGroupType::kRayGen;
+		}
+		else if(!!(info.m_shaderTypes & ShaderTypeBit::kMiss))
+		{
+			groupType = RayTracingShaderGroupType::kMiss;
+		}
+		else
+		{
+			ANKI_ASSERT(!!(info.m_shaderTypes & ShaderTypeBit::kAllHit));
+			groupType = RayTracingShaderGroupType::kHit;
+		}
+
 		// Set the group handle index
-		variant->m_shaderGroupHandleIndex = foundLib->getShaderGroupHandleIndex(getFilename(), mutationHash);
+		variant->m_shaderGroupHandleIndex = foundLib->getShaderGroupHandleIndex(getFilename(), mutationHash, groupType);
 	}
 
 	return variant;

+ 38 - 186
AnKi/Resource/ShaderProgramResource.h

@@ -23,42 +23,6 @@ class ShaderProgramResourceVariantInitInfo;
 /// @addtogroup resource
 /// @{
 
-/// The means to mutate a shader program.
-/// @memberof ShaderProgramResource
-class ShaderProgramResourceMutator
-{
-public:
-	CString m_name;
-	ConstWeakArray<MutatorValue> m_values;
-
-	ShaderProgramResourceMutator() = default;
-
-	ShaderProgramResourceMutator(const ShaderProgramResourceMutator&) = delete; // Non-copyable
-
-	ShaderProgramResourceMutator& operator=(const ShaderProgramResourceMutator&) = delete; // Non-copyable
-
-	Bool valueExists(MutatorValue v) const
-	{
-		for(MutatorValue x : m_values)
-		{
-			if(v == x)
-			{
-				return true;
-			}
-		}
-		return false;
-	}
-};
-
-/// Shader program resource variant.
-class ShaderProgramResourceConstant
-{
-public:
-	ResourceString m_name;
-	ShaderVariableDataType m_dataType = ShaderVariableDataType::kNone;
-	U32 m_index = kMaxU32;
-};
-
 /// Shader program resource variant.
 class ShaderProgramResourceVariant
 {
@@ -75,26 +39,6 @@ public:
 		return *m_prog;
 	}
 
-	/// Return true if the the variable is active in this variant.
-	Bool isConstantActive(const ShaderProgramResourceConstant& var) const
-	{
-		return m_activeConsts.get(var.m_index);
-	}
-
-	const ShaderProgramBinaryVariant& getBinaryVariant() const
-	{
-		ANKI_ASSERT(m_binaryVariant);
-		return *m_binaryVariant;
-	}
-
-	const Array<U32, 3>& getWorkgroupSizes() const
-	{
-		ANKI_ASSERT(m_workgroupSizes[0] != kMaxU32 && m_workgroupSizes[0] != 0);
-		ANKI_ASSERT(m_workgroupSizes[1] != kMaxU32 && m_workgroupSizes[1] != 0);
-		ANKI_ASSERT(m_workgroupSizes[2] != kMaxU32 && m_workgroupSizes[2] != 0);
-		return m_workgroupSizes;
-	}
-
 	/// Only for hit ray tracing programs.
 	U32 getShaderGroupHandleIndex() const
 	{
@@ -104,83 +48,57 @@ public:
 
 private:
 	ShaderProgramPtr m_prog;
-	const ShaderProgramBinaryVariant* m_binaryVariant = nullptr;
-	BitSet<128, U64> m_activeConsts = {false};
-	Array<U32, 3> m_workgroupSizes;
 	U32 m_shaderGroupHandleIndex = kMaxU32; ///< Cache the index of the handle here.
 };
 
-/// The value of a constant.
-class ShaderProgramResourceConstantValue
-{
-public:
-	union
-	{
-		U32 m_uint;
-		UVec2 m_uvec2;
-		UVec3 m_uvec3;
-		UVec4 m_uvec4;
-
-		I32 m_int;
-		IVec2 m_ivec2;
-		IVec3 m_ivec3;
-		IVec4 m_ivec4;
-
-		F32 m_float;
-		Vec2 m_vec2;
-		Vec3 m_vec3;
-		Vec4 m_vec4;
-	};
-
-	U32 m_constantIndex;
-	U8 _m_padding[sizeof(Vec4) - sizeof(m_constantIndex)];
-
-	ShaderProgramResourceConstantValue()
-	{
-		zeroMemory(*this);
-	}
-};
-static_assert(sizeof(ShaderProgramResourceConstantValue) == sizeof(Vec4) * 2, "Need it to be packed");
-
-/// Smart initializer of multiple ShaderProgramResourceConstantValue.
 class ShaderProgramResourceVariantInitInfo
 {
 	friend class ShaderProgramResource;
 
 public:
 	ShaderProgramResourceVariantInitInfo()
+		: ShaderProgramResourceVariantInitInfo(ShaderProgramResourcePtr())
 	{
 	}
 
 	ShaderProgramResourceVariantInitInfo(const ShaderProgramResourcePtr& ptr)
 		: m_ptr(ptr)
 	{
+		Char name[] = "Unnamed";
+		memcpy(m_techniqueName.getBegin(), name, sizeof(name));
 	}
 
 	~ShaderProgramResourceVariantInitInfo()
 	{
 	}
 
-	template<typename T>
-	ShaderProgramResourceVariantInitInfo& addConstant(CString name, const T& value);
-
 	ShaderProgramResourceVariantInitInfo& addMutation(CString name, MutatorValue t);
 
-	void requestMeshShaders(Bool request);
+	/// Request a technique. If not set it will choose the "Unnamed" technique.
+	void requestTechnique(CString technique)
+	{
+		const U32 len = technique.getLength();
+		ANKI_ASSERT(len <= kMaxTechniqueNameLength);
+		memcpy(m_techniqueName.getBegin(), technique.cstr(), len + 1);
+	}
+
+	/// Request specific shader types. You can ommit it if the program only contains one group of stages.
+	void requestShaderTypes(ShaderTypeBit types)
+	{
+		m_shaderTypes = types;
+	}
 
 private:
-	static constexpr U32 kMaxConstants = 32;
 	static constexpr U32 kMaxMutators = 32;
+	static constexpr U32 kMaxTechniqueNameLength = 23;
 
 	ShaderProgramResourcePtr m_ptr;
 
-	Array<ShaderProgramResourceConstantValue, kMaxConstants> m_constantValues;
-	BitSet<kMaxConstants> m_setConstants = {false};
-
 	Array<MutatorValue, kMaxMutators> m_mutation; ///< The order of storing the values is important. It will be hashed.
 	BitSet<kMaxMutators> m_setMutators = {false};
 
-	Bool m_meshShaders = false;
+	Array<Char, kMaxTechniqueNameLength + 1> m_techniqueName;
+	ShaderTypeBit m_shaderTypes = ShaderTypeBit::kNone;
 };
 
 /// Shader program resource. It loads special AnKi programs.
@@ -194,37 +112,12 @@ public:
 	/// Load the resource.
 	Error load(const ResourceFilename& filename, Bool async);
 
-	/// Get the array of constants.
-	ConstWeakArray<ShaderProgramResourceConstant> getConstants() const
-	{
-		return m_consts;
-	}
-
-	/// Try to find a constant.
-	const ShaderProgramResourceConstant* tryFindConstant(CString name) const
-	{
-		for(const ShaderProgramResourceConstant& m : m_consts)
-		{
-			if(m.m_name == name)
-			{
-				return &m;
-			}
-		}
-		return nullptr;
-	}
-
-	/// Get the array of mutators.
-	ConstWeakArray<ShaderProgramResourceMutator> getMutators() const
-	{
-		return m_mutators;
-	}
-
 	/// Try to find a mutator.
-	const ShaderProgramResourceMutator* tryFindMutator(CString name) const
+	const ShaderProgramBinaryMutator* tryFindMutator(CString name) const
 	{
-		for(const ShaderProgramResourceMutator& m : m_mutators)
+		for(const ShaderProgramBinaryMutator& m : m_binary->m_mutators)
 		{
-			if(m.m_name == name)
+			if(m.m_name.getBegin() == name)
 			{
 				return &m;
 			}
@@ -232,18 +125,12 @@ public:
 		return nullptr;
 	}
 
-	ShaderTypeBit getStages() const
-	{
-		return m_shaderStages;
-	}
-
 	const ShaderProgramBinary& getBinary() const
 	{
-		return m_binary.getBinary();
+		return *m_binary;
 	}
 
-	/// Get or create a graphics shader program variant. If returned variant is nullptr then it means that the mutation
-	/// is skipped and thus incorrect.
+	/// Get or create a graphics shader program variant. If returned variant is nullptr then it means that the mutation is skipped and thus incorrect.
 	/// @note It's thread-safe.
 	void getOrCreateVariant(const ShaderProgramResourceVariantInitInfo& info, const ShaderProgramResourceVariant*& variant) const;
 
@@ -254,68 +141,33 @@ public:
 	}
 
 private:
-	using Mutator = ShaderProgramResourceMutator;
-	using Const = ShaderProgramResourceConstant;
-
-	ShaderProgramBinaryWrapper m_binary;
-
-	ResourceDynamicArray<Const> m_consts;
-	ResourceDynamicArray<Mutator> m_mutators;
-
-	class ConstMapping
-	{
-	public:
-		U32 m_component = 0;
-		U32 m_constsIdx = 0; ///< Index in m_consts
-	};
-
-	ResourceDynamicArray<ConstMapping> m_constBinaryMapping;
+	ShaderProgramBinary* m_binary = nullptr;
 
 	mutable ResourceHashMap<U64, ShaderProgramResourceVariant*> m_variants;
 	mutable RWMutex m_mtx;
 
-	ShaderTypeBit m_shaderStages = ShaderTypeBit::kNone;
-
-	ShaderProgramResourceVariant* createNewVariant(const ShaderProgramResourceVariantInitInfo& info) const;
-
-	static Error parseConst(CString constName, U32& componentIdx, U32& componentCount, CString& name);
+	ShaderProgramResourceVariant* createNewVariant(const ShaderProgramResourceVariantInitInfo& info, U32 techniqueIdx) const;
 };
 
-template<typename T>
-inline ShaderProgramResourceVariantInitInfo& ShaderProgramResourceVariantInitInfo::addConstant(CString name, const T& value)
-{
-	const ShaderProgramResourceConstant* in = m_ptr->tryFindConstant(name);
-	if(in != nullptr)
-	{
-		ANKI_ASSERT(in->m_dataType == getShaderVariableTypeFromTypename<T>());
-		const U32 constIdx = U32(in - m_ptr->getConstants().getBegin());
-		m_constantValues[constIdx].m_constantIndex = constIdx;
-		memcpy(&m_constantValues[constIdx].m_int, &value, sizeof(T));
-		m_setConstants.set(constIdx);
-	}
-	else
-	{
-		ANKI_RESOURCE_LOGW("Constant not found: %s", name.cstr());
-	}
-	return *this;
-}
-
 inline ShaderProgramResourceVariantInitInfo& ShaderProgramResourceVariantInitInfo::addMutation(CString name, MutatorValue t)
 {
-	const ShaderProgramResourceMutator* m = m_ptr->tryFindMutator(name);
+	const ShaderProgramBinaryMutator* m = m_ptr->tryFindMutator(name);
 	ANKI_ASSERT(m);
-	ANKI_ASSERT(m->valueExists(t));
-	const PtrSize mutatorIdx = m - m_ptr->getMutators().getBegin();
+	[[maybe_unused]] Bool valueExits = false;
+	for(auto v : m->m_values)
+	{
+		if(v == t)
+		{
+			valueExits = true;
+			break;
+		}
+	}
+	ANKI_ASSERT(valueExits);
+	const PtrSize mutatorIdx = m - m_ptr->getBinary().m_mutators.getBegin();
 	m_mutation[mutatorIdx] = t;
 	m_setMutators.set(mutatorIdx);
 	return *this;
 }
-
-inline void ShaderProgramResourceVariantInitInfo::requestMeshShaders(Bool request)
-{
-	ANKI_ASSERT(!request || !!(m_ptr->getStages() & ShaderTypeBit::kAllModernGeometry));
-	m_meshShaders = request;
-}
 /// @}
 
 } // end namespace anki

+ 266 - 297
AnKi/Resource/ShaderProgramResourceSystem.cpp

@@ -16,12 +16,122 @@
 
 namespace anki {
 
-U64 ShaderProgramRaytracingLibrary::generateShaderGroupGroupHash(CString resourceFilename, U64 mutationHash)
+class ShaderProgramResourceSystem::ShaderH
+{
+public:
+	ShaderPtr m_shader;
+	U64 m_hash = 0; ///< Hash of the binary.
+};
+
+class ShaderProgramResourceSystem::ShaderGroup
+{
+public:
+	U32 m_rayGen = kMaxU32;
+	U32 m_miss = kMaxU32;
+	U32 m_chit = kMaxU32;
+	U32 m_ahit = kMaxU32;
+
+	ResourceDynamicArray<U64> m_groupHashes;
+};
+
+class ShaderProgramResourceSystem::Lib
+{
+public:
+	ResourceString m_name;
+	ResourceDynamicArray<ShaderH> m_shaders;
+	ResourceDynamicArray<ShaderGroup> m_rayGenGroups;
+	ResourceDynamicArray<ShaderGroup> m_missGroups;
+	ResourceDynamicArray<ShaderGroup> m_hitGroups;
+	ShaderTypeBit m_presentStages = ShaderTypeBit::kNone;
+
+	U32 addShader(const ShaderProgramBinaryCodeBlock& codeBlock, CString progName, ShaderType shaderType)
+	{
+		ShaderH* shader = nullptr;
+		for(ShaderH& s : m_shaders)
+		{
+			if(s.m_hash == codeBlock.m_hash)
+			{
+				shader = &s;
+				break;
+			}
+		}
+
+		if(shader == nullptr)
+		{
+			shader = m_shaders.emplaceBack();
+
+			ShaderInitInfo inf(progName);
+			inf.m_shaderType = shaderType;
+			inf.m_binary = codeBlock.m_binary;
+			shader->m_shader = GrManager::getSingleton().newShader(inf);
+			shader->m_hash = codeBlock.m_hash;
+
+			m_presentStages |= ShaderTypeBit(1 << shaderType);
+		}
+
+		return U32(shader - m_shaders.getBegin());
+	}
+
+	void addGroup(CString filename, U64 mutationHash, U32 rayGen, U32 miss, U32 chit, U32 ahit)
+	{
+		ResourceDynamicArray<ShaderGroup>* groupArray = nullptr;
+		RayTracingShaderGroupType groupType;
+		if(rayGen < kMaxU32)
+		{
+			groupType = RayTracingShaderGroupType::kRayGen;
+			groupArray = &m_rayGenGroups;
+		}
+		else if(miss < kMaxU32)
+		{
+			groupType = RayTracingShaderGroupType::kMiss;
+			groupArray = &m_missGroups;
+		}
+		else
+		{
+			ANKI_ASSERT(chit < kMaxU32 || ahit < kMaxU32);
+			groupType = RayTracingShaderGroupType::kHit;
+			groupArray = &m_hitGroups;
+		}
+
+		ShaderGroup* pgroup = nullptr;
+		for(ShaderGroup& s : *groupArray)
+		{
+			if(s.m_rayGen == rayGen && s.m_miss == miss && s.m_ahit == ahit && s.m_chit == chit)
+			{
+				pgroup = &s;
+				break;
+			}
+		}
+
+		if(!pgroup)
+		{
+			pgroup = groupArray->emplaceBack();
+			pgroup->m_rayGen = rayGen;
+			pgroup->m_miss = miss;
+			pgroup->m_chit = chit;
+			pgroup->m_ahit = ahit;
+		}
+
+		const U64 groupHash = ShaderProgramRaytracingLibrary::generateShaderGroupGroupHash(filename, mutationHash, groupType);
+#if ANKI_ASSERTIONS_ENABLED
+		for(U64 hash : pgroup->m_groupHashes)
+		{
+			ANKI_ASSERT(hash != groupHash && "Shouldn't find group with the same hash");
+		}
+#endif
+
+		pgroup->m_groupHashes.emplaceBack(groupHash);
+	}
+};
+
+U64 ShaderProgramRaytracingLibrary::generateShaderGroupGroupHash(CString resourceFilename, U64 mutationHash, RayTracingShaderGroupType groupType)
 {
 	ANKI_ASSERT(resourceFilename.getLength() > 0);
+	ANKI_ASSERT(groupType < RayTracingShaderGroupType::kCount);
 	String basename;
 	getFilepathFilename(resourceFilename, basename);
-	const U64 hash = appendHash(basename.cstr(), basename.getLength(), mutationHash);
+	U64 hash = appendHash(basename.cstr(), basename.getLength(), mutationHash);
+	hash = appendHash(&groupType, sizeof(groupType), hash);
 	return hash;
 }
 
@@ -47,101 +157,9 @@ Error ShaderProgramResourceSystem::createRayTracingPrograms(ResourceDynamicArray
 	ANKI_RESOURCE_LOGI("Creating ray tracing programs");
 	U32 rtProgramCount = 0;
 
-	class ShaderH
-	{
-	public:
-		ShaderPtr m_shader;
-		U64 m_hash = 0; ///< Hash of the binary.
-	};
-
-	class ShaderGroup
-	{
-	public:
-		U32 m_rayGen = kMaxU32;
-		U32 m_miss = kMaxU32;
-		U32 m_chit = kMaxU32;
-		U32 m_ahit = kMaxU32;
-		U64 m_hitGroupHash = 0;
-	};
-
-	class Lib
-	{
-	public:
-		ResourceString m_name;
-		ResourceDynamicArray<ShaderH> m_shaders;
-		ResourceDynamicArray<ShaderGroup> m_shaderGroups;
-		ShaderTypeBit m_presentStages = ShaderTypeBit::kNone;
-		U32 m_rayTypeCount = 0;
-		BitSet<64> m_rayTypeMask = {false};
-		U32 m_rayGenShaderGroupCount = 0;
-		U32 m_missShaderGroupCount = 0;
-		U32 m_hitShaderGroupCount = 0;
-
-		U32 addShader(const ShaderProgramBinaryCodeBlock& codeBlock, CString progName, ShaderType shaderType)
-		{
-			ShaderH* shader = nullptr;
-			for(ShaderH& s : m_shaders)
-			{
-				if(s.m_hash == codeBlock.m_hash)
-				{
-					shader = &s;
-					break;
-				}
-			}
-
-			if(shader == nullptr)
-			{
-				shader = m_shaders.emplaceBack();
-
-				ShaderInitInfo inf(progName);
-				inf.m_shaderType = shaderType;
-				inf.m_binary = codeBlock.m_binary;
-				shader->m_shader = GrManager::getSingleton().newShader(inf);
-				shader->m_hash = codeBlock.m_hash;
-
-				m_presentStages |= ShaderTypeBit(1 << shaderType);
-			}
-
-			return U32(shader - m_shaders.getBegin());
-		}
-
-		void addGroup(CString filename, U64 mutationHash, U32 rayGen, U32 miss, U32 chit, U32 ahit)
-		{
-			const U64 groupHash = ShaderProgramRaytracingLibrary::generateShaderGroupGroupHash(filename, mutationHash);
-#if ANKI_ASSERTIONS_ENABLED
-			for(const ShaderGroup& group : m_shaderGroups)
-			{
-				ANKI_ASSERT(group.m_hitGroupHash != groupHash && "Shouldn't find group with the same hash");
-			}
-#endif
-
-			ShaderGroup group;
-			group.m_rayGen = rayGen;
-			group.m_miss = miss;
-			group.m_chit = chit;
-			group.m_ahit = ahit;
-			group.m_hitGroupHash = groupHash;
-			m_shaderGroups.emplaceBack(group);
-
-			if(rayGen < kMaxU32)
-			{
-				++m_rayGenShaderGroupCount;
-			}
-			else if(miss < kMaxU32)
-			{
-				++m_missShaderGroupCount;
-			}
-			else
-			{
-				ANKI_ASSERT(chit < kMaxU32 || ahit < kMaxU32);
-				++m_hitShaderGroupCount;
-			}
-		}
-	};
-
 	ResourceDynamicArray<Lib> libs;
 
-	ANKI_CHECK(ResourceManager::getSingleton().getFilesystem().iterateAllFilenames([&](CString filename) -> Error {
+	const Error err = ResourceManager::getSingleton().getFilesystem().iterateAllFilenames([&](CString filename) -> Error {
 		// Check file extension
 		String extension;
 		getFilepathExtension(filename, extension);
@@ -151,204 +169,166 @@ Error ShaderProgramResourceSystem::createRayTracingPrograms(ResourceDynamicArray
 			return Error::kNone;
 		}
 
-		if(filename.find("ShaderBinaries/Rt") != 0)
-		{
-			// Doesn't start with the expected path, skip it
-			return Error::kNone;
-		}
-
 		// Get the binary
 		ResourceFilePtr file;
 		ANKI_CHECK(ResourceManager::getSingleton().getFilesystem().openFile(filename, file));
-		ShaderProgramBinaryWrapper binaryw(&ResourceMemoryPool::getSingleton());
-		ANKI_CHECK(binaryw.deserializeFromAnyFile(*file));
-		const ShaderProgramBinary& binary = binaryw.getBinary();
+		ShaderProgramBinary* binary;
+		ANKI_CHECK(deserializeShaderProgramBinaryFromAnyFile(*file, binary, ResourceMemoryPool::getSingleton()));
 
-		if(!(binary.m_presentShaderTypes & ShaderTypeBit::kAllRayTracing))
+		class Dummy
 		{
-			return Error::kNone;
-		}
+		public:
+			ShaderProgramBinary* m_binary;
+
+			~Dummy()
+			{
+				ResourceMemoryPool::getSingleton().free(m_binary);
+			}
+		} dummy{binary};
 
-		// Checks
-		if(binary.m_libraryName[0] == '\0')
+		if(!(binary->m_shaderTypes & ShaderTypeBit::kAllRayTracing))
 		{
-			ANKI_RESOURCE_LOGE("Library is missing from program: %s", filename.cstr());
-			return Error::kUserData;
+			return Error::kNone;
 		}
 
 		// Create the program name
 		String progName;
 		getFilepathFilename(filename, progName);
 
-		// Find or create the lib
-		Lib* lib = nullptr;
+		for(const ShaderProgramBinaryTechnique& technique : binary->m_techniques)
 		{
-			for(Lib& l : libs)
+			if(!(technique.m_shaderTypes & ShaderTypeBit::kAllRayTracing))
 			{
-				if(l.m_name == CString(&binary.m_libraryName[0]))
-				{
-					lib = &l;
-					break;
-				}
+				continue;
 			}
 
-			if(lib == nullptr)
-			{
-				libs.emplaceBack();
-				lib = &libs.getBack();
-				lib->m_name = CString(&binary.m_libraryName[0]);
-			}
-		}
+			const U32 techniqueIdx = U32(&technique - binary->m_techniques.getBegin());
 
-		// Update the ray type
-		const U32 rayTypeNumber = binary.m_rayType;
-		if(rayTypeNumber != kMaxU32)
-		{
-			lib->m_rayTypeCount = max(lib->m_rayTypeCount, rayTypeNumber + 1);
-			lib->m_rayTypeMask.set(rayTypeNumber);
-		}
-
-		// Ray gen
-		if(!!(binary.m_presentShaderTypes & ShaderTypeBit::kRayGen))
-		{
-			if(!!(binary.m_presentShaderTypes & ~ShaderTypeBit::kRayGen))
+			// Find or create the lib
+			Lib* lib = nullptr;
 			{
-				ANKI_RESOURCE_LOGE("Ray gen can't co-exist with other types: %s", filename.cstr());
-				return Error::kUserData;
-			}
-
-			if(binary.m_constants.getSize())
-			{
-				ANKI_RESOURCE_LOGE("Ray gen can't have spec constants ATM: %s", filename.cstr());
-				return Error::kUserData;
-			}
-
-			// Iterate all mutations
-			ConstWeakArray<ShaderProgramBinaryMutation> mutations;
-			ShaderProgramBinaryMutation dummyMutation;
-			if(binary.m_mutations.getSize() > 1)
-			{
-				mutations = binary.m_mutations;
-			}
-			else
-			{
-				dummyMutation.m_hash = 0;
-				dummyMutation.m_variantIndex = 0;
-				mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
-			}
-
-			for(const ShaderProgramBinaryMutation& mutation : mutations)
-			{
-				const ShaderProgramBinaryVariant& variant = binary.m_variants[mutation.m_variantIndex];
-				const U32 codeBlockIndex = variant.m_codeBlockIndices[ShaderType::kRayGen];
-				ANKI_ASSERT(codeBlockIndex != kMaxU32);
-				const U32 shaderIdx = lib->addShader(binary.m_codeBlocks[codeBlockIndex], progName, ShaderType::kRayGen);
-
-				lib->addGroup(filename, mutation.m_hash, shaderIdx, kMaxU32, kMaxU32, kMaxU32);
-			}
-		}
+				for(Lib& l : libs)
+				{
+					if(l.m_name == technique.m_name.getBegin())
+					{
+						lib = &l;
+						break;
+					}
+				}
 
-		// Miss shaders
-		if(!!(binary.m_presentShaderTypes & ShaderTypeBit::kMiss))
-		{
-			if(!!(binary.m_presentShaderTypes & ~ShaderTypeBit::kMiss))
-			{
-				ANKI_RESOURCE_LOGE("Miss shaders can't co-exist with other types: %s", filename.cstr());
-				return Error::kUserData;
+				if(lib == nullptr)
+				{
+					libs.emplaceBack();
+					lib = &libs.getBack();
+					lib->m_name = technique.m_name.getBegin();
+				}
 			}
 
-			if(binary.m_constants.getSize())
+			// Ray gen
+			if(!!(technique.m_shaderTypes & ShaderTypeBit::kRayGen))
 			{
-				ANKI_RESOURCE_LOGE("Miss can't have spec constants ATM: %s", filename.cstr());
-				return Error::kUserData;
-			}
+				// Iterate all mutations
+				ConstWeakArray<ShaderProgramBinaryMutation> mutations;
+				ShaderProgramBinaryMutation dummyMutation;
+				if(binary->m_mutations.getSize() > 1)
+				{
+					mutations = binary->m_mutations;
+				}
+				else
+				{
+					dummyMutation.m_hash = 0;
+					dummyMutation.m_variantIndex = 0;
+					mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
+				}
 
-			if(rayTypeNumber == kMaxU32)
-			{
-				ANKI_RESOURCE_LOGE("Miss shader should have set the ray type: %s", filename.cstr());
-				return Error::kUserData;
-			}
+				for(const ShaderProgramBinaryMutation& mutation : mutations)
+				{
+					const ShaderProgramBinaryVariant& variant = binary->m_variants[mutation.m_variantIndex];
+					const U32 codeBlockIndex = variant.m_techniqueCodeBlocks[techniqueIdx].m_codeBlockIndices[ShaderType::kRayGen];
+					const U32 shaderIdx = lib->addShader(binary->m_codeBlocks[codeBlockIndex], progName, ShaderType::kRayGen);
 
-			// Iterate all mutations
-			ConstWeakArray<ShaderProgramBinaryMutation> mutations;
-			ShaderProgramBinaryMutation dummyMutation;
-			if(binary.m_mutations.getSize() > 1)
-			{
-				mutations = binary.m_mutations;
-			}
-			else
-			{
-				dummyMutation.m_hash = 0;
-				dummyMutation.m_variantIndex = 0;
-				mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
+					lib->addGroup(filename, mutation.m_hash, shaderIdx, kMaxU32, kMaxU32, kMaxU32);
+				}
 			}
 
-			for(const ShaderProgramBinaryMutation& mutation : mutations)
+			// Miss
+			if(!!(technique.m_shaderTypes & ShaderTypeBit::kMiss))
 			{
-				const ShaderProgramBinaryVariant& variant = binary.m_variants[mutation.m_variantIndex];
-				const U32 codeBlockIndex = variant.m_codeBlockIndices[ShaderType::kMiss];
-				ANKI_ASSERT(codeBlockIndex != kMaxU32);
-				const U32 shaderIdx = lib->addShader(binary.m_codeBlocks[codeBlockIndex], progName, ShaderType::kMiss);
+				// Iterate all mutations
+				ConstWeakArray<ShaderProgramBinaryMutation> mutations;
+				ShaderProgramBinaryMutation dummyMutation;
+				if(binary->m_mutations.getSize() > 1)
+				{
+					mutations = binary->m_mutations;
+				}
+				else
+				{
+					dummyMutation.m_hash = 0;
+					dummyMutation.m_variantIndex = 0;
+					mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
+				}
 
-				lib->addGroup(filename, mutation.m_hash, kMaxU32, shaderIdx, kMaxU32, kMaxU32);
-			}
-		}
+				for(const ShaderProgramBinaryMutation& mutation : mutations)
+				{
+					const ShaderProgramBinaryVariant& variant = binary->m_variants[mutation.m_variantIndex];
+					const U32 codeBlockIndex = variant.m_techniqueCodeBlocks[techniqueIdx].m_codeBlockIndices[ShaderType::kMiss];
+					ANKI_ASSERT(codeBlockIndex < kMaxU32);
+					const U32 shaderIdx = lib->addShader(binary->m_codeBlocks[codeBlockIndex], progName, ShaderType::kMiss);
 
-		// Hit shaders
-		if(!!(binary.m_presentShaderTypes & (ShaderTypeBit::kAnyHit | ShaderTypeBit::kClosestHit)))
-		{
-			if(!!(binary.m_presentShaderTypes & ~(ShaderTypeBit::kAnyHit | ShaderTypeBit::kClosestHit)))
-			{
-				ANKI_RESOURCE_LOGE("Hit shaders can't co-exist with other types: %s", filename.cstr());
-				return Error::kUserData;
+					lib->addGroup(filename, mutation.m_hash, kMaxU32, shaderIdx, kMaxU32, kMaxU32);
+				}
 			}
 
-			if(rayTypeNumber == kMaxU32)
+			// Hit shaders
+			if(!!(technique.m_shaderTypes & ShaderTypeBit::kAllHit))
 			{
-				ANKI_RESOURCE_LOGE("Hit shaders should have set the ray type: %s", filename.cstr());
-				return Error::kUserData;
-			}
+				// Before you iterate the mutations do some work if there are none
+				ConstWeakArray<ShaderProgramBinaryMutation> mutations;
+				ShaderProgramBinaryMutation dummyMutation;
+				if(binary->m_mutations.getSize() > 1)
+				{
+					mutations = binary->m_mutations;
+				}
+				else
+				{
+					dummyMutation.m_hash = 0;
+					dummyMutation.m_variantIndex = 0;
+					mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
+				}
 
-			// Before you iterate the mutations do some work if there are none
-			ConstWeakArray<ShaderProgramBinaryMutation> mutations;
-			ShaderProgramBinaryMutation dummyMutation;
-			if(binary.m_mutations.getSize() > 1)
-			{
-				mutations = binary.m_mutations;
-			}
-			else
-			{
-				dummyMutation.m_hash = 0;
-				dummyMutation.m_variantIndex = 0;
-				mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
-			}
+				// Iterate all mutations
+				for(const ShaderProgramBinaryMutation& mutation : mutations)
+				{
+					if(mutation.m_variantIndex == kMaxU32)
+					{
+						continue;
+					}
 
-			// Iterate all mutations
-			for(const ShaderProgramBinaryMutation& mutation : mutations)
-			{
-				const ShaderProgramBinaryVariant& variant = binary.m_variants[mutation.m_variantIndex];
-				const U32 ahitCodeBlockIndex = variant.m_codeBlockIndices[ShaderType::kAnyHit];
-				const U32 chitCodeBlockIndex = variant.m_codeBlockIndices[ShaderType::kClosestHit];
-				ANKI_ASSERT(ahitCodeBlockIndex != kMaxU32 || chitCodeBlockIndex != kMaxU32);
+					const ShaderProgramBinaryVariant& variant = binary->m_variants[mutation.m_variantIndex];
+					const U32 ahitCodeBlockIndex = variant.m_techniqueCodeBlocks[techniqueIdx].m_codeBlockIndices[ShaderType::kAnyHit];
+					const U32 chitCodeBlockIndex = variant.m_techniqueCodeBlocks[techniqueIdx].m_codeBlockIndices[ShaderType::kClosestHit];
+					ANKI_ASSERT(ahitCodeBlockIndex != kMaxU32 || chitCodeBlockIndex != kMaxU32);
 
-				const U32 ahitShaderIdx = (ahitCodeBlockIndex != kMaxU32)
-											  ? lib->addShader(binary.m_codeBlocks[ahitCodeBlockIndex], progName, ShaderType::kAnyHit)
-											  : kMaxU32;
+					const U32 ahitShaderIdx = (ahitCodeBlockIndex != kMaxU32)
+												  ? lib->addShader(binary->m_codeBlocks[ahitCodeBlockIndex], progName, ShaderType::kAnyHit)
+												  : kMaxU32;
 
-				const U32 chitShaderIdx = (chitCodeBlockIndex != kMaxU32)
-											  ? lib->addShader(binary.m_codeBlocks[chitCodeBlockIndex], progName, ShaderType::kClosestHit)
-											  : kMaxU32;
+					const U32 chitShaderIdx = (chitCodeBlockIndex != kMaxU32)
+												  ? lib->addShader(binary->m_codeBlocks[chitCodeBlockIndex], progName, ShaderType::kClosestHit)
+												  : kMaxU32;
 
-				lib->addGroup(filename, mutation.m_hash, kMaxU32, kMaxU32, chitShaderIdx, ahitShaderIdx);
+					lib->addGroup(filename, mutation.m_hash, kMaxU32, kMaxU32, chitShaderIdx, ahitShaderIdx);
+				}
 			}
-		}
+		} // iterate techniques
 
 		return Error::kNone;
-	})); // For all RT filenames
+	}); // For all RT filenames
+	ANKI_CHECK(err);
 
-	// Create the libraries the value that goes to the m_resourceHashToShaderGroupHandleIndex hashmap is the index of
-	// the shader handle inside the program. Leverage the fact that there is a predefined order between shader types.
-	// See the ShaderProgram class for info.
+	// Create the libraries. The value that goes to the m_resourceHashToShaderGroupHandleIndex hashmap is the index of the shader handle inside the
+	// program. Leverage the fact that there is a predefined order between group types (ray gen first, miss and hit follows). See the ShaderProgram
+	// class for info.
 	if(libs.getSize() != 0)
 	{
 		outLibs.resize(libs.getSize());
@@ -364,72 +344,61 @@ Error ShaderProgramResourceSystem::createRayTracingPrograms(ResourceDynamicArray
 				return Error::kUserData;
 			}
 
-			if(inLib.m_rayTypeCount != inLib.m_rayTypeMask.getSetBitCount())
-			{
-				ANKI_RESOURCE_LOGE("Ray types are not contiguous for library: %s", inLib.m_name.cstr());
-				return Error::kUserData;
-			}
-
 			outLib.m_libraryName = inLib.m_name;
-			outLib.m_rayTypeCount = inLib.m_rayTypeCount;
 
 			ResourceDynamicArray<RayTracingHitGroup> initInfoHitGroups;
 			ResourceDynamicArray<Shader*> missShaders;
 			ResourceDynamicArray<Shader*> rayGenShaders;
+			U32 groupCount = 0;
 
-			// Add the hitgroups to the init info
-			for(U32 shaderGroupIdx = 0; shaderGroupIdx < inLib.m_shaderGroups.getSize(); ++shaderGroupIdx)
+			// Ray gen
+			for(const ShaderGroup& group : inLib.m_rayGenGroups)
 			{
-				const ShaderGroup& inShaderGroup = inLib.m_shaderGroups[shaderGroupIdx];
-				ANKI_ASSERT(inShaderGroup.m_hitGroupHash != 0);
+				rayGenShaders.emplaceBack(inLib.m_shaders[group.m_rayGen].m_shader.get());
 
-				if(inShaderGroup.m_ahit < kMaxU32 || inShaderGroup.m_chit < kMaxU32)
+				for(U64 hash : group.m_groupHashes)
 				{
-					// Hit shaders
-
-					ANKI_ASSERT(inShaderGroup.m_miss == kMaxU32 && inShaderGroup.m_rayGen == kMaxU32);
-					RayTracingHitGroup* infoHitGroup = initInfoHitGroups.emplaceBack();
+					outLib.m_resourceHashToShaderGroupHandleIndex.emplace(hash, groupCount);
+				}
 
-					if(inShaderGroup.m_ahit < kMaxU32)
-					{
-						infoHitGroup->m_anyHitShader = inLib.m_shaders[inShaderGroup.m_ahit].m_shader.get();
-					}
+				++groupCount;
+			}
 
-					if(inShaderGroup.m_chit < kMaxU32)
-					{
-						infoHitGroup->m_closestHitShader = inLib.m_shaders[inShaderGroup.m_chit].m_shader.get();
-					}
+			// Miss
+			for(const ShaderGroup& group : inLib.m_missGroups)
+			{
+				missShaders.emplaceBack(inLib.m_shaders[group.m_miss].m_shader.get());
 
-					// The hit shaders are after ray gen and miss shaders
-					const U32 idx = inLib.m_rayGenShaderGroupCount + inLib.m_missShaderGroupCount + initInfoHitGroups.getSize() - 1;
-					outLib.m_resourceHashToShaderGroupHandleIndex.emplace(inShaderGroup.m_hitGroupHash, idx);
-				}
-				else if(inShaderGroup.m_miss < kMaxU32)
+				for(U64 hash : group.m_groupHashes)
 				{
-					// Miss shader
+					outLib.m_resourceHashToShaderGroupHandleIndex.emplace(hash, groupCount);
+				}
 
-					ANKI_ASSERT(inShaderGroup.m_ahit == kMaxU32 && inShaderGroup.m_chit == kMaxU32 && inShaderGroup.m_rayGen == kMaxU32);
+				++groupCount;
+			}
 
-					missShaders.emplaceBack(inLib.m_shaders[inShaderGroup.m_miss].m_shader.get());
+			// Hit
+			for(const ShaderGroup& group : inLib.m_hitGroups)
+			{
+				RayTracingHitGroup* infoHitGroup = initInfoHitGroups.emplaceBack();
 
-					// The miss shaders are after ray gen
-					const U32 idx = inLib.m_rayGenShaderGroupCount + missShaders.getSize() - 1;
-					outLib.m_resourceHashToShaderGroupHandleIndex.emplace(inShaderGroup.m_hitGroupHash, idx);
-				}
-				else
+				if(group.m_ahit < kMaxU32)
 				{
-					// Ray gen shader
-
-					ANKI_ASSERT(inShaderGroup.m_ahit == kMaxU32 && inShaderGroup.m_chit == kMaxU32 && inShaderGroup.m_miss == kMaxU32
-								&& inShaderGroup.m_rayGen < kMaxU32);
+					infoHitGroup->m_anyHitShader = inLib.m_shaders[group.m_ahit].m_shader.get();
+				}
 
-					rayGenShaders.emplaceBack(inLib.m_shaders[inShaderGroup.m_rayGen].m_shader.get());
+				if(group.m_chit < kMaxU32)
+				{
+					infoHitGroup->m_closestHitShader = inLib.m_shaders[group.m_chit].m_shader.get();
+				}
 
-					// Ray gen shaders are first
-					const U32 idx = rayGenShaders.getSize() - 1;
-					outLib.m_resourceHashToShaderGroupHandleIndex.emplace(inShaderGroup.m_hitGroupHash, idx);
+				for(U64 hash : group.m_groupHashes)
+				{
+					outLib.m_resourceHashToShaderGroupHandleIndex.emplace(hash, groupCount);
 				}
-			} // end for all groups
+
+				++groupCount;
+			}
 
 			// Create the program
 			ShaderProgramInitInfo inf(inLib.m_name);

+ 19 - 7
AnKi/Resource/ShaderProgramResourceSystem.h

@@ -16,6 +16,16 @@ namespace anki {
 /// @addtogroup resource
 /// @{
 
+enum class RayTracingShaderGroupType : U8
+{
+	kRayGen,
+	kMiss,
+	kHit,
+
+	kCount,
+	kFirst = 0
+};
+
 /// This is a ray tracing library. Essentially a shader program with some functionality on how to get group indices.
 class ShaderProgramRaytracingLibrary
 {
@@ -38,11 +48,10 @@ public:
 		return m_program;
 	}
 
-	/// Given the filename of a program (that contains ray tracing shaders) and a specific mutation get the shader
-	/// handle index.
-	U32 getShaderGroupHandleIndex(CString resourceFilename, U64 mutationHash) const
+	/// Given the filename of a program (that contains ray tracing shaders) and a specific mutation get the shader handle index.
+	U32 getShaderGroupHandleIndex(CString resourceFilename, U64 mutationHash, RayTracingShaderGroupType groupType) const
 	{
-		return getIndex(generateShaderGroupGroupHash(resourceFilename, mutationHash));
+		return getIndex(generateShaderGroupGroupHash(resourceFilename, mutationHash, groupType));
 	}
 
 private:
@@ -52,10 +61,9 @@ private:
 	ResourceHashMap<U64, U32> m_resourceHashToShaderGroupHandleIndex;
 
 	/// Given the filename of a program (that contains ray tracing shaders) and a specific mutation get a hash back.
-	static U64 generateShaderGroupGroupHash(CString resourceFilename, U64 mutationHash);
+	static U64 generateShaderGroupGroupHash(CString resourceFilename, U64 mutationHash, RayTracingShaderGroupType groupType);
 
-	/// The hash generated by generateShaderGroupGroupHash() can be used to retrieve the group position in the
-	/// m_program.
+	/// The hash generated by generateShaderGroupGroupHash() can be used to retrieve the group position in the m_program.
 	U32 getIndex(U64 groupHash) const
 	{
 		auto it = m_resourceHashToShaderGroupHandleIndex.find(groupHash);
@@ -82,6 +90,10 @@ public:
 	}
 
 private:
+	class ShaderH;
+	class ShaderGroup;
+	class Lib;
+
 	ResourceDynamicArray<ShaderProgramRaytracingLibrary> m_rtLibraries;
 
 	static Error createRayTracingPrograms(ResourceDynamicArray<ShaderProgramRaytracingLibrary>& outLibs);

+ 4 - 2
AnKi/Scene/Components/ModelComponent.cpp

@@ -282,8 +282,10 @@ Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 				ModelPatchGeometryInfo inf;
 				m_model->getModelPatches()[i].getGeometryInfo(0, inf);
-				m_patchInfos[i].m_renderStateBucketIndices[t] = RenderStateBucketContainer::getSingleton().addUser(
-					state, t, (GrManager::getSingleton().getDeviceCapabilities().m_meshShaders) ? inf.m_meshletCount : 0);
+				const Bool wantsMesletCount = GrManager::getSingleton().getDeviceCapabilities().m_meshShaders
+											  && !(RenderingTechniqueBit(1 << t) & RenderingTechniqueBit::kAllRt);
+				m_patchInfos[i].m_renderStateBucketIndices[t] =
+					RenderStateBucketContainer::getSingleton().addUser(state, t, (wantsMesletCount) ? inf.m_meshletCount : 0);
 			}
 		}
 	}

+ 17 - 1
AnKi/ShaderCompiler/Common.h

@@ -21,6 +21,22 @@ namespace anki {
 #define ANKI_SHADER_COMPILER_LOGF(...) ANKI_LOG("SHCO", kFatal, __VA_ARGS__)
 #define ANKI_SHADER_COMPILER_LOGV(...) ANKI_LOG("SHCO", kVerbose, __VA_ARGS__)
 
+class ShaderCompilerMemoryPool : public HeapMemoryPool, public MakeSingleton<ShaderCompilerMemoryPool>
+{
+	template<typename>
+	friend class MakeSingleton;
+
+private:
+	ShaderCompilerMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData)
+		: HeapMemoryPool(allocCb, allocCbUserData, "ShaderCompilerMemPool")
+	{
+	}
+
+	~ShaderCompilerMemoryPool() = default;
+};
+
+ANKI_DEFINE_SUBMODULE_UTIL_CONTAINERS(ShaderCompiler, ShaderCompilerMemoryPool)
+
 constexpr U32 kMaxShaderBinaryNameLength = 127;
 
 using MutatorValue = I32; ///< The type of the mutator value
@@ -29,7 +45,7 @@ using MutatorValue = I32; ///< The type of the mutator value
 class ShaderProgramFilesystemInterface
 {
 public:
-	virtual Error readAllText(CString filename, String& txt) = 0;
+	virtual Error readAllText(CString filename, ShaderCompilerString& txt) = 0;
 };
 
 /// This controls if the compilation will continue after the parsing stage.

+ 11 - 7
AnKi/ShaderCompiler/Dxc.cpp

@@ -56,7 +56,8 @@ static CString profile(ShaderType shaderType)
 	return "";
 }
 
-Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16bitTypes, DynamicArray<U8>& spirv, String& errorMessage)
+Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16bitTypes, ShaderCompilerDynamicArray<U8>& spirv,
+						 ShaderCompilerString& errorMessage)
 {
 	Array<U64, 3> toHash = {g_nextFileId.fetchAdd(1), getCurrentProcessId(), getRandom() & kMaxU32};
 	const U64 rand = computeHash(&toHash[0], sizeof(toHash));
@@ -65,7 +66,7 @@ Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16b
 	ANKI_CHECK(getTempDirectory(tmpDir));
 
 	// Store HLSL to a file
-	String hlslFilename;
+	ShaderCompilerString hlslFilename;
 	hlslFilename.sprintf("%s/%" PRIu64 ".hlsl", tmpDir.cstr(), rand);
 
 	File hlslFile;
@@ -75,10 +76,10 @@ Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16b
 	hlslFile.close();
 
 	// Call DXC
-	String spvFilename;
+	ShaderCompilerString spvFilename;
 	spvFilename.sprintf("%s/%" PRIu64 ".spv", tmpDir.cstr(), rand);
 
-	DynamicArray<String> dxcArgs;
+	ShaderCompilerDynamicArray<ShaderCompilerString> dxcArgs;
 	dxcArgs.emplaceBack("-Fo");
 	dxcArgs.emplaceBack(spvFilename);
 	dxcArgs.emplaceBack("-Wall");
@@ -105,7 +106,7 @@ Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16b
 		dxcArgs.emplaceBack("-enable-16bit-types");
 	}
 
-	DynamicArray<CString> dxcArgs2;
+	ShaderCompilerDynamicArray<CString> dxcArgs2;
 	dxcArgs2.resize(dxcArgs.getSize());
 	for(U32 i = 0; i < dxcArgs.getSize(); ++i)
 	{
@@ -115,7 +116,7 @@ Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16b
 	while(true)
 	{
 		I32 exitCode;
-		String stdOut;
+		ShaderCompilerString stdOut;
 
 #if ANKI_OS_WINDOWS
 		CString dxcBin = ANKI_SOURCE_DIRECTORY "/ThirdParty/Bin/Windows64/dxc.exe";
@@ -131,7 +132,10 @@ Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16b
 		if(exitCode != 0)
 		{
 			// There was an error, run again just to get the stderr
-			ANKI_CHECK(Process::callProcess(dxcBin, dxcArgs2, nullptr, &errorMessage, exitCode));
+			String errorMessageTmp;
+			const Error err = Process::callProcess(dxcBin, dxcArgs2, nullptr, &errorMessageTmp, exitCode);
+			(void)err; // Shoudn't throw an error
+			errorMessage = errorMessageTmp;
 
 			if(!errorMessage.isEmpty() && errorMessage.find("The process cannot access the file because") != CString::kNpos)
 			{

+ 2 - 1
AnKi/ShaderCompiler/Dxc.h

@@ -14,7 +14,8 @@ namespace anki {
 /// @{
 
 /// Compile HLSL to SPIR-V.
-Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16bitTypes, DynamicArray<U8>& spirv, String& errorMessage);
+Error compileHlslToSpirv(CString src, ShaderType shaderType, Bool compileWith16bitTypes, ShaderCompilerDynamicArray<U8>& spirv,
+						 ShaderCompilerString& errorMessage);
 /// @}
 
 } // end namespace anki

+ 8 - 8
AnKi/ShaderCompiler/RadeonGpuAnalyzer.cpp

@@ -42,7 +42,7 @@ Error runRadeonGpuAnalyzer(CString rgaExecutable, ConstWeakArray<U8> spirv, Shad
 	// Store SPIRV
 	String tmpDir;
 	ANKI_CHECK(getTempDirectory(tmpDir));
-	String spvFilename;
+	ShaderCompilerString spvFilename;
 	spvFilename.sprintf("%s/AnKiRgaInput_%u.spv", tmpDir.cstr(), rand);
 	File spvFile;
 	ANKI_CHECK(spvFile.open(spvFilename, FileOpenFlag::kWrite | FileOpenFlag::kBinary));
@@ -51,7 +51,7 @@ Error runRadeonGpuAnalyzer(CString rgaExecutable, ConstWeakArray<U8> spirv, Shad
 	CleanupFile spvFileCleanup(spvFilename);
 
 	// Call RGA
-	String analysisFilename;
+	ShaderCompilerString analysisFilename;
 	analysisFilename.sprintf("%s/AnKiRgaOutAnalysis_%u.csv", tmpDir.cstr(), rand);
 
 	Array<CString, 7> args;
@@ -73,7 +73,7 @@ Error runRadeonGpuAnalyzer(CString rgaExecutable, ConstWeakArray<U8> spirv, Shad
 	}
 
 	// Construct the output filename
-	String outFilename;
+	ShaderCompilerString outFilename;
 	outFilename.sprintf("%s/gfx1030_AnKiRgaOutAnalysis_%u_%s.csv", tmpDir.cstr(), rand, getPipelineStageString(shaderType).cstr());
 
 	CleanupFile rgaFileCleanup(outFilename);
@@ -81,13 +81,13 @@ Error runRadeonGpuAnalyzer(CString rgaExecutable, ConstWeakArray<U8> spirv, Shad
 	// Read the file
 	File analysisFile;
 	ANKI_CHECK(analysisFile.open(outFilename, FileOpenFlag::kRead));
-	String analysisText;
+	ShaderCompilerString analysisText;
 	ANKI_CHECK(analysisFile.readAllText(analysisText));
 	analysisText.replaceAll("\r", "");
 	analysisFile.close();
 
 	// Parse the text
-	StringList lines;
+	ShaderCompilerStringList lines;
 	lines.splitString(analysisText, '\n');
 	if(lines.getSize() != 2)
 	{
@@ -95,10 +95,10 @@ Error runRadeonGpuAnalyzer(CString rgaExecutable, ConstWeakArray<U8> spirv, Shad
 		return Error::kFunctionFailed;
 	}
 
-	StringList tokens;
+	ShaderCompilerStringList tokens;
 	tokens.splitString(lines.getFront(), ',');
 
-	StringList values;
+	ShaderCompilerStringList values;
 	values.splitString(*(lines.getBegin() + 1), ',');
 
 	if(tokens.getSize() != tokens.getSize())
@@ -111,7 +111,7 @@ Error runRadeonGpuAnalyzer(CString rgaExecutable, ConstWeakArray<U8> spirv, Shad
 	out.m_sgprCount = kMaxU32;
 	out.m_isaSize = kMaxU32;
 	auto valuesIt = values.getBegin();
-	for(const String& token : tokens)
+	for(const ShaderCompilerString& token : tokens)
 	{
 		if(token.find("USED_VGPRs") != String::kNpos)
 		{

+ 65 - 355
AnKi/ShaderCompiler/ShaderProgramBinary.h

@@ -9,268 +9,24 @@
 
 #include <AnKi/ShaderCompiler/Common.h>
 #include <AnKi/ShaderCompiler/ShaderProgramBinaryExtra.h>
+#include <AnKi/Gr/Common.h>
 
 namespace anki {
 
-/// Storage or uniform variable.
-class ShaderProgramBinaryVariable
-{
-public:
-	Array<char, kMaxShaderBinaryNameLength + 1> m_name = {};
-	ShaderVariableDataType m_type = ShaderVariableDataType::kNone;
-
-	template<typename TSerializer, typename TClass>
-	static void serializeCommon(TSerializer& s, TClass self)
-	{
-		s.doArray("m_name", offsetof(ShaderProgramBinaryVariable, m_name), &self.m_name[0], self.m_name.getSize());
-		s.doValue("m_type", offsetof(ShaderProgramBinaryVariable, m_type), self.m_type);
-	}
-
-	template<typename TDeserializer>
-	void deserialize(TDeserializer& deserializer)
-	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryVariable&>(deserializer, *this);
-	}
-
-	template<typename TSerializer>
-	void serialize(TSerializer& serializer) const
-	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryVariable&>(serializer, *this);
-	}
-};
-
-/// Storage or uniform variable per variant.
-class ShaderProgramBinaryVariableInstance
-{
-public:
-	/// Points to ShaderProgramBinaryBlock::m_variables.
-	U32 m_index = kMaxU32;
-
-	ShaderVariableBlockInfo m_blockInfo;
-
-	template<typename TSerializer, typename TClass>
-	static void serializeCommon(TSerializer& s, TClass self)
-	{
-		s.doValue("m_index", offsetof(ShaderProgramBinaryVariableInstance, m_index), self.m_index);
-		s.doValue("m_blockInfo", offsetof(ShaderProgramBinaryVariableInstance, m_blockInfo), self.m_blockInfo);
-	}
-
-	template<typename TDeserializer>
-	void deserialize(TDeserializer& deserializer)
-	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryVariableInstance&>(deserializer, *this);
-	}
-
-	template<typename TSerializer>
-	void serialize(TSerializer& serializer) const
-	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryVariableInstance&>(serializer, *this);
-	}
-};
-
-/// Storage or uniform block.
-class ShaderProgramBinaryBlock
-{
-public:
-	Array<char, kMaxShaderBinaryNameLength + 1> m_name = {};
-	WeakArray<ShaderProgramBinaryVariable> m_variables;
-	U32 m_binding = kMaxU32;
-	U32 m_set = kMaxU32;
-
-	template<typename TSerializer, typename TClass>
-	static void serializeCommon(TSerializer& s, TClass self)
-	{
-		s.doArray("m_name", offsetof(ShaderProgramBinaryBlock, m_name), &self.m_name[0], self.m_name.getSize());
-		s.doValue("m_variables", offsetof(ShaderProgramBinaryBlock, m_variables), self.m_variables);
-		s.doValue("m_binding", offsetof(ShaderProgramBinaryBlock, m_binding), self.m_binding);
-		s.doValue("m_set", offsetof(ShaderProgramBinaryBlock, m_set), self.m_set);
-	}
-
-	template<typename TDeserializer>
-	void deserialize(TDeserializer& deserializer)
-	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryBlock&>(deserializer, *this);
-	}
-
-	template<typename TSerializer>
-	void serialize(TSerializer& serializer) const
-	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryBlock&>(serializer, *this);
-	}
-};
-
-/// Storage or uniform block per variant.
-class ShaderProgramBinaryBlockInstance
-{
-public:
-	/// Points to ShaderProgramBinary::m_uniformBlocks or m_storageBlocks.
-	U32 m_index = kMaxU32;
-
-	WeakArray<ShaderProgramBinaryVariableInstance> m_variableInstances;
-	U32 m_size = kMaxU32;
-
-	template<typename TSerializer, typename TClass>
-	static void serializeCommon(TSerializer& s, TClass self)
-	{
-		s.doValue("m_index", offsetof(ShaderProgramBinaryBlockInstance, m_index), self.m_index);
-		s.doValue("m_variableInstances", offsetof(ShaderProgramBinaryBlockInstance, m_variableInstances), self.m_variableInstances);
-		s.doValue("m_size", offsetof(ShaderProgramBinaryBlockInstance, m_size), self.m_size);
-	}
-
-	template<typename TDeserializer>
-	void deserialize(TDeserializer& deserializer)
-	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryBlockInstance&>(deserializer, *this);
-	}
-
-	template<typename TSerializer>
-	void serialize(TSerializer& serializer) const
-	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryBlockInstance&>(serializer, *this);
-	}
-};
-
-/// Sampler or texture or image.
-class ShaderProgramBinaryOpaque
-{
-public:
-	Array<char, kMaxShaderBinaryNameLength + 1> m_name = {};
-	ShaderVariableDataType m_type = ShaderVariableDataType::kNone;
-	U32 m_binding = kMaxU32;
-	U32 m_set = kMaxU32;
-
-	template<typename TSerializer, typename TClass>
-	static void serializeCommon(TSerializer& s, TClass self)
-	{
-		s.doArray("m_name", offsetof(ShaderProgramBinaryOpaque, m_name), &self.m_name[0], self.m_name.getSize());
-		s.doValue("m_type", offsetof(ShaderProgramBinaryOpaque, m_type), self.m_type);
-		s.doValue("m_binding", offsetof(ShaderProgramBinaryOpaque, m_binding), self.m_binding);
-		s.doValue("m_set", offsetof(ShaderProgramBinaryOpaque, m_set), self.m_set);
-	}
-
-	template<typename TDeserializer>
-	void deserialize(TDeserializer& deserializer)
-	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryOpaque&>(deserializer, *this);
-	}
-
-	template<typename TSerializer>
-	void serialize(TSerializer& serializer) const
-	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryOpaque&>(serializer, *this);
-	}
-};
-
-/// Sampler or texture or image per variant.
-class ShaderProgramBinaryOpaqueInstance
-{
-public:
-	/// Points to ShaderProgramBinary::m_opaques.
-	U32 m_index = kMaxU32;
-
-	U32 m_arraySize = kMaxU32;
-
-	template<typename TSerializer, typename TClass>
-	static void serializeCommon(TSerializer& s, TClass self)
-	{
-		s.doValue("m_index", offsetof(ShaderProgramBinaryOpaqueInstance, m_index), self.m_index);
-		s.doValue("m_arraySize", offsetof(ShaderProgramBinaryOpaqueInstance, m_arraySize), self.m_arraySize);
-	}
-
-	template<typename TDeserializer>
-	void deserialize(TDeserializer& deserializer)
-	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryOpaqueInstance&>(deserializer, *this);
-	}
-
-	template<typename TSerializer>
-	void serialize(TSerializer& serializer) const
-	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryOpaqueInstance&>(serializer, *this);
-	}
-};
-
-/// Specialization constant.
-class ShaderProgramBinaryConstant
-{
-public:
-	Array<char, kMaxShaderBinaryNameLength + 1> m_name;
-	ShaderVariableDataType m_type = ShaderVariableDataType::kNone;
-	U32 m_constantId = kMaxU32;
-
-	template<typename TSerializer, typename TClass>
-	static void serializeCommon(TSerializer& s, TClass self)
-	{
-		s.doArray("m_name", offsetof(ShaderProgramBinaryConstant, m_name), &self.m_name[0], self.m_name.getSize());
-		s.doValue("m_type", offsetof(ShaderProgramBinaryConstant, m_type), self.m_type);
-		s.doValue("m_constantId", offsetof(ShaderProgramBinaryConstant, m_constantId), self.m_constantId);
-	}
-
-	template<typename TDeserializer>
-	void deserialize(TDeserializer& deserializer)
-	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryConstant&>(deserializer, *this);
-	}
-
-	template<typename TSerializer>
-	void serialize(TSerializer& serializer) const
-	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryConstant&>(serializer, *this);
-	}
-};
-
-/// Specialization constant per variant.
-class ShaderProgramBinaryConstantInstance
-{
-public:
-	/// Points to ShaderProgramBinary::m_constants.
-	U32 m_index = kMaxU32;
-
-	template<typename TSerializer, typename TClass>
-	static void serializeCommon(TSerializer& s, TClass self)
-	{
-		s.doValue("m_index", offsetof(ShaderProgramBinaryConstantInstance, m_index), self.m_index);
-	}
-
-	template<typename TDeserializer>
-	void deserialize(TDeserializer& deserializer)
-	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryConstantInstance&>(deserializer, *this);
-	}
-
-	template<typename TSerializer>
-	void serialize(TSerializer& serializer) const
-	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryConstantInstance&>(serializer, *this);
-	}
-};
-
 /// A member of a ShaderProgramBinaryStruct.
 class ShaderProgramBinaryStructMember
 {
 public:
-	Array<char, kMaxShaderBinaryNameLength + 1> m_name = {};
-
-	/// If the value is ShaderVariableDataType::kNone then it's a struct.
+	Array<Char, kMaxShaderBinaryNameLength + 1> m_name = {};
+	U32 m_offset = kMaxU32;
 	ShaderVariableDataType m_type = ShaderVariableDataType::kNone;
 
-	/// If the type is another struct then this points to ShaderProgramBinary::m_structs.
-	U32 m_structIndex = kMaxU32;
-
-	/// It points to a ShaderProgramBinary::m_mutators. This mutator will turn on or off this member.
-	U32 m_dependentMutator = kMaxU32;
-
-	/// The value of the m_dependentMutator.
-	MutatorValue m_dependentMutatorValue = 0;
-
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
 		s.doArray("m_name", offsetof(ShaderProgramBinaryStructMember, m_name), &self.m_name[0], self.m_name.getSize());
+		s.doValue("m_offset", offsetof(ShaderProgramBinaryStructMember, m_offset), self.m_offset);
 		s.doValue("m_type", offsetof(ShaderProgramBinaryStructMember, m_type), self.m_type);
-		s.doValue("m_structIndex", offsetof(ShaderProgramBinaryStructMember, m_structIndex), self.m_structIndex);
-		s.doValue("m_dependentMutator", offsetof(ShaderProgramBinaryStructMember, m_dependentMutator), self.m_dependentMutator);
-		s.doValue("m_dependentMutatorValue", offsetof(ShaderProgramBinaryStructMember, m_dependentMutatorValue), self.m_dependentMutatorValue);
 	}
 
 	template<typename TDeserializer>
@@ -286,196 +42,166 @@ public:
 	}
 };
 
-/// Struct member per variant.
-class ShaderProgramBinaryStructMemberInstance
+/// A type that is a structure.
+class ShaderProgramBinaryStruct
 {
 public:
-	/// Points to ShaderProgramBinary::m_structs.
-	U32 m_index = kMaxU32;
-
-	/// The offset of the member in the struct.
-	U32 m_offset = kMaxU32;
-
-	U32 m_arraySize = kMaxU32;
+	Array<Char, kMaxShaderBinaryNameLength + 1> m_name = {};
+	WeakArray<ShaderProgramBinaryStructMember> m_members;
+	U32 m_size;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doValue("m_index", offsetof(ShaderProgramBinaryStructMemberInstance, m_index), self.m_index);
-		s.doValue("m_offset", offsetof(ShaderProgramBinaryStructMemberInstance, m_offset), self.m_offset);
-		s.doValue("m_arraySize", offsetof(ShaderProgramBinaryStructMemberInstance, m_arraySize), self.m_arraySize);
+		s.doArray("m_name", offsetof(ShaderProgramBinaryStruct, m_name), &self.m_name[0], self.m_name.getSize());
+		s.doValue("m_members", offsetof(ShaderProgramBinaryStruct, m_members), self.m_members);
+		s.doValue("m_size", offsetof(ShaderProgramBinaryStruct, m_size), self.m_size);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryStructMemberInstance&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryStruct&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryStructMemberInstance&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryStruct&>(serializer, *this);
 	}
 };
 
-/// A type that is a structure.
-class ShaderProgramBinaryStruct
+/// Contains the IR (SPIR-V).
+class ShaderProgramBinaryCodeBlock
 {
 public:
-	Array<char, kMaxShaderBinaryNameLength + 1> m_name;
-	WeakArray<ShaderProgramBinaryStructMember> m_members;
+	WeakArray<U8> m_binary;
+	U64 m_hash = 0;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doArray("m_name", offsetof(ShaderProgramBinaryStruct, m_name), &self.m_name[0], self.m_name.getSize());
-		s.doValue("m_members", offsetof(ShaderProgramBinaryStruct, m_members), self.m_members);
+		s.doValue("m_binary", offsetof(ShaderProgramBinaryCodeBlock, m_binary), self.m_binary);
+		s.doValue("m_hash", offsetof(ShaderProgramBinaryCodeBlock, m_hash), self.m_hash);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryStruct&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryCodeBlock&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryStruct&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryCodeBlock&>(serializer, *this);
 	}
 };
 
-/// Structure type per variant.
-class ShaderProgramBinaryStructInstance
+/// ShaderProgramBinaryTechnique class.
+class ShaderProgramBinaryTechnique
 {
 public:
-	/// Points to ShaderProgramBinary::m_structs.
-	U32 m_index;
-
-	WeakArray<ShaderProgramBinaryStructMemberInstance> m_memberInstances;
-	U32 m_size = kMaxU32;
+	Array<Char, kMaxShaderBinaryNameLength + 1> m_name;
+	ShaderTypeBit m_shaderTypes = ShaderTypeBit::kNone;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doValue("m_index", offsetof(ShaderProgramBinaryStructInstance, m_index), self.m_index);
-		s.doValue("m_memberInstances", offsetof(ShaderProgramBinaryStructInstance, m_memberInstances), self.m_memberInstances);
-		s.doValue("m_size", offsetof(ShaderProgramBinaryStructInstance, m_size), self.m_size);
+		s.doArray("m_name", offsetof(ShaderProgramBinaryTechnique, m_name), &self.m_name[0], self.m_name.getSize());
+		s.doValue("m_shaderTypes", offsetof(ShaderProgramBinaryTechnique, m_shaderTypes), self.m_shaderTypes);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryStructInstance&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryTechnique&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryStructInstance&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryTechnique&>(serializer, *this);
 	}
 };
 
-/// ShaderProgramBinaryVariant class.
-class ShaderProgramBinaryVariant
+/// Shader program mutator.
+class ShaderProgramBinaryMutator
 {
 public:
-	/// Index in ShaderProgramBinary::m_codeBlocks. kMaxU32 means no shader.
-	Array<U32, U32(ShaderType::kCount)> m_codeBlockIndices = {};
-
-	WeakArray<ShaderProgramBinaryBlockInstance> m_uniformBlocks;
-	WeakArray<ShaderProgramBinaryBlockInstance> m_storageBlocks;
-	ShaderProgramBinaryBlockInstance* m_pushConstantBlock = nullptr;
-	WeakArray<ShaderProgramBinaryOpaqueInstance> m_opaques;
-	WeakArray<ShaderProgramBinaryConstantInstance> m_constants;
-	WeakArray<ShaderProgramBinaryStructInstance> m_structs;
-	Array<U32, 3> m_workgroupSizes = {kMaxU32, kMaxU32, kMaxU32};
-
-	/// Indices to ShaderProgramBinary::m_constants.
-	Array<U32, 3> m_workgroupSizesConstants = {kMaxU32, kMaxU32, kMaxU32};
+	Array<Char, kMaxShaderBinaryNameLength + 1> m_name = {};
+	WeakArray<MutatorValue> m_values;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doArray("m_codeBlockIndices", offsetof(ShaderProgramBinaryVariant, m_codeBlockIndices), &self.m_codeBlockIndices[0],
-				  self.m_codeBlockIndices.getSize());
-		s.doValue("m_uniformBlocks", offsetof(ShaderProgramBinaryVariant, m_uniformBlocks), self.m_uniformBlocks);
-		s.doValue("m_storageBlocks", offsetof(ShaderProgramBinaryVariant, m_storageBlocks), self.m_storageBlocks);
-		s.doPointer("m_pushConstantBlock", offsetof(ShaderProgramBinaryVariant, m_pushConstantBlock), self.m_pushConstantBlock);
-		s.doValue("m_opaques", offsetof(ShaderProgramBinaryVariant, m_opaques), self.m_opaques);
-		s.doValue("m_constants", offsetof(ShaderProgramBinaryVariant, m_constants), self.m_constants);
-		s.doValue("m_structs", offsetof(ShaderProgramBinaryVariant, m_structs), self.m_structs);
-		s.doArray("m_workgroupSizes", offsetof(ShaderProgramBinaryVariant, m_workgroupSizes), &self.m_workgroupSizes[0],
-				  self.m_workgroupSizes.getSize());
-		s.doArray("m_workgroupSizesConstants", offsetof(ShaderProgramBinaryVariant, m_workgroupSizesConstants), &self.m_workgroupSizesConstants[0],
-				  self.m_workgroupSizesConstants.getSize());
+		s.doArray("m_name", offsetof(ShaderProgramBinaryMutator, m_name), &self.m_name[0], self.m_name.getSize());
+		s.doValue("m_values", offsetof(ShaderProgramBinaryMutator, m_values), self.m_values);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryVariant&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryMutator&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryVariant&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryMutator&>(serializer, *this);
 	}
 };
 
-/// Shader program mutator.
-class ShaderProgramBinaryMutator
+/// ShaderProgramBinaryTechniqueCodeBlocks class.
+class ShaderProgramBinaryTechniqueCodeBlocks
 {
 public:
-	Array<char, kMaxShaderBinaryNameLength + 1> m_name = {};
-	WeakArray<MutatorValue> m_values;
+	/// Points to ShaderProgramBinary::m_codeBlocks. If the shader type is not present the value is kMaxU32.
+	Array<U32, U32(ShaderType::kCount)> m_codeBlockIndices = {};
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doArray("m_name", offsetof(ShaderProgramBinaryMutator, m_name), &self.m_name[0], self.m_name.getSize());
-		s.doValue("m_values", offsetof(ShaderProgramBinaryMutator, m_values), self.m_values);
+		s.doArray("m_codeBlockIndices", offsetof(ShaderProgramBinaryTechniqueCodeBlocks, m_codeBlockIndices), &self.m_codeBlockIndices[0],
+				  self.m_codeBlockIndices.getSize());
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryMutator&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryTechniqueCodeBlocks&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryMutator&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryTechniqueCodeBlocks&>(serializer, *this);
 	}
 };
 
-/// Contains the IR (SPIR-V).
-class ShaderProgramBinaryCodeBlock
+/// ShaderProgramBinaryVariant class.
+class ShaderProgramBinaryVariant
 {
 public:
-	WeakArray<U8> m_binary;
-	U64 m_hash = 0;
+	/// One entry per technique.
+	WeakArray<ShaderProgramBinaryTechniqueCodeBlocks> m_techniqueCodeBlocks;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doValue("m_binary", offsetof(ShaderProgramBinaryCodeBlock, m_binary), self.m_binary);
-		s.doValue("m_hash", offsetof(ShaderProgramBinaryCodeBlock, m_hash), self.m_hash);
+		s.doValue("m_techniqueCodeBlocks", offsetof(ShaderProgramBinaryVariant, m_techniqueCodeBlocks), self.m_techniqueCodeBlocks);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryCodeBlock&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryVariant&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryCodeBlock&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryVariant&>(serializer, *this);
 	}
 };
 
@@ -485,18 +211,18 @@ class ShaderProgramBinaryMutation
 public:
 	WeakArray<MutatorValue> m_values;
 
-	/// Points to ShaderProgramBinary::m_variants.
-	U32 m_variantIndex = kMaxU32;
-
 	/// Mutation hash.
 	U64 m_hash = 0;
 
+	/// Points to ShaderProgramBinary::m_variants.
+	U32 m_variantIndex = kMaxU32;
+
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
 		s.doValue("m_values", offsetof(ShaderProgramBinaryMutation, m_values), self.m_values);
-		s.doValue("m_variantIndex", offsetof(ShaderProgramBinaryMutation, m_variantIndex), self.m_variantIndex);
 		s.doValue("m_hash", offsetof(ShaderProgramBinaryMutation, m_hash), self.m_hash);
+		s.doValue("m_variantIndex", offsetof(ShaderProgramBinaryMutation, m_variantIndex), self.m_variantIndex);
 	}
 
 	template<typename TDeserializer>
@@ -517,44 +243,28 @@ class ShaderProgramBinary
 {
 public:
 	Array<U8, 8> m_magic = {};
-	WeakArray<ShaderProgramBinaryMutator> m_mutators;
+	ShaderTypeBit m_shaderTypes = ShaderTypeBit::kNone;
 	WeakArray<ShaderProgramBinaryCodeBlock> m_codeBlocks;
-	WeakArray<ShaderProgramBinaryVariant> m_variants;
+	WeakArray<ShaderProgramBinaryMutator> m_mutators;
 
 	/// It's sorted using the mutation's hash.
 	WeakArray<ShaderProgramBinaryMutation> m_mutations;
 
-	WeakArray<ShaderProgramBinaryBlock> m_uniformBlocks;
-	WeakArray<ShaderProgramBinaryBlock> m_storageBlocks;
-	ShaderProgramBinaryBlock* m_pushConstantBlock = nullptr;
-	WeakArray<ShaderProgramBinaryOpaque> m_opaques;
-	WeakArray<ShaderProgramBinaryConstant> m_constants;
+	WeakArray<ShaderProgramBinaryVariant> m_variants;
+	WeakArray<ShaderProgramBinaryTechnique> m_techniques;
 	WeakArray<ShaderProgramBinaryStruct> m_structs;
-	ShaderTypeBit m_presentShaderTypes = ShaderTypeBit::kNone;
-
-	/// The name of the shader library. Mainly for RT shaders.
-	Array<char, 64> m_libraryName = {};
-
-	/// An arbitary number indicating the type of the ray.
-	U32 m_rayType = kMaxU32;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
 		s.doArray("m_magic", offsetof(ShaderProgramBinary, m_magic), &self.m_magic[0], self.m_magic.getSize());
-		s.doValue("m_mutators", offsetof(ShaderProgramBinary, m_mutators), self.m_mutators);
+		s.doValue("m_shaderTypes", offsetof(ShaderProgramBinary, m_shaderTypes), self.m_shaderTypes);
 		s.doValue("m_codeBlocks", offsetof(ShaderProgramBinary, m_codeBlocks), self.m_codeBlocks);
-		s.doValue("m_variants", offsetof(ShaderProgramBinary, m_variants), self.m_variants);
+		s.doValue("m_mutators", offsetof(ShaderProgramBinary, m_mutators), self.m_mutators);
 		s.doValue("m_mutations", offsetof(ShaderProgramBinary, m_mutations), self.m_mutations);
-		s.doValue("m_uniformBlocks", offsetof(ShaderProgramBinary, m_uniformBlocks), self.m_uniformBlocks);
-		s.doValue("m_storageBlocks", offsetof(ShaderProgramBinary, m_storageBlocks), self.m_storageBlocks);
-		s.doPointer("m_pushConstantBlock", offsetof(ShaderProgramBinary, m_pushConstantBlock), self.m_pushConstantBlock);
-		s.doValue("m_opaques", offsetof(ShaderProgramBinary, m_opaques), self.m_opaques);
-		s.doValue("m_constants", offsetof(ShaderProgramBinary, m_constants), self.m_constants);
+		s.doValue("m_variants", offsetof(ShaderProgramBinary, m_variants), self.m_variants);
+		s.doValue("m_techniques", offsetof(ShaderProgramBinary, m_techniques), self.m_techniques);
 		s.doValue("m_structs", offsetof(ShaderProgramBinary, m_structs), self.m_structs);
-		s.doValue("m_presentShaderTypes", offsetof(ShaderProgramBinary, m_presentShaderTypes), self.m_presentShaderTypes);
-		s.doArray("m_libraryName", offsetof(ShaderProgramBinary, m_libraryName), &self.m_libraryName[0], self.m_libraryName.getSize());
-		s.doValue("m_rayType", offsetof(ShaderProgramBinary, m_rayType), self.m_rayType);
 	}
 
 	template<typename TDeserializer>

+ 26 - 105
AnKi/ShaderCompiler/ShaderProgramBinary.xml

@@ -2,156 +2,77 @@
 	<includes>
 		<include file="&lt;AnKi/ShaderCompiler/Common.h&gt;"/>
 		<include file="&lt;AnKi/ShaderCompiler/ShaderProgramBinaryExtra.h&gt;"/>
-		<include file="&lt;AnKi/Gr/Enums.h&gt;"/>
+		<include file="&lt;AnKi/Gr/Common.h&gt;"/>
 	</includes>
 
 	<classes>
-		<class name="ShaderProgramBinaryVariable" comment="Storage or uniform variable">
-			<members>
-				<member name="m_name" type="char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
-				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::kNone" />
-			</members>
-		</class>
-
-		<class name="ShaderProgramBinaryVariableInstance" comment="Storage or uniform variable per variant">
-			<members>
-				<member name="m_index" type="U32" constructor="= kMaxU32" comment="Points to ShaderProgramBinaryBlock::m_variables" />
-				<member name="m_blockInfo" type="ShaderVariableBlockInfo" />
-			</members>
-		</class>
-
-		<class name="ShaderProgramBinaryBlock" comment="Storage or uniform block">
-			<members>
-				<member name="m_name" type="char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
-				<member name="m_variables" type="WeakArray&lt;ShaderProgramBinaryVariable&gt;" />
-				<member name="m_binding" type="U32" constructor="= kMaxU32" />
-				<member name="m_set" type="U32" constructor="= kMaxU32" />
-			</members>
-		</class>
-
-		<class name="ShaderProgramBinaryBlockInstance" comment="Storage or uniform block per variant">
-			<members>
-				<member name="m_index" type="U32" constructor="= kMaxU32" comment="Points to ShaderProgramBinary::m_uniformBlocks or m_storageBlocks" />
-				<member name="m_variableInstances" type="WeakArray&lt;ShaderProgramBinaryVariableInstance&gt;" />
-				<member name="m_size" type="U32" constructor="= kMaxU32" />
-			</members>
-		</class>
-
-		<class name="ShaderProgramBinaryOpaque" comment="Sampler or texture or image">
-			<members>
-				<member name="m_name" type="char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
-				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::kNone" />
-				<member name="m_binding" type="U32" constructor="= kMaxU32" />
-				<member name="m_set" type="U32" constructor="= kMaxU32" />
-			</members>
-		</class>
-
-		<class name="ShaderProgramBinaryOpaqueInstance" comment="Sampler or texture or image per variant">
-			<members>
-				<member name="m_index" type="U32" constructor="= kMaxU32" comment="Points to ShaderProgramBinary::m_opaques" />
-				<member name="m_arraySize" type="U32" constructor="= kMaxU32" />
-			</members>
-		</class>
-
-		<class name="ShaderProgramBinaryConstant" comment="Specialization constant">
-			<members>
-				<member name="m_name" type="char" array_size="kMaxShaderBinaryNameLength + 1" />
-				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::kNone" />
-				<member name="m_constantId" type="U32" constructor="= kMaxU32"/>
-			</members>
-		</class>
-
-		<class name="ShaderProgramBinaryConstantInstance" comment="Specialization constant per variant">
-			<members>
-				<member name="m_index" type="U32" constructor="= kMaxU32" comment="Points to ShaderProgramBinary::m_constants" />
-			</members>
-		</class>
-
 		<class name="ShaderProgramBinaryStructMember" comment="A member of a ShaderProgramBinaryStruct">
 			<members>
-				<member name="m_name" type="char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
-				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::kNone" comment="If the value is ShaderVariableDataType::kNone then it's a struct" />
-				<member name="m_structIndex" type="U32" constructor="= kMaxU32" comment="If the type is another struct then this points to ShaderProgramBinary::m_structs" />
-				<member name="m_dependentMutator" type="U32" constructor="= kMaxU32" comment="It points to a ShaderProgramBinary::m_mutators. This mutator will turn on or off this member" />
-				<member name="m_dependentMutatorValue" type="MutatorValue" constructor="= 0" comment="The value of the m_dependentMutator" />
-			</members>
-		</class>
-
-		<class name="ShaderProgramBinaryStructMemberInstance" comment="Struct member per variant">
-			<members>
-				<member name="m_index" type="U32" constructor="= kMaxU32" comment="Points to ShaderProgramBinary::m_structs" />
-				<member name="m_offset" type="U32" constructor="= kMaxU32" comment="The offset of the member in the struct" />
-				<member name="m_arraySize" type="U32" constructor="= kMaxU32" />
+				<member name="m_name" type="Char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
+				<member name="m_offset" type="U32" constructor="= kMaxU32" />
+				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::kNone" />
 			</members>
 		</class>
 
 		<class name="ShaderProgramBinaryStruct" comment="A type that is a structure">
 			<members>
-				<member name="m_name" type="char" array_size="kMaxShaderBinaryNameLength + 1" />
+				<member name="m_name" type="Char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
 				<member name="m_members" type="WeakArray&lt;ShaderProgramBinaryStructMember&gt;" />
+				<member name="m_size" type="U32" />
 			</members>
 		</class>
 
-		<class name="ShaderProgramBinaryStructInstance" comment="Structure type per variant">
+		<class name="ShaderProgramBinaryCodeBlock" comment="Contains the IR (SPIR-V)">
 			<members>
-				<member name="m_index" type="U32" comment="Points to ShaderProgramBinary::m_structs" />
-				<member name="m_memberInstances" type="WeakArray&lt;ShaderProgramBinaryStructMemberInstance&gt;" />
-				<member name="m_size" type="U32" constructor="= kMaxU32" />
+				<member name="m_binary" type="WeakArray&lt;U8&gt;" />
+				<member name="m_hash" type="U64" constructor="= 0" />
 			</members>
 		</class>
 
-		<class name="ShaderProgramBinaryVariant">
+		<class name="ShaderProgramBinaryTechnique">
 			<members>
-				<member name="m_codeBlockIndices" type="U32" array_size="U32(ShaderType::kCount)" comment="Index in ShaderProgramBinary::m_codeBlocks. kMaxU32 means no shader" constructor="= {}" />
-				<member name="m_uniformBlocks" type="WeakArray&lt;ShaderProgramBinaryBlockInstance&gt;" />
-				<member name="m_storageBlocks" type="WeakArray&lt;ShaderProgramBinaryBlockInstance&gt;" />
-				<member name="m_pushConstantBlock" type="ShaderProgramBinaryBlockInstance" pointer="true" constructor="= nullptr" />
-				<member name="m_opaques" type="WeakArray&lt;ShaderProgramBinaryOpaqueInstance&gt;" />
-				<member name="m_constants" type="WeakArray&lt;ShaderProgramBinaryConstantInstance&gt;" />
-				<member name="m_structs" type="WeakArray&lt;ShaderProgramBinaryStructInstance&gt;" />
-				<member name="m_workgroupSizes" type="U32" array_size="3" constructor="= {kMaxU32, kMaxU32, kMaxU32}" />
-				<member name="m_workgroupSizesConstants" type="U32" array_size="3" constructor="= {kMaxU32, kMaxU32, kMaxU32}" comment="Indices to ShaderProgramBinary::m_constants" />
+				<member name="m_name" type="Char" array_size="kMaxShaderBinaryNameLength + 1" />
+				<member name="m_shaderTypes" type="ShaderTypeBit" constructor="= ShaderTypeBit::kNone" />
 			</members>
 		</class>
 
 		<class name="ShaderProgramBinaryMutator" comment="Shader program mutator">
 			<members>
-				<member name="m_name" type="char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
+				<member name="m_name" type="Char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
 				<member name="m_values" type="WeakArray&lt;MutatorValue&gt;" />
 			</members>
 		</class>
 
-		<class name="ShaderProgramBinaryCodeBlock" comment="Contains the IR (SPIR-V)">
+		<class name="ShaderProgramBinaryTechniqueCodeBlocks">
 			<members>
-				<member name="m_binary" type="WeakArray&lt;U8&gt;" />
-				<member name="m_hash" type="U64" constructor="= 0" />
+				<member name="m_codeBlockIndices" type="U32" array_size="U32(ShaderType::kCount)" constructor="= {}" comment="Points to ShaderProgramBinary::m_codeBlocks. If the shader type is not present the value is kMaxU32" />
+			</members>
+		</class>
+		
+		<class name="ShaderProgramBinaryVariant">
+			<members>
+				<member name="m_techniqueCodeBlocks" type="WeakArray&lt;ShaderProgramBinaryTechniqueCodeBlocks&gt;" comment="One entry per technique" />
 			</members>
 		</class>
 
 		<class name="ShaderProgramBinaryMutation" comment="A mutation is a unique combination of mutator values">
 			<members>
 				<member name="m_values" type="WeakArray&lt;MutatorValue&gt;" />
-				<member name="m_variantIndex" type="U32" constructor="= kMaxU32" comment="Points to ShaderProgramBinary::m_variants" />
 				<member name="m_hash" type="U64" constructor="= 0" comment="Mutation hash" />
+				<member name="m_variantIndex" type="U32" constructor="= kMaxU32" comment="Points to ShaderProgramBinary::m_variants" />
 			</members>
 		</class>
 
 		<class name="ShaderProgramBinary">
 			<members>
 				<member name="m_magic" type="U8" array_size="8" constructor="= {}" />
-				<member name="m_mutators" type="WeakArray&lt;ShaderProgramBinaryMutator&gt;" />
+				<member name="m_shaderTypes" type="ShaderTypeBit" constructor="= ShaderTypeBit::kNone" />
 				<member name="m_codeBlocks" type="WeakArray&lt;ShaderProgramBinaryCodeBlock&gt;" />
-				<member name="m_variants" type="WeakArray&lt;ShaderProgramBinaryVariant&gt;" />
+				<member name="m_mutators" type="WeakArray&lt;ShaderProgramBinaryMutator&gt;" />
 				<member name="m_mutations" type="WeakArray&lt;ShaderProgramBinaryMutation&gt;" comment="It's sorted using the mutation's hash" />
-				<member name="m_uniformBlocks" type="WeakArray&lt;ShaderProgramBinaryBlock&gt;" />
-				<member name="m_storageBlocks" type="WeakArray&lt;ShaderProgramBinaryBlock&gt;" />
-				<member name="m_pushConstantBlock" type="ShaderProgramBinaryBlock" pointer="true" constructor="= nullptr" />
-				<member name="m_opaques" type="WeakArray&lt;ShaderProgramBinaryOpaque&gt;" />
-				<member name="m_constants" type="WeakArray&lt;ShaderProgramBinaryConstant&gt;" />
+				<member name="m_variants" type="WeakArray&lt;ShaderProgramBinaryVariant&gt;" />
+				<member name="m_techniques" type="WeakArray&lt;ShaderProgramBinaryTechnique&gt;" />
 				<member name="m_structs" type="WeakArray&lt;ShaderProgramBinaryStruct&gt;" />
-				<member name="m_presentShaderTypes" type="ShaderTypeBit" constructor="= ShaderTypeBit::kNone" />
-				<member name="m_libraryName" type="char" array_size="64" constructor="= {}" comment="The name of the shader library. Mainly for RT shaders" />
-				<member name="m_rayType" type="U32" constructor="= kMaxU32" comment="An arbitary number indicating the type of the ray" />
 			</members>
 		</class>
 	</classes>

File diff suppressed because it is too large
+ 135 - 833
AnKi/ShaderCompiler/ShaderProgramCompiler.cpp


+ 17 - 55
AnKi/ShaderCompiler/ShaderProgramCompiler.h

@@ -14,63 +14,15 @@ namespace anki {
 /// @addtogroup shader_compiler
 /// @{
 
-inline constexpr const char* kShaderBinaryMagic = "ANKISDR9"; //! WARNING: If changed change kShaderBinaryVersion
-constexpr U32 kShaderBinaryVersion = 9;
-
-/// A wrapper over the POD ShaderProgramBinary class.
-/// @memberof ShaderProgramCompiler
-class ShaderProgramBinaryWrapper
-{
-	friend Error compileShaderProgramInternal(CString fname, ShaderProgramFilesystemInterface& fsystem,
-											  ShaderProgramPostParseInterface* postParseCallback, ShaderProgramAsyncTaskInterface* taskManager,
-											  const ShaderCompilerOptions& compilerOptions, ShaderProgramBinaryWrapper& binary);
-
-public:
-	ShaderProgramBinaryWrapper(BaseMemoryPool* pool)
-		: m_pool(pool)
-	{
-	}
-
-	ShaderProgramBinaryWrapper(const ShaderProgramBinaryWrapper&) = delete; // Non-copyable
-
-	~ShaderProgramBinaryWrapper()
-	{
-		cleanup();
-	}
-
-	ShaderProgramBinaryWrapper& operator=(const ShaderProgramBinaryWrapper&) = delete; // Non-copyable
-
-	Error serializeToFile(CString fname) const;
-
-	Error deserializeFromFile(CString fname);
-
-	template<typename TFile>
-	Error deserializeFromAnyFile(TFile& fname);
-
-	const ShaderProgramBinary& getBinary() const
-	{
-		ANKI_ASSERT(m_binary);
-		return *m_binary;
-	}
-
-private:
-	BaseMemoryPool* m_pool = nullptr;
-	ShaderProgramBinary* m_binary = nullptr;
-	Bool m_singleAllocation = false;
-
-	void cleanup();
-};
+inline constexpr const char* kShaderBinaryMagic = "ANKISP0"; // WARNING: If changed change kShaderBinaryVersion
+constexpr U32 kShaderBinaryVersion = 0;
 
 template<typename TFile>
-Error ShaderProgramBinaryWrapper::deserializeFromAnyFile(TFile& file)
+Error deserializeShaderProgramBinaryFromAnyFile(TFile& file, ShaderProgramBinary*& binary, BaseMemoryPool& pool)
 {
-	cleanup();
 	BinaryDeserializer deserializer;
-	ANKI_CHECK(deserializer.deserialize(m_binary, *m_pool, file));
-
-	m_singleAllocation = true;
-
-	if(memcmp(kShaderBinaryMagic, &m_binary->m_magic[0], strlen(kShaderBinaryMagic)) != 0)
+	ANKI_CHECK(deserializer.deserialize(binary, pool, file));
+	if(memcmp(kShaderBinaryMagic, &binary->m_magic[0], strlen(kShaderBinaryMagic)) != 0)
 	{
 		ANKI_SHADER_COMPILER_LOGE("Corrupted or wrong version of shader binary.");
 		return Error::kUserData;
@@ -79,10 +31,20 @@ Error ShaderProgramBinaryWrapper::deserializeFromAnyFile(TFile& file)
 	return Error::kNone;
 }
 
+inline Error deserializeShaderProgramBinaryFromFile(CString fname, ShaderProgramBinary*& binary, BaseMemoryPool& pool)
+{
+	File file;
+	ANKI_CHECK(file.open(fname, FileOpenFlag::kRead | FileOpenFlag::kBinary));
+	ANKI_CHECK(deserializeShaderProgramBinaryFromAnyFile(file, binary, pool));
+	return Error::kNone;
+}
+
 /// Takes an AnKi special shader program and spits a binary.
 Error compileShaderProgram(CString fname, ShaderProgramFilesystemInterface& fsystem, ShaderProgramPostParseInterface* postParseCallback,
-						   ShaderProgramAsyncTaskInterface* taskManager, const ShaderCompilerOptions& compilerOptions,
-						   ShaderProgramBinaryWrapper& binary);
+						   ShaderProgramAsyncTaskInterface* taskManager, const ShaderCompilerOptions& compilerOptions, ShaderProgramBinary*& binary);
+
+/// Free the binary created ONLY by compileShaderProgram.
+void freeShaderProgramBinary(ShaderProgramBinary*& binary);
 /// @}
 
 } // end namespace anki

+ 76 - 234
AnKi/ShaderCompiler/ShaderProgramDump.cpp

@@ -13,158 +13,15 @@ namespace anki {
 
 #define ANKI_TAB "    "
 
-static void disassembleBlockInstance(const ShaderProgramBinaryBlockInstance& instance, const ShaderProgramBinaryBlock& block, StringList& lines)
+void dumpShaderProgramBinary(const ShaderDumpOptions& options, const ShaderProgramBinary& binary, ShaderCompilerString& humanReadable)
 {
-	lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s set %4u binding %4u size %4u\n", block.m_name.getBegin(), block.m_set, block.m_binding,
-						  instance.m_size);
+	ShaderCompilerStringList lines;
 
-	for(U32 i = 0; i < instance.m_variableInstances.getSize(); ++i)
-	{
-		const ShaderProgramBinaryVariableInstance& varInstance = instance.m_variableInstances[i];
-		const ShaderProgramBinaryVariable& var = block.m_variables[varInstance.m_index];
-
-		lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB ANKI_TAB "%-48s type %8s blockInfo %d,%d,%d,%d\n", var.m_name.getBegin(),
-							  getShaderVariableDataTypeInfo(var.m_type).m_name, varInstance.m_blockInfo.m_offset, varInstance.m_blockInfo.m_arraySize,
-							  varInstance.m_blockInfo.m_arrayStride, varInstance.m_blockInfo.m_matrixStride);
-	}
-}
-
-static void disassembleBlock(const ShaderProgramBinaryBlock& block, StringList& lines)
-{
-	lines.pushBackSprintf(ANKI_TAB "%-32s set %4u binding %4u\n", block.m_name.getBegin(), block.m_set, block.m_binding);
-
-	for(const ShaderProgramBinaryVariable& var : block.m_variables)
-	{
-		lines.pushBackSprintf(ANKI_TAB ANKI_TAB "%-48s type %8s\n", var.m_name.getBegin(), getShaderVariableDataTypeInfo(var.m_type).m_name);
-	}
-}
-
-void dumpShaderProgramBinary(const ShaderDumpOptions& options, const ShaderProgramBinary& binary, String& humanReadable)
-{
-	StringList lines;
-
-	if(binary.m_libraryName[0])
-	{
-		lines.pushBack("**LIBRARY**\n");
-		lines.pushBackSprintf(ANKI_TAB "%s\n", &binary.m_libraryName[0]);
-	}
-
-	if(binary.m_rayType != kMaxU32)
-	{
-		lines.pushBack("\n**RAY TYPE**\n");
-		lines.pushBackSprintf(ANKI_TAB "%u\n", binary.m_rayType);
-	}
-
-	lines.pushBack("\n**MUTATORS**\n");
-	if(binary.m_mutators.getSize() > 0)
-	{
-		for(const ShaderProgramBinaryMutator& mutator : binary.m_mutators)
-		{
-			lines.pushBackSprintf(ANKI_TAB "%-32s ", &mutator.m_name[0]);
-			for(U32 i = 0; i < mutator.m_values.getSize(); ++i)
-			{
-				lines.pushBackSprintf((i < mutator.m_values.getSize() - 1) ? "%d," : "%d", mutator.m_values[i]);
-			}
-			lines.pushBack("\n");
-		}
-	}
-	else
-	{
-		lines.pushBack(ANKI_TAB "N/A\n");
-	}
-
-	lines.pushBack("\n**UNIFORM BLOCKS**\n");
-	if(binary.m_uniformBlocks.getSize() > 0)
-	{
-		for(const ShaderProgramBinaryBlock& block : binary.m_uniformBlocks)
-		{
-			disassembleBlock(block, lines);
-		}
-	}
-	else
-	{
-		lines.pushBack(ANKI_TAB "N/A\n");
-	}
-
-	lines.pushBack("\n**STORAGE BLOCKS**\n");
-	if(binary.m_storageBlocks.getSize() > 0)
-	{
-		for(const ShaderProgramBinaryBlock& block : binary.m_storageBlocks)
-		{
-			disassembleBlock(block, lines);
-		}
-	}
-	else
-	{
-		lines.pushBack(ANKI_TAB "N/A\n");
-	}
-
-	lines.pushBack("\n**PUSH CONSTANTS**\n");
-	if(binary.m_pushConstantBlock)
-	{
-		disassembleBlock(*binary.m_pushConstantBlock, lines);
-	}
-	else
-	{
-		lines.pushBack(ANKI_TAB "N/A\n");
-	}
-
-	lines.pushBack("\n**OPAQUE**\n");
-	if(binary.m_opaques.getSize() > 0)
-	{
-		for(const ShaderProgramBinaryOpaque& o : binary.m_opaques)
-		{
-			lines.pushBackSprintf(ANKI_TAB "%-32s set %4u binding %4u type %12s\n", o.m_name.getBegin(), o.m_set, o.m_binding,
-								  getShaderVariableDataTypeInfo(o.m_type).m_name);
-		}
-	}
-	else
-	{
-		lines.pushBack(ANKI_TAB "N/A\n");
-	}
-
-	lines.pushBack("\n**CONSTANTS**\n");
-	if(binary.m_constants.getSize() > 0)
-	{
-		for(const ShaderProgramBinaryConstant& c : binary.m_constants)
-		{
-			lines.pushBackSprintf(ANKI_TAB "%-32s type %8s id %4u\n", c.m_name.getBegin(), getShaderVariableDataTypeInfo(c.m_type).m_name,
-								  c.m_constantId);
-		}
-	}
-	else
-	{
-		lines.pushBack(ANKI_TAB "N/A\n");
-	}
-
-	lines.pushBack("\n**STRUCTS**\n");
-	if(binary.m_structs.getSize() > 0)
-	{
-		for(const ShaderProgramBinaryStruct& s : binary.m_structs)
-		{
-			lines.pushBackSprintf(ANKI_TAB "%-32s\n", s.m_name.getBegin());
-
-			for(const ShaderProgramBinaryStructMember& member : s.m_members)
-			{
-				const CString typeStr = (member.m_type == ShaderVariableDataType::kNone) ? &binary.m_structs[member.m_structIndex].m_name[0]
-																						 : getShaderVariableDataTypeInfo(member.m_type).m_name;
-				const CString dependentMutator =
-					(member.m_dependentMutator != kMaxU32) ? binary.m_mutators[member.m_dependentMutator].m_name.getBegin() : "None";
-				lines.pushBackSprintf(ANKI_TAB ANKI_TAB "%-32s type %24s dependentMutator %-32s dependentMutatorValue %4d\n",
-									  member.m_name.getBegin(), typeStr.cstr(), dependentMutator.cstr(), member.m_dependentMutatorValue);
-			}
-		}
-	}
-	else
-	{
-		lines.pushBack(ANKI_TAB "N/A\n");
-	}
-
-	lines.pushBack("\n**BINARIES**\n");
+	lines.pushBackSprintf("\n**BINARIES (%u)**\n", binary.m_codeBlocks.getSize());
 	U32 count = 0;
 	for(const ShaderProgramBinaryCodeBlock& code : binary.m_codeBlocks)
 	{
-		lines.pushBackSprintf(ANKI_TAB "#bin%05u \n", count++);
+		lines.pushBackSprintf(ANKI_TAB "bin%05u \n", count++);
 
 		if(options.m_writeGlsl)
 		{
@@ -219,125 +76,110 @@ void dumpShaderProgramBinary(const ShaderDumpOptions& options, const ShaderProgr
 		}
 	}
 
-	lines.pushBack("\n**SHADER VARIANTS**\n");
-	count = 0;
-	for(const ShaderProgramBinaryVariant& variant : binary.m_variants)
+	// Mutators
+	lines.pushBackSprintf("\n**MUTATORS (%u)**\n", binary.m_mutators.getSize());
+	if(binary.m_mutators.getSize() > 0)
 	{
-		lines.pushBackSprintf(ANKI_TAB "#var%05u\n", count++);
-
-		// Uniform blocks
-		if(variant.m_uniformBlocks.getSize() > 0)
+		for(const ShaderProgramBinaryMutator& mutator : binary.m_mutators)
 		{
-			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Uniform blocks\n");
-			for(const ShaderProgramBinaryBlockInstance& instance : variant.m_uniformBlocks)
+			lines.pushBackSprintf(ANKI_TAB "%-32s values (", &mutator.m_name[0]);
+			for(U32 i = 0; i < mutator.m_values.getSize(); ++i)
 			{
-				disassembleBlockInstance(instance, binary.m_uniformBlocks[instance.m_index], lines);
+				lines.pushBackSprintf((i < mutator.m_values.getSize() - 1) ? "%d," : "%d)", mutator.m_values[i]);
 			}
+			lines.pushBack("\n");
 		}
+	}
 
-		// Storage blocks
-		if(variant.m_storageBlocks.getSize() > 0)
-		{
-			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Storage blocks\n");
-			for(const ShaderProgramBinaryBlockInstance& instance : variant.m_storageBlocks)
-			{
-				disassembleBlockInstance(instance, binary.m_storageBlocks[instance.m_index], lines);
-			}
-		}
+	// Techniques
+	lines.pushBackSprintf("\n**TECHNIQUES (%u)**\n", binary.m_techniques.getSize());
+	count = 0;
+	for(const ShaderProgramBinaryTechnique& t : binary.m_techniques)
+	{
+		lines.pushBackSprintf(ANKI_TAB "%-32s shaderTypes 0x%02x\n", t.m_name.getBegin(), U32(t.m_shaderTypes));
+	}
+
+	// Mutations
+	U32 skippedMutations = 0;
+	for(const ShaderProgramBinaryMutation& mutation : binary.m_mutations)
+	{
+		skippedMutations += mutation.m_variantIndex == kMaxU32;
+	}
 
-		// Opaque
-		if(variant.m_opaques.getSize() > 0)
+	lines.pushBackSprintf("\n**MUTATIONS (%u skipped %u)**\n", binary.m_mutations.getSize(), skippedMutations);
+	count = 0;
+	for(const ShaderProgramBinaryMutation& mutation : binary.m_mutations)
+	{
+		if(mutation.m_variantIndex != kMaxU32)
 		{
-			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Opaque\n");
-			for(const ShaderProgramBinaryOpaqueInstance& instance : variant.m_opaques)
-			{
-				const ShaderProgramBinaryOpaque& o = binary.m_opaques[instance.m_index];
-				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s set %4u binding %4u type %12s arraySize %4u\n", o.m_name.getBegin(), o.m_set,
-									  o.m_binding, getShaderVariableDataTypeInfo(o.m_type).m_name, instance.m_arraySize);
-			}
+			lines.pushBackSprintf(ANKI_TAB "mut%05u variantIndex var%05u hash 0x%016" PRIX64 " values (", count++, mutation.m_variantIndex,
+								  mutation.m_hash);
 		}
-
-		// Push constants
-		if(variant.m_pushConstantBlock)
+		else
 		{
-			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Push constants\n");
-			disassembleBlockInstance(*variant.m_pushConstantBlock, *binary.m_pushConstantBlock, lines);
+			lines.pushBackSprintf(ANKI_TAB "mut%05u variantIndex N/A      hash 0x%016" PRIX64 " values (", count++, mutation.m_hash);
 		}
 
-		// Constants
-		if(variant.m_constants.getSize() > 0)
+		if(mutation.m_values.getSize() > 0)
 		{
-			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Specialization constants\n");
-			for(const ShaderProgramBinaryConstantInstance& instance : variant.m_constants)
+			for(U32 i = 0; i < mutation.m_values.getSize(); ++i)
 			{
-				const ShaderProgramBinaryConstant& c = binary.m_constants[instance.m_index];
-				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s type %8s id %4u\n", c.m_name.getBegin(),
-									  getShaderVariableDataTypeInfo(c.m_type).m_name, c.m_constantId);
+				lines.pushBackSprintf((i < mutation.m_values.getSize() - 1) ? "%s %4d, " : "%s %4d", binary.m_mutators[i].m_name.getBegin(),
+									  I32(mutation.m_values[i]));
 			}
-		}
 
-		// Structs
-		if(variant.m_structs.getSize() > 0)
+			lines.pushBack(")");
+		}
+		else
 		{
-			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Structs\n");
-			for(const ShaderProgramBinaryStructInstance& instance : variant.m_structs)
-			{
-				const ShaderProgramBinaryStruct& s = binary.m_structs[instance.m_index];
-				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s size %4u\n", s.m_name.getBegin(), instance.m_size);
-
-				for(const ShaderProgramBinaryStructMemberInstance& memberInstance : instance.m_memberInstances)
-				{
-					const ShaderProgramBinaryStructMember& member = s.m_members[memberInstance.m_index];
-					lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB ANKI_TAB "%-32s offset %4u arraySize %4u\n", member.m_name.getBegin(),
-										  memberInstance.m_offset, memberInstance.m_arraySize);
-				}
-			}
+			lines.pushBack("N/A)");
 		}
 
+		lines.pushBack("\n");
+	}
+
+	// Variants
+	lines.pushBackSprintf("\n**SHADER VARIANTS (%u)**\n", binary.m_variants.getSize());
+	count = 0;
+	for(const ShaderProgramBinaryVariant& variant : binary.m_variants)
+	{
+		lines.pushBackSprintf(ANKI_TAB "var%05u\n", count++);
+
 		// Binary indices
-		lines.pushBack(ANKI_TAB ANKI_TAB "Binaries ");
-		for(ShaderType shaderType : EnumIterable<ShaderType>())
+		for(U32 t = 0; t < binary.m_techniques.getSize(); ++t)
 		{
-			if(variant.m_codeBlockIndices[shaderType] < kMaxU32)
-			{
-				lines.pushBackSprintf("#bin%05u", variant.m_codeBlockIndices[shaderType]);
-			}
-			else
-			{
-				lines.pushBack("-");
-			}
+			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "%-32s binaries (", binary.m_techniques[t].m_name.getBegin());
 
-			if(shaderType != ShaderType::kLast)
+			for(ShaderType s : EnumIterable<ShaderType>())
 			{
-				lines.pushBack(",");
+				if(variant.m_techniqueCodeBlocks[t].m_codeBlockIndices[s] < kMaxU32)
+				{
+					lines.pushBackSprintf("bin%05u", variant.m_techniqueCodeBlocks[t].m_codeBlockIndices[s]);
+				}
+				else
+				{
+					lines.pushBack("--------");
+				}
+
+				lines.pushBack((s == ShaderType::kCount - 1) ? ")\n" : ", ");
 			}
 		}
-		lines.pushBack("\n");
 	}
 
-	// Mutations
-	lines.pushBack("\n**MUTATIONS**\n");
-	count = 0;
-	for(const ShaderProgramBinaryMutation& mutation : binary.m_mutations)
+	// Structs
+	lines.pushBackSprintf("\n**STRUCTS (%u)**\n", binary.m_structs.getSize());
+	if(binary.m_structs.getSize() > 0)
 	{
-		lines.pushBackSprintf(ANKI_TAB "#mut%-4u variantIndex #var%05u hash 0x%016" PRIX64 " values (", count++, mutation.m_variantIndex,
-							  mutation.m_hash);
-		if(mutation.m_values.getSize() > 0)
+		for(const ShaderProgramBinaryStruct& s : binary.m_structs)
 		{
-			for(U32 i = 0; i < mutation.m_values.getSize(); ++i)
+			lines.pushBackSprintf(ANKI_TAB "%-32s size %4u\n", s.m_name.getBegin(), s.m_size);
+
+			for(const ShaderProgramBinaryStructMember& member : s.m_members)
 			{
-				lines.pushBackSprintf((i < mutation.m_values.getSize() - 1) ? "%s %4d, " : "%s %4d", binary.m_mutators[i].m_name.getBegin(),
-									  I32(mutation.m_values[i]));
+				const CString typeStr = getShaderVariableDataTypeInfo(member.m_type).m_name;
+				lines.pushBackSprintf(ANKI_TAB ANKI_TAB "%-32s type %5s offset %4d\n", member.m_name.getBegin(), typeStr.cstr(), member.m_offset);
 			}
-
-			lines.pushBack(")");
 		}
-		else
-		{
-			lines.pushBack("N/A)");
-		}
-
-		lines.pushBack("\n");
 	}
 
 	lines.join("", humanReadable);

+ 1 - 1
AnKi/ShaderCompiler/ShaderProgramDump.h

@@ -19,7 +19,7 @@ public:
 };
 
 /// Create a human readable representation of the shader binary.
-void dumpShaderProgramBinary(const ShaderDumpOptions& options, const ShaderProgramBinary& binary, String& humanReadable);
+void dumpShaderProgramBinary(const ShaderDumpOptions& options, const ShaderProgramBinary& binary, ShaderCompilerString& humanReadable);
 /// @}
 
 } // end namespace anki

+ 199 - 392
AnKi/ShaderCompiler/ShaderProgramParser.cpp

@@ -106,11 +106,11 @@ ShaderProgramParser::~ShaderProgramParser()
 {
 }
 
-void ShaderProgramParser::tokenizeLine(CString line, DynamicArray<String>& tokens) const
+void ShaderProgramParser::tokenizeLine(CString line, ShaderCompilerDynamicArray<ShaderCompilerString>& tokens) const
 {
 	ANKI_ASSERT(line.getLength() > 0);
 
-	String l = line;
+	ShaderCompilerString l = line;
 
 	// Replace all tabs with spaces
 	for(char& c : l)
@@ -122,21 +122,22 @@ void ShaderProgramParser::tokenizeLine(CString line, DynamicArray<String>& token
 	}
 
 	// Split
-	StringList spaceTokens;
+	ShaderCompilerStringList spaceTokens;
 	spaceTokens.splitString(l, ' ', false);
 
 	// Create the array
-	for(const String& s : spaceTokens)
+	for(const ShaderCompilerString& s : spaceTokens)
 	{
 		tokens.emplaceBack(s);
 	}
 }
 
-Error ShaderProgramParser::parsePragmaStart(const String* begin, const String* end, CString line, CString fname)
+Error ShaderProgramParser::parsePragmaTechniqueStart(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname)
 {
 	ANKI_ASSERT(begin && end);
 
-	if(begin >= end)
+	const PtrSize tokenCount = end - begin;
+	if(tokenCount != 1 && tokenCount != 2)
 	{
 		ANKI_PP_ERROR_MALFORMED();
 	}
@@ -147,34 +148,75 @@ Error ShaderProgramParser::parsePragmaStart(const String* begin, const String* e
 		ANKI_PP_ERROR_MALFORMED();
 	}
 
-	m_codeLines.pushBackSprintf("#ifdef ANKI_%s_SHADER", kShaderStageNames[shaderType].cstr());
-
+	ShaderCompilerString techniqueName;
 	++begin;
-	if(begin != end)
+	if(begin == end)
 	{
-		// Should be the last token
-		ANKI_PP_ERROR_MALFORMED();
+		// Last token
+		techniqueName = "Unnamed";
 	}
+	else
+	{
+		techniqueName = *begin;
 
-	// Set the mask
-	const ShaderTypeBit mask = ShaderTypeBit(1 << shaderType);
-	if(!!(mask & m_shaderTypes))
+		++begin;
+		if(begin != end)
+		{
+			ANKI_PP_ERROR_MALFORMED();
+		}
+	}
+
+	// Checks
+	if(insideTechnique())
 	{
-		ANKI_PP_ERROR_MALFORMED_MSG("Can't have #pragma start <shader> appearing more than once");
+		ANKI_PP_ERROR_MALFORMED_MSG("Need to close the previous technique_start before starting a new one");
 	}
-	m_shaderTypes |= mask;
 
-	// Check bounds
-	if(m_insideShader != ShaderType::kCount)
+	// Find the technique
+	Technique* technique = nullptr;
+	for(Technique& t : m_techniques)
 	{
-		ANKI_PP_ERROR_MALFORMED_MSG("Can't have #pragma start before you close the previous pragma start");
+		if(t.m_name == techniqueName)
+		{
+			if(!!(t.m_shaderTypes & ShaderTypeBit(1 << shaderType)))
+			{
+				ANKI_PP_ERROR_MALFORMED_MSG("technique_start with the same name and type appeared more than once");
+			}
+
+			technique = &t;
+			break;
+		}
+	}
+
+	// Done
+	TechniqueExtra* extra = nullptr;
+	if(!technique)
+	{
+		technique = m_techniques.emplaceBack();
+		technique->m_name = techniqueName;
+
+		extra = m_techniqueExtras.emplaceBack();
+	}
+	else
+	{
+		const U32 idx = U32(technique - m_techniques.getBegin());
+		extra = &m_techniqueExtras[idx];
 	}
-	m_insideShader = shaderType;
+
+	technique->m_shaderTypes |= ShaderTypeBit(1 << shaderType);
+
+	ANKI_ASSERT(extra->m_sourceLines[shaderType].getSize() == 0);
+	extra->m_sourceLines[shaderType] = m_commonSourceLines;
+	extra->m_sourceLines[shaderType].pushBackSprintf("#define ANKI_%s_SHADER 1", kShaderStageNames[shaderType].cstr());
+	extra->m_sourceLines[shaderType].pushBackSprintf("#define ANKI_TECHNIQUE_%s 1", techniqueName.cstr());
+
+	m_insideTechniqueIdx = U32(technique - m_techniques.getBegin());
+	m_insideTechniqueShaderType = shaderType;
 
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parsePragmaEnd(const String* begin, const String* end, CString line, CString fname)
+Error ShaderProgramParser::parsePragmaTechniqueEnd(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname)
 {
 	ANKI_ASSERT(begin && end);
 
@@ -190,33 +232,43 @@ Error ShaderProgramParser::parsePragmaEnd(const String* begin, const String* end
 		ANKI_PP_ERROR_MALFORMED();
 	}
 
+	ShaderCompilerString techniqueName;
 	++begin;
-	if(begin != end)
+	if(begin == end)
 	{
-		// Should be the last token
-		ANKI_PP_ERROR_MALFORMED();
+		// Last token
+		techniqueName = "Unnamed";
 	}
-
-	// Check bounds
-	if(m_insideShader == ShaderType::kCount)
+	else
 	{
-		ANKI_PP_ERROR_MALFORMED_MSG("Can't have #pragma end before you open with a pragma start");
+		techniqueName = *begin;
+
+		++begin;
+		if(begin != end)
+		{
+			ANKI_PP_ERROR_MALFORMED();
+		}
 	}
 
-	if(m_insideShader != shaderType)
+	// Checks
+	if(!insideTechnique())
 	{
-		ANKI_PP_ERROR_MALFORMED_MSG("Shader type in #pragma end doesn't match the one in previous #pragma start");
+		ANKI_PP_ERROR_MALFORMED_MSG("Forgot to insert a #pragma anki technique_start");
 	}
 
-	m_insideShader = ShaderType::kCount;
+	if(m_techniques.getBack().m_name != techniqueName || m_insideTechniqueShaderType != shaderType)
+	{
+		ANKI_PP_ERROR_MALFORMED_MSG("name or type doesn't match the one in technique_start");
+	}
 
-	// Write code
-	m_codeLines.pushBack("#endif // Shader guard");
+	// Done
+	m_insideTechniqueIdx = kMaxU32;
+	m_insideTechniqueShaderType = ShaderType::kCount;
 
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parsePragmaMutator(const String* begin, const String* end, CString line, CString fname)
+Error ShaderProgramParser::parsePragmaMutator(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname)
 {
 	ANKI_ASSERT(begin && end);
 
@@ -289,64 +341,7 @@ Error ShaderProgramParser::parsePragmaMutator(const String* begin, const String*
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parsePragmaLibraryName(const String* begin, const String* end, CString line, CString fname)
-{
-	ANKI_ASSERT(begin && end);
-
-	if(begin >= end)
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
-	if(m_libName.getLength() > 0)
-	{
-		ANKI_PP_ERROR_MALFORMED_MSG("Library name already set");
-	}
-
-	m_libName = *begin;
-
-	return Error::kNone;
-}
-
-Error ShaderProgramParser::parsePragmaRayType(const String* begin, const String* end, CString line, CString fname)
-{
-	ANKI_ASSERT(begin && end);
-
-	if(begin >= end)
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
-	if(m_rayType != kMaxU32)
-	{
-		ANKI_PP_ERROR_MALFORMED_MSG("Ray type already set");
-	}
-
-	ANKI_CHECK(begin->toNumber(m_rayType));
-
-	if(m_rayType > 128)
-	{
-		ANKI_PP_ERROR_MALFORMED_MSG("Ray type has a very large value");
-	}
-
-	return Error::kNone;
-}
-
-Error ShaderProgramParser::parsePragmaReflect(const String* begin, const String* end, CString line, CString fname)
-{
-	ANKI_ASSERT(begin && end);
-
-	if(begin >= end)
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
-	m_symbolsToReflect.pushBack(*begin);
-
-	return Error::kNone;
-}
-
-Error ShaderProgramParser::parsePragmaSkipMutation(const String* begin, const String* end, CString line, CString fname)
+Error ShaderProgramParser::parsePragmaSkipMutation(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname)
 {
 	ANKI_ASSERT(begin && end);
 
@@ -403,10 +398,10 @@ Error ShaderProgramParser::parsePragmaSkipMutation(const String* begin, const St
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parseInclude(const String* begin, const String* end, CString line, CString fname, U32 depth)
+Error ShaderProgramParser::parseInclude(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname, U32 depth)
 {
 	// Gather the path
-	String path;
+	ShaderCompilerString path;
 	for(; begin < end; ++begin)
 	{
 		path += *begin;
@@ -423,9 +418,10 @@ Error ShaderProgramParser::parseInclude(const String* begin, const String* end,
 
 	if((firstChar == '\"' && lastChar == '\"') || (firstChar == '<' && lastChar == '>'))
 	{
-		String fname2(path.begin() + 1, path.begin() + path.getLength() - 1);
+		ShaderCompilerString fname2(path.begin() + 1, path.begin() + path.getLength() - 1);
 
-		const Bool dontIgnore = fname2.find("AnKi/Shaders/") != String::kNpos || fname2.find("ThirdParty/") != String::kNpos;
+		const Bool dontIgnore =
+			fname2.find("AnKi/Shaders/") != ShaderCompilerString::kNpos || fname2.find("ThirdParty/") != ShaderCompilerString::kNpos;
 		if(!dontIgnore)
 		{
 			// The shaders can't include C++ files. Ignore the include
@@ -445,15 +441,15 @@ Error ShaderProgramParser::parseInclude(const String* begin, const String* end,
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth, U32 lineNumber)
+Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth, U32 lineNo)
 {
 	// Tokenize
-	DynamicArray<String> tokens;
+	ShaderCompilerDynamicArray<ShaderCompilerString> tokens;
 	tokenizeLine(line, tokens);
 	ANKI_ASSERT(tokens.getSize() > 0);
 
-	const String* token = tokens.getBegin();
-	const String* end = tokens.getEnd();
+	const ShaderCompilerString* token = tokens.getBegin();
+	const ShaderCompilerString* end = tokens.getEnd();
 
 	// Skip the hash
 	Bool foundAloneHash = false;
@@ -468,7 +464,7 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 		// We _must_ have an #include
 		ANKI_CHECK(parseInclude(token + 1, end, line, fname, depth));
 
-		m_codeLines.pushBackSprintf("#line %u \"%s\"", lineNumber + 2, fname.cstr());
+		getAppendSourceList().pushBackSprintf("#line %u \"%s\"", lineNo + 1, fname.cstr());
 	}
 	else if((token < end) && ((foundAloneHash && *token == "pragma") || *token == "#pragma"))
 	{
@@ -492,68 +488,47 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 			// Add the guard unique for this file
 			foundPragmaOnce = true;
 			const U64 hash = fname.computeHash();
-			m_codeLines.pushBackSprintf("#ifndef _ANKI_INCL_GUARD_%" PRIu64 "\n"
-										"#define _ANKI_INCL_GUARD_%" PRIu64,
-										hash, hash);
+			getAppendSourceList().pushBackSprintf("#ifndef _ANKI_INCL_GUARD_%" PRIu64 "\n"
+												  "#define _ANKI_INCL_GUARD_%" PRIu64,
+												  hash, hash);
+
+			getAppendSourceList().pushBackSprintf("#line %u \"%s\"", lineNo + 1, fname.cstr());
 		}
 		else if(*token == "anki")
 		{
 			// Must be a #pragma anki
 
 			++token;
-			Bool addLineBack = true;
 
 			if(*token == "mutator")
 			{
 				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaMutator(token + 1, end, line, fname));
 			}
-			else if(*token == "start")
+			else if(*token == "technique_start")
 			{
 				ANKI_CHECK(checkNoActiveStruct());
-				ANKI_CHECK(parsePragmaStart(token + 1, end, line, fname));
-				addLineBack = false;
+				ANKI_CHECK(parsePragmaTechniqueStart(token + 1, end, line, fname));
 			}
-			else if(*token == "end")
+			else if(*token == "technique_end")
 			{
 				ANKI_CHECK(checkNoActiveStruct());
-				ANKI_CHECK(parsePragmaEnd(token + 1, end, line, fname));
-				addLineBack = false;
+				ANKI_CHECK(parsePragmaTechniqueEnd(token + 1, end, line, fname));
 			}
 			else if(*token == "skip_mutation")
 			{
 				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaSkipMutation(token + 1, end, line, fname));
 			}
-			else if(*token == "library")
-			{
-				ANKI_CHECK(checkNoActiveStruct());
-				ANKI_CHECK(parsePragmaLibraryName(token + 1, end, line, fname));
-			}
-			else if(*token == "ray_type")
-			{
-				ANKI_CHECK(checkNoActiveStruct());
-				ANKI_CHECK(parsePragmaRayType(token + 1, end, line, fname));
-			}
-			else if(*token == "reflect")
+			else if(*token == "struct")
 			{
 				ANKI_CHECK(checkNoActiveStruct());
-				ANKI_CHECK(parsePragmaReflect(token + 1, end, line, fname));
+				ANKI_CHECK(parsePragmaStructBegin(token + 1, end, line, fname));
 			}
-			else if(*token == "struct")
+			else if(*token == "struct_end")
 			{
-				if(*(token + 1) == "end")
-				{
-					ANKI_CHECK(checkActiveStruct());
-					ANKI_CHECK(parsePragmaStructEnd(token + 1, end, line, fname));
-
-					m_codeLines.pushBackSprintf("#line %u \"%s\"", lineNumber, fname.cstr());
-				}
-				else
-				{
-					ANKI_CHECK(checkNoActiveStruct());
-					ANKI_CHECK(parsePragmaStructBegin(token + 1, end, line, fname));
-				}
+				ANKI_CHECK(checkActiveStruct());
+				ANKI_CHECK(parsePragmaStructEnd(token + 1, end, line, fname));
 			}
 			else if(*token == "member")
 			{
@@ -569,28 +544,25 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 				ANKI_PP_ERROR_MALFORMED();
 			}
 
-			if(addLineBack)
-			{
-				// Add the line as a comment because of hashing of the source
-				m_codeLines.pushBackSprintf("//%s", line.cstr());
-			}
+			// For good measure
+			getAppendSourceList().pushBackSprintf("#line %u \"%s\"", lineNo + 1, fname.cstr());
 		}
 		else
 		{
 			// Some other pragma, ignore
-			m_codeLines.pushBack(line);
+			getAppendSourceList().pushBack(line);
 		}
 	}
 	else
 	{
 		// Ignore
-		m_codeLines.pushBack(line);
+		getAppendSourceList().pushBack(line);
 	}
 
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parsePragmaStructBegin(const String* begin, const String* end, CString line, CString fname)
+Error ShaderProgramParser::parsePragmaStructBegin(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname)
 {
 	const U tokenCount = U(end - begin);
 	if(tokenCount != 1)
@@ -601,18 +573,7 @@ Error ShaderProgramParser::parsePragmaStructBegin(const String* begin, const Str
 	GhostStruct& gstruct = *m_ghostStructs.emplaceBack();
 	gstruct.m_name = *begin;
 
-	// Add a '_' to the struct name.
-	//
-	// Scenario:
-	// - The shader may have a "pragma reflect" of the struct
-	// - The SPIRV also contains the struct
-	//
-	// What happens:
-	// - The struct is in SPIRV and it will be reflected
-	// - The struct is also in ghost structs and it will be reflected
-	//
-	// This is undesirable because it will complicates reflection. So eliminate the struct from SPIRV by renaming it
-	m_codeLines.pushBackSprintf("struct %s_ {", begin->cstr());
+	getAppendSourceList().pushBackSprintf("struct %s {", begin->cstr());
 
 	ANKI_ASSERT(!m_insideStruct);
 	m_insideStruct = true;
@@ -620,31 +581,19 @@ Error ShaderProgramParser::parsePragmaStructBegin(const String* begin, const Str
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parsePragmaMember(const String* begin, const String* end, CString line, CString fname)
+Error ShaderProgramParser::parsePragmaMember(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname)
 {
 	ANKI_ASSERT(m_insideStruct);
 	const U tokenCount = U(end - begin);
-	if(tokenCount == 0)
+	if(tokenCount != 2)
 	{
 		ANKI_PP_ERROR_MALFORMED();
 	}
 
-	Member& member = *m_ghostStructs.getBack().m_members.emplaceBack();
-
-	// Relaxed
-	Bool relaxed = false;
-	if(*begin == "ANKI_RP")
-	{
-		relaxed = true;
-		++begin;
-	}
+	GhostStruct& structure = m_ghostStructs.getBack();
+	Member member;
 
 	// Type
-	if(begin == end)
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
 	const CString typeStr = *begin;
 	member.m_type = ShaderVariableDataType::kNone;
 	if(typeStr == "F32" || typeStr == "RF32")
@@ -673,107 +622,28 @@ Error ShaderProgramParser::parsePragmaMember(const String* begin, const String*
 		ANKI_PP_ERROR_MALFORMED_MSG("Unrecognized type");
 	}
 
-	++begin;
-
 	// Name
-	if(begin == end)
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
-	member.m_name = *begin;
 	++begin;
+	member.m_name = *begin;
 
-	// if MUTATOR_NAME is MUTATOR_VALUE
-	if(begin != end)
-	{
-		// "if"
-		if(*begin != "if")
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-		++begin;
-
-		// MUTATOR_NAME
-		if(begin == end)
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		const CString mutatorName = *begin;
-		for(U32 i = 0; i < m_mutators.getSize(); ++i)
-		{
-			if(m_mutators[i].m_name == mutatorName)
-			{
-				member.m_dependentMutator = i;
-				break;
-			}
-		}
-
-		if(member.m_dependentMutator == kMaxU32)
-		{
-			ANKI_PP_ERROR_MALFORMED_MSG("Mutator not found");
-		}
-
-		++begin;
-
-		// "is"
-		if(begin == end)
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		if(*begin != "is")
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		++begin;
-
-		// MUTATOR_VALUE
-		if(begin == end)
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		ANKI_CHECK(begin->toNumber(member.m_mutatorValue));
-
-		if(!mutatorHasValue(m_mutators[member.m_dependentMutator], member.m_mutatorValue))
-		{
-			ANKI_PP_ERROR_MALFORMED_MSG("Wrong mutator value");
-		}
-
-		++begin;
-	}
-
-	if(begin != end)
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
-	// Code
-	if(member.m_dependentMutator != kMaxU32)
-	{
-		m_codeLines.pushBackSprintf("#if %s == %d", m_mutators[member.m_dependentMutator].m_name.cstr(), member.m_mutatorValue);
-	}
+	// Rest
+	member.m_offset = (structure.m_members.getSize())
+						  ? structure.m_members.getBack().m_offset + getShaderVariableDataTypeInfo(structure.m_members.getBack().m_type).m_size
+						  : 0;
 
-	m_codeLines.pushBackSprintf("#\tdefine %s_%s_DEFINED 1", m_ghostStructs.getBack().m_name.cstr(), member.m_name.cstr());
-	m_codeLines.pushBackSprintf("\t%s %s %s;", (relaxed) ? "ANKI_RP" : "", typeStr.cstr(), member.m_name.cstr());
+	getAppendSourceList().pushBackSprintf("#define %s_%s_OFFSETOF %u", structure.m_name.cstr(), member.m_name.cstr(), member.m_offset);
+	getAppendSourceList().pushBackSprintf("\t%s %s;", typeStr.cstr(), member.m_name.cstr());
 
-	if(member.m_dependentMutator != kMaxU32)
-	{
-		m_codeLines.pushBack("#endif");
-	}
+	structure.m_members.emplaceBack(std::move(member));
 
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parsePragmaStructEnd(const String* begin, const String* end, CString line, CString fname)
+Error ShaderProgramParser::parsePragmaStructEnd(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname)
 {
 	ANKI_ASSERT(m_insideStruct);
 
-	const U tokenCount = U(end - begin);
-	if(tokenCount != 1)
+	if(begin != end)
 	{
 		ANKI_PP_ERROR_MALFORMED();
 	}
@@ -783,68 +653,36 @@ Error ShaderProgramParser::parsePragmaStructEnd(const String* begin, const Strin
 
 	if(gstruct.m_members.isEmpty())
 	{
-		ANKI_PP_ERROR_MALFORMED_MSG("The struct doesn't have any members");
+		ANKI_PP_ERROR_MALFORMED_MSG("Struct doesn't have any members");
 	}
 
-	m_codeLines.pushBack("};");
+	getAppendSourceList().pushBack("};");
 
 	for(U32 i = 0; i < gstruct.m_members.getSize(); ++i)
 	{
 		const Member& m = gstruct.m_members[i];
 
-		// #define XXX_OFFSETOF
-		if(i == 0)
-		{
-			m_codeLines.pushBackSprintf("#define %s_%s_OFFSETOF 0u", gstruct.m_name.cstr(), m.m_name.cstr());
-		}
-		else
-		{
-			const Member& prev = gstruct.m_members[i - 1];
-			m_codeLines.pushBackSprintf("#define %s_%s_OFFSETOF (%s_%s_OFFSETOF + %s_%s_SIZEOF)", structName.cstr(), m.m_name.cstr(),
-										structName.cstr(), prev.m_name.cstr(), structName.cstr(), prev.m_name.cstr());
-		}
-
-		// #if XXX_DEFINED
-		m_codeLines.pushBackSprintf("#if defined(%s_%s_DEFINED)", structName.cstr(), m.m_name.cstr());
-
-		// #	define XXX_SIZEOF
-		m_codeLines.pushBackSprintf("#\tdefine %s_%s_SIZEOF %uu", structName.cstr(), m.m_name.cstr(), getShaderVariableDataTypeInfo(m.m_type).m_size);
-
-		// #	define XXX_LOAD()
-		m_codeLines.pushBackSprintf("#\tdefine %s_%s_LOAD(buff, offset) buff.Load<%s>(%s_%s_OFFSETOF + (offset))%s", structName.cstr(),
-									m.m_name.cstr(), getShaderVariableDataTypeInfo(m.m_type).m_name, structName.cstr(), m.m_name.cstr(),
-									(i != gstruct.m_members.getSize() - 1) ? "," : "");
-
-		// #else
-		m_codeLines.pushBack("#else");
-
-		// #	define XXX_SIZEOF 0
-		m_codeLines.pushBackSprintf("#\tdefine %s_%s_SIZEOF 0u", structName.cstr(), m.m_name.cstr());
-
 		// #	define XXX_LOAD()
-		m_codeLines.pushBackSprintf("#\tdefine %s_%s_LOAD(buff, offset)", structName.cstr(), m.m_name.cstr());
-
-		// #endif
-		m_codeLines.pushBack("#endif");
+		getAppendSourceList().pushBackSprintf("#\tdefine %s_%s_LOAD(buff, offset) buff.Load<%s>(%s_%s_OFFSETOF + (offset))%s", structName.cstr(),
+											  m.m_name.cstr(), getShaderVariableDataTypeInfo(m.m_type).m_name, structName.cstr(), m.m_name.cstr(),
+											  (i != gstruct.m_members.getSize() - 1) ? "," : "");
 	}
 
 	// Now define the structure LOAD in HLSL
-	m_codeLines.pushBackSprintf("#define load%s(buff, offset) { \\", structName.cstr());
+	getAppendSourceList().pushBackSprintf("#define load%s(buff, offset) { \\", structName.cstr());
 	for(U32 i = 0; i < gstruct.m_members.getSize(); ++i)
 	{
 		const Member& m = gstruct.m_members[i];
-		m_codeLines.pushBackSprintf("\t%s_%s_LOAD(buff, offset) \\", structName.cstr(), m.m_name.cstr());
+		getAppendSourceList().pushBackSprintf("\t%s_%s_LOAD(buff, offset) \\", structName.cstr(), m.m_name.cstr());
 	}
-	m_codeLines.pushBack("}");
-
-	// Define the actual struct
-	m_codeLines.pushBackSprintf("#define %s %s_", structName.cstr(), structName.cstr());
+	getAppendSourceList().pushBack("}");
 
+	// Done
 	m_insideStruct = false;
 	return Error::kNone;
 }
 
-Error ShaderProgramParser::parsePragma16bit(const String* begin, const String* end, CString line, CString fname)
+Error ShaderProgramParser::parsePragma16bit(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname)
 {
 	ANKI_ASSERT(begin && end);
 
@@ -870,44 +708,46 @@ Error ShaderProgramParser::parseFile(CString fname, U32 depth)
 	Bool foundPragmaOnce = false;
 
 	// Load file in lines
-	String txt;
+	ShaderCompilerString txt;
 	ANKI_CHECK(m_fsystem->readAllText(fname, txt));
 
-	StringList lines;
+	m_hash = (m_hash) ? computeHash(txt.cstr(), txt.getLength()) : appendHash(txt.cstr(), txt.getLength(), m_hash);
+
+	ShaderCompilerStringList lines;
 	lines.splitString(txt, '\n', true);
 	if(lines.getSize() < 1)
 	{
 		ANKI_SHADER_COMPILER_LOGE("Source is empty");
 	}
 
-	m_codeLines.pushBackSprintf("#line 0 \"%s\"", fname.cstr());
+	getAppendSourceList().pushBackSprintf("#line 0 \"%s\"", fname.cstr());
 
 	// Parse lines
-	U32 lineCount = 0;
-	for(const String& line : lines)
+	U32 lineNo = 1;
+	for(const ShaderCompilerString& line : lines)
 	{
 		if(line.isEmpty())
 		{
-			m_codeLines.pushBack(" ");
+			getAppendSourceList().pushBack(" ");
 		}
-		else if(line.find("pragma") != String::kNpos || line.find("include") != String::kNpos)
+		else if(line.find("pragma") != ShaderCompilerString::kNpos || line.find("include") != ShaderCompilerString::kNpos)
 		{
 			// Possibly a preprocessor directive we care
-			ANKI_CHECK(parseLine(line.toCString(), fname, foundPragmaOnce, depth, lineCount));
+			ANKI_CHECK(parseLine(line.toCString(), fname, foundPragmaOnce, depth, lineNo));
 		}
 		else
 		{
 			// Just append the line
-			m_codeLines.pushBack(line.toCString());
+			getAppendSourceList().pushBack(line.toCString());
 		}
 
-		++lineCount;
+		++lineNo;
 	}
 
 	if(foundPragmaOnce)
 	{
 		// Append the guard
-		m_codeLines.pushBack("#endif // Include guard");
+		getAppendSourceList().pushBack("#endif // Include guard");
 	}
 
 	return Error::kNone;
@@ -916,7 +756,7 @@ Error ShaderProgramParser::parseFile(CString fname, U32 depth)
 Error ShaderProgramParser::parse()
 {
 	ANKI_ASSERT(!m_fname.isEmpty());
-	ANKI_ASSERT(m_codeLines.isEmpty());
+	ANKI_ASSERT(m_commonSourceLines.isEmpty());
 
 	const CString fname = m_fname;
 
@@ -925,124 +765,91 @@ Error ShaderProgramParser::parse()
 
 	// Checks
 	{
-		if(!m_shaderTypes)
+		if(m_techniques.getSize() == 0)
 		{
-			ANKI_SHADER_COMPILER_LOGE("Haven't found any shader types");
+			ANKI_SHADER_COMPILER_LOGE("No techniques were found");
 			return Error::kUserData;
 		}
 
-		if(!!(m_shaderTypes & ShaderTypeBit::kCompute))
-		{
-			if(m_shaderTypes != ShaderTypeBit::kCompute)
-			{
-				ANKI_SHADER_COMPILER_LOGE("Can't combine compute shader with other types of shaders");
-				return Error::kUserData;
-			}
-		}
-		else if(!!(m_shaderTypes & ShaderTypeBit::kAllGraphics))
+		if(insideTechnique())
 		{
-			if(!(m_shaderTypes & ShaderTypeBit::kVertex))
-			{
-				ANKI_SHADER_COMPILER_LOGE("Missing vertex shader");
-				return Error::kUserData;
-			}
-
-			if(!(m_shaderTypes & ShaderTypeBit::kFragment))
-			{
-				ANKI_SHADER_COMPILER_LOGE("Missing fragment shader");
-				return Error::kUserData;
-			}
+			ANKI_SHADER_COMPILER_LOGE("Forgot to end a technique");
+			return Error::kUserData;
 		}
 
-		if(m_insideShader != ShaderType::kCount)
+		if(m_insideStruct)
 		{
-			ANKI_SHADER_COMPILER_LOGE("Forgot a \"pragma anki end\"");
+			ANKI_SHADER_COMPILER_LOGE("Forgot to end a struct");
 			return Error::kUserData;
 		}
 	}
 
-	// Create the code lines
-	if(m_codeLines.getSize())
+	// Create the code lines for each technique
+	for(U32 i = 0; i < m_techniques.getSize(); ++i)
 	{
-		m_codeLines.join("\n", m_codeSource);
-		m_codeLines.destroy();
-	}
-
-	// Create the hash
-	{
-		if(m_codeSource.getLength())
+		for(ShaderType s : EnumIterable<ShaderType>())
 		{
-			m_codeSourceHash = appendHash(m_codeSource.getBegin(), m_codeSource.getLength(), kShaderHeaderHash);
-		}
-
-		if(m_libName.getLength() > 0)
-		{
-			m_codeSourceHash = appendHash(m_libName.getBegin(), m_libName.getLength(), m_codeSourceHash);
+			if(m_techniqueExtras[i].m_sourceLines[s].getSize())
+			{
+				ANKI_ASSERT(!!(m_techniques[i].m_shaderTypes & ShaderTypeBit(1 << s)));
+				m_techniqueExtras[i].m_sourceLines[s].join("\n", m_techniqueExtras[i].m_sources[s]);
+				m_techniqueExtras[i].m_sourceLines[s].destroy(); // Free mem
+			}
+			else
+			{
+				ANKI_ASSERT(!(m_techniques[i].m_shaderTypes & ShaderTypeBit(1 << s)));
+			}
 		}
-
-		m_codeSourceHash = appendHash(&m_rayType, sizeof(m_rayType), m_codeSourceHash);
 	}
 
+	m_commonSourceLines.destroy(); // Free mem
+
 	return Error::kNone;
 }
 
-void ShaderProgramParser::generateAnkiShaderHeader(ShaderType shaderType, const ShaderCompilerOptions& compilerOptions, String& header)
+void ShaderProgramParser::generateAnkiShaderHeader(ShaderType shaderType, const ShaderCompilerOptions& compilerOptions, ShaderCompilerString& header)
 {
 	header.sprintf(kShaderHeader, kShaderStageNames[shaderType].cstr(), compilerOptions.m_mobilePlatform,
 				   compilerOptions.m_forceFullFloatingPointPrecision, kMaxBindlessTextures, kMaxBindlessReadonlyTextureBuffers);
 }
 
-Error ShaderProgramParser::generateVariant(ConstWeakArray<MutatorValue> mutation, ShaderProgramParserVariant& variant) const
+void ShaderProgramParser::generateVariant(ConstWeakArray<MutatorValue> mutation, const ShaderProgramParserTechnique& technique, ShaderType shaderType,
+										  ShaderCompilerString& source) const
 {
 	// Sanity checks
-	ANKI_ASSERT(m_codeSource.getLength() > 0);
 	ANKI_ASSERT(mutation.getSize() == m_mutators.getSize());
 	for(U32 i = 0; i < mutation.getSize(); ++i)
 	{
 		ANKI_ASSERT(mutatorHasValue(m_mutators[i], mutation[i]) && "Value not found");
 	}
+	ANKI_ASSERT(!!(technique.m_shaderTypes & ShaderTypeBit(1 << shaderType)));
 
-	// Init variant
-	::new(&variant) ShaderProgramParserVariant();
-
-	// Create the mutator defines
-	String mutatorDefines;
+	source.destroy();
 	for(U32 i = 0; i < mutation.getSize(); ++i)
 	{
-		mutatorDefines += String().sprintf("#define %s %d\n", m_mutators[i].m_name.cstr(), mutation[i]);
+		source += ShaderCompilerString().sprintf("#define %s %d\n", m_mutators[i].m_name.cstr(), mutation[i]);
 	}
 
-	// Generate souce per stage
-	for(ShaderType shaderType : EnumIterable<ShaderType>())
-	{
-		if(!(ShaderTypeBit(1u << shaderType) & m_shaderTypes))
-		{
-			continue;
-		}
-
-		// Create the header
-		String header;
-		generateAnkiShaderHeader(shaderType, m_compilerOptions, header);
+	source += ShaderCompilerString().sprintf("#define ANKI_TECHNIQUE_%s 1\n", technique.m_name.cstr());
 
-		// Create the final source without the bindings
-		String finalSource;
-		finalSource += header;
-		if(m_16bitTypes)
-		{
-			finalSource += "#define ANKI_SUPPORTS_16BIT_TYPES 1\n";
-		}
-		else
-		{
-			finalSource += "#define ANKI_SUPPORTS_16BIT_TYPES 0\n";
-		}
-		finalSource += mutatorDefines;
-		finalSource += m_codeSource;
+	ShaderCompilerString header;
+	generateAnkiShaderHeader(shaderType, m_compilerOptions, header);
+	source += header;
 
-		// Move the source
-		variant.m_sources[shaderType] = std::move(finalSource);
+	if(m_16bitTypes)
+	{
+		source += "#define ANKI_SUPPORTS_16BIT_TYPES 1\n";
+	}
+	else
+	{
+		source += "#define ANKI_SUPPORTS_16BIT_TYPES 0\n";
 	}
 
-	return Error::kNone;
+	ANKI_ASSERT(&technique >= m_techniques.getBegin() && &technique < m_techniques.getEnd());
+	const U32 tIdx = U32(&technique - m_techniques.getBegin());
+
+	ANKI_ASSERT(m_techniqueExtras[tIdx].m_sources[shaderType].getLength() > 0);
+	source += m_techniqueExtras[tIdx].m_sources[shaderType];
 }
 
 Bool ShaderProgramParser::mutatorHasValue(const ShaderProgramParserMutator& mutator, MutatorValue value)

+ 64 - 90
AnKi/ShaderCompiler/ShaderProgramParser.h

@@ -22,55 +22,34 @@ class ShaderProgramParserVariant;
 /// @memberof ShaderProgramParser
 class ShaderProgramParserMutator
 {
-	friend ShaderProgramParser;
-
 public:
-	CString getName() const
-	{
-		return m_name;
-	}
-
-	ConstWeakArray<MutatorValue> getValues() const
-	{
-		return m_values;
-	}
-
-private:
-	String m_name;
-	DynamicArray<MutatorValue> m_values;
+	ShaderCompilerString m_name;
+	ShaderCompilerDynamicArray<MutatorValue> m_values;
 };
 
 /// @memberof ShaderProgramParser
 class ShaderProgramParserMember
 {
 public:
-	String m_name;
+	ShaderCompilerString m_name;
 	ShaderVariableDataType m_type;
-	U32 m_dependentMutator = kMaxU32;
-	MutatorValue m_mutatorValue = 0;
+	U32 m_offset = kMaxU32;
 };
 
 /// @memberof ShaderProgramParser
 class ShaderProgramParserGhostStruct
 {
 public:
-	DynamicArray<ShaderProgramParserMember> m_members;
-	String m_name;
+	ShaderCompilerDynamicArray<ShaderProgramParserMember> m_members;
+	ShaderCompilerString m_name;
 };
 
 /// @memberof ShaderProgramParser
-class ShaderProgramParserVariant
+class ShaderProgramParserTechnique
 {
-	friend class ShaderProgramParser;
-
 public:
-	CString getSource(ShaderType type) const
-	{
-		return m_sources[type];
-	}
-
-private:
-	Array<String, U32(ShaderType::kCount)> m_sources;
+	ShaderCompilerString m_name;
+	ShaderTypeBit m_shaderTypes = ShaderTypeBit::kNone;
 };
 
 /// This is a special preprocessor that run before the usual preprocessor. Its purpose is to add some meta information
@@ -80,18 +59,15 @@ private:
 /// #include {<> | ""}
 /// #pragma once
 /// #pragma anki mutator NAME VALUE0 [VALUE1 [VALUE2] ...]
-/// #pragma anki start {vert | tessc | tesse | geom | task | mesh | frag | comp | rgen | ahit | chit | miss | int | call}
-/// #pragma anki end {vert | tessc | tesse | geom | task | mesh | frag | comp | rgen | ahit | chit | miss | int | call}
-/// #pragma anki library "name"
-/// #pragma anki ray_type NUMBER
-/// #pragma anki reflect NAME
-/// #pragma anki skip_mutation MUTATOR0 VALUE0 MUTATOR1 VALUE1 [MUTATOR2 VALUE2 ...]
+/// #pragma anki skip_mutation MUTATOR0 VALUE0 [MUTATOR1 VALUE1 [MUTATOR2 VALUE2] ...]
 /// #pragma anki 16bit // Works only in HLSL. Gain 16bit types but loose min16xxx types
+/// #pragma anki technique_start {vert | tessc | tesse | geom | task | mesh | frag | comp | rgen | ahit | chit | miss | int | call} [NAME]
+/// #pragma anki technique_end {vert | tessc | tesse | geom | task | mesh | frag | comp | rgen | ahit | chit | miss | int | call} [NAME]
 ///
 /// #pragma anki struct NAME
-/// #	pragma anki member [ANKI_RP] TYPE NAME [if MUTATOR_NAME is MUTATOR_VALUE]
+/// #	pragma anki member TYPE NAME
 /// 	...
-/// #pragma anki struct end
+/// #pragma anki struct_end
 ///
 /// None of the pragmas should be in an ifdef-like guard. It's ignored.
 class ShaderProgramParser
@@ -112,42 +88,28 @@ public:
 	Bool skipMutation(ConstWeakArray<MutatorValue> mutation) const;
 
 	/// Get the source (and a few more things) given a list of mutators.
-	Error generateVariant(ConstWeakArray<MutatorValue> mutation, ShaderProgramParserVariant& variant) const;
+	void generateVariant(ConstWeakArray<MutatorValue> mutation, const ShaderProgramParserTechnique& technique, ShaderType shaderType,
+						 ShaderCompilerString& source) const;
 
 	ConstWeakArray<ShaderProgramParserMutator> getMutators() const
 	{
 		return m_mutators;
 	}
 
-	ShaderTypeBit getShaderTypes() const
-	{
-		return m_shaderTypes;
-	}
-
 	U64 getHash() const
 	{
-		ANKI_ASSERT(m_codeSourceHash != 0);
-		return m_codeSourceHash;
+		ANKI_ASSERT(m_hash != 0);
+		return m_hash;
 	}
 
-	CString getLibraryName() const
-	{
-		return m_libName;
-	}
-
-	U32 getRayType() const
-	{
-		return m_rayType;
-	}
-
-	const StringList& getSymbolsToReflect() const
+	ConstWeakArray<ShaderProgramParserGhostStruct> getGhostStructs() const
 	{
-		return m_symbolsToReflect;
+		return m_ghostStructs;
 	}
 
-	ConstWeakArray<ShaderProgramParserGhostStruct> getGhostStructs() const
+	ConstWeakArray<ShaderProgramParserTechnique> getTechniques() const
 	{
-		return m_ghostStructs;
+		return m_techniques;
 	}
 
 	Bool compileWith16bitTypes() const
@@ -156,61 +118,73 @@ public:
 	}
 
 	/// Generates the common header that will be used by all AnKi shaders.
-	static void generateAnkiShaderHeader(ShaderType shaderType, const ShaderCompilerOptions& compilerOptions, String& header);
+	static void generateAnkiShaderHeader(ShaderType shaderType, const ShaderCompilerOptions& compilerOptions, ShaderCompilerString& header);
 
 private:
 	using Mutator = ShaderProgramParserMutator;
 	using Member = ShaderProgramParserMember;
 	using GhostStruct = ShaderProgramParserGhostStruct;
+	using Technique = ShaderProgramParserTechnique;
 
 	class PartialMutationSkip
 	{
 	public:
-		DynamicArray<MutatorValue> m_partialMutation;
+		ShaderCompilerDynamicArray<MutatorValue> m_partialMutation;
+	};
+
+	class TechniqueExtra
+	{
+	public:
+		Array<ShaderCompilerStringList, U32(ShaderType::kCount)> m_sourceLines;
+		Array<ShaderCompilerString, U32(ShaderType::kCount)> m_sources;
 	};
 
 	static constexpr U32 kMaxIncludeDepth = 8;
 
-	String m_fname;
+	ShaderCompilerString m_fname;
 	ShaderProgramFilesystemInterface* m_fsystem = nullptr;
+	ShaderCompilerOptions m_compilerOptions;
 
-	StringList m_codeLines; ///< The code.
-	String m_codeSource;
-	U64 m_codeSourceHash = 0;
+	U64 m_hash = 0;
 
-	DynamicArray<Mutator> m_mutators;
-	DynamicArray<PartialMutationSkip> m_skipMutations;
+	ShaderCompilerStringList m_commonSourceLines; ///< Common code until now.
 
-	ShaderTypeBit m_shaderTypes = ShaderTypeBit::kNone;
-	ShaderType m_insideShader = ShaderType::kCount;
-	ShaderCompilerOptions m_compilerOptions;
+	ShaderCompilerDynamicArray<Technique> m_techniques;
+	ShaderCompilerDynamicArray<TechniqueExtra> m_techniqueExtras;
+	U32 m_insideTechniqueIdx = kMaxU32;
+	ShaderType m_insideTechniqueShaderType = ShaderType::kCount;
 
-	String m_libName;
-	U32 m_rayType = kMaxU32;
+	ShaderCompilerDynamicArray<Mutator> m_mutators;
+	ShaderCompilerDynamicArray<PartialMutationSkip> m_skipMutations;
 
-	StringList m_symbolsToReflect;
-
-	DynamicArray<GhostStruct> m_ghostStructs;
+	ShaderCompilerDynamicArray<GhostStruct> m_ghostStructs;
 	Bool m_insideStruct = false;
 
 	Bool m_16bitTypes = false;
 
+	ShaderCompilerStringList& getAppendSourceList()
+	{
+		return (insideTechnique()) ? m_techniqueExtras.getBack().m_sourceLines[m_insideTechniqueShaderType] : m_commonSourceLines;
+	}
+
+	Bool insideTechnique() const
+	{
+		return m_insideTechniqueIdx < kMaxU32;
+	}
+
 	Error parseFile(CString fname, U32 depth);
 	Error parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth, U32 lineNumber);
-	Error parseInclude(const String* begin, const String* end, CString line, CString fname, U32 depth);
-	Error parsePragmaMutator(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaStart(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaEnd(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaSkipMutation(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaLibraryName(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaRayType(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaReflect(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaStructBegin(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaStructEnd(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragmaMember(const String* begin, const String* end, CString line, CString fname);
-	Error parsePragma16bit(const String* begin, const String* end, CString line, CString fname);
-
-	void tokenizeLine(CString line, DynamicArray<String>& tokens) const;
+	Error parseInclude(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname, U32 depth);
+	Error parsePragmaMutator(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname);
+	Error parsePragmaTechniqueStart(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname);
+	Error parsePragmaTechniqueEnd(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname);
+	Error parsePragmaSkipMutation(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname);
+	Error parsePragmaStructBegin(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname);
+	Error parsePragmaStructEnd(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname);
+	Error parsePragmaMember(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname);
+	Error parsePragma16bit(const ShaderCompilerString* begin, const ShaderCompilerString* end, CString line, CString fname);
+
+	void tokenizeLine(CString line, ShaderCompilerDynamicArray<ShaderCompilerString>& tokens) const;
 
 	static Bool tokenIsComment(CString token)
 	{

+ 2 - 2
AnKi/Shaders/ApplyIrradianceToReflection.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/PackFunctions.hlsl>
 #include <AnKi/Shaders/LightFunctions.hlsl>
 
@@ -49,4 +49,4 @@
 	// Write it back
 	g_cubeTex[faceIdx][dispatchThreadId] = RVec4(prevColorWithIndirectDiffuse, 0.0);
 }
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/BlitCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/Blit.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/BlitRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/Blit.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/BloomCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/Bloom.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/BloomRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/Bloom.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/BloomUpscaleCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/BloomUpscale.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/BloomUpscaleRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/BloomUpscale.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/ClearTextureCompute.ankiprog

@@ -8,7 +8,7 @@
 #pragma anki mutator TEXTURE_DIMENSIONS 2 3
 #pragma anki mutator COMPONENT_TYPE 0 1 // 0 is float, 1 is uint
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/Common.hlsl>
 
 struct Constants
@@ -61,4 +61,4 @@ struct Constants
 #endif
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/ClusterBinning.ankiprog

@@ -7,7 +7,7 @@
 
 #pragma anki mutator OBJECT_TYPE 0 1 2 3 4 // Same as GpuSceneNonRenderableObjectType
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
 #include <AnKi/Shaders/VisibilityAndCollisionFunctions.hlsl>
@@ -226,4 +226,4 @@ constexpr UVec2 kSampleLocations[kSampleCount] = {LOCATION(1, -3), LOCATION(-1,
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/ClusterBinningPackVisibles.ankiprog

@@ -5,7 +5,7 @@
 
 #pragma anki mutator OBJECT_TYPE 0 1 2 3 4 // Same as GpuSceneNonRenderableObjectType
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
@@ -74,4 +74,4 @@ typedef GpuSceneGlobalIlluminationProbe GpuSceneType;
 #endif
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/ClusterBinningSetup.ankiprog

@@ -5,7 +5,7 @@
 
 // This shader prepares the indirect args of future dispatches
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Common.hlsl>
 #include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
@@ -68,4 +68,4 @@ constexpr U32 kPackVisiblesThreadgroupSize = 64;
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/DbgBillboard.ankiprog

@@ -46,7 +46,7 @@ struct VertOut
 	nointerpolation U32 m_textureIndex : TEX_INDEX;
 };
 
-#pragma anki start vert
+#pragma anki technique_start vert
 
 struct VertIn
 {
@@ -97,9 +97,9 @@ VertOut main(VertIn input)
 
 	return output;
 }
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/ImportanceSampling.hlsl>
 
 [[vk::binding(4)]] SamplerState g_trilinearRepeatSampler;
@@ -140,4 +140,4 @@ Vec4 main(VertOut input) : SV_TARGET0
 		return g_tex2.Sample(g_trilinearRepeatSampler, input.m_uv) * input.m_colorScale * colorFactor;
 	}
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 4 - 4
AnKi/Shaders/DbgRenderables.ankiprog

@@ -24,7 +24,7 @@ struct VertOut
 	Vec4 m_svPosition : SV_POSITION;
 };
 
-#pragma anki start vert
+#pragma anki technique_start vert
 struct VertIn
 {
 	[[vk::location(0)]] Vec3 m_position : POSITION;
@@ -52,9 +52,9 @@ VertOut main(VertIn input)
 
 	return output;
 }
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/ImportanceSampling.hlsl>
 
 // NOTE: Don't eliminate the binding because it confuses the descriptor set creation
@@ -83,4 +83,4 @@ Vec4 main(VertOut input) : SV_TARGET0
 	// Write the color
 	return g_consts.m_color;
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/DepthAwareBlurCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/DepthAwareBlur.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/DepthAwareBlurRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/DepthAwareBlur.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/DepthDownscaleCompute.ankiprog

@@ -7,7 +7,7 @@
 
 #pragma anki mutator WAVE_OPERATIONS 0 1
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/Common.hlsl>
 #include <AnKi/Shaders/Include/MiscRendererTypes.h>
 
@@ -94,4 +94,4 @@ AF4 SpdReduce4(AF4 v0, AF4 v1, AF4 v2, AF4 v3)
 	SpdDownsample(AU2(svGroupId.xy), AU1(svGroupIndex), AU1(g_consts.m_mipmapCount), AU1(g_consts.m_threadgroupCount), slice, offset);
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/DepthDownscaleRaster.ankiprog

@@ -3,11 +3,11 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/Common.hlsl>
 
 [[vk::binding(0)]] Texture2D<Vec4> g_inputTex;
@@ -17,4 +17,4 @@ F32 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 {
 	return g_inputTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).x;
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/DownscaleBlurCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/DownscaleBlur.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/DownscaleBlurRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/DownscaleBlur.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 4 - 4
AnKi/Shaders/DrawerStats.ankiprog

@@ -21,7 +21,7 @@ struct Constants
 
 [[vk::push_constant]] ConstantBuffer<Constants> g_consts;
 
-#pragma anki start vert
+#pragma anki technique_start vert
 
 struct VertOut
 {
@@ -39,9 +39,9 @@ VertOut main(U32 vertId : SV_VERTEXID)
 
 	return output;
 }
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 
 [[vk::binding(0)]] StructuredBuffer<U32> g_mdiDrawCounts;
 [[vk::binding(1)]] StructuredBuffer<DrawIndexedIndirectArgs> g_drawArguments;
@@ -120,4 +120,4 @@ main(Vec4 svPosition : SV_POSITION)
 	return (FragOut)0;
 #endif
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 4 - 4
AnKi/Shaders/FinalComposite.ankiprog

@@ -7,11 +7,11 @@
 #pragma anki mutator BLOOM_ENABLED 0 1
 #pragma anki mutator DBG_ENABLED 0 1
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/Functions.hlsl>
 #include <AnKi/Shaders/MotionBlur.hlsl>
 
@@ -83,4 +83,4 @@ RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 	return outColor;
 }
 
-#pragma anki end frag
+#pragma anki technique_end frag

+ 5 - 8
AnKi/Shaders/ForwardShadingFog.ankiprog

@@ -3,17 +3,14 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki mutator ANKI_TECHNIQUE 2
-
 #include <AnKi/Shaders/ForwardShadingCommon.hlsl>
 #include <AnKi/Shaders/Functions.hlsl>
 
-#pragma anki reflect AnKiLocalConstants
 #pragma anki struct AnKiLocalConstants
 #pragma anki member RVec3 m_fogColor
 #pragma anki member RF32 m_fogAlphaScale
 #pragma anki member RF32 m_fogDistanceOfMaxThikness
-#pragma anki struct end
+#pragma anki struct_end
 
 struct VertIn
 {
@@ -28,7 +25,7 @@ struct VertOut
 	nointerpolation U32 m_constantsOffset : UNIFORMS;
 };
 
-#pragma anki start vert
+#pragma anki technique_start vert Forward
 
 VertOut main(VertIn input)
 {
@@ -50,9 +47,9 @@ VertOut main(VertIn input)
 	return output;
 }
 
-#pragma anki end vert
+#pragma anki technique_end vert Forward
 
-#pragma anki start frag
+#pragma anki technique_start frag Forward
 
 FragOut main(VertOut input)
 {
@@ -64,4 +61,4 @@ FragOut main(VertOut input)
 
 	return output;
 }
-#pragma anki end frag
+#pragma anki technique_end frag Forward

+ 6 - 8
AnKi/Shaders/ForwardShadingGenericTransparent.ankiprog

@@ -3,18 +3,16 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki mutator ANKI_TECHNIQUE 2
 #pragma anki mutator TEXTURE 0 1
 #pragma anki mutator LIGHT 0 1
 
 #include <AnKi/Shaders/ForwardShadingCommon.hlsl>
 
-#pragma anki reflect AnKiLocalConstants
 #pragma anki struct AnKiLocalConstants
-#pragma anki member U32 m_texture if TEXTURE is 1
+#pragma anki member U32 m_texture
 #pragma anki member RVec4 m_colorScale
 #pragma anki member RVec4 m_colorBias
-#pragma anki struct end
+#pragma anki struct_end
 
 struct VertIn
 {
@@ -30,7 +28,7 @@ struct VertOut
 	nointerpolation U32 m_constantsOffset : UNIFORMS_OFFSET;
 };
 
-#pragma anki start vert
+#pragma anki technique_start vert Forward
 
 VertOut main(VertIn input)
 {
@@ -50,9 +48,9 @@ VertOut main(VertIn input)
 
 	return output;
 }
-#pragma anki end vert
+#pragma anki technique_end vert Forward
 
-#pragma anki start frag
+#pragma anki technique_start frag Forward
 
 FragOut main(VertOut input)
 {
@@ -75,4 +73,4 @@ FragOut main(VertOut input)
 
 	return output;
 }
-#pragma anki end frag
+#pragma anki technique_end frag Forward

+ 6 - 8
AnKi/Shaders/ForwardShadingParticles.ankiprog

@@ -3,7 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki mutator ANKI_TECHNIQUE 2
 #pragma anki mutator ANIMATED_TEXTURE 0 1
 #pragma anki mutator LIGHT 0 1
 
@@ -24,15 +23,14 @@ struct VertOut
 	Vec4 m_svPosition : SV_POSITION;
 };
 
-#pragma anki reflect AnKiLocalConstants
 #pragma anki struct AnKiLocalConstants
-#pragma anki member F32 m_animationPeriod if ANIMATED_TEXTURE is 1
+#pragma anki member F32 m_animationPeriod
 #pragma anki member Vec4 m_colorScale
 #pragma anki member Vec4 m_colorBias
 #pragma anki member U32 m_diffuseMap
-#pragma anki struct end
+#pragma anki struct_end
 
-#pragma anki start vert
+#pragma anki technique_start vert Forward
 
 VertOut main(VertIn input)
 {
@@ -68,9 +66,9 @@ VertOut main(VertIn input)
 
 	return output;
 }
-#pragma anki end vert
+#pragma anki technique_end vert Forward
 
-#pragma anki start frag
+#pragma anki technique_start frag Forward
 
 FragOut main(VertOut input)
 {
@@ -94,4 +92,4 @@ FragOut main(VertOut input)
 
 	return output;
 }
-#pragma anki end frag
+#pragma anki technique_end frag Forward

+ 2 - 2
AnKi/Shaders/FsrCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/Fsr.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/FsrRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/Fsr.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 227 - 143
AnKi/Shaders/GBufferGeneric.ankiprog

@@ -4,7 +4,6 @@
 // http://www.anki3d.org/LICENSE
 
 #pragma anki mutator ANKI_VELOCITY 0 1
-#pragma anki mutator ANKI_TECHNIQUE 0 1
 #pragma anki mutator ANKI_BONES 0 1
 #pragma anki mutator DIFFUSE_TEX 0 1
 #pragma anki mutator SPECULAR_TEX 0 1
@@ -16,15 +15,15 @@
 #pragma anki mutator ALPHA_TEST 0 1
 
 #pragma anki skip_mutation ALPHA_TEST 1 DIFFUSE_TEX 0
-#pragma anki skip_mutation ANKI_VELOCITY 1 ANKI_TECHNIQUE 1
 
 // Some defines the clear up things
+#define GBUFFER defined(ANKI_TECHNIQUE_GBuffer)
 #define REALLY_ALPHA_TEST (ALPHA_TEST && DIFFUSE_TEX)
-#define UVS (ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER || REALLY_ALPHA_TEST)
-#define REALLY_VELOCITY ((ANKI_VELOCITY || ANKI_BONES) && ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER)
-#define REALLY_USING_PARALLAX (PARALLAX == 1 && ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER && ALPHA_TEST == 0)
+#define UVS (GBUFFER || REALLY_ALPHA_TEST)
+#define REALLY_VELOCITY ((ANKI_VELOCITY || ANKI_BONES) && GBUFFER)
+#define REALLY_USING_PARALLAX (PARALLAX == 1 && GBUFFER && ALPHA_TEST == 0)
 
-#define VISUALIZE_MESHLETS (0 && ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER)
+#define VISUALIZE_MESHLETS (0 && GBUFFER)
 #define MESHLET_BACKFACE_CULLING 0
 #define MESHLET_OUTSIDE_OF_SCREEN_CULLING 1
 #define MESHLET_NO_SAMPLING_POINT_CULLING 1
@@ -38,33 +37,27 @@
 #include <AnKi/Shaders/PackFunctions.hlsl>
 #include <AnKi/Shaders/Functions.hlsl>
 #include <AnKi/Shaders/MaterialShadersCommon.hlsl>
+#include <AnKi/Shaders/RtShadows.hlsl>
 
-#pragma anki reflect AnKiLocalConstants
 #pragma anki struct AnKiLocalConstants
 
-#pragma anki member RVec3 m_diffColor if DIFFUSE_TEX is 0
-#pragma anki member U32 m_diffTex if DIFFUSE_TEX is 1
-
-#pragma anki member U32 m_normalTex if NORMAL_TEX is 1
-
-#pragma anki member RF32 m_roughness if ROUGHNESS_TEX is 0
-#pragma anki member U32 m_roughnessTex if ROUGHNESS_TEX is 1
-
-#pragma anki member RVec3 m_specColor if SPECULAR_TEX is 0
-#pragma anki member U32 m_specTex if SPECULAR_TEX is 1
-
-#pragma anki member RF32 m_metallic if METAL_TEX is 0
-#pragma anki member U32 m_metallicTex if METAL_TEX is 1
-
-#pragma anki member RVec3 m_emission if EMISSIVE_TEX is 0
-#pragma anki member U32 m_emissiveTex if EMISSIVE_TEX is 1
-
-#pragma anki member RF32 m_heightmapScale if PARALLAX is 1
-#pragma anki member U32 m_heightTex if PARALLAX is 1
-
+#pragma anki member U32 m_diffTex
+#pragma anki member U32 m_normalTex
+#pragma anki member U32 m_roughnessTex
+#pragma anki member U32 m_metallicTex
+#pragma anki member U32 m_specTex
+#pragma anki member U32 m_emissiveTex
+#pragma anki member U32 m_heightTex
+
+#pragma anki member RVec3 m_diffColor
+#pragma anki member RF32 m_roughness
+#pragma anki member RVec3 m_specColor
+#pragma anki member RF32 m_metallic
+#pragma anki member RVec3 m_emission
+#pragma anki member RF32 m_heightmapScale
 #pragma anki member RF32 m_subsurface
 
-#pragma anki struct end
+#pragma anki struct_end
 
 struct VertIn
 {
@@ -85,7 +78,7 @@ struct VertOut
 	Vec3 m_crntClipXyw : CRNT_CLIP;
 #endif
 
-#if ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER
+#if GBUFFER
 	Vec3 m_worldPos : WORLD_POS;
 	RVec3 m_normal : NORMAL;
 #endif
@@ -97,7 +90,6 @@ struct VertOut
 #endif
 };
 
-#if ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER
 struct FragOut
 {
 	Vec4 m_color0 : SV_TARGET0;
@@ -105,9 +97,8 @@ struct FragOut
 	Vec4 m_color2 : SV_TARGET2;
 	Vec2 m_color3 : SV_TARGET3;
 };
-#endif
 
-struct MeshShaderPayload
+struct TaskOut
 {
 	U32 m_firstMeshletIndex;
 	U32 m_visibleMeshletsRelativeIndices[kMeshletGroupSize / sizeof(U32)];
@@ -118,6 +109,11 @@ struct MeshShaderPayload
 	F32 m_positionScale;
 };
 
+struct MeshPerPrimitiveOut
+{
+	I32 m_cullPrimitive : SV_CULLPRIMITIVE; // TODO: Make it Bool when https://github.com/microsoft/DirectXShaderCompiler/issues/6042 is fixed
+};
+
 struct Mat3x4_2
 {
 	Mat3x4 m_a;
@@ -148,7 +144,7 @@ void skinning(UnpackedMeshVertex vert, U32 boneTransformsOffset, inout Vec3 pos,
 		prevSkinMat = prevSkinMat + mats.m_b * vert.m_boneWeights[i];
 	}
 
-#	if ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER
+#	if GBUFFER
 	prevPos = mul(prevSkinMat, Vec4(pos, 1.0)).xyz;
 	normal = mul(skinMat, Vec4(normal, 0.0)).xyz;
 #	endif
@@ -159,7 +155,7 @@ void skinning(UnpackedMeshVertex vert, U32 boneTransformsOffset, inout Vec3 pos,
 }
 #endif
 
-#if(ANKI_VELOCITY || ANKI_BONES) && ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER
+#if(ANKI_VELOCITY || ANKI_BONES) && GBUFFER
 void velocity(Mat3x4 worldTransform, Mat3x4 prevWorldTransform, Vec3 prevLocalPos, inout VertOut output)
 {
 	ANKI_MAYBE_UNUSED(prevWorldTransform);
@@ -181,8 +177,10 @@ void velocity(Mat3x4 worldTransform, Mat3x4 prevWorldTransform, Vec3 prevLocalPo
 }
 #endif
 
-#pragma anki start vert
-
+// ===========================================================================
+// Vert                                                                      =
+// ===========================================================================
+#if defined(ANKI_VERTEX_SHADER)
 VertOut main(VertIn input)
 {
 	VertOut output;
@@ -195,38 +193,39 @@ VertOut main(VertIn input)
 	const Mat3x4 prevWorldTransform = g_gpuScene.Load<Mat3x4>(renderable.m_worldTransformsOffset + sizeof(Mat3x4));
 	ANKI_MAYBE_UNUSED(prevWorldTransform);
 
-#if UVS
+#	if UVS
 	output.m_uv = vert.m_uv;
-#endif
+#	endif
 	Vec3 prevPos = vert.m_position;
 	ANKI_MAYBE_UNUSED(prevPos);
 	output.m_constantsOffset = renderable.m_constantsOffset;
 
 	// Do stuff
-#if ANKI_BONES
+#	if ANKI_BONES
 	skinning(vert, renderable.m_boneTransformsOrParticleEmitterOffset, vert.m_position, prevPos, vert.m_normal);
-#endif
+#	endif
 
 	const Vec3 worldPos = mul(worldTransform, Vec4(vert.m_position, 1.0));
 	output.m_svPosition = mul(g_globalConstants.m_viewProjectionMatrix, Vec4(worldPos, 1.0));
 
-#if ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER
+#	if GBUFFER
 	output.m_worldPos = worldPos;
 	output.m_normal = mul(worldTransform, Vec4(vert.m_normal, 0.0));
-#endif
+#	endif
 
-#if REALLY_VELOCITY
+#	if REALLY_VELOCITY
 	velocity(worldTransform, prevWorldTransform, prevPos, output);
-#endif
+#	endif
 
 	return output;
 };
+#endif // defined(ANKI_VERTEX_SHADER)
 
-#pragma anki end vert
-
-#pragma anki start task
-
-groupshared MeshShaderPayload s_payload;
+// ===========================================================================
+// Task                                                                      =
+// ===========================================================================
+#if defined(ANKI_TASK_SHADER)
+groupshared TaskOut s_payload;
 groupshared U32 s_visibleMeshletCount;
 
 struct FirstPayload
@@ -276,9 +275,9 @@ struct FirstPayload
 		const Meshlet meshlet = g_meshlets[firstMeshlet + svGroupIndex];
 		const Mat3x4 worldTransform = g_gpuScene.Load<Mat3x4>(renderable.m_worldTransformsOffset);
 
-#if MESHLET_BACKFACE_CULLING
+#	if MESHLET_BACKFACE_CULLING
 		cull = cullBackfaceMeshlet(meshlet, worldTransform, g_globalConstants.m_cameraTransform.getTranslationPart());
-#endif
+#	endif
 
 		const Mat4 wordTransform4 = {worldTransform.m_row0, worldTransform.m_row1, worldTransform.m_row2, Vec4(0.0f, 0.0f, 0.0f, 1.0f)};
 		const Mat4 mvp = mul(g_globalConstants.m_viewProjectionMatrix, wordTransform4);
@@ -287,21 +286,21 @@ struct FirstPayload
 		F32 aabbMinDepth;
 		projectAabb(meshlet.m_aabbMin, meshlet.m_aabbMax, mvp, minNdc, maxNdc, aabbMinDepth);
 
-#if MESHLET_OUTSIDE_OF_SCREEN_CULLING
+#	if MESHLET_OUTSIDE_OF_SCREEN_CULLING
 		// Outside of the screen
 		cull = cull || (any(minNdc > 1.0f) || any(maxNdc < -1.0f));
-#endif
+#	endif
 
-#if MESHLET_NO_SAMPLING_POINT_CULLING
+#	if MESHLET_NO_SAMPLING_POINT_CULLING
 		// Sampling points test
 		const Vec2 windowCoordsMin = ndcToUv(minNdc) * g_globalConstants.m_viewport.zw;
 		const Vec2 windowCoordsMax = ndcToUv(maxNdc) * g_globalConstants.m_viewport.zw;
 		cull = cull || any(round(windowCoordsMin) == round(windowCoordsMax));
-#endif
+#	endif
 
-#if MESHLET_HZB_CULLING
+#	if MESHLET_HZB_CULLING
 		cull = cull || (g_globalConstants.m_enableHzbTesting == 1u && cullHzb(minNdc, maxNdc, aabbMinDepth, g_hzbTexture, g_nearestClampSampler));
-#endif
+#	endif
 
 		if(!cull)
 		{
@@ -318,28 +317,24 @@ struct FirstPayload
 
 	DispatchMesh(s_visibleMeshletCount, 1, 1, s_payload);
 }
+#endif
 
-#pragma anki end task
-
-#pragma anki start mesh
-
-#if PRIMITIVE_ANY_CULLING
+// ===========================================================================
+// Mesh                                                                      =
+// ===========================================================================
+#if defined(ANKI_MESH_SHADER)
+#	if PRIMITIVE_ANY_CULLING
 groupshared Vec2 s_windowCoords[kMaxVerticesPerMeshlet];
 groupshared F32 s_clipW[kMaxVerticesPerMeshlet];
-
-struct MeshletPrimitiveOut
-{
-	I32 m_cullPrimitive : SV_CULLPRIMITIVE; // TODO: Make it Bool when https://github.com/microsoft/DirectXShaderCompiler/issues/6042 is fixed
-};
-#endif
+#	endif
 
 [numthreads(ANKI_MESH_SHADER_THREADGROUP_SIZE, 1, 1)] [outputtopology("triangle")] void
-main(in payload MeshShaderPayload payload, out vertices VertOut verts[kMaxVerticesPerMeshlet], out indices UVec3 indices[kMaxPrimitivesPerMeshlet],
+main(in payload TaskOut payload, out vertices VertOut verts[kMaxVerticesPerMeshlet], out indices UVec3 indices[kMaxPrimitivesPerMeshlet],
 	 U32 svGroupId : SV_GROUPID, U32 svGroupIndex : SV_GROUPINDEX
-#if PRIMITIVE_ANY_CULLING
+#	if PRIMITIVE_ANY_CULLING
 	 ,
-	 out primitives MeshletPrimitiveOut primitives[kMaxPrimitivesPerMeshlet]
-#endif
+	 out primitives MeshPerPrimitiveOut primitives[kMaxPrimitivesPerMeshlet]
+#	endif
 ) {
 	const U32 groupIdx = svGroupId / 4u;
 	const U32 localIdx = svGroupId % 4u;
@@ -367,45 +362,45 @@ main(in payload MeshShaderPayload payload, out vertices VertOut verts[kMaxVertic
 			const Mat3x4 prevWorldTransform = g_gpuScene.Load<Mat3x4>(payload.m_worldTransformsOffset + sizeof(Mat3x4));
 			ANKI_MAYBE_UNUSED(prevWorldTransform);
 
-#if UVS
+#	if UVS
 			output.m_uv = vert.m_uv;
-#endif
+#	endif
 			Vec3 prevPos = vert.m_position;
 			ANKI_MAYBE_UNUSED(prevPos);
 			output.m_constantsOffset = payload.m_constantsOffset;
 
 			// Do stuff
-#if ANKI_BONES
+#	if ANKI_BONES
 			skinning(vert, payload.m_boneTransformsOrParticleEmitterOffset, vert.m_position, prevPos, vert.m_normal);
-#endif
+#	endif
 
 			const Vec3 worldPos = mul(worldTransform, Vec4(vert.m_position, 1.0));
 			output.m_svPosition = mul(g_globalConstants.m_viewProjectionMatrix, Vec4(worldPos, 1.0f));
-#if PRIMITIVE_ANY_CULLING
+#	if PRIMITIVE_ANY_CULLING
 			s_windowCoords[idx] = ndcToUv(output.m_svPosition.xy / output.m_svPosition.w) * g_globalConstants.m_viewport.zw;
 			s_clipW[idx] = output.m_svPosition.w;
-#endif
+#	endif
 
-#if ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER
+#	if GBUFFER
 			output.m_worldPos = worldPos;
 			output.m_normal = mul(worldTransform, Vec4(vert.m_normal, 0.0));
-#endif
+#	endif
 
-#if REALLY_VELOCITY
+#	if REALLY_VELOCITY
 			velocity(worldTransform, prevWorldTransform, prevPos, output);
-#endif
+#	endif
 
-#if VISUALIZE_MESHLETS
+#	if VISUALIZE_MESHLETS
 			output.m_meshletIndex = relativeMeshletIdx;
-#endif
+#	endif
 
 			verts[idx] = output;
 		}
 	}
 
-#if PRIMITIVE_ANY_CULLING
+#	if PRIMITIVE_ANY_CULLING
 	GroupMemoryBarrierWithGroupSync();
-#endif
+#	endif
 
 	// Write the indices
 	const U32 primLoopCount = kMaxPrimitivesPerMeshlet / ANKI_MESH_SHADER_THREADGROUP_SIZE;
@@ -418,128 +413,127 @@ main(in payload MeshShaderPayload payload, out vertices VertOut verts[kMaxVertic
 			const UVec3 prim = g_unifiedGeom_R8G8B8A8_Uint[meshlet.m_firstPrimitive + idx].xyz;
 			indices[idx] = prim;
 
-#if PRIMITIVE_ANY_CULLING
+#	if PRIMITIVE_ANY_CULLING
 			Bool cull = false;
 
 			const Vec2 a = s_windowCoords[prim.x];
 			const Vec2 b = s_windowCoords[prim.y];
 			const Vec2 c = s_windowCoords[prim.z];
-#endif
+#	endif
 
-#if PRIMITIVE_BACKFACE_CULLING
+#	if PRIMITIVE_BACKFACE_CULLING
 			const Vec2 eb = c - a;
 			const Vec2 ec = b - a;
 
 			cull = cull || (eb.x * ec.y >= eb.y * ec.x);
-#endif
+#	endif
 
-#if PRIMITIVE_NO_SAMPLING_POINTS_CULLING
+#	if PRIMITIVE_NO_SAMPLING_POINTS_CULLING
 			const Vec2 windowCoordsMin = min3(a, b, c);
 			const Vec2 windowCoordsMax = max3(a, b, c);
 
 			cull = cull || any(round(windowCoordsMin) == round(windowCoordsMax));
-#endif
+#	endif
 
-#if PRIMITIVE_ANY_CULLING
+#	if PRIMITIVE_ANY_CULLING
 			// The computations above are only valid if all vertices are in front of perspective plane
 			cull = cull && min3(s_clipW[prim.x], s_clipW[prim.y], s_clipW[prim.z]) > 0.0f;
 
 			primitives[idx].m_cullPrimitive = cull;
-#endif
+#	endif
 		}
 	}
 }
+#endif // defined(ANKI_MESH_SHADER)
 
-#pragma anki end mesh
-
-#pragma anki start frag
-
-void doAlphaTest(RF32 alpha)
-{
-	if(alpha == 0.0)
-	{
-		discard;
-	}
-}
-
-#if ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_DEPTH
+// ===========================================================================
+// Frag                                                                      =
+// ===========================================================================
+#if defined(ANKI_FRAGMENT_SHADER)
+#	if !GBUFFER
 void main(VertOut input)
 {
 	ANKI_MAYBE_UNUSED(input);
-#	if REALLY_ALPHA_TEST
+#		if REALLY_ALPHA_TEST
 	const AnKiLocalConstants localConstants = loadAnKiLocalConstants(g_gpuScene, input.m_constantsOffset);
 	const RVec4 diffColorA = g_bindlessTextures2dF32[localConstants.m_diffTex].Sample(g_globalSampler, input.m_uv);
-	doAlphaTest(diffColorA.a);
-#	endif
+	if(diffColorA.a == 0.0f)
+	{
+		discard;
+	}
+#		endif
 }
 
-#elif ANKI_TECHNIQUE == ANKI_RENDERING_TECHNIQUE_GBUFFER
+#	else // GBUFFER
 
 FragOut main(VertOut input)
 {
 	const AnKiLocalConstants localConstants = loadAnKiLocalConstants(g_gpuScene, input.m_constantsOffset);
 
-#	if REALLY_USING_PARALLAX
+#		if REALLY_USING_PARALLAX
 	// TODO
 	const Vec2 uv = input.m_uv;
-#	else
+#		else
 	const Vec2 uv = input.m_uv;
-#	endif
+#		endif
 	ANKI_MAYBE_UNUSED(uv);
 
-#	if DIFFUSE_TEX
-#		if REALLY_ALPHA_TEST
+#		if DIFFUSE_TEX
+#			if REALLY_ALPHA_TEST
 	const RVec4 diffColorA = g_bindlessTextures2dF32[localConstants.m_diffTex].Sample(g_globalSampler, uv);
-	doAlphaTest(diffColorA.a);
+	if(diffColorA.a == 0.0f)
+	{
+		discard;
+	}
 	const RVec3 diffColor = diffColorA.rgb;
-#		else
+#			else
 	const RVec3 diffColor = g_bindlessTextures2dF32[localConstants.m_diffTex].Sample(g_globalSampler, uv).rgb;
-#		endif
-#	else
+#			endif
+#		else
 	const RVec3 diffColor = localConstants.m_diffColor;
-#	endif
+#		endif
 
-#	if SPECULAR_TEX
+#		if SPECULAR_TEX
 	const RVec3 specColor = g_bindlessTextures2dF32[localConstants.m_specTex].Sample(g_globalSampler, uv).rgb;
-#	else
+#		else
 	const RVec3 specColor = localConstants.m_specColor;
-#	endif
+#		endif
 
-#	if ROUGHNESS_TEX
+#		if ROUGHNESS_TEX
 	const RF32 roughness = g_bindlessTextures2dF32[localConstants.m_roughnessTex].Sample(g_globalSampler, uv).g;
-#	else
+#		else
 	const RF32 roughness = localConstants.m_roughness;
-#	endif
+#		endif
 
-#	if METAL_TEX
+#		if METAL_TEX
 	const RF32 metallic = g_bindlessTextures2dF32[localConstants.m_metallicTex].Sample(g_globalSampler, uv).b;
-#	else
+#		else
 	const RF32 metallic = localConstants.m_metallic;
-#	endif
+#		endif
 
-#	if NORMAL_TEX
+#		if NORMAL_TEX
 	const RVec3 nAtTangentspace = normalize((g_bindlessTextures2dF32[localConstants.m_normalTex].Sample(g_globalSampler, uv).rgb - 0.5) * 2.0);
 	const Vec3 viewDir = normalize(g_globalConstants.m_cameraTransform.getTranslationPart() - input.m_worldPos);
 	const RVec3 normal = perturbNormal(nAtTangentspace, viewDir, uv, normalize(input.m_normal));
-#	else
+#		else
 	const RVec3 normal = normalize(input.m_normal);
-#	endif
+#		endif
 
-#	if EMISSIVE_TEX
+#		if EMISSIVE_TEX
 	const RVec3 emission = g_bindlessTextures2dF32[localConstants.m_emissiveTex].Sample(g_globalSampler, uv).rgb;
-#	else
+#		else
 	const RVec3 emission = localConstants.m_emission;
-#	endif
+#		endif
 
-#	if ANKI_VELOCITY || ANKI_BONES
+#		if ANKI_VELOCITY || ANKI_BONES
 	const Vec2 prevNdc = input.m_prevClipXyw.xy / input.m_prevClipXyw.z;
 	const Vec2 crntNdc = input.m_crntClipXyw.xy / input.m_crntClipXyw.z;
 
 	// It's ndcToUv(prevNdc) - ndcToUv(crntNdc) or:
 	const Vec2 velocity = (prevNdc - crntNdc) * 0.5;
-#	else
+#		else
 	const Vec2 velocity = Vec2(1.0, 1.0);
-#	endif
+#		endif
 
 	GbufferInfo g;
 	g.m_diffuse = diffColor;
@@ -551,7 +545,7 @@ FragOut main(VertOut input)
 	g.m_metallic = metallic;
 	g.m_velocity = velocity;
 
-#	if VISUALIZE_MESHLETS
+#		if VISUALIZE_MESHLETS
 	const U32 meshletIdx = input.m_meshletIndex % 6u;
 	switch(meshletIdx)
 	{
@@ -574,12 +568,102 @@ FragOut main(VertOut input)
 		g.m_diffuse = Vec3(0.0f, 1.0f, 1.0f);
 		break;
 	}
-#	endif
+#		endif
 
 	FragOut output;
 	packGBuffer(g, output.m_color0, output.m_color1, output.m_color2, output.m_color3);
 	return output;
 }
-#endif
+#	endif
+#endif // defined(ANKI_FRAGMENT_SHADER)
+
+// ===========================================================================
+// RT Shadows                                                                =
+// ===========================================================================
+#if defined(ANKI_ANY_HIT_SHADER)
+
+#	if REALLY_ALPHA_TEST
+[[vk::shader_record_ext]] ConstantBuffer<GpuSceneRenderableVertex> g_gpuSceneRenderable;
+#	endif
+
+[shader("anyhit")] void main(inout RayPayload payload, in Barycentrics barycentrics)
+{
+	ANKI_MAYBE_UNUSED(payload);
+	ANKI_MAYBE_UNUSED(barycentrics);
+
+#	if REALLY_ALPHA_TEST
+	payload.m_shadowFactor = 1.0;
+
+	const Vec3 bary = Vec3(1.0f - barycentrics.m_value.x - barycentrics.m_value.y, barycentrics.m_value.x, barycentrics.m_value.y);
+
+	const GpuSceneMeshLod mesh = g_gpuScene.Load<GpuSceneMeshLod>(g_gpuSceneRenderable.m_meshLodOffset);
+
+	const U32 idx0 = g_unifiedGeom_R16_Uint[mesh.m_firstIndex + PrimitiveIndex() * 3 + 0];
+	const U32 idx1 = g_unifiedGeom_R16_Uint[mesh.m_firstIndex + PrimitiveIndex() * 3 + 1];
+	const U32 idx2 = g_unifiedGeom_R16_Uint[mesh.m_firstIndex + PrimitiveIndex() * 3 + 2];
+
+	const UnpackedMeshVertex vert0 = loadVertex(mesh, idx0, false);
+	const UnpackedMeshVertex vert1 = loadVertex(mesh, idx1, false);
+	const UnpackedMeshVertex vert2 = loadVertex(mesh, idx2, false);
+
+	const Vec2 uv = vert0.m_uv * bary.x + vert1.m_uv * bary.y + vert2.m_uv * bary.z;
+
+	const AnKiLocalConstants localConstants = loadAnKiLocalConstants(g_gpuScene, g_gpuSceneRenderable.m_constantsOffset);
+	const RVec4 diffColorA = g_bindlessTextures2dF32[localConstants.m_diffTex].SampleLevel(g_globalSampler, uv, 0.0);
+
+	if(diffColorA.a < 1.0)
+	{
+		payload.m_shadowFactor = 0.0;
+		AcceptHitAndEndSearch();
+	}
+	else
+	{
+		IgnoreHit();
+	}
+#	else
+	payload.m_shadowFactor = 0.0;
+	AcceptHitAndEndSearch();
+#	endif
+}
+#endif // defined(ANKI_ANY_HIT_SHADER)
+
+#if defined(ANKI_CLOSEST_HIT_SHADER)
+[shader("closesthit")] void main(inout RayPayload payload, in Barycentrics barycentrics)
+{
+	ANKI_MAYBE_UNUSED(payload);
+	ANKI_MAYBE_UNUSED(barycentrics);
+}
+#endif // defined(ANKI_CLOSEST_HIT_SHADER)
+
+// ===========================================================================
+// Define the techniques                                                     =
+// ===========================================================================
+#pragma anki technique_start vert GBuffer
+#pragma anki technique_end vert GBuffer
+
+#pragma anki technique_start task GBuffer
+#pragma anki technique_end task GBuffer
+
+#pragma anki technique_start mesh GBuffer
+#pragma anki technique_end mesh GBuffer
+
+#pragma anki technique_start frag GBuffer
+#pragma anki technique_end frag GBuffer
+
+#pragma anki technique_start vert Shadows
+#pragma anki technique_end vert Shadows
+
+#pragma anki technique_start task Shadows
+#pragma anki technique_end task Shadows
+
+#pragma anki technique_start mesh Shadows
+#pragma anki technique_end mesh Shadows
+
+#pragma anki technique_start frag Shadows
+#pragma anki technique_end frag Shadows
+
+#pragma anki technique_start ahit RtShadows
+#pragma anki technique_end ahit RtShadows
 
-#pragma anki end frag
+#pragma anki technique_start chit RtShadows
+#pragma anki technique_end chit RtShadows

+ 5 - 8
AnKi/Shaders/GBufferGpuParticles.ankiprog

@@ -3,11 +3,8 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki mutator ANKI_TECHNIQUE 0 1
-
 #include <AnKi/Shaders/MaterialShadersCommon.hlsl>
 
-#pragma anki reflect AnKiLocalConstants
 #pragma anki struct AnKiLocalConstants
 #pragma anki member RVec3 m_diffColor
 #pragma anki member RF32 m_roughness
@@ -15,7 +12,7 @@
 #pragma anki member RF32 m_metallic
 #pragma anki member RVec3 m_initialEmission
 #pragma anki member RVec3 m_finalEmission
-#pragma anki struct end
+#pragma anki struct_end
 
 struct VertIn
 {
@@ -39,7 +36,7 @@ struct FragOut
 	Vec2 m_color3 : SV_TARGET3;
 };
 
-#pragma anki start vert
+#pragma anki technique_start vert GBuffer
 
 VertOut main(VertIn input)
 {
@@ -79,9 +76,9 @@ VertOut main(VertIn input)
 
 	return output;
 }
-#pragma anki end vert
+#pragma anki technique_end vert GBuffer
 
-#pragma anki start frag
+#pragma anki technique_start frag GBuffer
 #include <AnKi/Shaders/PackFunctions.hlsl>
 
 FragOut main(VertOut input)
@@ -104,4 +101,4 @@ FragOut main(VertOut input)
 	packGBuffer(g, output.m_color0, output.m_color1, output.m_color2, output.m_color3);
 	return output;
 }
-#pragma anki end frag
+#pragma anki technique_end frag GBuffer

+ 4 - 4
AnKi/Shaders/GBufferPost.ankiprog

@@ -3,11 +3,11 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/PackFunctions.hlsl>
 #include <AnKi/Shaders/Functions.hlsl>
 #include <AnKi/Shaders/ClusteredShadingFunctions.hlsl>
@@ -116,4 +116,4 @@ FragOut main([[vk::location(0)]] Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITI
 	output.m_roughnessAndOther = roughnessAndOther;
 	return output;
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/GpuParticlesSimulation.ankiprog

@@ -5,7 +5,7 @@
 
 // This shader does a particle simulation
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Include/ParticleTypes.h>
 #include <AnKi/Shaders/Common.hlsl>
@@ -139,4 +139,4 @@ void initParticle(UVec3 svDispatchThreadId, out GpuParticle p)
 	g_particles[particleIdx] = particle;
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/GpuSceneMicroPatching.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Common.hlsl>
 
@@ -27,4 +27,4 @@
 	g_dstBuffer[dstDwordOffset + svGroupIndex] = g_srcBuffer[srcDwordOffset + svGroupIndex];
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/GpuVisibility.ankiprog

@@ -10,7 +10,7 @@
 
 #pragma anki skip_mutation DISTANCE_TEST 1 HZB_TEST 1
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Common.hlsl>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
@@ -262,4 +262,4 @@ struct DrawIndirectArgsWithPadding
 #endif
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/GpuVisibilityAccelerationStructures.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Common.hlsl>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
@@ -40,9 +40,9 @@
 	Vec3 sphereCenter;
 	if(visible)
 	{
-		sphereCenter = (bvolume.m_aabbMin + bvolume.m_aabbMax) * 0.5f;
-
 		bvolume = g_renderableBoundingVolumes[bvolumeIdx];
+
+		sphereCenter = (bvolume.m_aabbMin + bvolume.m_aabbMax) * 0.5f;
 		visible = testSphereSphereCollision(sphereCenter, bvolume.m_sphereRadius, g_consts.m_pointOfTest, g_consts.m_testRadius);
 	}
 
@@ -144,4 +144,4 @@
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/GpuVisibilityAccelerationStructuresZeroRemainingInstances.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Common.hlsl>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
@@ -29,4 +29,4 @@
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/GpuVisibilityNonRenderables.ankiprog

@@ -10,7 +10,7 @@
 #pragma anki skip_mutation CPU_FEEDBACK 1 OBJECT_TYPE 1
 #pragma anki skip_mutation CPU_FEEDBACK 1 OBJECT_TYPE 2
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Common.hlsl>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
@@ -147,4 +147,4 @@ Vec4 getSphere(GpuSceneGlobalIlluminationProbe l)
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/HzbGenPyramid.ankiprog

@@ -6,7 +6,7 @@
 #pragma anki mutator REDUCTION_TYPE 0 1 // 0: min 1: max
 #pragma anki mutator MIN_MAX_SAMPLER 0 1
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/Common.hlsl>
 
 struct Constants
@@ -115,4 +115,4 @@ AF4 SpdReduce4(AF4 v0, AF4 v1, AF4 v2, AF4 v3)
 	SpdDownsample(AU2(svGroupId.xy), AU1(svGroupIndex), AU1(g_consts.m_mipmapCount), AU1(g_consts.m_threadGroupCount), slice, offset);
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/HzbMaxDepth.ankiprog

@@ -5,7 +5,7 @@
 
 #include <AnKi/Shaders/Functions.hlsl>
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 [[vk::binding(0)]] Texture2D<Vec4> g_depthRt;
 [[vk::binding(1)]] SamplerState g_linearAnyClampSampler;
@@ -47,4 +47,4 @@ groupshared U32 s_maxDepth;
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/HzbMaxDepthProject.ankiprog

@@ -7,7 +7,7 @@
 
 #include <AnKi/Shaders/Functions.hlsl>
 
-#pragma anki start vert
+#pragma anki technique_start vert
 
 [[vk::binding(0)]] Texture2D<Vec4> g_maxDepthRt;
 
@@ -67,12 +67,12 @@ Vec4 main(U32 svVertexId : SV_VERTEXID, U32 svInstanceId : SV_INSTANCEID) : SV_P
 	return mul(g_consts.m_reprojectionMat, Vec4(ndc, 1.0));
 }
 
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 
 void main()
 {
 }
 
-#pragma anki end frag
+#pragma anki technique_end frag

+ 0 - 5
AnKi/Shaders/Include/MaterialTypes.h

@@ -61,9 +61,4 @@ enum class MaterialBinding : U32
 	kClusters = kClusterShadingLights + 2,
 };
 
-// Techniques
-#define ANKI_RENDERING_TECHNIQUE_GBUFFER 0
-#define ANKI_RENDERING_TECHNIQUE_DEPTH 1
-#define ANKI_RENDERING_TECHNIQUE_FORWARD 2
-
 ANKI_END_NAMESPACE

+ 2 - 2
AnKi/Shaders/IndirectDiffuseCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/IndirectDiffuse.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/IndirectDiffuseDenoiseCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/IndirectDiffuseDenoise.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/IndirectDiffuseDenoiseRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/IndirectDiffuseDenoise.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 4 - 4
AnKi/Shaders/IndirectDiffuseRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/IndirectDiffuse.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/IndirectDiffuseVrsSriGeneration.ankiprog

@@ -7,7 +7,7 @@
 #pragma anki mutator SHARED_MEMORY 0 1
 #pragma anki mutator LIMIT_RATE_TO_2X2 0 1
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Functions.hlsl>
 
@@ -164,4 +164,4 @@ F32 sampleViewPositionZ(Vec2 uv, I32 offsetX, I32 offsetY)
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/IndirectSpecularCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/IndirectSpecular.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/IndirectSpecularRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/IndirectSpecular.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 6 - 0
AnKi/Shaders/Intellisense.hlsl

@@ -30,6 +30,12 @@
 #define ANKI_END_NAMESPACE
 #define ANKI_HLSL 1
 
+#define ANKI_TASK_SHADER
+#define ANKI_VERTEX_SHADER
+#define ANKI_FRAGMENT_SHADER
+#define ANKI_MESH_SHADER
+#define ANKI_COMPUTE_SHADER
+
 using I8 = int;
 using I16 = int;
 using I32 = int;

+ 2 - 2
AnKi/Shaders/IrradianceDice.ankiprog

@@ -10,7 +10,7 @@
 #pragma anki mutator STORE_LOCATION 0 1 // 0: in a 3D texture, 1: In an SSBO
 #pragma anki mutator SECOND_BOUNCE 0 1
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Functions.hlsl>
 #include <AnKi/Shaders/PackFunctions.hlsl>
@@ -226,4 +226,4 @@ RVec3 sampleLightShadingTexture(const U32 face, UVec3 svGroupThreadId)
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/LensFlareSprite.ankiprog

@@ -13,7 +13,7 @@ struct VertOut
 	Vec4 m_svPosition : SV_POSITION;
 };
 
-#pragma anki start vert
+#pragma anki technique_start vert
 
 // The block contains data for all flares
 [[vk::binding(0)]] StructuredBuffer<LensFlareSprite> g_sprites;
@@ -35,9 +35,9 @@ VertOut main(U32 svVertexId : SV_VERTEXID, U32 svInstanceId : SV_INSTANCEID)
 
 	return output;
 }
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 
 [[vk::binding(1)]] SamplerState g_trilinearRepeatSampler;
 [[vk::binding(2)]] Texture2DArray<RVec4> g_tex;
@@ -47,4 +47,4 @@ RVec4 main(VertOut input) : SV_TARGET0
 	const RVec4 col = g_tex.Sample(g_trilinearRepeatSampler, input.m_uv);
 	return col * input.m_color;
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/LensFlareUpdateIndirectInfo.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/Common.hlsl>
 
 #define THREAD_COUNT_SQRT 8
@@ -58,4 +58,4 @@ groupshared U32 s_maxDepth;
 		g_indirectInfo[flareIdx].m_firstInstance = 0u;
 	}
 }
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/LightShading.ankiprog

@@ -3,11 +3,11 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/PackFunctions.hlsl>
 #include <AnKi/Shaders/Functions.hlsl>
 #include <AnKi/Shaders/RtShadows.hlsl>
@@ -134,4 +134,4 @@ RVec3 main(Vec4 svPosition : SV_POSITION, Vec2 uv : TEXCOORD) : SV_TARGET0
 	outColor = min(outColor, RVec3(kMaxRF32, kMaxRF32, kMaxRF32));
 	return outColor;
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 4 - 4
AnKi/Shaders/LightShadingApplyFog.ankiprog

@@ -3,11 +3,11 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 
 #include <AnKi/Shaders/Functions.hlsl>
 
@@ -47,4 +47,4 @@ RVec4 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 	return RVec4(inScattering, transmittance);
 }
 
-#pragma anki end frag
+#pragma anki technique_end frag

+ 4 - 4
AnKi/Shaders/LightShadingApplyIndirect.ankiprog

@@ -3,11 +3,11 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/BilateralFilter.hlsl>
 #include <AnKi/Shaders/PackFunctions.hlsl>
 #include <AnKi/Shaders/Functions.hlsl>
@@ -117,4 +117,4 @@ RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 	// Writeout
 	return min(diffuse + specular, RVec3(kMaxRF32, kMaxRF32, kMaxRF32));
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 4 - 4
AnKi/Shaders/LightShadingSkybox.ankiprog

@@ -5,7 +5,7 @@
 
 #pragma anki mutator METHOD 0 1 // 0: solid colod, 1: 2D image
 
-#pragma anki start vert
+#pragma anki technique_start vert
 
 #include <AnKi/Shaders/Common.hlsl>
 
@@ -25,9 +25,9 @@ VertOut main(U32 vertId : SV_VERTEXID)
 	return output;
 }
 
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 
 #include <AnKi/Shaders/Functions.hlsl>
 
@@ -80,4 +80,4 @@ RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD) : SV_TARGET0
 #endif
 }
 
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/MotionVectorsCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/MotionVectors.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/MotionVectorsRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/MotionVectors.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/RtShadowsDenoise.ankiprog

@@ -5,7 +5,7 @@
 
 #pragma anki mutator BLUR_ORIENTATION 0 1
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/BilateralFilter.hlsl>
 #include <AnKi/Shaders/PackFunctions.hlsl>
@@ -136,4 +136,4 @@ F32 computeVarianceCenter(Vec2 uv)
 	g_outUav[svDispatchThreadId.xy] = shadowFactor;
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 6 - 10
AnKi/Shaders/RtShadowsHit.ankiprog

@@ -3,21 +3,17 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki library RtShadows
-#pragma anki ray_type 0
-
 #pragma anki mutator ANKI_TECHNIQUE 3
 #pragma anki mutator ALPHA_TEXTURE 0 1
 
 #include <AnKi/Shaders/RtShadows.hlsl>
 #include <AnKi/Shaders/MaterialShadersCommon.hlsl>
 
-#pragma anki reflect AnKiLocalConstants
 #pragma anki struct AnKiLocalConstants
-#pragma anki member U32 m_diffTex if ALPHA_TEXTURE is 1
-#pragma anki struct end
+#pragma anki member U32 m_diffTex
+#pragma anki struct_end
 
-#pragma anki start ahit
+#pragma anki technique_start ahit RtShadows
 
 #if ALPHA_TEXTURE
 [[vk::shader_record_ext]] ConstantBuffer<GpuSceneRenderableVertex> g_gpuSceneRenderable;
@@ -62,12 +58,12 @@
 	AcceptHitAndEndSearch();
 #endif
 }
-#pragma anki end ahit
+#pragma anki technique_end ahit RtShadows
 
-#pragma anki start chit
+#pragma anki technique_start chit RtShadows
 [shader("closesthit")] void main(inout RayPayload payload, in Barycentrics barycentrics)
 {
 	ANKI_MAYBE_UNUSED(payload);
 	ANKI_MAYBE_UNUSED(barycentrics);
 }
-#pragma anki end chit
+#pragma anki technique_end chit RtShadows

+ 3 - 5
AnKi/Shaders/RtShadowsMiss.ankiprog

@@ -3,10 +3,8 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki library RtShadows
-#pragma anki ray_type 0
-
-#pragma anki start miss
+// TODO merge with ray gen
+#pragma anki technique_start miss RtShadows
 
 #include <AnKi/Shaders/RtShadows.hlsl>
 
@@ -14,4 +12,4 @@
 {
 	payload.m_shadowFactor = 1.0;
 }
-#pragma anki end miss
+#pragma anki technique_end miss RtShadows

+ 2 - 4
AnKi/Shaders/RtShadowsRayGen.ankiprog

@@ -4,10 +4,8 @@
 // http://www.anki3d.org/LICENSE
 
 #pragma anki mutator RAYS_PER_PIXEL 1 2 4 8
-#pragma anki library RtShadows
-#pragma anki ray_type 0
 
-#pragma anki start rgen
+#pragma anki technique_start rgen RtShadows
 
 #include <AnKi/Shaders/ImportanceSampling.hlsl>
 #include <AnKi/Shaders/PackFunctions.hlsl>
@@ -133,4 +131,4 @@ Vec3 genRandomDirection(U32 rayIdx, Vec2 uv)
 	// Store the moments
 	g_momentsImage[DispatchRaysIndex().xy] = Vec4(moments, 0.0, 0.0);
 }
-#pragma anki end rgen
+#pragma anki technique_end rgen RtShadows

+ 2 - 2
AnKi/Shaders/RtShadowsSbtBuild.ankiprog

@@ -6,7 +6,7 @@
 #include <AnKi/Shaders/RtShadows.hlsl>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 [[vk::binding(0)]] StructuredBuffer<GpuSceneRenderable> g_renderables;
 [[vk::binding(1)]] ByteAddressBuffer g_gpuScene;
@@ -48,4 +48,4 @@
 	g_sbtBuffer[sbtDwordOffset] = 0;
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/RtShadowsSetupSbtBuild.ankiprog

@@ -5,7 +5,7 @@
 
 #include <AnKi/Shaders/RtShadows.hlsl>
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 [[vk::binding(0)]] StructuredBuffer<U32> g_visibleRenderableIndices; // 1st element is the count
 
@@ -25,4 +25,4 @@
 	g_args[0] = args;
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/RtShadowsSvgfAtrous.ankiprog

@@ -5,7 +5,7 @@
 
 #pragma anki mutator LAST_PASS 0 1
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/RtShadows.hlsl>
 #include <AnKi/Shaders/BilateralFilter.hlsl>
@@ -148,4 +148,4 @@ F32 computeVarianceCenter(Vec2 uv)
 	g_varianceUav[svDispatchThreadId.xy] = Vec4(sumVariance, 0.0, 0.0, 0.0);
 #endif
 }
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/RtShadowsSvgfVariance.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/RtShadows.hlsl>
 #include <AnKi/Shaders/BilateralFilter.hlsl>
@@ -118,4 +118,4 @@ Vec3 toViewspace(Vec2 uv, F32 depth)
 	g_shadowUav[svDispatchThreadId] = outShadowFactor;
 	g_varianceUav[svDispatchThreadId] = Vec4(outVariance, 0.0, 0.0, 0.0);
 }
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/RtShadowsUpscale.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/RtShadows.hlsl>
 #include <AnKi/Shaders/Functions.hlsl>
@@ -57,4 +57,4 @@
 	g_fullShadowUav[svDispatchThreadId.xy] = sumShadowFactor;
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/ShadowMappingClearDepth.ankiprog

@@ -5,7 +5,7 @@
 
 #include <AnKi/Shaders/Common.hlsl>
 
-#pragma anki start vert
+#pragma anki technique_start vert
 
 Vec4 main(U32 svVertexId : SV_VERTEXID) : SV_POSITION
 {
@@ -15,10 +15,10 @@ Vec4 main(U32 svVertexId : SV_VERTEXID) : SV_POSITION
 	return Vec4(pos, 1.0, 1.0);
 }
 
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 void main()
 {
 }
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/ShadowMappingVetVisibility.ankiprog

@@ -5,7 +5,7 @@
 
 // A very simle compute shader that checks if the light shadows needs rendering or not.
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/Common.hlsl>
 #include <AnKi/Shaders/Include/GpuVisibilityTypes.h>
@@ -78,4 +78,4 @@ groupshared U32 s_renderLight;
 	}
 }
 
-#pragma anki end comp
+#pragma anki technique_end comp

+ 2 - 2
AnKi/Shaders/ShadowmapsResolveCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/ShadowmapsResolve.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/ShadowmapsResolveRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/ShadowmapsResolve.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/TemporalAACompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/TemporalAA.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/TemporalAARaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/TemporalAA.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/TonemapCompute.ankiprog

@@ -3,6 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 #include <AnKi/Shaders/Tonemap.hlsl>
-#pragma anki end comp
+#pragma anki technique_end comp

+ 4 - 4
AnKi/Shaders/TonemapRaster.ankiprog

@@ -3,10 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start vert
+#pragma anki technique_start vert
 #include <AnKi/Shaders/QuadVert.hlsl>
-#pragma anki end vert
+#pragma anki technique_end vert
 
-#pragma anki start frag
+#pragma anki technique_start frag
 #include <AnKi/Shaders/Tonemap.hlsl>
-#pragma anki end frag
+#pragma anki technique_end frag

+ 2 - 2
AnKi/Shaders/TonemappingAverageLuminance.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki start comp
+#pragma anki technique_start comp
 
 #include <AnKi/Shaders/TonemappingFunctions.hlsl>
 
@@ -93,4 +93,4 @@ groupshared F32 s_avgLum[THREAD_COUNT_X * THREAD_COUNT_Y];
 		writeExposureAndAverageLuminance(computeExposure(finalAvgLum, 0.0), finalAvgLum);
 	}
 }
-#pragma anki end comp
+#pragma anki technique_end comp

Some files were not shown because too many files changed in this diff