فهرست منبع

Work on the compiler

Panagiotis Christopoulos Charitos 6 سال پیش
والد
کامیت
cbf869a93d

+ 1 - 0
src/anki/AnKi.h

@@ -17,3 +17,4 @@
 #include <anki/Gr.h>
 #include <anki/Core.h>
 #include <anki/Importer.h>
+#include <anki/ShaderCompiler.h>

+ 10 - 0
src/anki/ShaderCompiler.h

@@ -0,0 +1,10 @@
+// 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/shader_compiler/ShaderProgramCompiler.h>
+
+/// @defgroup shader_compiler Shader compiler

+ 6 - 0
src/anki/gr/Enums.h

@@ -469,6 +469,12 @@ enum class ShaderTypeBit : U8
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderTypeBit, inline)
 
+inline ShaderTypeBit shaderTypeToBit(ShaderType type)
+{
+	ANKI_ASSERT(type < ShaderType::COUNT);
+	return ShaderTypeBit(1 << U32(type));
+}
+
 enum class ShaderVariableDataType : U8
 {
 	NONE,

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

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/util/Logger.h>
+#include <anki/util/String.h>
 
 namespace anki
 {
@@ -20,6 +21,12 @@ constexpr U32 MAX_SHADER_PROGRAM_INPUT_VARIABLES = 128;
 #define ANKI_SHADER_COMPILER_LOGW(...) ANKI_LOG(" SHC", WARNING, __VA_ARGS__)
 #define ANKI_SHADER_COMPILER_LOGF(...) ANKI_LOG(" SHC", FATAL, __VA_ARGS__)
 
+/// An interface used by the ShaderProgramParser and ShaderProgramCompiler to abstract file loading.
+class ShaderProgramFilesystemInterface
+{
+public:
+	virtual ANKI_USE_RESULT Error readAllText(CString filename, StringAuto& txt) = 0;
+};
 /// @}
 
 } // end namespace anki

+ 112 - 2
src/anki/shader_compiler/Glslang.cpp

@@ -4,6 +4,19 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/shader_compiler/Glslang.h>
+#include <anki/util/StringList.h>
+
+#if ANKI_COMPILER_GCC_COMPATIBLE
+#	pragma GCC diagnostic push
+#	pragma GCC diagnostic ignored "-Wundef"
+#	pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include <glslang/Public/ShaderLang.h>
+#include <glslang/SPIRV/GlslangToSpv.h>
+#include <glslang/StandAlone/DirStackFileIncluder.h>
+#if ANKI_COMPILER_GCC_COMPATIBLE
+#	pragma GCC diagnostic pop
+#endif
 
 namespace anki
 {
@@ -125,9 +138,9 @@ static TBuiltInResource setGlslangLimits()
 	return c;
 }
 
-TBuiltInResource GLSLANG_LIMITS = setGlslangLimits();
+static TBuiltInResource GLSLANG_LIMITS = setGlslangLimits();
 
-EShLanguage ankiToGlslangShaderType(ShaderType shaderType)
+static EShLanguage ankiToGlslangShaderType(ShaderType shaderType)
 {
 	EShLanguage gslangShader;
 	switch(shaderType)
@@ -158,4 +171,101 @@ EShLanguage ankiToGlslangShaderType(ShaderType shaderType)
 	return gslangShader;
 }
 
+static void logShaderErrorCode(CString error, CString source, GenericMemoryPoolAllocator<U8> alloc)
+{
+	StringAuto prettySrc(alloc);
+	StringListAuto lines(alloc);
+
+	static const char* padding = "==============================================================================";
+
+	lines.splitString(source, '\n', true);
+
+	I lineno = 0;
+	for(auto it = lines.getBegin(); it != lines.getEnd(); ++it)
+	{
+		++lineno;
+		StringAuto tmp(alloc);
+
+		if(!it->isEmpty())
+		{
+			tmp.sprintf("%8d: %s\n", lineno, &(*it)[0]);
+		}
+		else
+		{
+			tmp.sprintf("%8d:\n", lineno);
+		}
+
+		prettySrc.append(tmp);
+	}
+
+	ANKI_SHADER_COMPILER_LOGE("Shader compilation failed:\n%s\n%s\n%s\n%s\n%s\n%s",
+		padding,
+		&error[0],
+		padding,
+		&prettySrc[0],
+		padding,
+		&error[0]);
+}
+
+Error preprocessGlsl(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;
+}
+
+Error compilerGlslToSpirv(
+	CString src, ShaderType shaderType, GenericMemoryPoolAllocator<U8> tmpAlloc, DynamicArrayAuto<U8>& spirv)
+{
+	const EShLanguage stage = ankiToGlslangShaderType(shaderType);
+	const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
+	const glslang::EShTargetLanguageVersion langVersion = glslang::EShTargetSpv_1_3;
+
+	glslang::TShader shader(stage);
+	Array<const char*, 1> csrc = {{&src[0]}};
+	shader.setStrings(&csrc[0], 1);
+	shader.setEnvTarget(glslang::EShTargetSpv, langVersion);
+	if(!shader.parse(&GLSLANG_LIMITS, 100, false, messages))
+	{
+		logShaderErrorCode(shader.getInfoLog(), src, tmpAlloc);
+		return Error::USER_DATA;
+	}
+
+	// Setup the program
+	glslang::TProgram program;
+	program.addShader(&shader);
+
+	if(!program.link(messages))
+	{
+		ANKI_SHADER_COMPILER_LOGE("glslang failed to link a shader");
+		return Error::USER_DATA;
+	}
+
+	// Gen SPIRV
+	glslang::SpvOptions spvOptions;
+	spvOptions.optimizeSize = true;
+	spvOptions.disableOptimizer = false;
+	std::vector<unsigned int> glslangSpirv;
+	glslang::GlslangToSpv(*program.getIntermediate(stage), glslangSpirv, &spvOptions);
+
+	// Store
+	spirv.resize(glslangSpirv.size() * sizeof(unsigned int));
+	memcpy(&spirv[0], &glslangSpirv[0], spirv.getSizeInBytes());
+
+	return Error::NONE;
+}
+
 } // end namespace anki

