Browse Source

More work on the parser

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
f2020f03e6

+ 18 - 16
src/anki/shader_compiler/Common.h

@@ -1,16 +1,18 @@
-// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-/// @addtogroup shader_compiler
-/// @{
-
-#define ANKI_SHADER_COMPILER_LOGI(...) ANKI_LOG("SHDR", NORMAL, __VA_ARGS__)
-#define ANKI_SHADER_COMPILER_LOGE(...) ANKI_LOG("SHDR", ERROR, __VA_ARGS__)
-#define ANKI_SHADER_COMPILER_LOGW(...) ANKI_LOG("SHDR", WARNING, __VA_ARGS__)
-#define ANKI_SHADER_COMPILER_LOGF(...) ANKI_LOG("SHDR", FATAL, __VA_ARGS__)
-
-/// @}
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/Config.h>
+
+/// @addtogroup shader_compiler
+/// @{
+
+#define ANKI_SHADER_COMPILER_LOGI(...) ANKI_LOG(" SHC", NORMAL, __VA_ARGS__)
+#define ANKI_SHADER_COMPILER_LOGE(...) ANKI_LOG(" SHC", ERROR, __VA_ARGS__)
+#define ANKI_SHADER_COMPILER_LOGW(...) ANKI_LOG(" SHC", WARNING, __VA_ARGS__)
+#define ANKI_SHADER_COMPILER_LOGF(...) ANKI_LOG(" SHC", FATAL, __VA_ARGS__)
+
+/// @}

