From 3ff4265ccf08cce75e7d3f5dc14c789d45e94e08 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Fri, 16 Dec 2022 02:44:10 +0100
Subject: [PATCH] Add changes to framework from previous work on denoiser
 module improving shader compilation

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 README.md                                     |   3 +-
 include/vkcv/File.hpp                         |  65 ++++++++++
 include/vkcv/ShaderProgram.hpp                |   1 -
 .../vkcv/algorithm/SinglePassDownsampler.cpp  |  75 ++---------
 .../src/vkcv/effects/BloomAndFlaresEffect.cpp |   2 +-
 .../vkcv/effects/GammaCorrectionEffect.cpp    |   2 +-
 modules/shader_compiler/config/GLSLANG.cmake  |   2 +-
 .../include/vkcv/shader/Compiler.hpp          |  26 +++-
 .../include/vkcv/shader/GLSLCompiler.hpp      |   2 +-
 .../include/vkcv/shader/GlslangCompiler.hpp   |  19 ---
 .../include/vkcv/shader/HLSLCompiler.hpp      |   2 +-
 .../src/vkcv/shader/Compiler.cpp              |  38 ++++++
 .../src/vkcv/shader/GLSLCompiler.cpp          |  12 +-
 .../src/vkcv/shader/GlslangCompiler.cpp       |  53 ++------
 .../src/vkcv/shader/HLSLCompiler.cpp          |  22 +++-
 modules/tone_mapping/README.md                |  15 +++
 .../src/vkcv/tone/ToneMapping.cpp             |   2 +-
 .../src/vkcv/upscaling/FSRUpscaling.cpp       |  81 ++++--------
 .../src/vkcv/upscaling/NISUpscaling.cpp       |  58 ++-------
 src/vkcv/File.cpp                             | 121 ++++++++++++++++++
 src/vkcv/ShaderProgram.cpp                    |  44 ++-----
 21 files changed, 360 insertions(+), 285 deletions(-)
 create mode 100644 modules/tone_mapping/README.md

diff --git a/README.md b/README.md
index c45910f2..f73ca45d 100644
--- a/README.md
+++ b/README.md
@@ -43,13 +43,14 @@ the framework if used. You can configure/adjust the build using CMake if necessa
  - [Algorithm](modules/algorithm/README.md)
  - [Asset-Loader](modules/asset_loader/README.md)
  - [Camera](modules/asset_loader/README.md)
- - [GUI](modules/gui/README.md)
  - [Effects](modules/effects/README.md)
  - [Geometry](modules/geometry/README.md)
+ - [GUI](modules/gui/README.md)
  - [Material](modules/material/README.md)
  - [Meshlet](modules/meshlet/README.md)
  - [Scene](modules/scene/README.md)
  - [Shader-Compiler](modules/shader_compiler/README.md)
+ - [Tone-Mapping](modules/tone_mapping/README.md)
  - [Upscaling](modules/upscaling/README.md)
 
 ### Projects (optional):
diff --git a/include/vkcv/File.hpp b/include/vkcv/File.hpp
index 51491b1b..f6895d41 100644
--- a/include/vkcv/File.hpp
+++ b/include/vkcv/File.hpp
@@ -6,6 +6,7 @@
  */
 
 #include <filesystem>