+ 7 - 14
src/anki/shader_compiler/Glslang.h

@@ -6,28 +6,21 @@
 #pragma once
 
 #include <anki/shader_compiler/Common.h>
+#include <anki/util/String.h>
 #include <anki/gr/Enums.h>
 
-#if ANKI_COMPILER_GCC_COMPATIBLE
-#	pragma GCC diagnostic push
-#	pragma GCC diagnostic ignored "-Wundef"
-#	pragma GCC diagnostic ignored "-Wconversion"
-#endif
-#include <glslang/Public/ShaderLang.h>
-#include <glslang/SPIRV/GlslangToSpv.h>
-#include <glslang/StandAlone/DirStackFileIncluder.h>
-#if ANKI_COMPILER_GCC_COMPATIBLE
-#	pragma GCC diagnostic pop
-#endif
-
 namespace anki
 {
 
 /// @addtogroup shader_compiler
 /// @{
-extern TBuiltInResource GLSLANG_LIMITS;
 
-EShLanguage ankiToGlslangShaderType(ShaderType shaderType);
+/// Run glslang's preprocessor.
+ANKI_USE_RESULT Error preprocessGlsl(CString in, StringAuto& out);
+
+/// Compile glsl to SPIR-V.
+ANKI_USE_RESULT Error compilerGlslToSpirv(
+	CString src, ShaderType shaderType, GenericMemoryPoolAllocator<U8> tmpAlloc, DynamicArrayAuto<U8>& spirv);
 /// @}
 
 } // end namespace anki

+ 129 - 20
src/anki/shader_compiler/ShaderProgramBinary.h

@@ -7,7 +7,9 @@
 
 #pragma once
 
-#include <anki/src/shader_compiler/Common.h>
+#include <anki/shader_compiler/Common.h>
+#include <anki/shader_compiler/ShaderProgramBinaryExtra.h>
+#include <anki/gr/Enums.h>
 
 namespace anki
 {
@@ -22,12 +24,28 @@ public:
 	Bool m_instanced;
 	ShaderVariableDataType m_dataType;
 
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doValue("m_nameSize", offsetof(ShaderProgramBinaryInput, m_nameSize), self.m_nameSize);
+		s.doValue("m_firstSpecializationConstantIndex",
+			offsetof(ShaderProgramBinaryInput, m_firstSpecializationConstantIndex),
+			self.m_firstSpecializationConstantIndex);
+		s.doValue("m_instanced", offsetof(ShaderProgramBinaryInput, m_instanced), self.m_instanced);
+		s.doValue("m_dataType", offsetof(ShaderProgramBinaryInput, m_dataType), self.m_dataType);
+		s.doDynamicArray("m_name", offsetof(ShaderProgramBinaryInput, m_name), self.m_name, self.m_nameSize);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryInput&>(deserializer, *this);
+	}
+
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializer.writeDynamicArray(m_name, m_nameSize);
-		serializer.writeValue(m_nameSize) serializer.writeValue(m_firstSpecializationConstantIndex)
-			serializer.writeValue(m_instanced) serializer.writeValue(m_dataType)
+		serializeCommon<TSerializer, const ShaderProgramBinaryInput&>(serializer, *this);
 	}
 };
 
@@ -39,13 +57,28 @@ public:
 	I32* m_values;
 	U32 m_nameSize;
 	U32 m_valueCount;
+	Bool m_instanceCount;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doValue("m_nameSize", offsetof(ShaderProgramBinaryMutator, m_nameSize), self.m_nameSize);
+		s.doValue("m_valueCount", offsetof(ShaderProgramBinaryMutator, m_valueCount), self.m_valueCount);
+		s.doValue("m_instanceCount", offsetof(ShaderProgramBinaryMutator, m_instanceCount), self.m_instanceCount);
+		s.doDynamicArray("m_name", offsetof(ShaderProgramBinaryMutator, m_name), self.m_name, self.m_nameSize);
+		s.doDynamicArray("m_values", offsetof(ShaderProgramBinaryMutator, m_values), self.m_values, self.m_valueCount);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryMutator&>(deserializer, *this);
+	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializer.writeDynamicArray(m_name, m_nameSize);
-		serializer.writeDynamicArray(m_values, m_valueCount);
-		serializer.writeValue(m_nameSize) serializer.writeValue(m_valueCount)
+		serializeCommon<TSerializer, const ShaderProgramBinaryMutator&>(serializer, *this);
 	}
 };
 
