Panagiotis Christopoulos Charitos 6 years ago
parent
commit
b8421ce925

+ 3 - 0
src/anki/shader_compiler/Common.h

@@ -7,6 +7,7 @@
 
 #include <anki/util/Logger.h>
 #include <anki/util/String.h>
+#include <anki/util/BitSet.h>
 
 namespace anki
 {
@@ -17,6 +18,8 @@ namespace anki
 constexpr U32 MAX_SHADER_PROGRAM_INPUT_VARIABLES = 128;
 constexpr U32 MAX_SHADER_BINARY_NAME_LENGTH = 63;
 
+using ActiveProgramInputVariableMask = BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES, U64>;
+
 #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__)

+ 2 - 5
src/anki/shader_compiler/ShaderProgramBinary.h

@@ -84,7 +84,7 @@ public:
 class ShaderProgramBinaryVariant
 {
 public:
-	Array<U64, MAX_SHADER_PROGRAM_INPUT_VARIABLES / (sizeof(U64) * 8)> m_activeVariables;
+	ActiveProgramInputVariableMask m_activeVariables = {false};
 	I32* m_mutatorValues;
 	ShaderVariableBlockInfo* m_blockInfos;
 	I16* m_bindings;
@@ -97,10 +97,7 @@ public:
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doArray("m_activeVariables",
-			offsetof(ShaderProgramBinaryVariant, m_activeVariables),
-			&self.m_activeVariables[0],
-			MAX_SHADER_PROGRAM_INPUT_VARIABLES / (sizeof(U64) * 8));
+		s.doValue("m_activeVariables", offsetof(ShaderProgramBinaryVariant, m_activeVariables), self.m_activeVariables);
 		s.doValue(
 			"m_mutatorValueCount", offsetof(ShaderProgramBinaryVariant, m_mutatorValueCount), self.m_mutatorValueCount);
 		s.doValue("m_inputVariableCount",

+ 1 - 1
src/anki/shader_compiler/ShaderProgramBinary.xml

@@ -26,7 +26,7 @@
 
 		<class name="ShaderProgramBinaryVariant">
 			<members>
-				<member name="m_activeVariables" type="U64" array_size="MAX_SHADER_PROGRAM_INPUT_VARIABLES / (sizeof(U64) * 8)" />
+				<member name="m_activeVariables" type="ActiveProgramInputVariableMask" constructor="false" />
 				<member name="m_mutatorValues" type="I32" pointer="true" array_size="m_mutatorValueCount" />
 				<member name="m_blockInfos" type="ShaderVariableBlockInfo" pointer="true" array_size="m_inputVariableCount" />
 				<member name="m_bindings" type="I16" pointer="true" array_size="m_inputVariableCount" />

+ 31 - 0
src/anki/shader_compiler/ShaderProgramBinaryExtra.h

@@ -44,4 +44,35 @@ public:
 	}
 };
 
+/// Serialize ActiveProgramInputVariableMask
+template<typename TSerializer, typename T>
+void serializeActiveProgramInputVariableMask(T x, TSerializer& s)
+{
+	s.doArray("bitset", 0, &x.getData()[0], x.getData().getSize());
+}
+
+/// Serialize ActiveProgramInputVariableMask
+template<>
+class SerializeFunctor<ActiveProgramInputVariableMask>
+{
+public:
+	template<typename TSerializer>
+	void operator()(const ActiveProgramInputVariableMask& x, TSerializer& serializer)
+	{
+		serializeActiveProgramInputVariableMask<TSerializer, const ActiveProgramInputVariableMask&>(x, serializer);
+	}
+};
+
+/// Deserialize ActiveProgramInputVariableMask
+template<>
+class DeserializeFunctor<ActiveProgramInputVariableMask>
+{
+public:
+	template<typename TDeserializer>
+	void operator()(ActiveProgramInputVariableMask& x, TDeserializer& deserialize)
+	{
+		serializeShaderVariableBlockInfo<TDeserializer, ActiveProgramInputVariableMask&>(x, deserialize);
+	}
+};
+
 } // end namespace anki

+ 127 - 22
src/anki/shader_compiler/ShaderProgramCompiler.cpp

@@ -83,11 +83,10 @@ void ShaderProgramBinaryWrapper::cleanup()
 }
 
 /// Spin the dials. Used to compute all mutator combinations.