+#include <vector>
 
 namespace vkcv {
 
@@ -22,5 +23,69 @@ namespace vkcv {
 	 * @return A unique path for a temporary directory
 	 */
 	std::filesystem::path generateTemporaryDirectoryPath();
+	
+	/**
+	 * @brief Write content data from a vector to a file at
+	 * a given path.
+	 *
+	 * @param[in] path Path of file
+	 * @param[in] content Custom data vector
+	 * @return True on success, false otherwise
+	 */
+	bool writeContentToFile(const std::filesystem::path &path,
+							const std::vector<char>& content);
+	
+	/**
+	 * @brief Write binary data from a vector to a file at
+	 * a given path.
+	 *
+	 * @param[in] path Path of file
+	 * @param[in] binary Custom binary vector
+	 * @return True on success, false otherwise
+	 */
+	bool writeBinaryToFile(const std::filesystem::path &path,
+						   const std::vector<uint32_t>& binary);
+	
+	/**
+	 * @brief Write text to a file at a given path.
+	 *
+	 * @param[in] path Path of file
+	 * @param[in] text Custom text as string
+	 * @return True on success, false otherwise
+	 */
+	bool writeTextToFile(const std::filesystem::path &path,
+						 const std::string& text);
+	
+	/**
+	 * @brief Read content data from a file at a given path
+	 * into a vector.
+	 *
+	 * @param[in] path Path of file
+	 * @param[in] content Custom data vector reference
+	 * @return True on success, false otherwise
+	 */
+	bool readContentFromFile(const std::filesystem::path &path,
+							 std::vector<char>& content);
+	
+	/**
+	 * @brief Read binary data from a file at a given path
+	 * into a vector.
+	 *
+	 * @param[in] path Path of file
+	 * @param[in] content Custom binary vector reference
+	 * @return True on success, false otherwise
+	 */
+	bool readBinaryFromFile(const std::filesystem::path &path,
+							std::vector<uint32_t>& binary);
+	
+	/**
+	 * @brief Read text from a file at a given path.
+	 *
+	 * @param[in] path Path of file
+	 * @param[in] text Custom text as string reference
+	 * @return True on success, false otherwise
+	 */
+	bool readTextFromFile(const std::filesystem::path &path,
+						  std::string& text);
 
 } // namespace vkcv
diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp
index 11156fdb..c14a3631 100644
--- a/include/vkcv/ShaderProgram.hpp
+++ b/include/vkcv/ShaderProgram.hpp
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 #include <filesystem>
-#include <fstream>
 #include <iostream>
 #include <spirv_cross.hpp>
 #include <unordered_map>
diff --git a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
index 051ae80d..21ea671c 100644
--- a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
+++ b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
@@ -96,53 +96,6 @@ namespace vkcv::algorithm {
 		return descriptorBindings;
 	}
 	
-	static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) {
-		std::ofstream file (shaderPath.string(), std::ios::out);
-		
-		if (!file.is_open()) {
-			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str());
-			return false;
-		}
-		
-		file.seekp(0);
-		file.write(code.c_str(), static_cast<std::streamsize>(code.length()));
-		file.close();
-		
-		return true;
-	}
-	
-	static bool compileSPDShader(vkcv::shader::GLSLCompiler& compiler,
-								 const std::string &source,
-								 const shader::ShaderCompiledFunction& compiled) {
-		std::filesystem::path directory = generateTemporaryDirectoryPath();
-		
-		if (!std::filesystem::create_directory(directory)) {
-			vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str());
-			return false;
-		}
-		
-		if (!writeShaderCode(directory / "ffx_a.h", FFX_A_H_SHADER)) {
-			return false;
-		}
-		
-		if (!writeShaderCode(directory / "ffx_spd.h", FFX_SPD_H_SHADER)) {
-			return false;
-		}
-		
-		return compiler.compileSource(
-				vkcv::ShaderStage::COMPUTE,
-				source.c_str(),
-				[&directory, &compiled] (vkcv::ShaderStage shaderStage,
-										 const std::filesystem::path& path) {
-					if (compiled) {
-						compiled(shaderStage, path);
-					}
-					
-					std::filesystem::remove_all(directory);
-				}, directory
-		);
-	}
-	
 	SinglePassDownsampler::SinglePassDownsampler(Core &core,
 												 const SamplerHandle &sampler) :
 		 vkcv::Downsampler(core),
@@ -215,23 +168,19 @@ namespace vkcv::algorithm {
 		}
 		
 		ShaderProgram program;