@@ -53,17 +86,57 @@ public:
 class ShaderProgramBinaryVariant
 {
 public:
+	Array<U64, MAX_SHADER_PROGRAM_INPUT_VARIABLES / (sizeof(U64) * 8)> m_activeVariables;
 	I32* m_mutatorValues;
+	ShaderVariableBlockInfo* m_blockInfos;
+	I16* m_bindings;
 	U32 m_mutatorValueCount;
-	BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES, U32> m_activeVariables;
-	U32 m_codeIndex;
+	U32 m_inputVariableCount;
+	Array<U32, U32(ShaderType::COUNT)> m_binaryIndices; ///< Index in ShaderProgramBinary::m_codeBlocks.
+	U32 m_blockSize;
+	Bool m_usesPushConstants;
+
+	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_mutatorValueCount", offsetof(ShaderProgramBinaryVariant, m_mutatorValueCount), self.m_mutatorValueCount);
+		s.doValue("m_inputVariableCount",
+			offsetof(ShaderProgramBinaryVariant, m_inputVariableCount),
+			self.m_inputVariableCount);
+		s.doArray("m_binaryIndices",
+			offsetof(ShaderProgramBinaryVariant, m_binaryIndices),
+			&self.m_binaryIndices[0],
+			U32(ShaderType::COUNT));
+		s.doValue("m_blockSize", offsetof(ShaderProgramBinaryVariant, m_blockSize), self.m_blockSize);
+		s.doValue(
+			"m_usesPushConstants", offsetof(ShaderProgramBinaryVariant, m_usesPushConstants), self.m_usesPushConstants);
+		s.doDynamicArray("m_mutatorValues",
+			offsetof(ShaderProgramBinaryVariant, m_mutatorValues),
+			self.m_mutatorValues,
+			self.m_mutatorValueCount);
+		s.doDynamicArray("m_blockInfos",
+			offsetof(ShaderProgramBinaryVariant, m_blockInfos),
+			self.m_blockInfos,
+			self.m_inputVariableCount);
+		s.doDynamicArray(
+			"m_bindings", offsetof(ShaderProgramBinaryVariant, m_bindings), self.m_bindings, self.m_inputVariableCount);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryVariant&>(deserializer, *this);
+	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializer.writeDynamicArray(m_mutatorValues, m_mutatorValueCount);
-		serializer.writeValue(m_mutatorValueCount) serializer.writeValue(m_activeVariables)
-			serializer.writeValue(m_codeIndex)
+		serializeCommon<TSerializer, const ShaderProgramBinaryVariant&>(serializer, *this);
 	}
 };
 
@@ -72,13 +145,25 @@ class ShaderProgramBinaryCode
 {
 public:
 	U8* m_binary;
-	U32 m_binarySize;
+	PtrSize m_binarySize;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doValue("m_binarySize", offsetof(ShaderProgramBinaryCode, m_binarySize), self.m_binarySize);
+		s.doDynamicArray("m_binary", offsetof(ShaderProgramBinaryCode, m_binary), self.m_binary, self.m_binarySize);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryCode&>(deserializer, *this);
+	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializer.writeDynamicArray(m_binary, m_binarySize);
-		serializer.writeValue(m_binarySize)
+		serializeCommon<TSerializer, const ShaderProgramBinaryCode&>(serializer, *this);
 	}
 };
 
@@ -93,15 +178,39 @@ public:
 	U32 m_mutatorCount;
 	U32 m_inputVariableCount;
 	U32 m_codeBlockCount;
+	U32 m_descriptorSet;
+	ShaderTypeBit m_presentShaderTypes;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doArray("m_magic", offsetof(ShaderProgramBinary, m_magic), &self.m_magic[0], 8);
+		s.doValue("m_mutatorCount", offsetof(ShaderProgramBinary, m_mutatorCount), self.m_mutatorCount);
+		s.doValue(
+			"m_inputVariableCount", offsetof(ShaderProgramBinary, m_inputVariableCount), self.m_inputVariableCount);
+		s.doValue("m_codeBlockCount", offsetof(ShaderProgramBinary, m_codeBlockCount), self.m_codeBlockCount);
+		s.doValue("m_descriptorSet", offsetof(ShaderProgramBinary, m_descriptorSet), self.m_descriptorSet);
+		s.doValue(
+			"m_presentShaderTypes", offsetof(ShaderProgramBinary, m_presentShaderTypes), self.m_presentShaderTypes);
+		s.doDynamicArray("m_mutators", offsetof(ShaderProgramBinary, m_mutators), self.m_mutators, self.m_mutatorCount);
+		s.doDynamicArray("m_inputVariables",
+			offsetof(ShaderProgramBinary, m_inputVariables),
+			self.m_inputVariables,
+			self.m_inputVariableCount);
+		s.doDynamicArray(
+			"m_codeBlocks", offsetof(ShaderProgramBinary, m_codeBlocks), self.m_codeBlocks, self.m_codeBlockCount);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinary&>(deserializer, *this);
+	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializer.writeArray(m_magic, 8) serializer.writeDynamicArray(m_mutators, m_mutatorCount);
-		serializer.writeDynamicArray(m_inputVariables, m_inputVariableCount);
-		serializer.writeDynamicArray(m_codeBlocks, m_codeBlockCount);
-		serializer.writeValue(m_mutatorCount) serializer.writeValue(m_inputVariableCount)
-			serializer.writeValue(m_codeBlockCount)
+		serializeCommon<TSerializer, const ShaderProgramBinary&>(serializer, *this);
 	}
 };
 

+ 14 - 4
src/anki/shader_compiler/ShaderProgramBinary.xml

@@ -1,6 +1,8 @@
 <serializer>
 	<includes>
-		<include file="&lt;anki/src/shader_compiler/Common.h&gt;"/>
+		<include file="&lt;anki/shader_compiler/Common.h&gt;"/>
+		<include file="&lt;anki/shader_compiler/ShaderProgramBinaryExtra.h&gt;"/>
+		<include file="&lt;anki/gr/Enums.h&gt;"/>
 	</includes>
 
 	<classes>
@@ -20,22 +22,28 @@
 				<member name="m_values" type="I32" pointer="true" array_size="m_valueCount" />
 				<member name="m_nameSize" type="U32" />
 				<member name="m_valueCount" type="U32" />
+				<member name="m_instanceCount" type="Bool" />
 			</members>
 		</class>
 
 		<class name="ShaderProgramBinaryVariant">
 			<members>
+				<member name="m_activeVariables" type="U64" array_size="MAX_SHADER_PROGRAM_INPUT_VARIABLES / (sizeof(U64) * 8)" />
 				<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" />
 				<member name="m_mutatorValueCount" type="U32" />