-static Bool spinDials(DynamicArrayAuto<U32> dials, ConstWeakArray<ShaderProgramParserMutator> mutators)
+static Bool spinDials(DynamicArrayAuto<U32>& dials, ConstWeakArray<ShaderProgramParserMutator> mutators)
 {
 	ANKI_ASSERT(dials.getSize() == mutators.getSize() && dials.getSize() > 0);
-	constexpr Bool done = true;
-	constexpr Bool notDone = false;
+	Bool done = true;
 
 	U crntDial = dials.getSize() - 1;
 	while(true)
@@ -100,7 +99,8 @@ static Bool spinDials(DynamicArrayAuto<U32> dials, ConstWeakArray<ShaderProgramP
 			if(crntDial == 0)
 			{
 				// Reached the 1st dial, stop spinning
-				return done;
+				done = true;
+				break;
 			}
 			else
 			{
@@ -110,11 +110,12 @@ static Bool spinDials(DynamicArrayAuto<U32> dials, ConstWeakArray<ShaderProgramP
 		}
 		else
 		{
-			return notDone;
+			done = false;
+			break;
 		}
 	}
 
-	return notDone;
+	return done;
 }
 
 static Error compileVariant(ConstWeakArray<ShaderProgramParserMutatorState> mutatorStates,
@@ -133,15 +134,14 @@ static Error compileVariant(ConstWeakArray<ShaderProgramParserMutatorState> muta
 
 	// Active vars
 	{
-		BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES, U64> active(false);
+		variant.m_activeVariables = ActiveProgramInputVariableMask(false);
 		for(PtrSize i = 0; i < parser.getInputs().getSize(); ++i)
 		{
 			if(parserVariant.isInputActive(parser.getInputs()[i]))
 			{
-				active.set(i, true);
+				variant.m_activeVariables.set(i, true);
 			}
 		}
-		variant.m_activeVariables = active.getData();
 	}
 
 	// Compile stages
@@ -333,13 +333,18 @@ Error compileShaderProgram(CString fname,
 		// - Populate the binary variant
 		do
 		{
+			for(PtrSize i = 0; i < parser.getMutators().getSize(); ++i)
+			{
+				states[i].m_value = parser.getMutators()[i].getValues()[dials[i]];
+			}
+
 			ShaderProgramBinaryVariant& variant = *variants.emplaceBack();
 			ANKI_CHECK(
 				compileVariant(states, parser, variant, codeBlocks, codeBlockHashes, tempAllocator, binaryAllocator));
 		} while(!spinDials(dials, parser.getMutators()));
 
 		// Store to binary
-		binary.m_variantCount = U32(variants.getSizeInBytes());
+		binary.m_variantCount = U32(variants.getSize());
 		PtrSize size, storage;
 		variants.moveAndReset(binary.m_variants, size, storage);
 
@@ -359,7 +364,7 @@ Error compileShaderProgram(CString fname,
 			states, parser, binary.m_variants[0], codeBlocks, codeBlockHashes, tempAllocator, binaryAllocator));
 		ANKI_ASSERT(codeBlocks.getSize() == U32(__builtin_popcount(U32(parser.getShaderTypes()))));
 
-		binary.m_codeBlockCount = 1;
+		binary.m_codeBlockCount = U32(codeBlocks.getSize());
 		PtrSize size, storage;
 		codeBlocks.moveAndReset(binary.m_codeBlocks, size, storage);
 	}
@@ -373,15 +378,17 @@ Error compileShaderProgram(CString fname,
 
 void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAuto& humanReadable)
 {
+#define ANKI_TAB "    "
+
 	GenericMemoryPoolAllocator<U8> alloc = humanReadable.getAllocator();
 	StringListAuto lines(alloc);
 
-	lines.pushBack("MUTATORS\n");
+	lines.pushBack("**MUTATORS**\n");
 	if(binary.m_mutatorCount > 0)
 	{
 		for(U i = 0; i < binary.m_mutatorCount; ++i)
 		{
-			lines.pushBackSprintf("\"%s\"", &binary.m_mutators[i].m_name[0]);
+			lines.pushBackSprintf(ANKI_TAB "\"%s\"", &binary.m_mutators[i].m_name[0]);
 			for(U j = 0; j < binary.m_mutators[i].m_valueCount; ++j)
 			{
 				lines.pushBackSprintf(" %d", binary.m_mutators[i].m_values[j]);
@@ -391,27 +398,31 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 	}
 	else
 	{
-		lines.pushBack("N/A\n");
+		lines.pushBack(ANKI_TAB "N/A\n");
 	}
 
-	lines.pushBack("\nINPUT VARIABLES\n");
+	lines.pushBack("\n**INPUT VARIABLES**\n");
 	if(binary.m_inputVariableCount > 0)
 	{
 		for(U i = 0; i < binary.m_inputVariableCount; ++i)
 		{
 			const ShaderProgramBinaryInput& input = binary.m_inputVariables[i];
-			lines.pushBackSprintf("\"%s\" ", &input.m_name[0]);
-			lines.pushBackSprintf("firstSpecializationConstant %" PRIu32 " ", input.m_firstSpecializationConstantIndex);
-			lines.pushBackSprintf("instanced  %" PRIu32 " ", U32(input.m_instanced));
+			lines.pushBackSprintf(ANKI_TAB "\"%s\" ", &input.m_name[0]);
+			if(input.m_firstSpecializationConstantIndex < MAX_U32)
+			{
+				lines.pushBackSprintf(
+					"firstSpecializationConstant %" PRIu32 " ", input.m_firstSpecializationConstantIndex);
+			}
+			lines.pushBackSprintf("instanced %" PRIu32 " ", U32(input.m_instanced));
 			lines.pushBackSprintf("dataType %" PRIu8 "\n", U8(input.m_dataType));
 		}
 	}
 	else
 	{
-		lines.pushBack("N/A\n");
+		lines.pushBack(ANKI_TAB "N/A\n");
 	}
 
-	lines.pushBack("\nBINARIES\n");
+	lines.pushBack("\n**BINARIES**\n");
 	for(U i = 0; i < binary.m_codeBlockCount; ++i)
 	{
 		spirv_cross::CompilerGLSL::Options options;
@@ -424,12 +435,106 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 		compiler.set_common_options(options);
 
 		std::string glsl = compiler.compile();
-		lines.pushBackSprintf("idx %" PRIuFAST32 ":\n%s\n--------\n", i, glsl.c_str());
+		StringListAuto sourceLines(alloc);
+		sourceLines.splitString(glsl.c_str(), '\n');
+		StringAuto newGlsl(alloc);
+		sourceLines.join("\n" ANKI_TAB ANKI_TAB, newGlsl);
+
+		lines.pushBackSprintf(ANKI_TAB "%" PRIuFAST32 " \n" ANKI_TAB ANKI_TAB "%s\n", i, newGlsl.cstr());
 	}
 
-	// TODO variants
+	lines.pushBack("\n**SHADER VARIANTS**\n");
+	for(U i = 0; i < binary.m_variantCount; ++i)
+	{
+		const ShaderProgramBinaryVariant& variant = binary.m_variants[i];
+
+		lines.pushBackSprintf(ANKI_TAB "%" PRIuFAST32 "\n", i);
+
+		// Misc
+		ANKI_ASSERT(variant.m_activeVariables.getData().getSize() == 2);
+		lines.pushBackSprintf(ANKI_TAB ANKI_TAB "activeVariables 0b%" ANKI_PRIb64 " 0b%" ANKI_PRIb64 " ",
+			ANKI_FORMAT_U64(variant.m_activeVariables.getData()[1]),
+			ANKI_FORMAT_U64(variant.m_activeVariables.getData()[0]));
+		lines.pushBackSprintf(
+			"blockSize %" PRIu32 " usesPushConstants %" PRIu8 "\n", variant.m_blockSize, variant.m_usesPushConstants);
+
+		// Mutator values
+		lines.pushBack(ANKI_TAB ANKI_TAB "mutatorValues ");
+		if(variant.m_mutatorValueCount > 0)
+		{
+			for(U j = 0; j < variant.m_mutatorValueCount; ++j)
+			{
+				lines.pushBackSprintf(
+					"\"%s\" %" PRId32 " ", &binary.m_mutators[j].m_name[0], variant.m_mutatorValues[j]);
+			}
+		}
+		else
+		{
+			lines.pushBack("N/A");
+		}
+		lines.pushBack("\n");
+
+		// Block infos
+		lines.pushBack(ANKI_TAB ANKI_TAB "blockInfos ");
+		if(variant.m_inputVariableCount > 0)
+		{
+			for(U j = 0; j < variant.m_inputVariableCount; ++j)
+			{
+				const ShaderVariableBlockInfo& inf = variant.m_blockInfos[j];
+				lines.pushBackSprintf("%" PRIi16 "|%" PRIi16 "|%" PRIi16 "|%" PRIi16 " ",
+					inf.m_offset,
+					inf.m_arraySize,
+					inf.m_arrayStride,
+					inf.m_matrixStride);
+			}
+		}
+		else
+		{
+			lines.pushBack("N/A");
+		}
+		lines.pushBack("\n");
+
+		// Bindings
+		lines.pushBack(ANKI_TAB ANKI_TAB "bindings ");
+		if(variant.m_inputVariableCount > 0)
+		{
+			for(U j = 0; j < variant.m_inputVariableCount; ++j)
+			{
+				if(variant.m_bindings[j] < 0)
+				{
+					lines.pushBack("N/A ");
+				}
+				else
+				{
+					lines.pushBackSprintf("%" PRIi16 " ", variant.m_bindings[j]);
+				}
+			}
+		}
+		else
+		{
+			lines.pushBack("N/A");
+		}
+		lines.pushBack("\n");
+
+		// Binary indices
+		lines.pushBack(ANKI_TAB ANKI_TAB "binaries ");
+		for(ShaderType shaderType = ShaderType::FIRST; shaderType < ShaderType::COUNT; ++shaderType)
+		{
+			if(variant.m_binaryIndices[shaderType] < MAX_U32)
+			{
+				lines.pushBackSprintf("%" PRIu32 " ", variant.m_binaryIndices[shaderType]);
+			}
+			else
+			{
+				lines.pushBack("N/A ");
+			}
+		}
+		lines.pushBack("\n");
+	}
 
 	lines.join("", humanReadable);
+
+#undef ANKI_TAB
 }
 
 } // end namespace anki

+ 6 - 0
src/anki/shader_compiler/ShaderProgramCompiler.h

@@ -43,6 +43,12 @@ public:
 
 	ANKI_USE_RESULT Error deserializeFromFile(CString fname);
 
+	const ShaderProgramBinary& getBinary() const
+	{
+		ANKI_ASSERT(m_binary);
+		return *m_binary;
+	}
+
 private:
 	GenericMemoryPoolAllocator<U8> m_alloc;
 	ShaderProgramBinary* m_binary = nullptr;

+ 8 - 6
src/anki/shader_compiler/ShaderProgramParser.cpp

@@ -262,8 +262,6 @@ Error ShaderProgramParser::parsePragmaDescriptorSet(
 		ANKI_PP_ERROR_MALFORMED_MSG("The descriptor set index is too high");
 	}
 
-	m_globalsLines.pushFrontSprintf("#define _ANKI_DSET %u", m_set);
-
 	return Error::NONE;
 }
 
@@ -489,7 +487,7 @@ Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const Strin
 
 			if(comp == 0)
 			{
-				inputDeclaration.sprintf("const %s %s = %s(_anki_const_%s_%u",
+				inputDeclaration.sprintf("%s %s = %s(_anki_const_%s_%u",
 					dataTypeStr.cstr(),
 					input.m_name.cstr(),
 					dataTypeStr.cstr(),
@@ -515,7 +513,7 @@ Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const Strin
 
 		m_globalsLines.pushBack("#else");
 		m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", input.m_name.cstr());
-		m_globalsLines.pushBack("#endf");
+		m_globalsLines.pushBack("#endif");
 	}
 	else if(isSampler || isTexture)
 	{
@@ -540,7 +538,7 @@ Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const Strin
 
 		m_globalsLines.pushBack("#else");
 		m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", input.m_name.cstr());
-		m_globalsLines.pushBack("#endf");
+		m_globalsLines.pushBack("#endif");
 	}
 	else
 	{
@@ -935,8 +933,9 @@ Error ShaderProgramParser::parse()
 	}
 
 	// Create the UBO source code
-	if(m_uboStructLines.getSize() > 0)
 	{
+		m_uboStructLines.pushFrontSprintf("#define _ANKI_DSET %u", m_set);
+
 		m_uboStructLines.pushFront("struct _AnkiUniforms {");
 		m_uboStructLines.pushBack("};");
 
@@ -1217,6 +1216,8 @@ Error ShaderProgramParser::generateVariant(
 			variant.m_bindings[in.m_idx] = I16(texOrSamplerBinding++);
 		}
 
+		defines.pushBack(" ");
+
 		if(!defines.isEmpty())
 		{
 			defines.join("\n", bindingDefines);
@@ -1238,6 +1239,7 @@ Error ShaderProgramParser::generateVariant(
 		finalSource.append(StringAuto(m_alloc).sprintf("#define ANKI_%s 1\n", SHADER_STAGE_NAMES[shaderType].cstr()));
 		finalSource.append(activeInputs);
 		finalSource.append(pushConstantDefineSrc);
+		finalSource.append(bindingDefines);
 		finalSource.append(m_uboSource);
 		finalSource.append(m_globalsSource);
 		finalSource.append(m_codeSource);

+ 1 - 1
src/anki/shader_compiler/ShaderProgramParser.h

@@ -152,7 +152,7 @@ public:
 
 	U32 getBinding(const ShaderProgramParserInput& in) const
 	{
-		ANKI_ASSERT(in.isSampler() && in.isTexture() && m_bindings[in.m_idx] >= 0);
+		ANKI_ASSERT((in.isSampler() || in.isTexture()) && m_bindings[in.m_idx] >= 0);
 		return U32(m_bindings[in.m_idx]);
 	}
 

+ 2 - 0
src/anki/util/File.cpp

@@ -471,6 +471,8 @@ PtrSize File::tell()
 	{
 		ANKI_ASSERT(0);
 	}
+
+	return 0;
 }
 
 Error File::identifyFile(const CString& filename,

+ 17 - 0
src/anki/util/Functions.h

@@ -33,6 +33,23 @@ namespace anki
 /// Make a preprocessor token a string.
 #define ANKI_STRINGIZE(a) _ANKI_STRINGIZE(a)
 
+/// Format to print bits
+#define ANKI_PRIb8 "c%c%c%c%c%c%c%c"
+#define ANKI_PRIb16 ANKI_PRIb8 "%c%c%c%c%c%c%c%c"
+#define ANKI_PRIb32 ANKI_PRIb16 "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
+#define ANKI_PRIb64 ANKI_PRIb32 "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
+
+#define _ANKI_FORMAT_HELPER(byte, bit) (U64(byte) & (U64(1) << U64(bit))) ? '1' : '0'
+
+#define ANKI_FORMAT_U8(byte) \
+	_ANKI_FORMAT_HELPER((byte), 7), _ANKI_FORMAT_HELPER((byte), 6), _ANKI_FORMAT_HELPER((byte), 5), \
+		_ANKI_FORMAT_HELPER((byte), 4), _ANKI_FORMAT_HELPER((byte), 3), _ANKI_FORMAT_HELPER((byte), 2), \
+		_ANKI_FORMAT_HELPER((byte), 1), _ANKI_FORMAT_HELPER((byte), 0)
+
+#define ANKI_FORMAT_U16(u16) ANKI_FORMAT_U8(u16 >> 8), ANKI_FORMAT_U8(u16)
+#define ANKI_FORMAT_U32(u32) ANKI_FORMAT_U16(u32 >> 16), ANKI_FORMAT_U16(u32)
+#define ANKI_FORMAT_U64(u64) ANKI_FORMAT_U32(u64 >> 32), ANKI_FORMAT_U32(u64)
+
 // ANKI_ENABLE_METHOD & ANKI_ENABLE_ARG trickery copied from Tick library
 template<typename T, int N>
 struct DummyType

+ 13 - 4
src/anki/util/serializer.py

@@ -25,7 +25,7 @@ ctx = Context()
 
 
 class MemberInfo:
-    __slots__ = ["name", "base_type", "array_size", "comment", "pointer"]
+    __slots__ = ["name", "base_type", "array_size", "comment", "pointer", "constructor"]
 
     def __init__(self):
         self.name = None
@@ -33,6 +33,7 @@ class MemberInfo:
         self.array_size = 1
         self.comment = None
         self.pointer = False
+        self.constructor = None
 
     def is_dynamic_array(self, member_arr):
         if not self.pointer:
@@ -117,6 +118,9 @@ def gen_class(root_el):
         if member_el.get("comment"):
             member.comment = member_el.get("comment")
 
+        if member_el.get("constructor"):
+            member.constructor = member_el.get("constructor")
+
         member_arr.append(member)
 
     # Write members
@@ -127,12 +131,17 @@ def gen_class(root_el):
         else:
             comment = ""
 
+        if member.constructor:
+            constructor = "= {%s}" % member.constructor
+        else:
+            constructor = ""
+
         if member.pointer:
-            writeln("%s* %s; %s" % (member.base_type, member.name, comment))
+            writeln("%s* %s%s; %s" % (member.base_type, member.name, constructor, comment))
         elif member.array_size != "1":
-            writeln("Array<%s, %s> %s; %s" % (member.base_type, member.array_size, member.name, comment))
+            writeln("Array<%s, %s> %s%s; %s" % (member.base_type, member.array_size, member.name, constructor, comment))
         else:
-            writeln("%s %s; %s" % (member.base_type, member.name, comment))
+            writeln("%s %s%s; %s" % (member.base_type, member.name, constructor, comment))
     ident(-1)
 
     # Before serialize make sure the dynamic arrays are last

+ 73 - 1
tests/shader_compiler/ShaderProgramCompiler.cpp

@@ -9,6 +9,74 @@
 ANKI_TEST(ShaderCompiler, ShaderProgramCompiler)
 {
 	const CString sourceCode = R"(
+#pragma anki mutator instanceCount INSTANCE_COUNT 1 2 4 8 16 32 64
+#pragma anki mutator LOD 0 1 2
+#pragma anki mutator PASS 0 1 2 3
+#pragma anki mutator DIFFUSE_TEX 0 1
+#pragma anki mutator SPECULAR_TEX 0 1
+#pragma anki mutator ROUGHNESS_TEX 0 1
+#pragma anki mutator METAL_TEX 0 1
+#pragma anki mutator NORMAL_TEX 0 1
+#pragma anki mutator PARALLAX 0 1
+#pragma anki mutator EMISSIVE_TEX 0 1
+#pragma anki mutator BONES 0 1
+#pragma anki mutator VELOCITY 0 1
+
+#pragma anki input instanced Mat4 mvp
+#if PASS == 0
+#	pragma anki input instanced Mat3 rotationMat
+#endif
+#if PASS == 0 && PARALLAX == 1
+#	pragma anki input instanced Mat4 modelViewMat
+#endif
+#if PASS == 0 && VELOCITY == 1
+#	pragma anki input instanced Mat4 prevMvp
+#endif
+
+#if DIFFUSE_TEX == 0 && PASS == 0
+#	pragma anki input const Vec3 diffColor
+#endif
+#if SPECULAR_TEX == 0 && PASS == 0
+#	pragma anki input const Vec3 specColor
+#endif
+#if ROUGHNESS_TEX == 0 && PASS == 0
+#	pragma anki input const F32 roughness
+#endif
+#if METAL_TEX == 0 && PASS == 0
+#	pragma anki input const F32 metallic
+#endif
+#if EMISSIVE_TEX == 0 && PASS == 0
+#	pragma anki input const Vec3 emission
+#endif
+#if PARALLAX == 1 && PASS == 0 && LOD == 0
+#	pragma anki input const F32 heightMapScale
+#endif
+#if PASS == 0
+#	pragma anki input const F32 subsurface
+#endif
+#pragma anki input sampler globalSampler
+#if DIFFUSE_TEX == 1 && PASS == 0
+#	pragma anki input texture2D diffTex
+#endif
+#if SPECULAR_TEX == 1 && PASS == 0
+#	pragma anki input texture2D specTex
+#endif
+#if ROUGHNESS_TEX == 1 && PASS == 0
+#	pragma anki input texture2D roughnessTex
+#endif
+#if METAL_TEX == 1 && PASS == 0
+#	pragma anki input texture2D metallicTex
+#endif
+#if NORMAL_TEX == 1 && PASS == 0 && LOD < 2
+#	pragma anki input texture2D normalTex
+#endif
+#if PARALLAX == 1 && PASS == 0 && LOD == 0
+#	pragma anki input texture2D heightTex
+#endif
+#if EMISSIVE_TEX == 1 && PASS == 0
+#	pragma anki input texture2D emissiveTex
+#endif
+
 #pragma anki start vert
 out gl_PerVertex
 {
@@ -17,7 +85,7 @@ out gl_PerVertex
 
 void main()
 {
-	gl_Position = Vec4(gl_Position);
+	gl_Position = Vec4(gl_VertexID);
 }
 #pragma anki end
 
@@ -53,4 +121,8 @@ void main()
 	HeapAllocator<U8> alloc(allocAligned, nullptr);
 	ShaderProgramBinaryWrapper binary(alloc);
 	ANKI_TEST_EXPECT_NO_ERR(compileShaderProgram("test.glslp", fsystem, alloc, 128, 1, 1, GpuVendor::AMD, binary));
+
+	StringAuto dis(alloc);
+	disassembleShaderProgramBinary(binary.getBinary(), dis);
+	ANKI_LOGI("Binary disassembly:\n%s\n", dis.cstr());
 }