-		if (m_sampler) {
-			compileSPDShader(
-					compiler,
-					SPDINTEGRATIONLINEARSAMPLER_GLSL_SHADER,
-					[&program](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-						program.addShader(shaderStage, path);
-					}
-			);
-		} else {
-			compileSPDShader(
-					compiler,
+		compiler.compileSourceWithHeaders(
+				ShaderStage::COMPUTE,
+				m_sampler?
+					SPDINTEGRATIONLINEARSAMPLER_GLSL_SHADER :
 					SPDINTEGRATION_GLSL_SHADER,
-					[&program](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-						program.addShader(shaderStage, path);
-					}
-			);
-		}
+				{
+					{ "ffx_a.h", FFX_A_H_SHADER },
+					{ "ffx_spd.h", FFX_SPD_H_SHADER }
+				},
+				[&program](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+					program.addShader(shaderStage, path);
+				}
+		);
 		
 		m_pipeline = m_core.createComputePipeline(ComputePipelineConfig(
 			program,
diff --git a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
index 133b3800..6f096ecd 100644
--- a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
+++ b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
@@ -150,7 +150,7 @@ namespace vkcv::effects {
 		ShaderProgram program;
 		compiler.compileSource(
 				vkcv::ShaderStage::COMPUTE,
-				shaderSource.c_str(),
+				shaderSource,
 				[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 					program.addShader(shaderStage, path);
 				},
diff --git a/modules/effects/src/vkcv/effects/GammaCorrectionEffect.cpp b/modules/effects/src/vkcv/effects/GammaCorrectionEffect.cpp
index 088a3dbd..d97df3fd 100644
--- a/modules/effects/src/vkcv/effects/GammaCorrectionEffect.cpp
+++ b/modules/effects/src/vkcv/effects/GammaCorrectionEffect.cpp
@@ -41,7 +41,7 @@ namespace vkcv::effects {
 		
 		compiler.compileSource(
 			ShaderStage::COMPUTE,
-			GAMMACORRECTION_COMP_SHADER.c_str(),
+			GAMMACORRECTION_COMP_SHADER,
 			[&program](ShaderStage stage, const std::filesystem::path &path) {
 				program.addShader(stage, path);
 			}
diff --git a/modules/shader_compiler/config/GLSLANG.cmake b/modules/shader_compiler/config/GLSLANG.cmake
index 98bd4549..86bab2d3 100644
--- a/modules/shader_compiler/config/GLSLANG.cmake
+++ b/modules/shader_compiler/config/GLSLANG.cmake
@@ -10,7 +10,7 @@ if (${glslang_status})
 	set(ENABLE_GLSLANG_WEBMIN_DEVEL OFF CACHE INTERNAL "")
 	set(ENABLE_EMSCRIPTEN_SINGLE_FILE OFF CACHE INTERNAL "")
 	set(ENABLE_EMSCRIPTEN_ENVIRONMENT_NODE OFF CACHE INTERNAL "")
-	set(ENABLE_HLSL OFF CACHE INTERNAL "")
+	set(ENABLE_HLSL ON CACHE INTERNAL "")
 	set(ENABLE_RTTI OFF CACHE INTERNAL "")
 	set(ENABLE_EXCEPTIONS OFF CACHE INTERNAL "")
 	set(ENABLE_OPT OFF CACHE INTERNAL "")
diff --git a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp
index 8096e9e2..e30cdb34 100644
--- a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp
+++ b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp
@@ -48,9 +48,30 @@ namespace vkcv::shader {
          * @param[in] includePath Include path for shaders
          * @return Result if the compilation succeeds
          */
-		virtual bool compileSource(ShaderStage shaderStage, const char* shaderSource,
+		virtual bool compileSource(ShaderStage shaderStage,
+								   const std::string& shaderSource,
 								   const ShaderCompiledFunction& compiled,
 								   const std::filesystem::path& includePath) = 0;
+		
+		/**
+		 * Compile a shader from source for a target stage with some included headers
+		 * as source as well and an event function called if the compilation completes.
+		 *
+		 * The included shaders will be stored temporarily for shader compilation in
+		 * a directory which gets used as include path. So these files can be used via
+		 * include instructions in the shader source code as if they were stored in the
+		 * same directory.
+		 *
+		 * @param[in] shaderStage Shader pipeline stage
+		 * @param[in] shaderSource Source of shader
+		 * @param[in] shaderHeaders Headers of shader
+		 * @param[in] compiled Shader compilation event
+		 * @return Result if the compilation succeeds
+		 */
+		bool compileSourceWithHeaders(ShaderStage shaderStage,
+									  const std::string& shaderSource,
+									  const std::unordered_map<std::filesystem::path, std::string>& shaderHeaders,
+									  const ShaderCompiledFunction& compiled);
 
         /**
          * Compile a shader from a specific file path for a target stage with
@@ -81,8 +102,7 @@ namespace vkcv::shader {
          * @param[in] update Flag to update shaders during runtime
          */
 		void compileProgram(ShaderProgram& program,
-							const std::unordered_map<ShaderStage,
-							const std::filesystem::path>& stages,
+							const std::unordered_map<ShaderStage, const std::filesystem::path>& stages,
 							const ShaderProgramCompiledFunction& compiled,
 							const std::filesystem::path& includePath = "",
 							bool update = false);
diff --git a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
index 48b3024f..cebc8bb7 100644
--- a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
+++ b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
@@ -45,7 +45,7 @@ namespace vkcv::shader {
          * @return Result if the compilation succeeds
          */
 		bool compileSource(ShaderStage shaderStage,
-						   const char* shaderSource,
+						   const std::string& shaderSource,
 						   const ShaderCompiledFunction& compiled,
 						   const std::filesystem::path& includePath = "") override;
 		
diff --git a/modules/shader_compiler/include/vkcv/shader/GlslangCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/GlslangCompiler.hpp
index 79a86cf5..837b9b1d 100644
--- a/modules/shader_compiler/include/vkcv/shader/GlslangCompiler.hpp
+++ b/modules/shader_compiler/include/vkcv/shader/GlslangCompiler.hpp
@@ -16,25 +16,6 @@ namespace vkcv::shader {
 	 * An abstract class to handle Glslang runtime shader compilation.
 	 */
 	class GlslangCompiler : public Compiler {
-	protected:
-		/**
-		 *
-		 *
-		 * @param[in] shaderPath Filepath of shader
-		 * @param[out] spirv
-		 * @return
-		 */
-		static bool writeSpirvCode(const std::filesystem::path &shaderPath,
-								   const std::vector<uint32_t>& spirv);
-		
-		/**
-		 *
-		 *
-		 * @param[in] shaderPath Filepath of shader
-		 * @return
-		 */
-		static std::vector<char> readShaderCode(const std::filesystem::path &shaderPath);
-		
 	public:
 		/**
 		 * The constructor of a runtime Glslang shader compiler instance.
diff --git a/modules/shader_compiler/include/vkcv/shader/HLSLCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/HLSLCompiler.hpp
index 9baf2cbf..dad4c38b 100644
--- a/modules/shader_compiler/include/vkcv/shader/HLSLCompiler.hpp
+++ b/modules/shader_compiler/include/vkcv/shader/HLSLCompiler.hpp
@@ -42,7 +42,7 @@ namespace vkcv::shader {
 		 * @return Result if the compilation succeeds
 		 */
 		bool compileSource(ShaderStage shaderStage,
-						   const char* shaderSource,
+						   const std::string& shaderSource,
 						   const ShaderCompiledFunction& compiled,
 						   const std::filesystem::path& includePath = "") override;
 		
diff --git a/modules/shader_compiler/src/vkcv/shader/Compiler.cpp b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp
index dcf9e516..c7935f89 100644
--- a/modules/shader_compiler/src/vkcv/shader/Compiler.cpp
+++ b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp
@@ -1,8 +1,46 @@
 
 #include "vkcv/shader/Compiler.hpp"
 
+#include <vkcv/File.hpp>
+#include <vkcv/Logger.hpp>
+
 namespace vkcv::shader {
 	
+	bool Compiler::compileSourceWithHeaders(ShaderStage shaderStage,
+											const std::string &shaderSource,
+											const std::unordered_map<std::filesystem::path, std::string> &shaderHeaders,
+											const ShaderCompiledFunction &compiled) {
+		const std::filesystem::path directory = generateTemporaryDirectoryPath();
+		
+		if (!std::filesystem::create_directory(directory)) {
+			vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.c_str());
+			return false;
+		}
+		
+		for (const auto& header : shaderHeaders) {
+			if (header.first.has_parent_path()) {
+				std::filesystem::create_directories(directory / header.first.parent_path());
+			}
+			
+			if (!writeTextToFile(directory / header.first, header.second)) {
+				return false;
+			}
+		}
+		
+		return compileSource(
+				shaderStage,
+				shaderSource,
+				[&directory, &compiled] (vkcv::ShaderStage shaderStage,
+										 const std::filesystem::path& path) {
+					if (compiled) {
+						compiled(shaderStage, path);
+					}
+					
+					std::filesystem::remove_all(directory);
+				}, directory
+		);
+	}
+	
 	void Compiler::compileProgram(ShaderProgram& program,
 								  const std::unordered_map<ShaderStage, const std::filesystem::path>& stages,
 								  const ShaderProgramCompiledFunction& compiled,
diff --git a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp
index 0f423557..a3856076 100644
--- a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp
+++ b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp
@@ -153,7 +153,7 @@ namespace vkcv::shader {
 	}
 	
 	bool GLSLCompiler::compileSource(ShaderStage shaderStage,
-									 const char* shaderSource,
+									 const std::string& shaderSource,
 									 const ShaderCompiledFunction &compiled,
 									 const std::filesystem::path& includePath) {
 		const EShLanguage language = findShaderLanguage(shaderStage);
@@ -166,12 +166,12 @@ namespace vkcv::shader {
 		glslang::TShader shader (language);
 		switch (m_target) {
 			case GLSLCompileTarget::SUBGROUP_OP:
-				shader.setEnvClient(glslang::EShClientVulkan,glslang::EShTargetVulkan_1_1);
-				shader.setEnvTarget(glslang::EShTargetSpv,glslang::EShTargetSpv_1_3);
+				shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
+				shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
 				break;
 			case GLSLCompileTarget::RAY_TRACING:
-				shader.setEnvClient(glslang::EShClientVulkan,glslang::EShTargetVulkan_1_2);
-				shader.setEnvTarget(glslang::EShTargetSpv,glslang::EShTargetSpv_1_4);
+				shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_2);
+				shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4);
 				break;
 			default:
 				break;
@@ -257,7 +257,7 @@ namespace vkcv::shader {
 		
 		const std::filesystem::path tmp_path = generateTemporaryFilePath();
 		
-		if (!writeSpirvCode(tmp_path, spirv)) {
+		if (!writeBinaryToFile(tmp_path, spirv)) {
 			vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk");
 			return false;
 		}
diff --git a/modules/shader_compiler/src/vkcv/shader/GlslangCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/GlslangCompiler.cpp
index 9ee6952b..1f20daae 100644
--- a/modules/shader_compiler/src/vkcv/shader/GlslangCompiler.cpp
+++ b/modules/shader_compiler/src/vkcv/shader/GlslangCompiler.cpp
@@ -1,9 +1,9 @@
 
 #include "vkcv/shader/GlslangCompiler.hpp"
 
+#include <vkcv/File.hpp>
 #include <vkcv/Logger.hpp>
 
-#include <fstream>
 #include <glslang/SPIRV/GlslangToSpv.h>
 
 namespace vkcv::shader {
@@ -35,57 +35,22 @@ namespace vkcv::shader {
 		return *this;
 	}
 	
-	bool GlslangCompiler::writeSpirvCode(const std::filesystem::path &shaderPath,
-										 const std::vector<uint32_t>& spirv) {
-		std::ofstream file (shaderPath.string(), std::ios::out | std::ios::binary);
-		
-		if (!file.is_open()) {
-			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str());
-			return false;
-		}
-		
-		const auto fileSize = static_cast<std::streamsize>(
-				sizeof(uint32_t) * spirv.size()
-		);
-		
-		file.seekp(0);
-		file.write(reinterpret_cast<const char*>(spirv.data()), fileSize);
-		file.close();
-		
-		return true;
-	}
-	
-	std::vector<char> GlslangCompiler::readShaderCode(const std::filesystem::path &shaderPath) {
-		std::ifstream file (shaderPath.string(), std::ios::ate);
-		
-		if (!file.is_open()) {
-			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str());
-			return std::vector<char>{};
-		}
-		
-		std::streamsize fileSize = file.tellg();
-		std::vector<char> buffer (fileSize + 1);
-		
-		file.seekg(0);
-		file.read(buffer.data(), fileSize);
-		file.close();
-		
-		buffer[fileSize] = '\0';
-		return buffer;
-	}
-	
 	void GlslangCompiler::compile(ShaderStage shaderStage,
 								  const std::filesystem::path &shaderPath,
 								  const ShaderCompiledFunction &compiled,
 								  const std::filesystem::path &includePath,
 								  bool update) {
-		const std::vector<char> code = readShaderCode(shaderPath);
-		bool result;
+		std::string shaderCode;
+		bool result = readTextFromFile(shaderPath, shaderCode);
+		
+		if (!result) {
+			vkcv_log(LogLevel::ERROR, "Loading shader failed: (%s)", shaderPath.string().c_str());
+		}
 		
 		if (!includePath.empty()) {
-			result = compileSource(shaderStage, code.data(), compiled, includePath);
+			result = compileSource(shaderStage, shaderCode, compiled, includePath);
 		} else {
-			result = compileSource(shaderStage, code.data(), compiled, shaderPath.parent_path());
+			result = compileSource(shaderStage, shaderCode, compiled, shaderPath.parent_path());
 		}
 		
 		if (!result) {
diff --git a/modules/shader_compiler/src/vkcv/shader/HLSLCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/HLSLCompiler.cpp
index 6724d008..629f941c 100644
--- a/modules/shader_compiler/src/vkcv/shader/HLSLCompiler.cpp
+++ b/modules/shader_compiler/src/vkcv/shader/HLSLCompiler.cpp
@@ -153,7 +153,7 @@ namespace vkcv::shader {
 	}
 	
 	bool HLSLCompiler::compileSource(ShaderStage shaderStage,
-									 const char* shaderSource,
+									 const std::string& shaderSource,
 									 const ShaderCompiledFunction &compiled,
 									 const std::filesystem::path& includePath) {
 		const EShLanguage language = findShaderLanguage(shaderStage);
@@ -164,11 +164,22 @@ namespace vkcv::shader {
 		}
 		
 		glslang::TShader shader (language);
+		shader.setEntryPoint("main");
+		
 		switch (m_target) {
 			default:
+				shader.setEnvClient(glslang::EShClientNone, glslang::EShTargetVulkan_1_1);
+				shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
 				break;
 		}
 		
+		shader.setEnvInput(
+				glslang::EShSourceHlsl,
+				language,
+				glslang::EShClientNone,
+				100
+		);
+		
 		glslang::TProgram program;
 		std::string source (shaderSource);
 		
@@ -204,7 +215,12 @@ namespace vkcv::shader {
 		
 		const auto messages = (EShMessages)(
 				EShMsgSpvRules |
-				EShMsgVulkanRules
+				EShMsgVulkanRules |
+				EShMsgReadHlsl |
+				EShMsgHlslOffsets |
+				EShMsgHlslEnable16BitTypes |
+				EShMsgHlslLegalization |
+				EShMsgHlslDX9Compatible
 		);
 		
 		std::string preprocessedHLSL;
@@ -249,7 +265,7 @@ namespace vkcv::shader {
 		
 		const std::filesystem::path tmp_path = generateTemporaryFilePath();
 		
-		if (!writeSpirvCode(tmp_path, spirv)) {
+		if (!writeBinaryToFile(tmp_path, spirv)) {
 			vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk");
 			return false;
 		}
diff --git a/modules/tone_mapping/README.md b/modules/tone_mapping/README.md
new file mode 100644
index 00000000..8a883001
--- /dev/null
+++ b/modules/tone_mapping/README.md
@@ -0,0 +1,15 @@
+# Tone Mapping
+
+A VkCV module to use tone mapping on images in realtime after rendering
+
+## Build
+
+### Dependencies (required):
+
+| Name of dependency                                       | Used as submodule |
+|----------------------------------------------------------|-------------------|
+| [glsl-tone-map](https://github.com/dmnsgn/glsl-tone-map) | ✅                 |
+
+## Docs
+
+Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__tone.html) to this module.
diff --git a/modules/tone_mapping/src/vkcv/tone/ToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/ToneMapping.cpp
index 3323d879..7150d172 100644
--- a/modules/tone_mapping/src/vkcv/tone/ToneMapping.cpp
+++ b/modules/tone_mapping/src/vkcv/tone/ToneMapping.cpp
@@ -65,7 +65,7 @@ namespace vkcv::tone {
 		
 		compiler.compileSource(
 				ShaderStage::COMPUTE,
-				stream.str().c_str(),
+				stream.str(),
 				[&](ShaderStage stage, const std::filesystem::path &path) {
 					program.addShader(stage, path);
 				}
diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
index bacb4ce0..dd92b42a 100644
--- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
@@ -13,7 +13,6 @@
 #include "FSR_Pass.glsl.hxx"
 
 #include <vkcv/File.hpp>
-#include <vkcv/Logger.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
 
 namespace vkcv::upscaling {
@@ -107,52 +106,6 @@ namespace vkcv::upscaling {
 	    return descriptorBindings;
 	}
 	
-	static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) {
-		std::ofstream file (shaderPath.string(), std::ios::out);
-		
-		if (!file.is_open()) {
-			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str());
-			return false;
-		}
-		
-		file.seekp(0);
-		file.write(code.c_str(), static_cast<std::streamsize>(code.length()));
-		file.close();
-		
-		return true;
-	}
-	
-	static bool compileFSRShader(vkcv::shader::GLSLCompiler& compiler,
-								 const shader::ShaderCompiledFunction& compiled) {
-		std::filesystem::path directory = generateTemporaryDirectoryPath();
-		
-		if (!std::filesystem::create_directory(directory)) {
-			vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str());
-			return false;
-		}
-		
-		if (!writeShaderCode(directory / "ffx_a.h", FFX_A_H_SHADER)) {
-			return false;
-		}
-		
-		if (!writeShaderCode(directory / "ffx_fsr1.h", FFX_FSR1_H_SHADER)) {
-			return false;
-		}
-		
-		return compiler.compileSource(
-				vkcv::ShaderStage::COMPUTE,
-				FSR_PASS_GLSL_SHADER.c_str(),
-				[&directory, &compiled] (vkcv::ShaderStage shaderStage,
-										 const std::filesystem::path& path) {
-				if (compiled) {
-					compiled(shaderStage, path);
-				}
-				
-				std::filesystem::remove_all(directory);
-			}, directory
-		);
-	}
-	
 	FSRUpscaling::FSRUpscaling(Core& core) :
 	Upscaling(core),
 	m_easuPipeline(),
@@ -216,12 +169,20 @@ namespace vkcv::upscaling {
 		
 		{
 			ShaderProgram program;
-			compileFSRShader(easuCompiler, [&program](vkcv::ShaderStage shaderStage,
-				const std::filesystem::path& path) {
-				program.addShader(shaderStage, path);
-			});
+			easuCompiler.compileSourceWithHeaders(
+					ShaderStage::COMPUTE,
+					FSR_PASS_GLSL_SHADER,
+					{
+						{ "ffx_a.h", FFX_A_H_SHADER },
+						{ "ffx_fsr1.h", FFX_FSR1_H_SHADER }
+					},
+					[&program](vkcv::ShaderStage shaderStage,
+							   const std::filesystem::path& path) {
+						program.addShader(shaderStage, path);
+					}
+			);
 
-			m_easuPipeline = m_core.createComputePipeline({program,{
+			m_easuPipeline = m_core.createComputePipeline({ program, {
 				m_easuDescriptorSetLayout
 			}});
 
@@ -238,10 +199,18 @@ namespace vkcv::upscaling {
 		
 		{
 			ShaderProgram program;
-			compileFSRShader(rcasCompiler, [&program](vkcv::ShaderStage shaderStage,
-					const std::filesystem::path& path) {
-				program.addShader(shaderStage, path);
-			});
+			rcasCompiler.compileSourceWithHeaders(
+					ShaderStage::COMPUTE,
+					FSR_PASS_GLSL_SHADER,
+					{
+							{ "ffx_a.h", FFX_A_H_SHADER },
+							{ "ffx_fsr1.h", FFX_FSR1_H_SHADER }
+					},
+					[&program](vkcv::ShaderStage shaderStage,
+							   const std::filesystem::path& path) {
+						program.addShader(shaderStage, path);
+					}
+			);
 
 			m_rcasPipeline = m_core.createComputePipeline({ program, {
 				m_rcasDescriptorSetLayout
diff --git a/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
index 8c3a973e..40eb63ff 100644
--- a/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
@@ -89,48 +89,6 @@ namespace vkcv::upscaling {
 		return image.getHandle();
 	}
 	
-	static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) {
-		std::ofstream file (shaderPath.string(), std::ios::out);
-		
-		if (!file.is_open()) {
-			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str());
-			return false;
-		}
-		
-		file.seekp(0);
-		file.write(code.c_str(), static_cast<std::streamsize>(code.length()));
-		file.close();
-		
-		return true;
-	}
-	
-	static bool compileNISShader(vkcv::shader::GLSLCompiler& compiler,
-								 const shader::ShaderCompiledFunction& compiled) {
-		std::filesystem::path directory = generateTemporaryDirectoryPath();
-		
-		if (!std::filesystem::create_directory(directory)) {
-			vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str());
-			return false;
-		}
-		
-		if (!writeShaderCode(directory / "NIS_Scaler.h", NIS_SCALER_H_SHADER)) {
-			return false;
-		}
-		
-		return compiler.compileSource(
-				vkcv::ShaderStage::COMPUTE,
-				NIS_MAIN_GLSL_SHADER.c_str(),
-				[&directory, &compiled] (vkcv::ShaderStage shaderStage,
-										 const std::filesystem::path& path) {
-				if (compiled) {
-					compiled(shaderStage, path);
-				}
-				
-				std::filesystem::remove_all(directory);
-			}, directory
-		);
-	}
-	
 	NISUpscaling::NISUpscaling(Core &core) :
 	Upscaling(core),
 	m_scalerPipeline(),
@@ -178,16 +136,22 @@ namespace vkcv::upscaling {
 		
 		{
 			ShaderProgram program;
-			compileNISShader(scalerCompiler, [&program](vkcv::ShaderStage shaderStage,
-														const std::filesystem::path& path) {
-				program.addShader(shaderStage, path);
-			});
+			scalerCompiler.compileSourceWithHeaders(
+					ShaderStage::COMPUTE,
+					NIS_MAIN_GLSL_SHADER,
+					{
+						{ "NIS_Scaler.h", NIS_SCALER_H_SHADER }
+					},
+					[&program](vkcv::ShaderStage shaderStage,
+							   const std::filesystem::path& path) {
+						program.addShader(shaderStage, path);
+					}
+			);
 			
 			m_scalerPipeline = m_core.createComputePipeline({program,{
 					m_scalerDescriptorSetLayout
 			}});
 			
-			
 			DescriptorWrites writes;
 			writes.writeUniformBuffer(
 					0, m_scalerConstants.getHandle(), true
diff --git a/src/vkcv/File.cpp b/src/vkcv/File.cpp
index f840fdb0..32ded3e5 100644
--- a/src/vkcv/File.cpp
+++ b/src/vkcv/File.cpp
@@ -9,6 +9,8 @@
 #include <unistd.h>
 #endif
 
+#include <fstream>
+
 #include "vkcv/Logger.hpp"
 
 namespace vkcv {
@@ -54,5 +56,124 @@ namespace vkcv {
 
 		return tmp / name;
 	}
+	
+	bool writeContentToFile(const std::filesystem::path &path,
+							const std::vector<char>& content) {
+		std::ofstream file (path.string(), std::ios::out);
+		
+		if (!file.is_open()) {
+			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", path.c_str());
+			return false;
+		}
+		
+		file.seekp(0);
+		file.write(content.data(), static_cast<std::streamsize>(content.size()));
+		file.close();
+		
+		return true;
+	}
+	
+	bool writeBinaryToFile(const std::filesystem::path &path,
+						   const std::vector<uint32_t>& binary) {
+		std::ofstream file (path.string(), std::ios::out);
+		
+		if (!file.is_open()) {
+			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", path.c_str());
+			return false;
+		}
+		
+		file.seekp(0);
+		file.write(
+				reinterpret_cast<const char*>(binary.data()),
+				static_cast<std::streamsize>(binary.size() * sizeof(uint32_t))
+		);
+		file.close();
+		
+		return true;
+	}
+	
+	bool writeTextToFile(const std::filesystem::path &path,
+						 const std::string& text) {
+		std::ofstream file (path.string(), std::ios::out);
+		
+		if (!file.is_open()) {
+			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", path.c_str());
+			return false;
+		}
+		
+		file.seekp(0);
+		file.write(text.c_str(), static_cast<std::streamsize>(text.length()));
+		file.close();
+		
+		return true;
+	}
+	
+	bool readContentFromFile(const std::filesystem::path &path,
+							 std::vector<char>& content) {
+		std::ifstream file (path.string(), std::ios::ate);
+		
+		if (!file.is_open()) {
+			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", path.c_str());
+			return false;
+		}
+		
+		const std::streamsize fileSize = file.tellg();
+		content.resize(fileSize);
+		
+		file.seekg(0);
+		file.read(content.data(), fileSize);
+		file.close();
+		
+		return true;
+	}
+	
+	bool readBinaryFromFile(const std::filesystem::path &path,
+							std::vector<uint32_t>& binary) {
+		std::ifstream file (path.string(), std::ios::ate);
+		
+		if (!file.is_open()) {
+			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", path.c_str());
+			return false;
+		}
+		
+		const std::streamsize fileSize = file.tellg();
+		
+		if (fileSize % sizeof(uint32_t) != 0) {
+			vkcv_log(LogLevel::ERROR, "The file is not a valid binary: %s", path.c_str());
+			return false;
+		}
+		
+		binary.resize(fileSize / sizeof(uint32_t));
+		
+		file.seekg(0);
+		file.read(
+				reinterpret_cast<char*>(binary.data()),
+				static_cast<std::streamsize>(binary.size() * sizeof(uint32_t))
+		);
+		file.close();
+		
+		return true;
+	}
+	
+	bool readTextFromFile(const std::filesystem::path &path,
+						  std::string& text) {
+		std::ifstream file (path.string(), std::ios::ate);
+		
+		if (!file.is_open()) {
+			vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", path.c_str());
+			return false;
+		}
+		
+		const std::streamsize fileSize = file.tellg();
+		std::vector<char> buffer (fileSize);
+		buffer.resize(fileSize);
+		
+		file.seekg(0);
+		file.read(buffer.data(), fileSize);
+		file.close();
+		
+		text = std::string(buffer.data(), buffer.size());
+		return true;
+	}
 
 } // namespace vkcv
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 508f4de5..099af639 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -5,38 +5,11 @@
  */
 
 #include "vkcv/ShaderProgram.hpp"
+
+#include "vkcv/File.hpp"
 #include "vkcv/Logger.hpp"
 
 namespace vkcv {
-	/**
-	 * Reads the file of a given shader code.
-	 * Only used within the class.
-	 * @param[in] relative path to the shader code
-	 * @return vector of chars as a buffer for the code
-	 */
-	std::vector<uint32_t> readShaderCode(const std::filesystem::path &shaderPath) {
-		std::ifstream file(shaderPath.string(), std::ios::ate | std::ios::binary);
-
-		if (!file.is_open()) {
-			vkcv_log(LogLevel::ERROR, "The file could not be opened: %s", shaderPath.c_str());
-			return std::vector<uint32_t>();
-		}
-
-		size_t fileSize = (size_t)file.tellg();
-
-		if (fileSize % sizeof(uint32_t) != 0) {
-			vkcv_log(LogLevel::ERROR, "The file is not a valid shader: %s", shaderPath.c_str());
-			return std::vector<uint32_t>();
-		}
-
-		std::vector<uint32_t> buffer(fileSize / sizeof(uint32_t));
-
-		file.seekg(0);
-		file.read(reinterpret_cast<char*>(buffer.data()), fileSize);
-		file.close();
-
-		return buffer;
-	}
 
 	VertexAttachmentFormat convertFormat(spirv_cross::SPIRType::BaseType basetype,
 										 uint32_t vecsize) {
@@ -85,15 +58,14 @@ namespace vkcv {
 			vkcv_log(LogLevel::WARNING, "Overwriting existing shader stage");
 		}
 
-		const std::vector<uint32_t> shaderCode = readShaderCode(path);
-
-		if (shaderCode.empty()) {
+		std::vector<uint32_t> shaderCode;
+		if ((!readBinaryFromFile(path, shaderCode)) || (shaderCode.empty())) {
 			return false;
-		} else {
-			m_Shaders.insert(std::make_pair(stage, shaderCode));
-			reflectShader(stage);
-			return true;
 		}
+		
+		m_Shaders.insert(std::make_pair(stage, shaderCode));
+		reflectShader(stage);
+		return true;
 	}
 
 	const std::vector<uint32_t> &ShaderProgram::getShaderBinary(ShaderStage stage) const {
-- 
GitLab