-				<member name="m_activeVariables" type="BitSet&lt;MAX_SHADER_PROGRAM_INPUT_VARIABLES, U32&gt;" />
-				<member name="m_codeIndex" type="U32" />
+				<member name="m_inputVariableCount" type="U32" />
+				<member name="m_binaryIndices" type="U32" array_size="U32(ShaderType::COUNT)" comment="Index in ShaderProgramBinary::m_codeBlocks" />
+				<member name="m_blockSize" type="U32" />
+				<member name="m_usesPushConstants" type="Bool" />
 			</members>
 		</class>
 
 		<class name="ShaderProgramBinaryCode">
 			<members>
 				<member name="m_binary" type="U8" pointer="true" array_size="m_binarySize" />
-				<member name="m_binarySize" type="U32" />
+				<member name="m_binarySize" type="PtrSize" />
 			</members>
 		</class>
 
@@ -48,6 +56,8 @@
 				<member name="m_mutatorCount" type="U32" />
 				<member name="m_inputVariableCount" type="U32" />
 				<member name="m_codeBlockCount" type="U32" />
+				<member name="m_descriptorSet" type="U32" />
+				<member name="m_presentShaderTypes" type="ShaderTypeBit" />
 			</members>
 		</class>
 	</classes>

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

@@ -0,0 +1,47 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/shader_compiler/Common.h>
+#include <anki/gr/utils/Functions.h>
+#include <anki/util/Serializer.h>
+
+namespace anki
+{
+
+/// Serialize/deserialize ShaderVariableBlockInfo
+template<typename TSerializer, typename TShaderVariableBlockInfo>
+void serializeShaderVariableBlockInfo(TShaderVariableBlockInfo x, TSerializer& s)
+{
+	s.doValue("m_offset", offsetof(ShaderVariableBlockInfo, m_offset), x.m_offset);
+	s.doValue("m_arraySize", offsetof(ShaderVariableBlockInfo, m_arraySize), x.m_arraySize);
+	s.doValue("m_arrayStride", offsetof(ShaderVariableBlockInfo, m_arrayStride), x.m_arrayStride);
+	s.doValue("m_matrixStride", offsetof(ShaderVariableBlockInfo, m_matrixStride), x.m_matrixStride);
+}
+
+/// Serialize ShaderVariableBlockInfo
+template<>
+class SerializeFunctor<ShaderVariableBlockInfo>
+{
+public:
+	template<typename TSerializer>
+	void operator()(const ShaderVariableBlockInfo& x, TSerializer& serializer)
+	{
+		serializeShaderVariableBlockInfo<TSerializer, ShaderVariableBlockInfo&>(x, serializer);
+	}
+};
+
+/// Deserialize ShaderVariableBlockInfo
+template<>
+class DeserializeFunctor<ShaderVariableBlockInfo>
+{
+public:
+	template<typename TDeserializer>
+	void operator()(ShaderVariableBlockInfo& x, TDeserializer& deserialize)
+	{
+		serializeShaderVariableBlockInfo<TDeserializer, const ShaderVariableBlockInfo&>(x, deserialize);
+	}
+};
+
+} // end namespace anki

+ 193 - 0
src/anki/shader_compiler/ShaderProgramCompiler.cpp

