Browse Source

Add HLSL compiling support

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
bd097f8845

+ 1 - 0
AnKi/ShaderCompiler/Common.h

@@ -19,6 +19,7 @@ namespace anki {
 #define ANKI_SHADER_COMPILER_LOGE(...) ANKI_LOG("SHCO", kError, __VA_ARGS__)
 #define ANKI_SHADER_COMPILER_LOGE(...) ANKI_LOG("SHCO", kError, __VA_ARGS__)
 #define ANKI_SHADER_COMPILER_LOGW(...) ANKI_LOG("SHCO", kWarning, __VA_ARGS__)
 #define ANKI_SHADER_COMPILER_LOGW(...) ANKI_LOG("SHCO", kWarning, __VA_ARGS__)
 #define ANKI_SHADER_COMPILER_LOGF(...) ANKI_LOG("SHCO", kFatal, __VA_ARGS__)
 #define ANKI_SHADER_COMPILER_LOGF(...) ANKI_LOG("SHCO", kFatal, __VA_ARGS__)
+#define ANKI_SHADER_COMPILER_LOGV(...) ANKI_LOG("SHCO", kVerbose, __VA_ARGS__)
 
 
 constexpr U32 kMaxShaderBinaryNameLength = 127;
 constexpr U32 kMaxShaderBinaryNameLength = 127;
 
 

+ 159 - 0
AnKi/ShaderCompiler/Dxc.cpp

@@ -0,0 +1,159 @@
+// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/ShaderCompiler/Dxc.h>
+#include <AnKi/Util/Process.h>
+#include <AnKi/Util/Filesystem.h>
+#include <AnKi/Util/File.h>
+
+namespace anki {
+
+namespace {
+
+class CleanupFile
+{
+public:
+	StringRaii m_fileToDelete;
+
+	CleanupFile(BaseMemoryPool* pool, CString filename)
+		: m_fileToDelete(pool, filename)
+	{
+	}
+
+	~CleanupFile()
+	{
+		if(!m_fileToDelete.isEmpty())
+		{
+			const Error err = removeFile(m_fileToDelete);
+			if(!err)
+			{
+				ANKI_SHADER_COMPILER_LOGV("Deleted %s", m_fileToDelete.cstr());
+			}
+		}
+	}
+};
+
+} // end anonymous namespace
+
+static CString profile(ShaderType shaderType)
+{
+	switch(shaderType)
+	{
+	case ShaderType::kVertex:
+		return "vs_6_7";
+		break;
+	case ShaderType::kFragment:
+		return "ps_6_7";
+		break;
+	case ShaderType::kTessellationEvaluation:
+		return "ds_6_7";
+		break;
+	case ShaderType::kTessellationControl:
+		return "ds_6_7";
+		break;
+	case ShaderType::kGeometry:
+		return "gs_6_7";
+		break;
+	case ShaderType::kCompute:
+		return "cs_6_7";
+		break;
+	case ShaderType::kRayGen:
+	case ShaderType::kAnyHit:
+	case ShaderType::kClosestHit:
+	case ShaderType::kMiss:
+	case ShaderType::kIntersection:
+	case ShaderType::kCallable:
+		return "lib_6_7";
+		break;
+	default:
+		ANKI_ASSERT(0);
+	};
+
+	return "";
+}
+
+Error compileHlslToSpirv(CString src, ShaderType shaderType, BaseMemoryPool& tmpPool, DynamicArrayRaii<U8>& spirv,
+						 StringRaii& errorMessage)
+{
+	srand(U32(time(0)));
+	const I32 r = rand();
+
+	StringRaii tmpDir(&tmpPool);
+	ANKI_CHECK(getTempDirectory(tmpDir));
+
+	// Store src to a file
+	StringRaii hlslFilename(&tmpPool);
+	hlslFilename.sprintf("%s/%d.hlsl", tmpDir.cstr(), r);
+
+	File hlslFile;
+	ANKI_CHECK(hlslFile.open(hlslFilename, FileOpenFlag::kWrite));
+	CleanupFile hlslFileCleanup(&tmpPool, hlslFilename);
+	ANKI_CHECK(hlslFile.writeText(src));
+	hlslFile.close();
+
+	// Call DXC
+	StringRaii spvFilename(&tmpPool);
+	spvFilename.sprintf("%s/%d.spv", tmpDir.cstr(), r);
+
+	DynamicArrayRaii<StringRaii> dxcArgs(&tmpPool);
+	dxcArgs.emplaceBack(&tmpPool, "-Wall");
+	dxcArgs.emplaceBack(&tmpPool, "-Wextra");
+	dxcArgs.emplaceBack(&tmpPool, "-Wconversion");
+	dxcArgs.emplaceBack(&tmpPool, "-Werror");
+	dxcArgs.emplaceBack(&tmpPool, "-Wfatal-errors");
+	dxcArgs.emplaceBack(&tmpPool, "-Wno-unused-const-variable");
+	dxcArgs.emplaceBack(&tmpPool, "-enable-16bit-types");
+	dxcArgs.emplaceBack(&tmpPool, "-HV");
+	dxcArgs.emplaceBack(&tmpPool, "2021");
+	dxcArgs.emplaceBack(&tmpPool, "-E");
+	dxcArgs.emplaceBack(&tmpPool, "main");
+	dxcArgs.emplaceBack(&tmpPool, "-Fo");
+	dxcArgs.emplaceBack(&tmpPool, spvFilename);
+	dxcArgs.emplaceBack(&tmpPool, "-T");
+	dxcArgs.emplaceBack(&tmpPool, profile(shaderType));
+	dxcArgs.emplaceBack(&tmpPool, "-spirv");
+	dxcArgs.emplaceBack(&tmpPool, "-fspv-target-env=vulkan1.1spirv1.4");
+	dxcArgs.emplaceBack(&tmpPool, hlslFilename);
+
+	DynamicArrayRaii<CString> dxcArgs2(&tmpPool, dxcArgs.getSize());
+	for(U32 i = 0; i < dxcArgs.getSize(); ++i)
+	{
+		dxcArgs2[i] = dxcArgs[i];
+	}
+
+	Process proc;
+	ANKI_CHECK(proc.start(ANKI_SOURCE_DIRECTORY "/ThirdParty/Bin/Windows64/dxc.exe", dxcArgs2));
+
+	I32 exitCode;
+	ProcessStatus status;
+	ANKI_CHECK(proc.wait(60.0_sec, &status, &exitCode));
+
+	if(!(status == ProcessStatus::kNotRunning && exitCode == 0))
+	{
+		if(exitCode != 0)
+		{
+			ANKI_CHECK(proc.readFromStderr(errorMessage));
+		}
+
+		if(errorMessage.isEmpty())
+		{
+			errorMessage = "Unknown error";
+		}
+
+		return Error::kFunctionFailed;
+	}
+
+	CleanupFile spvFileCleanup(&tmpPool, spvFilename);
+
+	// Read the spirv back
+	File spvFile;
+	ANKI_CHECK(spvFile.open(spvFilename, FileOpenFlag::kRead));
+	spirv.resize(U32(spvFile.getSize()));
+	ANKI_CHECK(spvFile.read(&spirv[0], spirv.getSizeInBytes()));
+
+	return Error::kNone;
+}
+
+} // end namespace anki

+ 21 - 0
AnKi/ShaderCompiler/Dxc.h

@@ -0,0 +1,21 @@
+// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/ShaderCompiler/Common.h>
+#include <AnKi/Util/String.h>
+
+namespace anki {
+
+/// @addtogroup shader_compiler
+/// @{
+
+/// Compile HLSL to SPIR-V.
+Error compileHlslToSpirv(CString src, ShaderType shaderType, BaseMemoryPool& tmpPool, DynamicArrayRaii<U8>& spirv,
+						 StringRaii& errorMessage);
+/// @}
+
+} // end namespace anki

+ 3 - 2
AnKi/ShaderCompiler/Glslang.cpp

@@ -276,8 +276,8 @@ Error preprocessGlsl(CString in, StringRaii& out)
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
-Error compilerGlslToSpirv(CString src, ShaderType shaderType, BaseMemoryPool& tmpPool, DynamicArrayRaii<U8>& spirv,
-						  StringRaii& errorMessage)
+Error compileGlslToSpirv(CString src, ShaderType shaderType, BaseMemoryPool& tmpPool, DynamicArrayRaii<U8>& spirv,
+						 StringRaii& errorMessage)
 {
 {
 #if ANKI_GLSLANG_DUMP
 #if ANKI_GLSLANG_DUMP
 	// Dump it
 	// Dump it
@@ -304,6 +304,7 @@ Error compilerGlslToSpirv(CString src, ShaderType shaderType, BaseMemoryPool& tm
 	shader.setStrings(&csrc[0], 1);
 	shader.setStrings(&csrc[0], 1);
 	shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
 	shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
 	shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4);
 	shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4);
+	shader.setOverrideVersion(460);
 	if(!shader.parse(&GLSLANG_LIMITS, 100, false, messages))
 	if(!shader.parse(&GLSLANG_LIMITS, 100, false, messages))
 	{
 	{
 		createErrorLog(shader.getInfoLog(), src, tmpPool, errorMessage);
 		createErrorLog(shader.getInfoLog(), src, tmpPool, errorMessage);

+ 2 - 2
AnKi/ShaderCompiler/Glslang.h

@@ -17,8 +17,8 @@ namespace anki {
 Error preprocessGlsl(CString in, StringRaii& out);
 Error preprocessGlsl(CString in, StringRaii& out);
 
 
 /// Compile glsl to SPIR-V.
 /// Compile glsl to SPIR-V.
-Error compilerGlslToSpirv(CString src, ShaderType shaderType, BaseMemoryPool& tmpPool, DynamicArrayRaii<U8>& spirv,
-						  StringRaii& errorMessage);
+Error compileGlslToSpirv(CString src, ShaderType shaderType, BaseMemoryPool& tmpPool, DynamicArrayRaii<U8>& spirv,
+						 StringRaii& errorMessage);
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 12 - 3
AnKi/ShaderCompiler/ShaderProgramCompiler.cpp

@@ -6,6 +6,7 @@
 #include <AnKi/ShaderCompiler/ShaderProgramCompiler.h>
 #include <AnKi/ShaderCompiler/ShaderProgramCompiler.h>
 #include <AnKi/ShaderCompiler/ShaderProgramParser.h>
 #include <AnKi/ShaderCompiler/ShaderProgramParser.h>
 #include <AnKi/ShaderCompiler/Glslang.h>
 #include <AnKi/ShaderCompiler/Glslang.h>
+#include <AnKi/ShaderCompiler/Dxc.h>
 #include <AnKi/ShaderCompiler/ShaderProgramReflection.h>
 #include <AnKi/ShaderCompiler/ShaderProgramReflection.h>
 #include <AnKi/Util/Serializer.h>
 #include <AnKi/Util/Serializer.h>
 #include <AnKi/Util/HashMap.h>
 #include <AnKi/Util/HashMap.h>
@@ -180,8 +181,16 @@ static Error compileSpirv(ConstWeakArray<MutatorValue> mutation, const ShaderPro
 		}
 		}
 
 
 		// Compile
 		// Compile
-		ANKI_CHECK(compilerGlslToSpirv(parserVariant.getSource(shaderType), shaderType, tempPool, spirv[shaderType],
-									   errorLog));
+		if(!parser.isHlsl())
+		{
+			ANKI_CHECK(compileGlslToSpirv(parserVariant.getSource(shaderType), shaderType, tempPool, spirv[shaderType],
+										  errorLog));
+		}
+		else
+		{
+			ANKI_CHECK(compileHlslToSpirv(parserVariant.getSource(shaderType), shaderType, tempPool, spirv[shaderType],
+										  errorLog));
+		}
 		ANKI_ASSERT(spirv[shaderType].getSize() > 0);
 		ANKI_ASSERT(spirv[shaderType].getSize() > 0);
 	}
 	}
 
 
@@ -996,7 +1005,7 @@ Error compileShaderProgramInternal(CString fname, ShaderProgramFilesystemInterfa
 	binaryW.m_binary = newInstance<ShaderProgramBinary>(binaryPool);
 	binaryW.m_binary = newInstance<ShaderProgramBinary>(binaryPool);
 	ShaderProgramBinary& binary = *binaryW.m_binary;
 	ShaderProgramBinary& binary = *binaryW.m_binary;
 	binary = {};
 	binary = {};
-	memcpy(&binary.m_magic[0], SHADER_BINARY_MAGIC, 8);
+	memcpy(&binary.m_magic[0], kShaderBinaryMagic, 8);
 
 
 	// Parse source
 	// Parse source
 	ShaderProgramParser parser(fname, &fsystem, &tempPool, compilerOptions);
 	ShaderProgramParser parser(fname, &fsystem, &tempPool, compilerOptions);

+ 3 - 3
AnKi/ShaderCompiler/ShaderProgramCompiler.h

@@ -14,8 +14,8 @@ namespace anki {
 /// @addtogroup shader_compiler
 /// @addtogroup shader_compiler
 /// @{
 /// @{
 
 
-constexpr const char* SHADER_BINARY_MAGIC = "ANKISDR8"; ///< WARNING: If changed change SHADER_BINARY_VERSION
-constexpr U32 SHADER_BINARY_VERSION = 8;
+inline constexpr const char* kShaderBinaryMagic = "ANKISDR8"; //! WARNING: If changed change kShaderBinaryVersion
+constexpr U32 kShaderBinaryVersion = 8;
 
 
 /// A wrapper over the POD ShaderProgramBinary class.
 /// A wrapper over the POD ShaderProgramBinary class.
 /// @memberof ShaderProgramCompiler
 /// @memberof ShaderProgramCompiler
@@ -72,7 +72,7 @@ Error ShaderProgramBinaryWrapper::deserializeFromAnyFile(TFile& file)
 
 
 	m_singleAllocation = true;
 	m_singleAllocation = true;
 
 
-	if(memcmp(SHADER_BINARY_MAGIC, &m_binary->m_magic[0], strlen(SHADER_BINARY_MAGIC)) != 0)
+	if(memcmp(kShaderBinaryMagic, &m_binary->m_magic[0], strlen(kShaderBinaryMagic)) != 0)
 	{
 	{
 		ANKI_SHADER_COMPILER_LOGE("Corrupted or wrong version of shader binary.");
 		ANKI_SHADER_COMPILER_LOGE("Corrupted or wrong version of shader binary.");
 		return Error::kUserData;
 		return Error::kUserData;

+ 41 - 7
AnKi/ShaderCompiler/ShaderProgramParser.cpp

@@ -19,8 +19,7 @@ inline constexpr Array<CString, U32(ShaderType::kCount)> kShaderStageNames = {
 	{"VERTEX", "TESSELLATION_CONTROL", "TESSELLATION_EVALUATION", "GEOMETRY", "FRAGMENT", "COMPUTE", "RAY_GEN",
 	{"VERTEX", "TESSELLATION_CONTROL", "TESSELLATION_EVALUATION", "GEOMETRY", "FRAGMENT", "COMPUTE", "RAY_GEN",
 	 "ANY_HIT", "CLOSEST_HIT", "MISS", "INTERSECTION", "CALLABLE"}};
 	 "ANY_HIT", "CLOSEST_HIT", "MISS", "INTERSECTION", "CALLABLE"}};
 
 
-inline constexpr char kShaderHeader[] = R"(#version 460 core
-#define ANKI_%s_SHADER 1
+inline constexpr char kShaderHeader[] = R"(#define ANKI_%s_SHADER 1
 #define ANKI_PLATFORM_MOBILE %d
 #define ANKI_PLATFORM_MOBILE %d
 #define ANKI_FORCE_FULL_FP_PRECISION %d
 #define ANKI_FORCE_FULL_FP_PRECISION %d
 
 
@@ -418,7 +417,7 @@ Error ShaderProgramParser::parseInclude(const StringRaii* begin, const StringRai
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
-Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth)
+Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth, U32 lineNumber)
 {
 {
 	// Tokenize
 	// Tokenize
 	DynamicArrayRaii<StringRaii> tokens(m_pool);
 	DynamicArrayRaii<StringRaii> tokens(m_pool);
@@ -440,6 +439,8 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 	{
 	{
 		// We _must_ have an #include
 		// We _must_ have an #include
 		ANKI_CHECK(parseInclude(token + 1, end, line, fname, depth));
 		ANKI_CHECK(parseInclude(token + 1, end, line, fname, depth));
+
+		m_codeLines.pushBackSprintf("#line %u \"%s\"", lineNumber + 2, fname.cstr());
 	}
 	}
 	else if((token < end) && ((foundAloneHash && *token == "pragma") || *token == "#pragma"))
 	else if((token < end) && ((foundAloneHash && *token == "pragma") || *token == "#pragma"))
 	{
 	{
@@ -526,6 +527,10 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 				ANKI_CHECK(checkActiveStruct());
 				ANKI_CHECK(checkActiveStruct());
 				ANKI_CHECK(parsePragmaMember(token + 1, end, line, fname));
 				ANKI_CHECK(parsePragmaMember(token + 1, end, line, fname));
 			}
 			}
+			else if(*token == "hlsl")
+			{
+				ANKI_CHECK(parsePragmaHlsl(token + 1, end, line, fname));
+			}
 			else
 			else
 			{
 			{
 				ANKI_PP_ERROR_MALFORMED();
 				ANKI_PP_ERROR_MALFORMED();
@@ -823,10 +828,25 @@ Error ShaderProgramParser::parsePragmaStructEnd(const StringRaii* begin, const S
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
+Error ShaderProgramParser::parsePragmaHlsl(const StringRaii* begin, const StringRaii* end, CString line, CString fname)
+{
+	ANKI_ASSERT(begin && end);
+
+	// Check tokens
+	if(begin != end)
+	{
+		ANKI_PP_ERROR_MALFORMED();
+	}
+
+	m_hlsl = true;
+
+	return Error::kNone;
+}
+
 Error ShaderProgramParser::parseFile(CString fname, U32 depth)
 Error ShaderProgramParser::parseFile(CString fname, U32 depth)
 {
 {
 	// First check the depth
 	// First check the depth
-	if(depth > MAX_INCLUDE_DEPTH)
+	if(depth > kMaxIncludeDepth)
 	{
 	{
 		ANKI_SHADER_COMPILER_LOGE("The include depth is too high. Probably circular includance");
 		ANKI_SHADER_COMPILER_LOGE("The include depth is too high. Probably circular includance");
 	}
 	}
@@ -838,25 +858,34 @@ Error ShaderProgramParser::parseFile(CString fname, U32 depth)
 	ANKI_CHECK(m_fsystem->readAllText(fname, txt));
 	ANKI_CHECK(m_fsystem->readAllText(fname, txt));
 
 
 	StringListRaii lines(m_pool);
 	StringListRaii lines(m_pool);
-	lines.splitString(txt.toCString(), '\n');
+	lines.splitString(txt.toCString(), '\n', true);
 	if(lines.getSize() < 1)
 	if(lines.getSize() < 1)
 	{
 	{
 		ANKI_SHADER_COMPILER_LOGE("Source is empty");
 		ANKI_SHADER_COMPILER_LOGE("Source is empty");
 	}
 	}
 
 
+	m_codeLines.pushBackSprintf("#line 0 \"%s\"", fname.cstr());
+
 	// Parse lines
 	// Parse lines
+	U32 lineCount = 0;
 	for(const String& line : lines)
 	for(const String& line : lines)
 	{
 	{
-		if(line.find("pragma") != String::kNpos || line.find("include") != String::kNpos)
+		if(line.isEmpty())
+		{
+			m_codeLines.pushBack(" ");
+		}
+		else if(line.find("pragma") != String::kNpos || line.find("include") != String::kNpos)
 		{
 		{
 			// Possibly a preprocessor directive we care
 			// Possibly a preprocessor directive we care
-			ANKI_CHECK(parseLine(line.toCString(), fname, foundPragmaOnce, depth));
+			ANKI_CHECK(parseLine(line.toCString(), fname, foundPragmaOnce, depth, lineCount));
 		}
 		}
 		else
 		else
 		{
 		{
 			// Just append the line
 			// Just append the line
 			m_codeLines.pushBack(line.toCString());
 			m_codeLines.pushBack(line.toCString());
 		}
 		}
+
+		++lineCount;
 	}
 	}
 
 
 	if(foundPragmaOnce)
 	if(foundPragmaOnce)
@@ -916,6 +945,11 @@ Error ShaderProgramParser::parse()
 		}
 		}
 	}
 	}
 
 
+	if(!m_hlsl)
+	{
+		m_codeLines.pushFront(StringRaii(m_pool, "#extension  GL_GOOGLE_cpp_style_line_directive : enable"));
+	}
+
 	// Create the code lines
 	// Create the code lines
 	if(m_codeLines.getSize())
 	if(m_codeLines.getSize())
 	{
 	{

+ 11 - 2
AnKi/ShaderCompiler/ShaderProgramParser.h

@@ -112,6 +112,7 @@ private:
 /// #pragma anki ray_type NUMBER
 /// #pragma anki ray_type NUMBER
 /// #pragma anki reflect NAME
 /// #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 hlsl // By default it's GLSL
 ///
 ///
 /// #pragma anki struct NAME
 /// #pragma anki struct NAME
 /// #	pragma anki member [ANKI_RP] TYPE NAME [if MUTATOR_NAME is MUTATOR_VALUE]
 /// #	pragma anki member [ANKI_RP] TYPE NAME [if MUTATOR_NAME is MUTATOR_VALUE]
@@ -176,6 +177,11 @@ public:
 		return m_ghostStructs;
 		return m_ghostStructs;
 	}
 	}
 
 
+	Bool isHlsl() const
+	{
+		return m_hlsl;
+	}
+
 	/// Generates the common header that will be used by all AnKi shaders.
 	/// Generates the common header that will be used by all AnKi shaders.
 	static void generateAnkiShaderHeader(ShaderType shaderType, const ShaderCompilerOptions& compilerOptions,
 	static void generateAnkiShaderHeader(ShaderType shaderType, const ShaderCompilerOptions& compilerOptions,
 										 StringRaii& header);
 										 StringRaii& header);
@@ -196,7 +202,7 @@ private:
 		}
 		}
 	};
 	};
 
 
-	static constexpr U32 MAX_INCLUDE_DEPTH = 8;
+	static constexpr U32 kMaxIncludeDepth = 8;
 
 
 	BaseMemoryPool* m_pool = nullptr;
 	BaseMemoryPool* m_pool = nullptr;
 	StringRaii m_fname;
 	StringRaii m_fname;
@@ -221,8 +227,10 @@ private:
 	DynamicArrayRaii<GhostStruct> m_ghostStructs = {m_pool};
 	DynamicArrayRaii<GhostStruct> m_ghostStructs = {m_pool};
 	Bool m_insideStruct = false;
 	Bool m_insideStruct = false;
 
 
+	Bool m_hlsl = false;
+
 	Error parseFile(CString fname, U32 depth);
 	Error parseFile(CString fname, U32 depth);
-	Error parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth);
+	Error parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth, U32 lineNumber);
 	Error parseInclude(const StringRaii* begin, const StringRaii* end, CString line, CString fname, U32 depth);
 	Error parseInclude(const StringRaii* begin, const StringRaii* end, CString line, CString fname, U32 depth);
 	Error parsePragmaMutator(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 	Error parsePragmaMutator(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 	Error parsePragmaStart(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 	Error parsePragmaStart(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
@@ -234,6 +242,7 @@ private:
 	Error parsePragmaStructBegin(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 	Error parsePragmaStructBegin(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 	Error parsePragmaStructEnd(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 	Error parsePragmaStructEnd(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 	Error parsePragmaMember(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 	Error parsePragmaMember(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
+	Error parsePragmaHlsl(const StringRaii* begin, const StringRaii* end, CString line, CString fname);
 
 
 	void tokenizeLine(CString line, DynamicArrayRaii<StringRaii>& tokens) const;
 	void tokenizeLine(CString line, DynamicArrayRaii<StringRaii>& tokens) const;
 
 

+ 3 - 4
AnKi/Shaders/Common.glsl

@@ -8,14 +8,13 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Shaders/Include/Common.h>
 #include <AnKi/Shaders/Include/Common.h>
-#include <AnKi/Shaders/TextureFunctions.glsl>
+#if ANKI_GLSL
+#	include <AnKi/Shaders/TextureFunctions.glsl>
+#endif
 
 
 // Macros
 // Macros
 #define UV_TO_NDC(x_) ((x_)*2.0 - 1.0)
 #define UV_TO_NDC(x_) ((x_)*2.0 - 1.0)
 #define NDC_TO_UV(x_) ((x_)*0.5 + 0.5)
 #define NDC_TO_UV(x_) ((x_)*0.5 + 0.5)
-#define saturate(x_) clamp((x_), 0.0, 1.0)
-#define saturateRp(x) min(x, F32(kMaxF16))
-#define mad(a_, b_, c_) fma((a_), (b_), (c_))
 
 
 // Techniques
 // Techniques
 #define RENDERING_TECHNIQUE_GBUFFER 0
 #define RENDERING_TECHNIQUE_GBUFFER 0

+ 80 - 41
AnKi/Shaders/Functions.glsl

@@ -11,7 +11,7 @@
 Vec3 dither(Vec3 col, F32 C)
 Vec3 dither(Vec3 col, F32 C)
 {
 {
 	Vec3 vDither = Vec3(dot(Vec2(171.0, 231.0), gl_FragCoord.xy));
 	Vec3 vDither = Vec3(dot(Vec2(171.0, 231.0), gl_FragCoord.xy));
-	vDither.rgb = fract(vDither.rgb / Vec3(103.0, 71.0, 97.0));
+	vDither.rgb = frac(vDither.rgb / Vec3(103.0, 71.0, 97.0));
 
 
 	col = col * (255.0 / C) + vDither.rgb;
 	col = col * (255.0 / C) + vDither.rgb;
 	col = floor(col) / 255.0;
 	col = floor(col) / 255.0;
@@ -23,7 +23,7 @@ Vec3 dither(Vec3 col, F32 C)
 F32 dither(F32 col, F32 C)
 F32 dither(F32 col, F32 C)
 {
 {
 	F32 vDither = dot(Vec2(171.0, 231.0), gl_FragCoord.xy);
 	F32 vDither = dot(Vec2(171.0, 231.0), gl_FragCoord.xy);
-	vDither = fract(vDither / 103.0);
+	vDither = frac(vDither / 103.0);
 
 
 	col = col * (255.0 / C) + vDither;
 	col = col * (255.0 / C) + vDither;
 	col = floor(col) / 255.0;
 	col = floor(col) / 255.0;
@@ -72,8 +72,8 @@ Vec4 projectPerspective(Vec4 vec, F32 m00, F32 m11, F32 m22, F32 m23)
 // Stolen from shadertoy.com/view/4tyGDD
 // Stolen from shadertoy.com/view/4tyGDD
 Vec4 textureCatmullRom4Samples(texture2D tex, sampler sampl, Vec2 uv, Vec2 texSize)
 Vec4 textureCatmullRom4Samples(texture2D tex, sampler sampl, Vec2 uv, Vec2 texSize)
 {
 {
-	const Vec2 halff = 2.0 * fract(0.5 * uv * texSize - 0.25) - 1.0;
-	const Vec2 f = fract(halff);
+	const Vec2 halff = 2.0 * frac(0.5 * uv * texSize - 0.25) - 1.0;
+	const Vec2 f = frac(halff);
 	const Vec2 sum0 = (2.0 * f - 3.5) * f + 0.5;
 	const Vec2 sum0 = (2.0 * f - 3.5) * f + 0.5;
 	const Vec2 sum1 = (2.0 * f - 2.5) * f - 0.5;
 	const Vec2 sum1 = (2.0 * f - 2.5) * f - 0.5;
 	Vec4 w = Vec4(f * sum0 + 1.0, f * sum1);
 	Vec4 w = Vec4(f * sum0 + 1.0, f * sum1);
@@ -87,7 +87,11 @@ Vec4 textureCatmullRom4Samples(texture2D tex, sampler sampl, Vec2 uv, Vec2 texSi
 #endif
 #endif
 
 
 // Stolen from shadertoy.com/view/4df3Dn
 // Stolen from shadertoy.com/view/4df3Dn
+#if ANKI_GLSL
 Vec4 textureBicubic(texture2D tex, sampler sampl, Vec2 uv, F32 lod, Vec2 texSize)
 Vec4 textureBicubic(texture2D tex, sampler sampl, Vec2 uv, F32 lod, Vec2 texSize)
+#else
+Vec4 textureBicubic(Texture2D tex, SamplerState sampl, Vec2 uv, F32 lod, Vec2 texSize)
+#endif
 {
 {
 #define w0(a) ((1.0 / 6.0) * ((a) * ((a) * (-(a) + 3.0) - 3.0) + 1.0))
 #define w0(a) ((1.0 / 6.0) * ((a) * ((a) * (-(a) + 3.0) - 3.0) + 1.0))
 #define w1(a) ((1.0 / 6.0) * ((a) * (a) * (3.0 * (a)-6.0) + 4.0))
 #define w1(a) ((1.0 / 6.0) * ((a) * (a) * (3.0 * (a)-6.0) + 4.0))
@@ -97,11 +101,15 @@ Vec4 textureBicubic(texture2D tex, sampler sampl, Vec2 uv, F32 lod, Vec2 texSize
 #define g1(a) (w2(a) + w3(a))
 #define g1(a) (w2(a) + w3(a))
 #define h0(a) (-1.0 + w1(a) / (w0(a) + w1(a)))
 #define h0(a) (-1.0 + w1(a) / (w0(a) + w1(a)))
 #define h1(a) (1.0 + w3(a) / (w2(a) + w3(a)))
 #define h1(a) (1.0 + w3(a) / (w2(a) + w3(a)))
-#define texSample(uv) textureLod(tex, sampl, uv, lod)
+#if ANKI_GLSL
+#	define texSample(uv) textureLod(tex, sampl, uv, lod)
+#else
+#	define texSample(uv) tex.SampleLevel(sampl, uv, lod)
+#endif
 
 
 	uv = uv * texSize + 0.5;
 	uv = uv * texSize + 0.5;
 	const Vec2 iuv = floor(uv);
 	const Vec2 iuv = floor(uv);
-	const Vec2 fuv = fract(uv);
+	const Vec2 fuv = frac(uv);
 
 
 	const F32 g0x = g0(fuv.x);
 	const F32 g0x = g0(fuv.x);
 	const F32 g1x = g1(fuv.x);
 	const F32 g1x = g1(fuv.x);
@@ -130,9 +138,10 @@ Vec4 textureBicubic(texture2D tex, sampler sampl, Vec2 uv, F32 lod, Vec2 texSize
 
 
 F32 rand(Vec2 n)
 F32 rand(Vec2 n)
 {
 {
-	return 0.5 + 0.5 * fract(sin(dot(n, Vec2(12.9898, 78.233))) * 43758.5453);
+	return 0.5 + 0.5 * frac(sin(dot(n, Vec2(12.9898, 78.233))) * 43758.5453);
 }
 }
 
 
+#if ANKI_GLSL
 Vec4 nearestDepthUpscale(Vec2 uv, texture2D depthFull, texture2D depthHalf, texture2D colorTex,
 Vec4 nearestDepthUpscale(Vec2 uv, texture2D depthFull, texture2D depthHalf, texture2D colorTex,
 						 sampler linearAnyClampSampler, Vec2 linearDepthCf, F32 depthThreshold)
 						 sampler linearAnyClampSampler, Vec2 linearDepthCf, F32 depthThreshold)
 {
 {
@@ -231,6 +240,7 @@ Vec4 bilateralUpsample(texture2D depthHigh, texture2D depthLow, texture2D colorL
 
 
 	return sum / normalize;
 	return sum / normalize;
 }
 }
+#endif
 
 
 Vec3 getCubemapDirection(const Vec2 norm, const U32 faceIdx)
 Vec3 getCubemapDirection(const Vec2 norm, const U32 faceIdx)
 {
 {
@@ -250,7 +260,7 @@ Vec2 convertCubeUvs(const Vec3 v, out F32 faceIndex)
 	F32 mag;
 	F32 mag;
 	Vec2 uv;
 	Vec2 uv;
 
 
-	if(all(greaterThanEqual(absV.zz, absV.xy)))
+	if(absV.z >= absV.x && absV.z >= absV.y)
 	{
 	{
 		faceIndex = (v.z < 0.0) ? 5.0 : 4.0;
 		faceIndex = (v.z < 0.0) ? 5.0 : 4.0;
 		uv = Vec2((v.z < 0.0) ? -v.x : v.x, -v.y);
 		uv = Vec2((v.z < 0.0) ? -v.x : v.x, -v.y);
@@ -279,7 +289,7 @@ Vec2 convertCubeUvsu(const Vec3 v, out U32 faceIndex)
 	F32 mag;
 	F32 mag;
 	Vec2 uv;
 	Vec2 uv;
 
 
-	if(all(greaterThanEqual(absV.zz, absV.xy)))
+	if(absV.z >= absV.x && absV.z >= absV.y)
 	{
 	{
 		faceIndex = (v.z < 0.0) ? 5u : 4u;
 		faceIndex = (v.z < 0.0) ? 5u : 4u;
 		uv = Vec2((v.z < 0.0) ? -v.x : v.x, -v.y);
 		uv = Vec2((v.z < 0.0) ? -v.x : v.x, -v.y);
@@ -301,17 +311,20 @@ Vec2 convertCubeUvsu(const Vec3 v, out U32 faceIndex)
 	return 0.5 / mag * uv + 0.5;
 	return 0.5 / mag * uv + 0.5;
 }
 }
 
 
+#if ANKI_GLSL
 ANKI_RP Vec3 grayScale(const ANKI_RP Vec3 col)
 ANKI_RP Vec3 grayScale(const ANKI_RP Vec3 col)
 {
 {
 	const ANKI_RP F32 grey = (col.r + col.g + col.b) * (1.0 / 3.0);
 	const ANKI_RP F32 grey = (col.r + col.g + col.b) * (1.0 / 3.0);
 	return Vec3(grey);
 	return Vec3(grey);
 }
 }
+#endif
 
 
 Vec3 saturateColor(const Vec3 col, const F32 factor)
 Vec3 saturateColor(const Vec3 col, const F32 factor)
 {
 {
 	const Vec3 lumCoeff = Vec3(0.2125, 0.7154, 0.0721);
 	const Vec3 lumCoeff = Vec3(0.2125, 0.7154, 0.0721);
-	const Vec3 intensity = Vec3(dot(col, lumCoeff));
-	return mix(intensity, col, factor);
+	const F32 d = dot(col, lumCoeff);
+	const Vec3 intensity = Vec3(d, d, d);
+	return lerp(intensity, col, factor);
 }
 }
 
 
 Vec3 gammaCorrection(Vec3 gamma, Vec3 col)
 Vec3 gammaCorrection(Vec3 gamma, Vec3 col)
@@ -319,6 +332,7 @@ Vec3 gammaCorrection(Vec3 gamma, Vec3 col)
 	return pow(col, 1.0 / gamma);
 	return pow(col, 1.0 / gamma);
 }
 }
 
 
+#if ANKI_GLSL
 // Can use 0.15 for sharpenFactor
 // Can use 0.15 for sharpenFactor
 Vec3 readSharpen(texture2D tex, sampler sampl, Vec2 uv, F32 sharpenFactor, Bool detailed)
 Vec3 readSharpen(texture2D tex, sampler sampl, Vec2 uv, F32 sharpenFactor, Bool detailed)
 {
 {
@@ -348,9 +362,9 @@ Vec3 readErosion(texture2D tex, sampler sampl, const Vec2 uv)
 {
 {
 	Vec3 minValue = textureLod(tex, sampl, uv, 0.0).rgb;
 	Vec3 minValue = textureLod(tex, sampl, uv, 0.0).rgb;
 
 
-#define ANKI_EROSION(x, y) \
-	col2 = textureLodOffset(sampler2D(tex, sampl), uv, 0.0, IVec2(x, y)).rgb; \
-	minValue = min(col2, minValue);
+#	define ANKI_EROSION(x, y) \
+		col2 = textureLodOffset(sampler2D(tex, sampl), uv, 0.0, IVec2(x, y)).rgb; \
+		minValue = min(col2, minValue);
 
 
 	Vec3 col2;
 	Vec3 col2;
 	ANKI_EROSION(1, 1);
 	ANKI_EROSION(1, 1);
@@ -362,10 +376,11 @@ Vec3 readErosion(texture2D tex, sampler sampl, const Vec2 uv)
 	ANKI_EROSION(-1, 0);
 	ANKI_EROSION(-1, 0);
 	ANKI_EROSION(0, -1);
 	ANKI_EROSION(0, -1);
 
 
-#undef ANKI_EROSION
+#	undef ANKI_EROSION
 
 
 	return minValue;
 	return minValue;
 }
 }
+#endif
 
 
 // 5 color heatmap from a factor.
 // 5 color heatmap from a factor.
 Vec3 heatmap(const F32 factor)
 Vec3 heatmap(const F32 factor)
@@ -375,19 +390,19 @@ Vec3 heatmap(const F32 factor)
 
 
 	if(intPart < 1.0)
 	if(intPart < 1.0)
 	{
 	{
-		return mix(Vec3(0.0, 0.0, 0.0), Vec3(0.0, 0.0, 1.0), fractional);
+		return lerp(Vec3(0.0, 0.0, 0.0), Vec3(0.0, 0.0, 1.0), fractional);
 	}
 	}
 	else if(intPart < 2.0)
 	else if(intPart < 2.0)
 	{
 	{
-		return mix(Vec3(0.0, 0.0, 1.0), Vec3(0.0, 1.0, 0.0), fractional);
+		return lerp(Vec3(0.0, 0.0, 1.0), Vec3(0.0, 1.0, 0.0), fractional);
 	}
 	}
 	else if(intPart < 3.0)
 	else if(intPart < 3.0)
 	{
 	{
-		return mix(Vec3(0.0, 1.0, 0.0), Vec3(1.0, 1.0, 0.0), fractional);
+		return lerp(Vec3(0.0, 1.0, 0.0), Vec3(1.0, 1.0, 0.0), fractional);
 	}
 	}
 	else
 	else
 	{
 	{
-		return mix(Vec3(1.0, 1.0, 0.0), Vec3(1.0, 0.0, 0.0), fractional);
+		return lerp(Vec3(1.0, 1.0, 0.0), Vec3(1.0, 0.0, 0.0), fractional);
 	}
 	}
 }
 }
 
 
@@ -425,7 +440,7 @@ Bool incorrectColor(const Vec3 c)
 
 
 F32 areaElement(const F32 x, const F32 y)
 F32 areaElement(const F32 x, const F32 y)
 {
 {
-	return atan(x * y, sqrt(x * x + y * y + 1.0));
+	return atan2(x * y, sqrt(x * x + y * y + 1.0));
 }
 }
 
 
 // Compute the solid angle of a cube. Solid angle is the area of a sphere when projected into a cubemap. It's also the
 // Compute the solid angle of a cube. Solid angle is the area of a sphere when projected into a cubemap. It's also the
@@ -433,7 +448,8 @@ F32 areaElement(const F32 x, const F32 y)
 // http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/
 // http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/
 F32 cubeCoordSolidAngle(Vec2 norm, F32 cubeFaceSize)
 F32 cubeCoordSolidAngle(Vec2 norm, F32 cubeFaceSize)
 {
 {
-	const Vec2 invSize = Vec2(1.0 / cubeFaceSize);
+	const F32 s = 1.0f / cubeFaceSize;
+	const Vec2 invSize = Vec2(s, s);
 	const Vec2 v0 = norm - invSize;
 	const Vec2 v0 = norm - invSize;
 	const Vec2 v1 = norm + invSize;
 	const Vec2 v1 = norm + invSize;
 	return areaElement(v0.x, v0.y) - areaElement(v0.x, v1.y) - areaElement(v1.x, v0.y) + areaElement(v1.x, v1.y);
 	return areaElement(v0.x, v0.y) - areaElement(v0.x, v1.y) - areaElement(v1.x, v0.y) + areaElement(v1.x, v1.y);
@@ -441,7 +457,7 @@ F32 cubeCoordSolidAngle(Vec2 norm, F32 cubeFaceSize)
 
 
 // A convenience function to skip out of bounds invocations on post-process compute shaders. Both the arguments should
 // A convenience function to skip out of bounds invocations on post-process compute shaders. Both the arguments should
 // be constexpr.
 // be constexpr.
-#if defined(ANKI_COMPUTE_SHADER)
+#if defined(ANKI_COMPUTE_SHADER) && ANKI_GLSL
 Bool skipOutOfBoundsInvocations(UVec2 workgroupSize, UVec2 globalInvocationCount)
 Bool skipOutOfBoundsInvocations(UVec2 workgroupSize, UVec2 globalInvocationCount)
 {
 {
 	if((globalInvocationCount.x % workgroupSize.x) != 0u || (globalInvocationCount.y % workgroupSize.y) != 0u)
 	if((globalInvocationCount.x % workgroupSize.x) != 0u || (globalInvocationCount.y % workgroupSize.y) != 0u)
@@ -480,7 +496,7 @@ Mat3 rotationFromDirection(Vec3 zAxis)
 #endif
 #endif
 }
 }
 
 
-#if defined(ANKI_COMPUTE_SHADER)
+#if defined(ANKI_COMPUTE_SHADER) && ANKI_GLSL
 // See getOptimalGlobalInvocationId8x8Amd
 // See getOptimalGlobalInvocationId8x8Amd
 U32 _ABfiM(U32 src, U32 ins, U32 bits)
 U32 _ABfiM(U32 src, U32 ins, U32 bits)
 {
 {
@@ -562,11 +578,12 @@ F32 gaussianWeight(F32 s, F32 x)
 	return p;
 	return p;
 }
 }
 
 
+#if ANKI_GLSL
 Vec4 bilinearFiltering(texture2D tex, sampler nearestSampler, Vec2 uv, F32 lod, Vec2 textureSize)
 Vec4 bilinearFiltering(texture2D tex, sampler nearestSampler, Vec2 uv, F32 lod, Vec2 textureSize)
 {
 {
 	const Vec2 texelSize = 1.0 / textureSize;
 	const Vec2 texelSize = 1.0 / textureSize;
 	const Vec2 unnormTexCoord = (uv * textureSize) - 0.5;
 	const Vec2 unnormTexCoord = (uv * textureSize) - 0.5;
-	const Vec2 f = fract(unnormTexCoord);
+	const Vec2 f = frac(unnormTexCoord);
 	const Vec2 snapTexCoord = (floor(unnormTexCoord) + 0.5) / textureSize;
 	const Vec2 snapTexCoord = (floor(unnormTexCoord) + 0.5) / textureSize;
 	const Vec4 s1 = textureLod(tex, nearestSampler, uv, lod);
 	const Vec4 s1 = textureLod(tex, nearestSampler, uv, lod);
 	const Vec4 s2 = textureLod(tex, nearestSampler, uv + Vec2(texelSize.x, 0.0), lod);
 	const Vec4 s2 = textureLod(tex, nearestSampler, uv + Vec2(texelSize.x, 0.0), lod);
@@ -574,12 +591,13 @@ Vec4 bilinearFiltering(texture2D tex, sampler nearestSampler, Vec2 uv, F32 lod,
 	const Vec4 s4 = textureLod(tex, nearestSampler, uv + texelSize, lod);
 	const Vec4 s4 = textureLod(tex, nearestSampler, uv + texelSize, lod);
 	return mix(mix(s1, s2, f.x), mix(s3, s4, f.x), f.y);
 	return mix(mix(s1, s2, f.x), mix(s3, s4, f.x), f.y);
 }
 }
+#endif
 
 
 // https://www.shadertoy.com/view/WsfBDf
 // https://www.shadertoy.com/view/WsfBDf
 Vec3 animateBlueNoise(Vec3 inputBlueNoise, U32 frameIdx)
 Vec3 animateBlueNoise(Vec3 inputBlueNoise, U32 frameIdx)
 {
 {
 	const F32 goldenRatioConjugate = 0.61803398875;
 	const F32 goldenRatioConjugate = 0.61803398875;
-	return fract(inputBlueNoise + F32(frameIdx % 64u) * goldenRatioConjugate);
+	return frac(inputBlueNoise + F32(frameIdx % 64u) * goldenRatioConjugate);
 }
 }
 
 
 #if defined(ANKI_FRAGMENT_SHADER)
 #if defined(ANKI_FRAGMENT_SHADER)
@@ -594,7 +612,8 @@ F32 computeMipLevel(Vec2 normalizedUvs)
 }
 }
 #endif
 #endif
 
 
-#if defined(U64)
+#if ANKI_GLSL
+#	if ANKI_SUPPORTS_64BIT
 /// The regular findLSB in glslang has some issues since it invokes a builtin that is only supposed to be used with
 /// The regular findLSB in glslang has some issues since it invokes a builtin that is only supposed to be used with
 /// 32bit input. This is an alternative implementation but it expects that the input is not zero.
 /// 32bit input. This is an alternative implementation but it expects that the input is not zero.
 I32 findLSB2(U64 v)
 I32 findLSB2(U64 v)
@@ -603,13 +622,14 @@ I32 findLSB2(U64 v)
 	const I32 lsb2 = findLSB(U32(v >> 32ul));
 	const I32 lsb2 = findLSB(U32(v >> 32ul));
 	return (lsb1 >= 0) ? lsb1 : lsb2 + 32;
 	return (lsb1 >= 0) ? lsb1 : lsb2 + 32;
 }
 }
-#endif
+#	endif
 
 
 /// Define an alternative findLSB to go in pair with the 64bit version.
 /// Define an alternative findLSB to go in pair with the 64bit version.
 I32 findLSB2(U32 v)
 I32 findLSB2(U32 v)
 {
 {
 	return findLSB(v);
 	return findLSB(v);
 }
 }
+#endif
 
 
 /// Encode the shading rate to be stored in an SRI. The rates should be power of two, can't be zero and can't exceed 4.
 /// Encode the shading rate to be stored in an SRI. The rates should be power of two, can't be zero and can't exceed 4.
 /// So the possible values are 1,2,4
 /// So the possible values are 1,2,4
@@ -620,23 +640,23 @@ U32 encodeVrsRate(UVec2 rateXY)
 
 
 Vec3 visualizeVrsRate(UVec2 rate)
 Vec3 visualizeVrsRate(UVec2 rate)
 {
 {
-	if(rate == UVec2(1u))
+	if(all(rate == UVec2(1u, 1u)))
 	{
 	{
 		return Vec3(1.0, 0.0, 0.0);
 		return Vec3(1.0, 0.0, 0.0);
 	}
 	}
-	else if(rate == UVec2(2u, 1u) || rate == UVec2(1u, 2u))
+	else if(all(rate == UVec2(2u, 1u)) || all(rate == UVec2(1u, 2u)))
 	{
 	{
 		return Vec3(1.0, 0.5, 0.0);
 		return Vec3(1.0, 0.5, 0.0);
 	}
 	}
-	else if(rate == UVec2(2u) || rate == UVec2(4u, 1u) || rate == UVec2(1u, 4u))
+	else if(all(rate == UVec2(2u, 2u)) || all(rate == UVec2(4u, 1u)) || all(rate == UVec2(1u, 4u)))
 	{
 	{
 		return Vec3(1.0, 1.0, 0.0);
 		return Vec3(1.0, 1.0, 0.0);
 	}
 	}
-	else if(rate == UVec2(4u, 2u) || rate == UVec2(2u, 4u))
+	else if(all(rate == UVec2(4u, 2u)) || all(rate == UVec2(2u, 4u)))
 	{
 	{
 		return Vec3(0.65, 1.0, 0.0);
 		return Vec3(0.65, 1.0, 0.0);
 	}
 	}
-	else if(rate == UVec2(4u))
+	else if(all(rate == UVec2(4u, 4u)))
 	{
 	{
 		return Vec3(0.0, 1.0, 0.0);
 		return Vec3(0.0, 1.0, 0.0);
 	}
 	}
@@ -658,41 +678,60 @@ UVec2 decodeVrsRate(U32 texel)
 /// 3D coordinates to equirectangular 2D coordinates.
 /// 3D coordinates to equirectangular 2D coordinates.
 Vec2 equirectangularMapping(Vec3 v)
 Vec2 equirectangularMapping(Vec3 v)
 {
 {
-	Vec2 uv = Vec2(atan(v.z, v.x), asin(v.y));
-	uv *= vec2(0.1591, 0.3183);
+	Vec2 uv = Vec2(atan2(v.z, v.x), asin(v.y));
+	uv *= Vec2(0.1591, 0.3183);
 	uv += 0.5;
 	uv += 0.5;
 	return uv;
 	return uv;
 }
 }
 
 
 Vec3 linearToSRgb(Vec3 linearRgb)
 Vec3 linearToSRgb(Vec3 linearRgb)
 {
 {
-	linearRgb = max(Vec3(6.10352e-5), linearRgb);
-	return min(linearRgb * 12.92, pow(max(linearRgb, 0.00313067), Vec3(1.0 / 2.4)) * 1.055 - 0.055);
+	const F32 a = 6.10352e-5;
+	const F32 b = 1.0 / 2.4;
+	linearRgb = max(Vec3(a, a, a), linearRgb);
+	return min(linearRgb * 12.92, pow(max(linearRgb, 0.00313067), Vec3(b, b, b)) * 1.055 - 0.055);
 }
 }
 
 
 Vec3 sRgbToLinear(Vec3 sRgb)
 Vec3 sRgbToLinear(Vec3 sRgb)
 {
 {
+#if ANKI_GLSL
 	const bvec3 cutoff = lessThan(sRgb, Vec3(0.04045));
 	const bvec3 cutoff = lessThan(sRgb, Vec3(0.04045));
 	const Vec3 higher = pow((sRgb + 0.055) / 1.055, Vec3(2.4));
 	const Vec3 higher = pow((sRgb + 0.055) / 1.055, Vec3(2.4));
 	const Vec3 lower = sRgb / 12.92;
 	const Vec3 lower = sRgb / 12.92;
 	return mix(higher, lower, cutoff);
 	return mix(higher, lower, cutoff);
+#else
+	const bool3 cutoff = sRgb < Vec3(0.04045, 0.04045, 0.04045);
+	const Vec3 higher = pow((sRgb + 0.055) / 1.055, Vec3(2.4, 2.4, 2.4));
+	const Vec3 lower = sRgb / 12.92;
+	return lerp(higher, lower, cutoff);
+#endif
 }
 }
 
 
+#if ANKI_GLSL
 ANKI_RP Vec3 filmGrain(ANKI_RP Vec3 color, Vec2 uv, ANKI_RP F32 strength, ANKI_RP F32 time)
 ANKI_RP Vec3 filmGrain(ANKI_RP Vec3 color, Vec2 uv, ANKI_RP F32 strength, ANKI_RP F32 time)
 {
 {
 	const F32 x = (uv.x + 4.0) * (uv.y + 4.0) * time;
 	const F32 x = (uv.x + 4.0) * (uv.y + 4.0) * time;
 	const F32 grain = 1.0 - (mod((mod(x, 13.0) + 1.0) * (mod(x, 123.0) + 1.0), 0.01) - 0.005) * strength;
 	const F32 grain = 1.0 - (mod((mod(x, 13.0) + 1.0) * (mod(x, 123.0) + 1.0), 0.01) - 0.005) * strength;
 	return color * grain;
 	return color * grain;
 }
 }
+#else
+template<typename TVec3, typename TFloat>
+TVec3 filmGrain(TVec3 color, Vec2 uv, TFloat strength, TFloat time)
+{
+	const TFloat x = (uv.x + 4.0) * (uv.y + 4.0) * time;
+	const TFloat grain = 1.0 - (mod((mod(x, 13.0) + 1.0) * (mod(x, 123.0) + 1.0), 0.01) - 0.005) * strength;
+	return color * grain;
+}
+#endif
 
 
 /// Sin approximation: https://www.desmos.com/calculator/svgcjfskne
 /// Sin approximation: https://www.desmos.com/calculator/svgcjfskne
-ANKI_RP F32 fastSin(ANKI_RP F32 x)
+F32 fastSin(F32 x)
 {
 {
-	const ANKI_RP F32 k2Pi = 2.0 * kPi;
-	const ANKI_RP F32 kPiOver2 = kPi / 2.0;
+	const F32 k2Pi = 2.0 * kPi;
+	const F32 kPiOver2 = kPi / 2.0;
 
 
 	x = (x + kPiOver2) / (k2Pi) + 0.75;
 	x = (x + kPiOver2) / (k2Pi) + 0.75;
-	x = fract(x);
+	x = frac(x);
 	x = x * 2.0 - 1.0;
 	x = x * 2.0 - 1.0;
 	x = x * abs(x) - x;
 	x = x * abs(x) - x;
 	x *= 4.0;
 	x *= 4.0;
@@ -700,7 +739,7 @@ ANKI_RP F32 fastSin(ANKI_RP F32 x)
 }
 }
 
 
 /// Cos approximation
 /// Cos approximation
-ANKI_RP F32 fastCos(ANKI_RP F32 x)
+F32 fastCos(F32 x)
 {
 {
 	return fastSin(x + kPi / 2.0);
 	return fastSin(x + kPi / 2.0);
 }
 }

+ 131 - 6
AnKi/Shaders/Include/Common.h

@@ -10,6 +10,10 @@
 
 
 #	include <AnKi/Math.h>
 #	include <AnKi/Math.h>
 
 
+#	define ANKI_HLSL 0
+#	define ANKI_GLSL 0
+#	define ANKI_CPP 1
+
 #	define ANKI_BEGIN_NAMESPACE namespace anki {
 #	define ANKI_BEGIN_NAMESPACE namespace anki {
 #	define ANKI_END_NAMESPACE }
 #	define ANKI_END_NAMESPACE }
 #	define ANKI_SHADER_FUNC_INLINE inline
 #	define ANKI_SHADER_FUNC_INLINE inline
@@ -17,9 +21,7 @@
 #	define ANKI_SHADER_STATIC_ASSERT(cond_) static_assert(cond_)
 #	define ANKI_SHADER_STATIC_ASSERT(cond_) static_assert(cond_)
 
 
 ANKI_BEGIN_NAMESPACE
 ANKI_BEGIN_NAMESPACE
-
 using Address = U64;
 using Address = U64;
-
 using ScalarVec4 = Array<F32, 4>;
 using ScalarVec4 = Array<F32, 4>;
 using ScalarMat3x4 = Array<F32, 12>;
 using ScalarMat3x4 = Array<F32, 12>;
 using ScalarMat4 = Array<F32, 16>;
 using ScalarMat4 = Array<F32, 16>;
@@ -27,8 +29,121 @@ ANKI_END_NAMESPACE
 
 
 #	define ANKI_RP
 #	define ANKI_RP
 
 
+//! == HLSL ============================================================================================================
+#elif defined(__HLSL_VERSION)
+#	define ANKI_HLSL 1
+#	define ANKI_GLSL 0
+#	define ANKI_CPP 0
+
+#	define ANKI_BEGIN_NAMESPACE
+#	define ANKI_END_NAMESPACE
+#	define ANKI_SHADER_FUNC_INLINE
+
+#	define ANKI_SHADER_STATIC_ASSERT(cond_)
+
+#	define constexpr static const
+
+typedef float F32;
+constexpr uint kSizeof_F32 = 4u;
+typedef float2 Vec2;
+constexpr uint kSizeof_Vec2 = 8u;
+typedef float3 Vec3;
+constexpr uint kSizeof_Vec3 = 12u;
+typedef float4 Vec4;
+constexpr uint kSizeof_Vec4 = 16u;
+
+typedef float16_t F16;
+constexpr uint kSizeof_F16 = 2u;
+typedef float16_t2 HVec2;
+constexpr uint kSizeof_HVec2 = 4u;
+typedef float16_t3 HVec3;
+constexpr uint kSizeof_HVec3 = 6u;
+typedef float16_t4 HVec4;
+constexpr uint kSizeof_HVec4 = 8u;
+
+typedef uint16_t U16;
+constexpr uint kSizeof_U16 = 2u;
+typedef uint16_t2 U16Vec2;
+constexpr uint kSizeof_U16Vec2 = 4u;
+typedef uint16_t3 U16Vec3;
+constexpr uint kSizeof_U16Vec3 = 6u;
+typedef uint16_t4 U16Vec4;
+constexpr uint kSizeof_U16Vec4 = 8u;
+
+typedef int16_t I16;
+constexpr uint kSizeof_I16 = 2u;
+typedef int16_t2 I16Vec2;
+constexpr uint kSizeof_I16Vec2 = 4u;
+typedef int16_t3 I16Vec3;
+constexpr uint kSizeof_I16Vec3 = 6u;
+typedef int16_t4 I16Vec4;
+constexpr uint kSizeof_I16Vec4 = 8u;
+
+typedef uint U32;
+constexpr uint kSizeof_U32 = 4u;
+typedef uint32_t2 UVec2;
+constexpr uint kSizeof_UVec2 = 8u;
+typedef uint32_t3 UVec3;
+constexpr uint kSizeof_UVec3 = 12u;
+typedef uint32_t4 UVec4;
+constexpr uint kSizeof_UVec4 = 16u;
+
+typedef int I32;
+constexpr uint kSizeof_I32 = 4u;
+typedef int32_t2 IVec2;
+constexpr uint kSizeof_IVec2 = 8u;
+typedef int32_t3 IVec3;
+constexpr uint kSizeof_IVec3 = 12u;
+typedef int32_t4 IVec4;
+constexpr uint kSizeof_IVec4 = 16u;
+
+#	if ANKI_SUPPORTS_64BIT
+typedef uint64_t U64;
+constexpr uint kSizeof_U64 = 8u;
+typedef uint64_t2 U64Vec2;
+constexpr uint kSizeof_U64Vec2 = 16u;
+typedef uint64_t3 U64Vec3;
+constexpr uint kSizeof_U64Vec3 = 24u;
+typedef uint64_t4 U64Vec4;
+constexpr uint kSizeof_U64Vec4 = 32u;
+
+typedef int64_t I64;
+constexpr uint kSizeof_I64 = 8u;
+typedef int64_t2 I64Vec2;
+constexpr uint kSizeof_I64Vec2 = 16u;
+typedef int64_t3 I64Vec3;
+constexpr uint kSizeof_I64Vec3 = 24u;
+typedef int64_t4 I64Vec4;
+constexpr uint kSizeof_I64Vec4 = 32u;
+#	endif
+
+typedef float3x3 Mat3;
+
+typedef bool Bool;
+
+#	if 0
+typedef min16float RF32;
+typedef min16float2 RVec2;
+typedef min16float3 RVec3;
+typedef min16float4 RVec4;
+#	endif
+
+constexpr F32 kEpsilonf = 0.000001f;
+constexpr F16 kEpsilonhf = (F16)0.0001f; // Divisions by this should be OK according to http://weitz.de/ieee/
+
+constexpr U32 kMaxU32 = 0xFFFFFFFFu;
+constexpr F32 kMaxF32 = 3.402823e+38;
+constexpr F16 kMaxF16 = (F16)65504.0;
+constexpr F16 kMinF16 = (F16)0.00006104;
+
+constexpr F32 kPi = 3.14159265358979323846f;
+
 //! == GLSL ============================================================================================================
 //! == GLSL ============================================================================================================
 #else
 #else
+#	define ANKI_HLSL 0
+#	define ANKI_GLSL 1
+#	define ANKI_CPP 0
+
 #	define ANKI_BEGIN_NAMESPACE
 #	define ANKI_BEGIN_NAMESPACE
 #	define ANKI_END_NAMESPACE
 #	define ANKI_END_NAMESPACE
 #	define ANKI_SHADER_FUNC_INLINE
 #	define ANKI_SHADER_FUNC_INLINE
@@ -281,12 +396,19 @@ Vec4 pow(Vec4 a, F32 b)
 {
 {
 	return pow(a, Vec4(b));
 	return pow(a, Vec4(b));
 }
 }
-#endif
 
 
-//! == Consts ==========================================================================================================
-ANKI_BEGIN_NAMESPACE
+Bool all(Bool b)
+{
+	return b;
+}
+
+#	define saturate(x_) clamp((x_), 0.0, 1.0)
+#	define saturateRp(x) min(x, F32(kMaxF16))
+#	define mad(a_, b_, c_) fma((a_), (b_), (c_))
+#	define frac(x) fract(x)
+#	define lerp(a, b, t) mix(a, b, t)
+#	define atan2(x, y) atan(x, y)
 
 
-#if !defined(__cplusplus)
 constexpr F32 kEpsilonf = 0.000001f;
 constexpr F32 kEpsilonf = 0.000001f;
 constexpr F16 kEpsilonhf = 0.0001hf; // Divisions by this should be OK according to http://weitz.de/ieee/
 constexpr F16 kEpsilonhf = 0.0001hf; // Divisions by this should be OK according to http://weitz.de/ieee/
 constexpr ANKI_RP F32 kEpsilonRp = F32(kEpsilonhf);
 constexpr ANKI_RP F32 kEpsilonRp = F32(kEpsilonhf);
@@ -299,6 +421,9 @@ constexpr F16 kMinF16 = 0.00006104hf;
 constexpr F32 kPi = 3.14159265358979323846f;
 constexpr F32 kPi = 3.14159265358979323846f;
 #endif
 #endif
 
 
+//! == Consts ==========================================================================================================
+ANKI_BEGIN_NAMESPACE
+
 /// The renderer will group drawcalls into instances up to this number.
 /// The renderer will group drawcalls into instances up to this number.
 constexpr U32 kMaxInstanceCount = 64u;
 constexpr U32 kMaxInstanceCount = 64u;
 
 

+ 1 - 1
AnKi/Util/String.h

@@ -889,7 +889,7 @@ public:
 		destroy();
 		destroy();
 		if(!b.isEmpty())
 		if(!b.isEmpty())
 		{
 		{
-			create(b.getBegin(), b.getEnd() - 1);
+			create(b.getBegin(), b.getEnd());
 		}
 		}
 		return *this;
 		return *this;
 	}
 	}

+ 1 - 1
Tests/Gr/GrCommon.h

@@ -21,7 +21,7 @@ inline ShaderPtr createShader(CString src, ShaderType type, GrManager& gr,
 	header.append(src);
 	header.append(src);
 	DynamicArrayRaii<U8> spirv(&pool);
 	DynamicArrayRaii<U8> spirv(&pool);
 	StringRaii errorLog(&pool);
 	StringRaii errorLog(&pool);
-	ANKI_TEST_EXPECT_NO_ERR(compilerGlslToSpirv(header, type, pool, spirv, errorLog));
+	ANKI_TEST_EXPECT_NO_ERR(compileGlslToSpirv(header, type, pool, spirv, errorLog));
 
 
 	ShaderInitInfo initInf(type, spirv);
 	ShaderInitInfo initInf(type, spirv);
 	initInf.m_constValues = specVals;
 	initInf.m_constValues = specVals;

+ 5 - 5
Tools/Shader/ShaderProgramBinaryDumpMain.cpp

@@ -14,18 +14,18 @@ Options:
 -stats : Print performance statistics for all shaders. By default it doesn't
 -stats : Print performance statistics for all shaders. By default it doesn't
 )";
 )";
 
 
-static Error parseCommandLineArgs(int argc, char** argv, Bool& dumpStats, StringRaii& filename)
+static Error parseCommandLineArgs(WeakArray<char*> argv, Bool& dumpStats, StringRaii& filename)
 {
 {
 	// Parse config
 	// Parse config
-	if(argc < 2)
+	if(argv.getSize() < 2)
 	{
 	{
 		return Error::kUserData;
 		return Error::kUserData;
 	}
 	}
 
 
 	dumpStats = false;
 	dumpStats = false;
-	filename = argv[argc - 1];
+	filename = argv[argv.getSize() - 1];
 
 
-	for(I i = 1; i < argc - 1; i++)
+	for(U32 i = 1; i < argv.getSize() - 1; i++)
 	{
 	{
 		if(strcmp(argv[i], "-stats") == 0)
 		if(strcmp(argv[i], "-stats") == 0)
 		{
 		{
@@ -194,7 +194,7 @@ int main(int argc, char** argv)
 	HeapMemoryPool pool(allocAligned, nullptr);
 	HeapMemoryPool pool(allocAligned, nullptr);
 	StringRaii filename(&pool);
 	StringRaii filename(&pool);
 	Bool dumpStats;
 	Bool dumpStats;
-	if(parseCommandLineArgs(argc, argv, dumpStats, filename))
+	if(parseCommandLineArgs(WeakArray<char*>(argv, argc), dumpStats, filename))
 	{
 	{
 		ANKI_LOGE(kUsage, argv[0]);
 		ANKI_LOGE(kUsage, argv[0]);
 		return 1;
 		return 1;