+ 414 - 21
src/anki/shader_compiler/ShaderProgramParser.cpp

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/shader_compiler/ShaderProgramParser.h>
+#include <anki/shader_compiler/Glslang.h>
 
 namespace anki
 {
@@ -16,6 +17,9 @@ namespace anki
 	ANKI_SHADER_COMPILER_LOGE("%s: " msg_ ": %s", fname.cstr(), line.cstr()); \
 	return Error::USER_DATA
 
+static const Array<CString, U32(ShaderType::COUNT)> SHADER_STAGE_NAMES = {
+	{"VERTEX", "TESSELLATION_CONTROL", "TESSELLATION_EVALUATION", "GEOMETRY", "FRAGMENT", "COMPUTE"}};
+
 static ANKI_USE_RESULT Error computeShaderVariableDataType(const CString& str, ShaderVariableDataType& out)
 {
 	Error err = Error::NONE;
@@ -101,7 +105,27 @@ static ANKI_USE_RESULT Error computeShaderVariableDataType(const CString& str, S
 	return err;
 }
 
-void ShaderProgramParser::tokenizeLine(CString line, DynamicArrayAuto<StringAuto>& tokens)
+static ANKI_USE_RESULT Error preprocessCommon(CString in, StringAuto& out)
+{
+	glslang::TShader shader(EShLangVertex);
+	Array<const char*, 1> csrc = {{&in[0]}};
+	shader.setStrings(&csrc[0], 1);
+
+	DirStackFileIncluder includer;
+	EShMessages messages = EShMsgDefault;
+	std::string glslangOut;
+	if(!shader.preprocess(&GLSLANG_LIMITS, 450, ENoProfile, false, false, messages, &glslangOut, includer))
+	{
+		ANKI_SHADER_COMPILER_LOGE("Preprocessing failed:\n%s", shader.getInfoLog());
+		return Error::USER_DATA;
+	}
+
+	out.append(glslangOut.c_str());
+
+	return Error::NONE;
+}
+
+void ShaderProgramParser::tokenizeLine(CString line, DynamicArrayAuto<StringAuto>& tokens) const
 {
 	ANKI_ASSERT(line.getLength() > 0);
 
@@ -165,38 +189,33 @@ Error ShaderProgramParser::parsePragmaStart(const StringAuto* begin, const Strin
 	if(*begin == "vert")
 	{
 		shaderType = ShaderType::VERTEX;
-		m_lines.pushBack("#ifdef ANKI_VERTEX_SHADER");
 	}
 	else if(*begin == "tessc")
 	{
 		shaderType = ShaderType::TESSELLATION_CONTROL;
-		m_lines.pushBack("#ifdef ANKI_TESSELATION_CONTROL_SHADER");
 	}
 	else if(*begin == "tesse")
 	{
-		shaderType = ShaderType::TESSELLATION_EVALUATION;
-		m_lines.pushBack("#ifdef ANKI_TESSELLATION_EVALUATION_SHADER");
 	}
 	else if(*begin == "geom")
 	{
 		shaderType = ShaderType::GEOMETRY;
-		m_lines.pushBack("#ifdef ANKI_GEOMETRY_SHADER");
 	}
 	else if(*begin == "frag")
 	{
 		shaderType = ShaderType::FRAGMENT;
-		m_lines.pushBack("#ifdef ANKI_FRAGMENT_SHADER");
 	}
 	else if(*begin == "comp")
 	{
 		shaderType = ShaderType::COMPUTE;
-		m_lines.pushBack("#ifdef ANKI_COMPUTE_SHADER");
 	}
 	else
 	{
 		ANKI_PP_ERROR_MALFORMED();
 	}
 
+	m_codeLines.pushBackSprintf("#ifdef ANKI_%s", SHADER_STAGE_NAMES[shaderType].cstr());
+
 	++begin;
 	if(begin != end)
 	{
@@ -240,7 +259,7 @@ Error ShaderProgramParser::parsePragmaEnd(const StringAuto* begin, const StringA
 	m_insideShader = false;
 
 	// Write code
-	m_lines.pushBack("#endif // Shader guard");
+	m_codeLines.pushBack("#endif // Shader guard");
 
 	return Error::NONE;
 }
@@ -338,14 +357,24 @@ Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const Strin
 			ANKI_PP_ERROR_MALFORMED();
 		}
 
+		// Add an tag for later pre-processing (when trying to see if the variable is present)
+		m_codeLines.pushBackSprintf("//_anki_input_pesent %s", input.m_name.cstr());
+
+		m_globalsLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", input.m_name.cstr());
 		m_globalsLines.pushBackSprintf("#define %s_DEFINED 1", input.m_name.cstr());
 
+		// TODO add vector support
+
 		m_globalsLines.pushBackSprintf("layout(constant_id = %u) const %s %s = %s(0);",
 			input.m_specConstId,
 			dataTypeStr.cstr(),
 			input.m_name.cstr(),
 			dataTypeStr.cstr(),
 			input.m_name.cstr());
+
+		m_globalsLines.pushBack("#else");
+		m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", input.m_name.cstr());
+		m_globalsLines.pushBack("#endf");
 	}
 	else if(isSampler || isTexture)
 	{
@@ -357,12 +386,20 @@ Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const Strin
 			ANKI_PP_ERROR_MALFORMED();
 		}
 
+		// Add an tag for later pre-processing (when trying to see if the variable is present)
+		m_codeLines.pushBackSprintf("//_anki_input_pesent %s", input.m_name.cstr());
+
+		m_globalsLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", input.m_name.cstr());
 		m_globalsLines.pushBackSprintf("#define %s_DEFINED 1", input.m_name.cstr());
 
 		m_globalsLines.pushBackSprintf("layout(set = _ANKI_DSET, binding = _ANKI_%s_BINDING) uniform %s %s;",
 			input.m_name.cstr(),
 			dataTypeStr.cstr(),
 			input.m_name.cstr());
+
+		m_globalsLines.pushBack("#else");
+		m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", input.m_name.cstr());
+		m_globalsLines.pushBack("#endf");
 	}
 	else
 	{
@@ -372,11 +409,11 @@ Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const Strin
 		const char* type = dataTypeStr.cstr();
 
 		// Add an tag for later pre-processing (when trying to see if the variable is present)
-		m_lines.pushBackSprintf("//_anki_input_pesent %s", name);
+		m_codeLines.pushBackSprintf("//_anki_input_pesent %s", name);
 
 		if(input.m_instanced)
 		{
-			m_uboStructLines.pushBackSprintf("#if _ANKI_UNI_%s_ACTIVE", name);
+			m_uboStructLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", name);
 			m_uboStructLines.pushBack("#if _ANKI_INSTANCE_COUNT > 1");
 			m_uboStructLines.pushBackSprintf("%s _anki_uni_%s[_ANKI_INSTANCE_COUNT];", type, name);
 			m_uboStructLines.pushBack("#else");
@@ -384,7 +421,7 @@ Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const Strin
 			m_uboStructLines.pushBack("#endif");
 			m_uboStructLines.pushBack("#endif");
 
-			m_globalsLines.pushBackSprintf("#if _ANKI_UNI_%s_ACTIVE", name);
+			m_globalsLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", name);
 			m_globalsLines.pushBack("#ifdef ANKI_VERTEX_SHADER");
 			m_globalsLines.pushBackSprintf("#define %s_DEFINED 1", name);
 			m_globalsLines.pushBack("#if _ANKI_INSTANCE_COUNT > 1");
@@ -392,18 +429,22 @@ Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const Strin
 			m_globalsLines.pushBack("#else");
 			m_globalsLines.pushBackSprintf("%s %s = _anki_unis._anki_uni_%s;", type, name, name);
 			m_globalsLines.pushBack("#endif");
-			m_globalsLines.pushBack("#endif");
+			m_globalsLines.pushBack("#endif //ANKI_VERTEX_SHADER");
+			m_globalsLines.pushBack("#else");
+			m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", name);
 			m_globalsLines.pushBack("#endif");
 		}
 		else
 		{
-			m_uboStructLines.pushBackSprintf("#if _ANKI_UNI_%s_ACTIVE", name);
+			m_uboStructLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", name);
 			m_uboStructLines.pushBackSprintf("%s _anki_uni_%s;", type, name);
 			m_uboStructLines.pushBack("#endif");
 
-			m_globalsLines.pushBackSprintf("#if _ANKI_UNI_%s_ACTIVE", name);
+			m_globalsLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", name);
 			m_globalsLines.pushBackSprintf("%s %s = _anki_unis_._anki_uni_%s;", type, name, name);
 			m_globalsLines.pushBackSprintf("#define %s_DEFINED 1", name);
+			m_globalsLines.pushBack("#else");
+			m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", name);
 			m_globalsLines.pushBack("#endif");
 		}
 	}
@@ -596,8 +637,8 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 			// Add the guard unique for this file
 			foundPragmaOnce = true;
 			const U64 hash = fname.computeHash();
-			m_lines.pushBackSprintf("#ifndef _ANKI_INCL_GUARD_%llu\n"
-									"#define _ANKI_INCL_GUARD_%llu",
+			m_codeLines.pushBackSprintf("#ifndef _ANKI_INCL_GUARD_%llu\n"
+										"#define _ANKI_INCL_GUARD_%llu",
 				hash,
 				hash);
 		}
@@ -636,13 +677,13 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 		{
 			// Some other pragma
 			ANKI_SHADER_COMPILER_LOGW("Ignoring: %s", line.cstr());
-			m_lines.pushBack(line);
+			m_codeLines.pushBack(line);
 		}
 	}
 	else
 	{
 		// Ignore
-		m_lines.pushBack(line);
+		m_codeLines.pushBack(line);
 	}
 
 	return Error::NONE;
@@ -680,14 +721,366 @@ Error ShaderProgramParser::parseFile(CString fname, U32 depth)
 		else
 		{
 			// Just append the line
-			m_lines.pushBack(line.toCString());
+			m_codeLines.pushBack(line.toCString());
 		}
 	}
 
 	if(foundPragmaOnce)
 	{
 		// Append the guard
-		m_lines.pushBack("#endif // Include guard");
+		m_codeLines.pushBack("#endif // Include guard");
+	}
+
+	if(m_insideShader)
+	{
+		ANKI_SHADER_COMPILER_LOGE("Forgot a \"pragma anki end\"");
+		return Error::USER_DATA;
+	}
+
+	return Error::NONE;
+}
+
+Error ShaderProgramParser::parse()
+{
+	ANKI_ASSERT(!m_fname.isEmpty());
+	ANKI_ASSERT(m_codeLines.isEmpty());
+
+	const CString fname = m_fname;
+
+	// Parse recursively
+	ANKI_CHECK(parseFile(fname, 0));
+
+	// Checks
+	{
+		if(m_foundAtLeastOneInstancedInput != (m_instancedMutatorIdx != MAX_U32))
+		{
+			ANKI_SHADER_COMPILER_LOGE("If there is an instanced mutator there should be at least one instanced input");
+			return Error::USER_DATA;
+		}
+
+		if(!!(m_shaderTypes & ShaderTypeBit::COMPUTE))
+		{
+			if(m_shaderTypes != ShaderTypeBit::COMPUTE)
+			{
+				ANKI_SHADER_COMPILER_LOGE("Can't combine compute shader with other types of shaders");
+				return Error::USER_DATA;
+			}
+
+			if(m_instancedMutatorIdx != MAX_U32)
+			{
+				ANKI_SHADER_COMPILER_LOGE("Can't have instanced mutators in compute programs");
+				return Error::USER_DATA;
+			}
+		}
+		else
+		{
+			if(!(m_shaderTypes & ShaderTypeBit::VERTEX))
+			{
+				ANKI_SHADER_COMPILER_LOGE("Missing vertex shader");
+				return Error::USER_DATA;
+			}
+
+			if(!(m_shaderTypes & ShaderTypeBit::FRAGMENT))
+			{
+				ANKI_SHADER_COMPILER_LOGE("Missing fragment shader");
+				return Error::USER_DATA;
+			}
+		}
+	}
+
+	// Create the UBO source code
+	if(m_uboStructLines.getSize() > 0)
+	{
+		m_uboStructLines.pushFront("struct _AnkiUniforms {");
+		m_uboStructLines.pushBack("};");
+
+		m_uboStructLines.pushBack("#if _ANKI_USE_PUSH_CONSTANTS == 1");
+		m_uboStructLines.pushBackSprintf(
+			"layout(push_constant, std140, row_major) uniform _anki_pc {_AnkiUniforms _anki_unis;};");
+		m_uboStructLines.pushBack("#else");
+		m_uboStructLines.pushBack(
+			"layout(set = _ANKI_DSET, binding = 0, row_major) uniform _anki_ubo {_AnkiUniforms _anki_unis;};");
+		m_uboStructLines.pushBack("#endif\n");
+
+		m_uboStructLines.join("\n", m_uboSource);
+		m_uboStructLines.destroy();
+	}
+
+	// Create the globals source code
+	if(m_globalsLines.getSize() > 0)
+	{
+		m_globalsLines.join("\n", m_globalsSource);
+		m_globalsLines.destroy();
+	}
+
+	// Create the code lines
+	if(m_codeLines.getSize())
+	{
+		m_codeLines.join("\n", m_codeSource);
+		m_codeLines.destroy();
+	}
+
+	return Error::NONE;
+}
+
+Error ShaderProgramParser::findActiveInputVars(CString source, BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES>& active) const
+{
+	StringAuto preprocessedSrc(m_alloc);
+	ANKI_CHECK(preprocessCommon(source, preprocessedSrc));
+
+	StringListAuto lines(m_alloc);
+	lines.splitString(preprocessedSrc, '\n');
+
+	for(const String& line : lines)
+	{
+		if(line.find("//_anki_input_pesent") == String::NPOS)
+		{
+			continue;
+		}
+
+		DynamicArrayAuto<StringAuto> tokens(m_alloc);
+		tokenizeLine(line, tokens);
+		ANKI_ASSERT(tokens.getSize() == 2);
+
+		// Find the input var
+		Bool found = false;
+		for(const Input& in : m_inputs)
+		{
+			if(in.m_name == tokens[1])
+			{
+				active.set(in.m_idx);
+				break;
+			}
+		}
+		(void)found;
+		ANKI_ASSERT(found);
+	}
+
+	return Error::NONE;
+}
+
+Error ShaderProgramParser::generateSource(
+	ConstWeakArray<ShaderProgramParserMutatorState> mutatorStates, ShaderProgramVariant& variant) const
+{
+	// Sanity checks
+	ANKI_ASSERT(m_codeSource.getLength() > 0);
+	ANKI_ASSERT(mutatorStates.getSize() == m_mutators.getSize());
+	BitSet<128> mutatorPresent = {false};
+	for(const ShaderProgramParserMutatorState& state : mutatorStates)
+	{
+		ANKI_ASSERT(state.m_mutator >= &m_mutators[0] && state.m_mutator <= &m_mutators[m_mutators.getSize() - 1]);
+		const U idx = state.m_mutator - &m_mutators[0];
+		ANKI_ASSERT(!mutatorPresent.get(idx) && "Appeared 2 times");
+		mutatorPresent.set(idx);
+
+		Bool found = false;
+		for(Mutator::ValueType val : state.m_mutator->m_values)
+		{
+			if(val == state.m_value)
+			{
+				found = true;
+				break;
+			}
+		}
+		ANKI_ASSERT(found && "Value not found");
+	}
+
+	// Init variant
+	variant.m_alloc = m_alloc;
+	variant.m_bindings.create(m_alloc, m_inputs.getSize(), -1);
+	variant.m_blockInfos.create(m_alloc, m_inputs.getSize());
+
+	// Get instance count, one mutation has it
+	U32 instanceCount = 1;
+	if(m_instancedMutatorIdx != MAX_U32)
+	{
+		for(const ShaderProgramParserMutatorState& state : mutatorStates)
+		{
+			const U32 idx = U32(state.m_mutator - &m_mutators[0]);
+
+			if(idx == m_instancedMutatorIdx)
+			{
+				ANKI_ASSERT(state.m_value > 0);
+				instanceCount = state.m_value;
+				break;
+			}
+		}
+	}
+
+	// Create the mutator defines
+	StringAuto mutatorDefines(m_alloc);
+	for(const ShaderProgramParserMutatorState& state : mutatorStates)
+	{
+		mutatorDefines.append(
+			StringAuto(m_alloc).sprintf("#define %s %d\n", state.m_mutator->m_name.cstr(), state.m_value));
+	}
+
+	// Find active vars by running the preprocessor
+	StringAuto activeInputs(m_alloc);
+	if(m_inputs.getSize() > 0)
+	{
+		StringAuto src(m_alloc);
+		src.append(mutatorDefines);
+		src.append(m_globalsSource);
+		ANKI_CHECK(findActiveInputVars(src, variant.m_activeInputVarsMask));
+
+		StringListAuto lines(m_alloc);
+		for(const Input& in : m_inputs)
+		{
+			const Bool active = variant.m_activeInputVarsMask.get(in.m_idx);
+			lines.pushBackSprintf("#define _ANKI_ACTIVATE_INPUT_%s %u", in.m_name.cstr(), active);
+		}
+
+		lines.join("\n", activeInputs);
+	}
+
+	// Initialize the active vars that are inside a UBO
+	for(const Input& in : m_inputs)
+	{
+		if(!variant.m_activeInputVarsMask.get(in.m_idx))
+		{
+			continue;
+		}
+
+		if(!in.inUbo())
+		{
+			continue;
+		}
+
+		ShaderVariableBlockInfo& blockInfo = variant.m_blockInfos[in.m_idx];
+
+		// std140 rules
+		blockInfo.m_offset = I16(variant.m_uniBlockSize);
+		blockInfo.m_arraySize = (in.m_instanced) ? I16(instanceCount) : 1;
+
+		if(in.m_dataType == ShaderVariableDataType::FLOAT || in.m_dataType == ShaderVariableDataType::INT
+			|| in.m_dataType == ShaderVariableDataType::UINT)
+		{
+			blockInfo.m_arrayStride = sizeof(Vec4);
+
+			if(blockInfo.m_arraySize == 1)
+			{
+				// No need to align the in.m_offset
+				variant.m_uniBlockSize += sizeof(F32);
+			}
+			else
+			{
+				alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+				variant.m_uniBlockSize += sizeof(Vec4) * blockInfo.m_arraySize;
+			}
+		}
+		else if(in.m_dataType == ShaderVariableDataType::VEC2 || in.m_dataType == ShaderVariableDataType::IVEC2
+				|| in.m_dataType == ShaderVariableDataType::UVEC2)
+		{
+			blockInfo.m_arrayStride = sizeof(Vec4);
+
+			if(blockInfo.m_arraySize == 1)
+			{
+				alignRoundUp(sizeof(Vec2), blockInfo.m_offset);
+				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec2);
+			}
+			else
+			{
+				alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
+			}
+		}
+		else if(in.m_dataType == ShaderVariableDataType::VEC3 || in.m_dataType == ShaderVariableDataType::IVEC3
+				|| in.m_dataType == ShaderVariableDataType::UVEC3)
+		{
+			alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+			blockInfo.m_arrayStride = sizeof(Vec4);
+
+			if(blockInfo.m_arraySize == 1)
+			{
+				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec3);
+			}
+			else
+			{
+				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
+			}
+		}
+		else if(in.m_dataType == ShaderVariableDataType::VEC4 || in.m_dataType == ShaderVariableDataType::IVEC4
+				|| in.m_dataType == ShaderVariableDataType::UVEC4)
+		{
+			blockInfo.m_arrayStride = sizeof(Vec4);
+			alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+			variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
+		}
+		else if(in.m_dataType == ShaderVariableDataType::MAT3)
+		{
+			alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+			blockInfo.m_arrayStride = sizeof(Vec4) * 3;
+			variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * 3 * blockInfo.m_arraySize;
+			blockInfo.m_matrixStride = sizeof(Vec4);
+		}
+		else if(in.m_dataType == ShaderVariableDataType::MAT4)
+		{
+			alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
+			blockInfo.m_arrayStride = sizeof(Mat4);
+			variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Mat4) * blockInfo.m_arraySize;
+			blockInfo.m_matrixStride = sizeof(Vec4);
+		}
+		else
+		{
+			ANKI_ASSERT(0);
+		}
+	}
+
+	// Find if it's using push constants
+	StringAuto pushConstantDefineSrc(m_alloc);
+	{
+		variant.m_usesPushConstants = variant.m_uniBlockSize <= m_pushConstSize;
+		pushConstantDefineSrc.sprintf("#define _ANKI_USE_PUSH_CONSTANTS %u\n", variant.m_usesPushConstants);
+	}
+
+	// Handle the bindings for the textures and samplers
+	StringAuto bindingDefines(m_alloc);
+	{
+		StringListAuto defines(m_alloc);
+		U32 texOrSamplerBinding = variant.m_usesPushConstants ? 0 : 1;
+		for(const Input& in : m_inputs)
+		{
+			if(!variant.m_activeInputVarsMask.get(in.m_idx))
+			{
+				continue;
+			}
+
+			if(!in.isSampler() && !in.isTexture())
+			{
+				continue;
+			}
+
+			defines.pushBackSprintf("#define _ANKI_%s_BINDING %u", in.m_name.cstr(), texOrSamplerBinding);
+			variant.m_bindings[in.m_idx] = I16(texOrSamplerBinding++);
+		}
+
+		if(!defines.isEmpty())
+		{
+			defines.join("\n", bindingDefines);
+		}
+	}
+
+	// Generate souce per stage
+	for(ShaderType shaderType = ShaderType::FIRST; shaderType < ShaderType::COUNT; ++shaderType)
+	{
+		if(!((1u << ShaderTypeBit(shaderType)) & m_shaderTypes))
+		{
+			continue;
+		}
+
+		// Create the final source without the bindings
+		StringAuto finalSource(m_alloc);
+		finalSource.append(mutatorDefines);
+		finalSource.append(StringAuto(m_alloc).sprintf("#define ANKI_%s 1\n", SHADER_STAGE_NAMES[shaderType].cstr()));
+		finalSource.append(activeInputs);
+		finalSource.append(pushConstantDefineSrc);
+		finalSource.append(m_uboSource);
+		finalSource.append(m_globalsSource);
+		finalSource.append(m_codeSource);
+
+		// Move the source
+		variant.m_sources[shaderType] = std::move(finalSource);
 	}
 
 	return Error::NONE;