@@ -0,0 +1,193 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/shader_compiler/ShaderProgramCompiler.h>
+#include <anki/shader_compiler/ShaderProgramParser.h>
+#include <anki/shader_compiler/Glslang.h>
+#include <anki/util/Serializer.h>
+
+namespace anki
+{
+
+/// XXX
+static Bool spinDials(DynamicArrayAuto<U32> dials, ConstWeakArray<ShaderProgramParserMutator> mutators)
+{
+	// XXX
+	return false;
+}
+
+Error ShaderProgramBinaryWrapper::serializeToFile(CString fname) const
+{
+	ANKI_ASSERT(m_binary);
+
+	File file;
+	ANKI_CHECK(file.open(fname, FileOpenFlag::WRITE | FileOpenFlag::BINARY));
+
+	BinarySerializer serializer;
+	ANKI_CHECK(serializer.serialize(*m_binary, m_alloc, file));
+
+	return Error::NONE;
+}
+
+Error ShaderProgramBinaryWrapper::deserializeFromFile(CString fname)
+{
+	cleanup();
+
+	File file;
+	ANKI_CHECK(file.open(fname, FileOpenFlag::READ | FileOpenFlag::BINARY));
+
+	BinaryDeserializer deserializer;
+	ANKI_CHECK(deserializer.deserialize(m_binary, m_alloc, file));
+
+	m_singleAllocation = true;
+
+	return Error::NONE;
+}
+
+Error ShaderProgramCompiler::compile(CString fname,
+	ShaderProgramFilesystemInterface* fsystem,
+	GenericMemoryPoolAllocator<U8> tempAllocator,
+	U32 pushConstantsSize,
+	U32 backendMinor,
+	U32 backendMajor,
+	GpuVendor gpuVendor,
+	ShaderProgramBinaryWrapper& binary)
+{
+	ANKI_ASSERT(fsystem);
+
+	binary.cleanup();
+	GenericMemoryPoolAllocator<U8> binaryAllocator;
+
+	// Parse source
+	ShaderProgramParser parser(fname, fsystem, tempAllocator, pushConstantsSize, backendMinor, backendMajor, gpuVendor);
+	ANKI_CHECK(parser.parse());
+
+	// Create all variants
+	if(parser.getMutators().getSize() > 0)
+	{
+		// Initialize
+		DynamicArrayAuto<ShaderProgramParserMutatorState> states(tempAllocator, parser.getMutators().getSize());
+		DynamicArrayAuto<U32> dials(tempAllocator, parser.getMutators().getSize());
+		for(PtrSize i = 0; i < parser.getMutators().getSize(); ++i)
+		{
+			states[i].m_mutator = &parser.getMutators()[i];
+			states[i].m_value = parser.getMutators()[i].getValues()[0];
+
+			dials[i] = 0;
+		}
+		DynamicArrayAuto<ShaderProgramBinaryVariant> variants(binaryAllocator);
+		DynamicArrayAuto<ShaderProgramBinaryCode> codeBlocks(binaryAllocator);
+		DynamicArrayAuto<U64> codeBlockHashes(tempAllocator);
+
+		// Spin for all possible combinations of mutators and
+		// - Create the spirv
+		// - Populate the binary variant
+		ShaderProgramParserVariant parserVariant;
+		do
+		{
+			ShaderProgramBinaryVariant& variant = *variants.emplaceBack();
+			variant = {};
+
+			// Generate the source and the rest for the variant
+			ANKI_CHECK(parser.generateVariant(states, parserVariant));
+
+			// Compile
+			for(ShaderType shaderType = ShaderType::FIRST; shaderType < ShaderType::COUNT; ++shaderType)
+			{
+				if(!(shaderTypeToBit(shaderType) & parser.getShaderStages()))
+				{
+					continue;
+				}
+
+				// Compile
+				DynamicArrayAuto<U8> spirv(tempAllocator);
+				ANKI_CHECK(compilerGlslToSpirv(parserVariant.getSource(shaderType), shaderType, tempAllocator, spirv));
+				ANKI_ASSERT(spirv.getSize() > 0);
+
+				// Check if the spirv is already generated
+				const U64 hash = computeHash(&spirv[0], spirv.getSize());
+				Bool found = false;
+				for(PtrSize i = 0; i < codeBlockHashes.getSize(); ++i)
+				{
+					if(codeBlockHashes[i] == hash)
+					{
+						// Found it
+						variant.m_binaryIndices[shaderType] = U32(i);
+						found = true;
+						break;
+					}
+				}
+
+				// Create it if not found
+				if(!found)
+				{
+					U8* code = binaryAllocator.allocate(spirv.getSizeInBytes());
+					memcpy(code, &spirv[0], spirv.getSizeInBytes());
+
+					ShaderProgramBinaryCode block;
+					block.m_binary = code;
+					block.m_binarySize = spirv.getSizeInBytes();
+					codeBlocks.emplaceBack(block);
+
+					codeBlockHashes.emplaceBack(hash);
+
+					variant.m_binaryIndices[shaderType] = U32(codeBlocks.getSize() - 1);
+				}
+			}
+
+			// Active vars
+			BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES, U64> active(false);
+			for(PtrSize i = 0; i < parser.getInputs().getSize(); ++i)
+			{
+				if(parserVariant.isInputActive(parser.getInputs()[i]))
+				{
+					active.set(i, true);
+				}
+			}
+			variant.m_activeVariables = active.getData();
+
+			// Mutator values
+			variant.m_mutatorValues = binaryAllocator.newArray<I32>(parser.getMutators().getSize());
+			for(PtrSize i = 0; i < parser.getMutators().getSize(); ++i)
+			{
+				variant.m_mutatorValues[i] = states[i].m_value;
+			}
+
+			// Input vars
+			ShaderVariableBlockInfo defaultInfo;
+			defaultInfo.m_arraySize = -1;
+			defaultInfo.m_arrayStride = -1;
+			defaultInfo.m_matrixStride = -1;
+			defaultInfo.m_offset = -1;
+			variant.m_blockInfos =
+				binaryAllocator.newArray<ShaderVariableBlockInfo>(parser.getInputs().getSize(), defaultInfo);
+
+			variant.m_bindings = binaryAllocator.newArray<I16>(parser.getInputs().getSize(), -1);
+
+			for(U32 i = 0; i < parser.getInputs().getSize(); ++i)
+			{
+				const ShaderProgramParserInput& parserInput = parser.getInputs()[i];
+				if(!parserVariant.isInputActive(parserInput))
+				{
+					continue;
+				}
+
+				if(parserInput.inUbo())
+				{
+					variant.m_blockInfos[i] = parserVariant.getBlockInfo(parserInput);
+				}
+			}
+
+		} while(spinDials(dials, parser.getMutators()));
+	}
+	else
+	{
+		// TODO
+	}
+
+	return Error::NONE;
+}
+
+} // end namespace anki

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

@@ -5,11 +5,58 @@
 
 #pragma once
 
+#include <anki/shader_compiler/ShaderProgramBinary.h>
+#include <anki/util/String.h>
+#include <anki/gr/Common.h>
+
 namespace anki
 {
 
 /// @addtogroup shader_compiler
 /// @{
+
+/// A wrapper over the POD ShaderProgramBinary class.
+/// @memberof ShaderProgramCompiler
+class ShaderProgramBinaryWrapper : public NonCopyable
+{
+	friend class ShaderProgramCompiler;
+
+public:
+	ShaderProgramBinaryWrapper(GenericMemoryPoolAllocator<U8> alloc)
+		: m_alloc(alloc)
+	{
+	}
+
+	~ShaderProgramBinaryWrapper()
+	{
+		cleanup();
+	}
+
+	ANKI_USE_RESULT Error serializeToFile(CString fname) const;
+
+	ANKI_USE_RESULT Error deserializeFromFile(CString fname);
+
+private:
+	GenericMemoryPoolAllocator<U8> m_alloc;
+	ShaderProgramBinary* m_binary = nullptr;
+	Bool m_singleAllocation = false;
+
+	void cleanup();
+};
+
+/// Takes an AnKi special shader program and spits a binary.
+class ShaderProgramCompiler : public NonCopyable
+{
+public:
+	ANKI_USE_RESULT Error compile(CString fname,
+		ShaderProgramFilesystemInterface* fsystem,
+		GenericMemoryPoolAllocator<U8> tempAllocator,
+		U32 pushConstantsSize,
+		U32 backendMinor,
+		U32 backendMajor,
+		GpuVendor gpuVendor,
+		ShaderProgramBinaryWrapper& binary);
+};
 /// @}
 
 } // end namespace anki

+ 5 - 25
src/anki/shader_compiler/ShaderProgramParser.cpp

@@ -216,26 +216,6 @@ static U32 shaderVariableScalarType(ShaderVariableDataType type)
 	return out;
 }
 
-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);
@@ -623,9 +603,9 @@ Error ShaderProgramParser::parsePragmaMutator(
 
 	// Instanced
 	{
-		if(*begin == "instanced")
+		if(*begin == "instanceCount")
 		{
-			mutator.m_instanced = true;
+			mutator.m_instanceCount = true;
 
 			// Check
 			if(m_instancedMutatorIdx != MAX_U32)
@@ -638,7 +618,7 @@ Error ShaderProgramParser::parsePragmaMutator(
 		}
 		else
 		{
-			mutator.m_instanced = false;
+			mutator.m_instanceCount = false;
 		}
 	}
 
@@ -702,7 +682,7 @@ Error ShaderProgramParser::parsePragmaMutator(
 	}
 
 	// Update some source
-	if(mutator.m_instanced)
+	if(mutator.m_instanceCount)
 	{
 		m_globalsLines.pushFrontSprintf("#define _ANKI_INSTANCE_COUNT %s", mutator.m_name.cstr());
 	}
@@ -984,7 +964,7 @@ Error ShaderProgramParser::parse()
 Error ShaderProgramParser::findActiveInputVars(CString source, BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES>& active) const
 {
 	StringAuto preprocessedSrc(m_alloc);
-	ANKI_CHECK(preprocessCommon(source, preprocessedSrc));
+	ANKI_CHECK(preprocessGlsl(source, preprocessedSrc));
 
 	StringListAuto lines(m_alloc);
 	lines.splitString(preprocessedSrc, '\n');

+ 12 - 14
src/anki/shader_compiler/ShaderProgramParser.h

@@ -46,15 +46,15 @@ public:
 		return m_values;
 	}
 
-	Bool isInstanced() const
+	Bool isInstanceCount() const
 	{
-		return m_instanced;
+		return m_instanceCount;
 	}
 
 private:
 	StringAuto m_name;
 	DynamicArrayAuto<I32> m_values;
-	Bool m_instanced = false;
+	Bool m_instanceCount = false;
 };
 
 /// @memberof ShaderProgramParser
@@ -139,11 +139,17 @@ public:
 		return m_sources[type];
 	}
 
-	Bool isInputActive(const ShaderProgramParserInput& in)
+	Bool isInputActive(const ShaderProgramParserInput& in) const
 	{
 		return m_activeInputVarsMask.get(in.m_idx);
 	}
 
+	const ShaderVariableBlockInfo& getBlockInfo(const ShaderProgramParserInput& in) const
+	{
+		ANKI_ASSERT(in.inUbo() && isInputActive(in));
+		return m_blockInfos[in.m_idx];
+	}
+
 private:
 	GenericMemoryPoolAllocator<U8> m_alloc;
 	Array<String, U(ShaderType::COUNT)> m_sources;
@@ -162,14 +168,6 @@ public:
 	ShaderProgramParserMutator::ValueType m_value = MAX_I32;
 };
 
-/// An interface used by the ShaderProgramParser to abstract file loading.
-/// @memberof ShaderProgramParser
-class ShaderProgramParserFilesystemInterface
-{
-public:
-	ANKI_USE_RESULT virtual Error readAllText(CString filename, StringAuto& txt) = 0;
-};
-
 /// This is a special preprocessor that run before the usual preprocessor. Its purpose is to add some meta information
 /// in the shader programs.
 ///