+ 40 - 19
src/anki/shader_compiler/ShaderProgramParser.h

@@ -22,6 +22,8 @@ class ShaderProgramVariant;
 /// @addtogroup resource
 /// @{
 
+constexpr U32 MAX_SHADER_PROGRAM_INPUT_VARIABLES = 128;
+
 /// @memberof ShaderProgramParser
 class ShaderProgramParserMutator
 {
@@ -85,7 +87,7 @@ public:
 	}
 
 	/// @param constantId It's the vulkan spec const index.
-	Bool isConstant(U32* constantId) const
+	Bool isConstant(U32* constantId = nullptr) const
 	{
 		if(constantId)
 		{
@@ -94,6 +96,22 @@ public:
 		return m_specConstId != MAX_U32;
 	}
 
+	Bool isTexture() const
+	{
+		return m_dataType >= ShaderVariableDataType::TEXTURE_FIRST
+			   && m_dataType <= ShaderVariableDataType::TEXTURE_LAST;
+	}
+
+	Bool isSampler() const
+	{
+		return m_dataType == ShaderVariableDataType::SAMPLER;
+	}
+
+	Bool inUbo() const
+	{
+		return !isConstant() && !isTexture() && !isSampler();
+	}
+
 private:
 	StringAuto m_name;
 	U32 m_idx = MAX_U32; ///< Index inside an array.
@@ -105,6 +123,8 @@ private:
 /// @memberof ShaderProgramParser
 class ShaderProgramVariant
 {
+	friend class ShaderProgramParser;
+
 public:
 	CString getSource(ShaderType type) const
 	{
@@ -122,9 +142,8 @@ private:
 	DynamicArray<ShaderVariableBlockInfo> m_blockInfos;
 	DynamicArray<I16> m_bindings;
 	U32 m_uniBlockSize = 0;
-	U8 m_bindingCount = 0;
 	Bool m_usesPushConstants = false;
-	BitSet<128> m_activeInputVarsMask = {false};
+	BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES> m_activeInputVarsMask = {false};
 };
 
 /// @memberof ShaderProgramParser
@@ -159,17 +178,14 @@ public:
 class ShaderProgramParser : public NonCopyable
 {
 public:
-	ShaderProgramParser(
-		CString fname, ShaderProgramParserFilesystemInterface* fsystem, GenericMemoryPoolAllocator<U8> alloc)
+	ShaderProgramParser(CString fname,
+		ShaderProgramParserFilesystemInterface* fsystem,
+		GenericMemoryPoolAllocator<U8> alloc,
+		U32 pushConstantsSize)
 		: m_alloc(alloc)
 		, m_fname(alloc, fname)
 		, m_fsystem(fsystem)
-		, m_lines(alloc)
-		, m_globalsLines(alloc)
-		, m_uboStructLines(alloc)
-		, m_finalSource(alloc)
-		, m_mutators(alloc)
-		, m_inputs(alloc)
+		, m_pushConstSize(pushConstantsSize)
 	{
 	}
 
@@ -181,7 +197,8 @@ public:
 	ANKI_USE_RESULT Error parse();
 
 	/// Get the source (and a few more things) given a list of mutators.
-	ShaderProgramVariant generateSource(ConstWeakArray<ShaderProgramParserMutatorState> mutatorStates) const;
+	ANKI_USE_RESULT Error generateSource(
+		ConstWeakArray<ShaderProgramParserMutatorState> mutatorStates, ShaderProgramVariant& variant) const;
 
 	ConstWeakArray<ShaderProgramParserMutator> getMutators() const
 	{
@@ -213,19 +230,22 @@ private:
 	StringAuto m_fname;
 	ShaderProgramParserFilesystemInterface* m_fsystem = nullptr;
 
-	StringListAuto m_lines; ///< The code.
-	StringListAuto m_globalsLines;
-	StringListAuto m_uboStructLines;
-	StringAuto m_finalSource;
+	StringListAuto m_codeLines = {m_alloc}; ///< The code.
+	StringListAuto m_globalsLines = {m_alloc};
+	StringListAuto m_uboStructLines = {m_alloc};
+	StringAuto m_codeSource = {m_alloc};
+	StringAuto m_globalsSource = {m_alloc};
+	StringAuto m_uboSource = {m_alloc};
 
-	DynamicArrayAuto<Mutator> m_mutators;
-	DynamicArrayAuto<Input> m_inputs;
+	DynamicArrayAuto<Mutator> m_mutators = {m_alloc};
+	DynamicArrayAuto<Input> m_inputs = {m_alloc};
 
 	ShaderTypeBit m_shaderTypes = ShaderTypeBit::NONE;
 	Bool m_insideShader = false;
 	U32 m_set = 0;
 	U32 m_instancedMutatorIdx = MAX_U32;
 	U32 m_specConstIdx = 0;
+	const U32 m_pushConstSize = 0;
 	Bool m_foundAtLeastOneInstancedInput = false;
 
 	ANKI_USE_RESULT Error parseFile(CString fname, U32 depth);
@@ -240,7 +260,8 @@ private:
 	ANKI_USE_RESULT Error parsePragmaDescriptorSet(
 		const StringAuto* begin, const StringAuto* end, CString line, CString fname);
 
-	void tokenizeLine(CString line, DynamicArrayAuto<StringAuto>& tokens);
+	ANKI_USE_RESULT Error findActiveInputVars(CString source, BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES>& active) const;
+	void tokenizeLine(CString line, DynamicArrayAuto<StringAuto>& tokens) const;
 
 	static Bool tokenIsComment(CString token)
 	{