@@ -187,7 +185,7 @@ class ShaderProgramParser : public NonCopyable
 {
 public:
 	ShaderProgramParser(CString fname,
-		ShaderProgramParserFilesystemInterface* fsystem,
+		ShaderProgramFilesystemInterface* fsystem,
 		GenericMemoryPoolAllocator<U8> alloc,
 		U32 pushConstantsSize,
 		U32 backendMinor,
@@ -242,7 +240,7 @@ private:
 
 	GenericMemoryPoolAllocator<U8> m_alloc;
 	StringAuto m_fname;
-	ShaderProgramParserFilesystemInterface* m_fsystem = nullptr;
+	ShaderProgramFilesystemInterface* m_fsystem = nullptr;
 
 	StringListAuto m_codeLines = {m_alloc}; ///< The code.
 	StringListAuto m_globalsLines = {m_alloc};

+ 18 - 12
src/anki/util/BitSet.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/util/StdTypes.h>
+#include <anki/util/Array.h>
 #include <initializer_list>
 #include <cstring>
 
@@ -21,6 +21,15 @@ namespace anki
 template<U32 N, typename TChunkType = U8>
 class BitSet
 {
+protected:
+	using ChunkType = TChunkType;
+
+	/// Number of bits a chunk holds.
+	static constexpr U32 CHUNK_BIT_COUNT = sizeof(ChunkType) * 8;
+
+	/// Number of chunks.
+	static constexpr U32 CHUNK_COUNT = (N + (CHUNK_BIT_COUNT - 1)) / CHUNK_BIT_COUNT;
+
 public:
 	/// Constructor. It will set all the bits or unset them.
 	BitSet(Bool set)
@@ -158,7 +167,7 @@ public:
 	/// Set all bits.
 	void setAll()
 	{
-		memset(m_chunks, 0xFF, sizeof(m_chunks));
+		memset(&m_chunks[0], 0xFF, sizeof(m_chunks));
 		zeroUnusedBits();
 	}
 
@@ -179,7 +188,7 @@ public:
 	/// Unset all bits.
 	void unsetAll()
 	{
-		memset(m_chunks, 0, sizeof(m_chunks));
+		memset(&m_chunks[0], 0, sizeof(m_chunks));
 	}
 
 	/// Flip the bits at the given position. It will go from 1 to 0 or from 0 to 1.
@@ -237,16 +246,13 @@ public:
 		return MAX_U32;
 	}
 
-protected:
-	using ChunkType = TChunkType;
-
-	/// Number of bits a chunk holds.
-	static const U32 CHUNK_BIT_COUNT = sizeof(ChunkType) * 8;
-
-	/// Number of chunks.
-	static const U32 CHUNK_COUNT = (N + (CHUNK_BIT_COUNT - 1)) / CHUNK_BIT_COUNT;
+	Array<TChunkType, CHUNK_COUNT> getData() const
+	{
+		return m_chunks;
+	}
 
-	ChunkType m_chunks[CHUNK_COUNT];
+protected:
+	Array<ChunkType, CHUNK_COUNT> m_chunks;
 
 	BitSet()
 	{

+ 43 - 14
src/anki/util/Serializer.h

@@ -13,6 +13,32 @@ namespace anki
 /// @addtogroup util_file
 /// @{
 
+#define _ANKI_SIMPLE_TYPE (std::is_integral<T>::value || std::is_floating_point<T>::value || std::is_enum<T>::value)
+
+/// Serialize functor. Used to add serialization code to classes that you can't add a serialize() method.
+template<typename T>
+class SerializeFunctor
+{
+public:
+	template<typename TSerializer>
+	void operator()(const T& x, TSerializer& serializer)
+	{
+		x.serialize(serializer);
+	}
+};
+
+/// Deserialize functor. Used to add deserialization code to classes that you can't add a serialize() method.
+template<typename T>
+class DeserializeFunctor
+{
+public:
+	template<typename TDeserializer>
+	void operator()(T& x, TDeserializer& deserializer)
+	{
+		x.deserialize(deserializer);
+	}
+};
+
 /// Serializes to binary files.
 class BinarySerializer : public NonCopyable
 {
@@ -39,8 +65,8 @@ public:
 		doArray(varName, memberOffset, &x, 1);
 	}
 
-	/// Write an array of int and float values. Can't call this directly.
-	template<typename T, ANKI_ENABLE(!std::is_integral<T>::value && !std::is_floating_point<T>::value)>
+	/// Write an array of complex values. Can't call this directly.
+	template<typename T, ANKI_ENABLE(!_ANKI_SIMPLE_TYPE)>
 	void doArray(CString varName, PtrSize memberOffset, const T* arr, PtrSize size)
 	{
 		if(!m_err)
@@ -49,8 +75,8 @@ public:
 		}
 	}
 
-	/// Write an array of complex types. Can't call this directly.
-	template<typename T, ANKI_ENABLE(std::is_integral<T>::value || std::is_floating_point<T>::value)>
+	/// Write an array of int or float types. Can't call this directly.
+	template<typename T, ANKI_ENABLE(_ANKI_SIMPLE_TYPE)>
 	void doArray(CString varName, PtrSize memberOffset, const T* arr, PtrSize size)
 	{
 		// Do nothing, it's already copied
@@ -64,7 +90,7 @@ public:
 	}
 
 	/// Write a dynamic array of complex types. Can't call this directly.
-	template<typename T, ANKI_ENABLE(!std::is_integral<T>::value && !std::is_floating_point<T>::value)>
+	template<typename T, ANKI_ENABLE(!_ANKI_SIMPLE_TYPE)>
 	void doDynamicArray(CString varName, PtrSize memberOffset, const T* arr, PtrSize size)
 	{
 		if(!m_err)
@@ -74,7 +100,7 @@ public:
 	}
 
 	/// Write a dynamic array of int and float values. Can't call this directly.
-	template<typename T, ANKI_ENABLE(std::is_integral<T>::value || std::is_floating_point<T>::value)>
+	template<typename T, ANKI_ENABLE(_ANKI_SIMPLE_TYPE)>
 	void doDynamicArray(CString varName, PtrSize memberOffset, const T* arr, PtrSize size)
 	{
 		if(!m_err)
@@ -120,6 +146,7 @@ private:
 	static void checkStruct()
 	{
 		static_assert(std::is_pod<T>::value, "Only PODs are supported in this serializer");
+		static_assert(alignof(T) <= ANKI_SAFE_ALIGNMENT, "Alignments can't exceed ANKI_SAFE_ALIGNMENT");
 	}
 };
 
@@ -134,36 +161,38 @@ public:
 	template<typename T>
 	static ANKI_USE_RESULT Error deserialize(T*& x, GenericMemoryPoolAllocator<U8> allocator, File& file);
 
-	/// Write a single value. Can't call this directly.
+	/// Read a single value. Can't call this directly.
 	template<typename T>
-	void doValue(CString varName, PtrSize memberOffset, const T& x)
+	void doValue(CString varName, PtrSize memberOffset, T& x)
 	{
 		// Do nothing
 	}
 
-	/// Write an array. Can't call this directly.
+	/// Read an array. Can't call this directly.
 	template<typename T>
-	void doArray(CString varName, PtrSize memberOffset, const T* arr, PtrSize size)
+	void doArray(CString varName, PtrSize memberOffset, T* arr, PtrSize size)
 	{
 		// Do nothing
 	}
 
-	/// Write a pointer. Can't call this directly.
+	/// Read a pointer. Can't call this directly.
 	template<typename T>
-	void doPointer(CString varName, PtrSize memberOffset, const T* ptr)
+	void doPointer(CString varName, PtrSize memberOffset, T* ptr)
 	{
 		// Do nothing
 	}
 
-	/// Write a dynamic array of complex types. Can't call this directly.
+	/// Read a dynamic array of complex types. Can't call this directly.
 	template<typename T>
-	void doDynamicArray(CString varName, PtrSize memberOffset, const T* arr, PtrSize size)
+	void doDynamicArray(CString varName, PtrSize memberOffset, T* arr, PtrSize size)
 	{
 		// Do nothing
 	}
 };
 /// @}
 
+#undef _ANKI_SIMPLE_TYPE
+
 } // end namespace anki
 
 #include <anki/util/Serializer.inl.h>

+ 2 - 2
src/anki/util/Serializer.inl.h

@@ -102,7 +102,7 @@ Error BinarySerializer::doArrayComplexType(const T* arr, PtrSize size)
 		m_structureFilePos.emplaceBack(m_alloc, structFilePos);
 
 		// Serialize the pointers
-		arr[i].serialize(*this);
+		SerializeFunctor<T>()(arr[i], *this);
 		ANKI_CHECK(m_err);
 
 		// Advance file pos
@@ -149,7 +149,7 @@ Error BinarySerializer::doDynamicArrayComplexType(const T* arr, PtrSize size, Pt
 		{
 			m_structureFilePos.emplaceBack(m_alloc, arrayFilePos);
 
-			arr[i].serialize(*this);
+			SerializeFunctor<T>()(arr[i], *this);
 
 			m_structureFilePos.popBack(m_alloc);
 			arrayFilePos += sizeof(T);

+ 30 - 17
src/anki/util/serializer.py

@@ -34,6 +34,19 @@ class MemberInfo:
         self.comment = None
         self.pointer = False
 
+    def is_dynamic_array(self, member_arr):
+        if not self.pointer:
+            return False
+
+        for member in member_arr:
+            if member.name == str(member.array_size):
+                return False
+
+        return True
+
+    def is_pointer(self, member_arr):
+        return self.pointer and not self.is_dynamic_array(member_arr)
+
 
 def parse_commandline():
     """ Parse the command line arguments """
@@ -94,9 +107,7 @@ def gen_class(root_el):
 
         member.array_size = member_el.get("array_size")
         if not member.array_size:
-            member.array_size = 1
-        elif str(member.array_size).isdigit():
-            member.array_size = int(member.array_size)
+            member.array_size = "1"
 
         if member_el.get("pointer") and member_el.get("pointer") == "true":
             member.pointer = True
@@ -118,33 +129,35 @@ def gen_class(root_el):
 
         if member.pointer:
             writeln("%s* %s; %s" % (member.base_type, member.name, comment))
-        elif member.array_size > 1:
-            writeln("Array<%s, %d> %s; %s" % (member.base_type, member.array_size, member.name, comment))
+        elif member.array_size != "1":
+            writeln("Array<%s, %s> %s; %s" % (member.base_type, member.array_size, member.name, comment))
         else:
             writeln("%s %s; %s" % (member.base_type, member.name, comment))
     ident(-1)
 
+    # Before serialize make sure the dynamic arrays are last
+    member_arr_copy = member_arr.copy()
+    member_arr.sort(key=lambda x: x.is_dynamic_array(member_arr_copy))
+
     # Write the serialization and deserialization code
     writeln("")
     ident(1)
     writeln("template<typename TSerializer, typename TClass>")
-    writeln("static void serializeCommon(TSerializer& serializer, TClass self)")
+    writeln("static void serializeCommon(TSerializer& s, TClass self)")
     writeln("{")
     ident(1)
 
     for member in member_arr:
-        if member.pointer and str(member.array_size) == 1:
-            writeln("serializer.doPointer(\"%s\", offsetof(%s, %s), self.%s);" % (member.name, name, member.name,
-                                                                                  member.name))
-        elif member.pointer:
-            writeln("serializer.doDynamicArray(\"%s\", offsetof(%s, %s), self.%s, self.%s);" %
-                    (member.name, name, member.name, member.name, member.array_size))
-        elif member.array_size > 1:
-            writeln("serializer.doArray(\"%s\", offsetof(%s, %s), &self.%s[0], %d);" % (member.name, name, member.name,
-                                                                                        member.name, member.array_size))
+        if member.is_pointer(member_arr_copy):
+            writeln("s.doPointer(\"%s\", offsetof(%s, %s), self.%s);" % (member.name, name, member.name, member.name))
+        elif member.is_dynamic_array(member_arr_copy):
+            writeln("s.doDynamicArray(\"%s\", offsetof(%s, %s), self.%s, self.%s);" % (member.name, name, member.name,
+                                                                                       member.name, member.array_size))
+        elif member.array_size != "1":
+            writeln("s.doArray(\"%s\", offsetof(%s, %s), &self.%s[0], %s);" % (member.name, name, member.name,
+                                                                               member.name, member.array_size))
         else:
-            writeln("serializer.doValue(\"%s\", offsetof(%s, %s), self.%s);" % (member.name, name, member.name,
-                                                                                member.name))
+            writeln("s.doValue(\"%s\", offsetof(%s, %s), self.%s);" % (member.name, name, member.name, member.name))
 
     ident(-1)
     writeln("}")

+ 1 - 1
tests/shader_compiler/ShaderProgramParser.cpp

@@ -10,7 +10,7 @@ ANKI_TEST(ShaderCompiler, ShaderCompilerParser)
 {
 	HeapAllocator<U8> alloc(allocAligned, nullptr);
 
-	class FilesystemInterface : public ShaderProgramParserFilesystemInterface
+	class FilesystemInterface : public ShaderProgramFilesystemInterface
 	{
 	public:
 		U32 count = 0;

+ 1 - 1
tests/util/SerializerTest.h

@@ -17,8 +17,8 @@ class ClassB
 {
 public:
 	Array<U8, 3> m_array;
-	U8 m_darraySize;
 	U32* m_darray;
+	U8 m_darraySize;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& serializer, TClass self)

+ 1 - 1
tests/util/SerializerTest.xml

@@ -7,8 +7,8 @@
 		<class name="ClassB">
 			<members>
 				<member name="m_array" type="U8" array_size="3" />
-				<member name="m_darraySize" type="U8" />
 				<member name="m_darray" type="U32" pointer="true" array_size="m_darraySize" />
+				<member name="m_darraySize" type="U8" />
 			</members>
 		</class>