From b0a7409035339e97ee924eb84547b15e7b0dd169 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Sat, 12 Jun 2021 23:09:57 +0200
Subject: [PATCH] [#73] Added module to compile shaders during runtime

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 .gitmodules                                   |   5 +-
 include/vkcv/ShaderProgram.hpp                |   2 +-
 modules/CMakeLists.txt                        |   1 +
 modules/shader_compiler/CMakeLists.txt        |  34 +++
 modules/shader_compiler/config/GLSLANG.cmake  |  25 ++
 .../include/vkcv/shader/Compiler.hpp          |  17 ++
 .../include/vkcv/shader/GLSLCompiler.hpp      |  28 ++
 modules/shader_compiler/lib/glslang           |   1 +
 .../src/vkcv/shader/GLSLCompiler.cpp          | 261 ++++++++++++++++++
 projects/first_triangle/CMakeLists.txt        |   4 +-
 projects/first_triangle/src/main.cpp          |  26 +-
 src/vkcv/ShaderProgram.cpp                    |  12 +-
 12 files changed, 403 insertions(+), 13 deletions(-)
 create mode 100644 modules/shader_compiler/CMakeLists.txt
 create mode 100644 modules/shader_compiler/config/GLSLANG.cmake
 create mode 100644 modules/shader_compiler/include/vkcv/shader/Compiler.hpp
 create mode 100644 modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
 create mode 160000 modules/shader_compiler/lib/glslang
 create mode 100644 modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp

diff --git a/.gitmodules b/.gitmodules
index 983b7537..62938a4b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -15,4 +15,7 @@
 	url = https://github.com/nothings/stb.git
 [submodule "modules/camera/lib/glm"]
 	path = modules/camera/lib/glm
-	url = https://github.com/g-truc/glm.git
\ No newline at end of file
+	url = https://github.com/g-truc/glm.git
+[submodule "modules/shader_compiler/lib/glslang"]
+	path = modules/shader_compiler/lib/glslang
+	url = https://github.com/KhronosGroup/glslang.git
diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp
index ce28cccf..99de20d8 100644
--- a/include/vkcv/ShaderProgram.hpp
+++ b/include/vkcv/ShaderProgram.hpp
@@ -53,7 +53,7 @@ namespace vkcv {
         const VertexLayout &getVertexLayout() const;
 		size_t getPushConstantSize() const;
 
-        const std::vector<std::vector<DescriptorBinding>> getReflectedDescriptors() const;
+        const std::vector<std::vector<DescriptorBinding>>& getReflectedDescriptors() const;
 
 	private:
         std::unordered_map<ShaderStage, Shader> m_Shaders;
diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt
index f29ff2fc..e8efea49 100644
--- a/modules/CMakeLists.txt
+++ b/modules/CMakeLists.txt
@@ -2,4 +2,5 @@
 # Add new modules here:
 add_subdirectory(asset_loader)
 add_subdirectory(camera)
+add_subdirectory(shader_compiler)
 add_subdirectory(testing)
diff --git a/modules/shader_compiler/CMakeLists.txt b/modules/shader_compiler/CMakeLists.txt
new file mode 100644
index 00000000..4b674ec4
--- /dev/null
+++ b/modules/shader_compiler/CMakeLists.txt
@@ -0,0 +1,34 @@
+cmake_minimum_required(VERSION 3.16)
+project(vkcv_shader_compiler)
+
+# setting c++ standard for the module
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(vkcv_shader_compiler_source ${PROJECT_SOURCE_DIR}/src)
+set(vkcv_shader_compiler_include ${PROJECT_SOURCE_DIR}/include)
+
+# Add source and header files to the module
+set(vkcv_shader_compiler_sources
+		${vkcv_shader_compiler_include}/vkcv/shader/GLSLCompiler.hpp
+		${vkcv_shader_compiler_source}/vkcv/shader/GLSLCompiler.cpp
+)
+
+# adding source files to the module
+add_library(vkcv_shader_compiler STATIC ${vkcv_shader_compiler_sources})
+
+# Setup some path variables to load libraries
+set(vkcv_shader_compiler_lib lib)
+set(vkcv_shader_compiler_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_shader_compiler_lib})
+
+# Check and load GLSLANG
+include(config/GLSLANG.cmake)
+
+# link the required libraries to the module
+target_link_libraries(vkcv_shader_compiler ${vkcv_shader_compiler_libraries} vkcv)
+
+# including headers of dependencies and the VkCV framework
+target_include_directories(vkcv_shader_compiler SYSTEM BEFORE PRIVATE ${vkcv_shader_compiler_includes} ${vkcv_include})
+
+# add the own include directory for public headers
+target_include_directories(vkcv_shader_compiler BEFORE PUBLIC ${vkcv_shader_compiler_include})
diff --git a/modules/shader_compiler/config/GLSLANG.cmake b/modules/shader_compiler/config/GLSLANG.cmake
new file mode 100644
index 00000000..b592c61d
--- /dev/null
+++ b/modules/shader_compiler/config/GLSLANG.cmake
@@ -0,0 +1,25 @@
+
+if (EXISTS "${vkcv_shader_compiler_lib_path}/glslang")
+	set(SKIP_GLSLANG_INSTALL ON CACHE INTERNAL "")
+	set(ENABLE_SPVREMAPPER OFF CACHE INTERNAL "")
+	set(ENABLE_GLSLANG_BINARIES OFF CACHE INTERNAL "")
+	set(ENABLE_GLSLANG_JS OFF CACHE INTERNAL "")
+	set(ENABLE_GLSLANG_WEBMIN OFF CACHE INTERNAL "")
+	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_RTTI OFF CACHE INTERNAL "")
+	set(ENABLE_EXCEPTIONS OFF CACHE INTERNAL "")
+	set(ENABLE_OPT OFF CACHE INTERNAL "")
+	set(ENABLE_PCH OFF CACHE INTERNAL "")
+	set(ENABLE_CTEST OFF CACHE INTERNAL "")
+	set(USE_CCACHE OFF CACHE INTERNAL "")
+	
+	add_subdirectory(${vkcv_shader_compiler_lib}/glslang)
+	
+	list(APPEND vkcv_shader_compiler_libraries glslang SPIRV)
+	list(APPEND vkcv_shader_compiler_includes ${vkcv_shader_compiler_lib})
+else()
+	message(WARNING "GLSLANG is required..! Update the submodules!")
+endif ()
diff --git a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp
new file mode 100644
index 00000000..d7b7af71
--- /dev/null
+++ b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <vkcv/Event.hpp>
+
+namespace vkcv::shader {
+	
+	typedef typename event_function<ShaderStage, const std::filesystem::path&>::type ShaderCompiledFunction;
+	
+	class Compiler {
+	private:
+	public:
+		virtual void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath,
+							 const ShaderCompiledFunction& compiled, bool update = false) = 0;
+		
+	};
+	
+}
diff --git a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
new file mode 100644
index 00000000..7105d93a
--- /dev/null
+++ b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <filesystem>
+
+#include <vkcv/ShaderStage.hpp>
+#include "Compiler.hpp"
+
+namespace vkcv::shader {
+	
+	class GLSLCompiler {
+	private:
+	public:
+		GLSLCompiler();
+		
+		GLSLCompiler(const GLSLCompiler& other);
+		GLSLCompiler(GLSLCompiler&& other) = default;
+	
+		~GLSLCompiler();
+		
+		GLSLCompiler& operator=(const GLSLCompiler& other);
+		GLSLCompiler& operator=(GLSLCompiler&& other) = default;
+		
+		void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath,
+					 const ShaderCompiledFunction& compiled, bool update = false);
+		
+	};
+	
+}
diff --git a/modules/shader_compiler/lib/glslang b/modules/shader_compiler/lib/glslang
new file mode 160000
index 00000000..fe151586
--- /dev/null
+++ b/modules/shader_compiler/lib/glslang
@@ -0,0 +1 @@
+Subproject commit fe15158676657bf965e41c32e15ae5db7ea2ab6a
diff --git a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp
new file mode 100644
index 00000000..4fcddb31
--- /dev/null
+++ b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp
@@ -0,0 +1,261 @@
+
+#include "vkcv/shader/GLSLCompiler.hpp"
+
+#include <fstream>
+#include <glslang/SPIRV/GlslangToSpv.h>
+
+#include <vkcv/Logger.hpp>
+
+namespace vkcv::shader {
+	
+	static uint32_t s_CompilerCount = 0;
+	
+	GLSLCompiler::GLSLCompiler() {
+		if (s_CompilerCount == 0) {
+			glslang::InitializeProcess();
+		}
+		
+		s_CompilerCount++;
+	}
+	
+	GLSLCompiler::GLSLCompiler(const GLSLCompiler &other) {
+		s_CompilerCount++;
+	}
+	
+	GLSLCompiler::~GLSLCompiler() {
+		s_CompilerCount--;
+		
+		if (s_CompilerCount == 0) {
+			glslang::FinalizeProcess();
+		}
+	}
+	
+	GLSLCompiler &GLSLCompiler::operator=(const GLSLCompiler &other) {
+		s_CompilerCount++;
+		return *this;
+	}
+	
+	constexpr EShLanguage findShaderLanguage(ShaderStage shaderStage) {
+		switch (shaderStage) {
+			case ShaderStage::VERTEX:
+				return EShLangVertex;
+			case ShaderStage::TESS_CONTROL:
+				return EShLangTessControl;
+			case ShaderStage::TESS_EVAL:
+				return EShLangTessEvaluation;
+			case ShaderStage::GEOMETRY:
+				return EShLangGeometry;
+			case ShaderStage::FRAGMENT:
+				return EShLangFragment;
+			case ShaderStage::COMPUTE:
+				return EShLangCompute;
+			default:
+				return EShLangCount;
+		}
+	}
+	
+	static void initResources(TBuiltInResource& resources) {
+		resources.maxLights = 32;
+		resources.maxClipPlanes = 6;
+		resources.maxTextureUnits = 32;
+		resources.maxTextureCoords = 32;
+		resources.maxVertexAttribs = 64;
+		resources.maxVertexUniformComponents = 4096;
+		resources.maxVaryingFloats = 64;
+		resources.maxVertexTextureImageUnits = 32;
+		resources.maxCombinedTextureImageUnits = 80;
+		resources.maxTextureImageUnits = 32;
+		resources.maxFragmentUniformComponents = 4096;
+		resources.maxDrawBuffers = 32;
+		resources.maxVertexUniformVectors = 128;
+		resources.maxVaryingVectors = 8;
+		resources.maxFragmentUniformVectors = 16;
+		resources.maxVertexOutputVectors = 16;
+		resources.maxFragmentInputVectors = 15;
+		resources.minProgramTexelOffset = -8;
+		resources.maxProgramTexelOffset = 7;
+		resources.maxClipDistances = 8;
+		resources.maxComputeWorkGroupCountX = 65535;
+		resources.maxComputeWorkGroupCountY = 65535;
+		resources.maxComputeWorkGroupCountZ = 65535;
+		resources.maxComputeWorkGroupSizeX = 1024;
+		resources.maxComputeWorkGroupSizeY = 1024;
+		resources.maxComputeWorkGroupSizeZ = 64;
+		resources.maxComputeUniformComponents = 1024;
+		resources.maxComputeTextureImageUnits = 16;
+		resources.maxComputeImageUniforms = 8;
+		resources.maxComputeAtomicCounters = 8;
+		resources.maxComputeAtomicCounterBuffers = 1;
+		resources.maxVaryingComponents = 60;
+		resources.maxVertexOutputComponents = 64;
+		resources.maxGeometryInputComponents = 64;
+		resources.maxGeometryOutputComponents = 128;
+		resources.maxFragmentInputComponents = 128;
+		resources.maxImageUnits = 8;
+		resources.maxCombinedImageUnitsAndFragmentOutputs = 8;
+		resources.maxCombinedShaderOutputResources = 8;
+		resources.maxImageSamples = 0;
+		resources.maxVertexImageUniforms = 0;
+		resources.maxTessControlImageUniforms = 0;
+		resources.maxTessEvaluationImageUniforms = 0;
+		resources.maxGeometryImageUniforms = 0;
+		resources.maxFragmentImageUniforms = 8;
+		resources.maxCombinedImageUniforms = 8;
+		resources.maxGeometryTextureImageUnits = 16;
+		resources.maxGeometryOutputVertices = 256;
+		resources.maxGeometryTotalOutputComponents = 1024;
+		resources.maxGeometryUniformComponents = 1024;
+		resources.maxGeometryVaryingComponents = 64;
+		resources.maxTessControlInputComponents = 128;
+		resources.maxTessControlOutputComponents = 128;
+		resources.maxTessControlTextureImageUnits = 16;
+		resources.maxTessControlUniformComponents = 1024;
+		resources.maxTessControlTotalOutputComponents = 4096;
+		resources.maxTessEvaluationInputComponents = 128;
+		resources.maxTessEvaluationOutputComponents = 128;
+		resources.maxTessEvaluationTextureImageUnits = 16;
+		resources.maxTessEvaluationUniformComponents = 1024;
+		resources.maxTessPatchComponents = 120;
+		resources.maxPatchVertices = 32;
+		resources.maxTessGenLevel = 64;
+		resources.maxViewports = 16;
+		resources.maxVertexAtomicCounters = 0;
+		resources.maxTessControlAtomicCounters = 0;
+		resources.maxTessEvaluationAtomicCounters = 0;
+		resources.maxGeometryAtomicCounters = 0;
+		resources.maxFragmentAtomicCounters = 8;
+		resources.maxCombinedAtomicCounters = 8;
+		resources.maxAtomicCounterBindings = 1;
+		resources.maxVertexAtomicCounterBuffers = 0;
+		resources.maxTessControlAtomicCounterBuffers = 0;
+		resources.maxTessEvaluationAtomicCounterBuffers = 0;
+		resources.maxGeometryAtomicCounterBuffers = 0;
+		resources.maxFragmentAtomicCounterBuffers = 1;
+		resources.maxCombinedAtomicCounterBuffers = 1;
+		resources.maxAtomicCounterBufferSize = 16384;
+		resources.maxTransformFeedbackBuffers = 4;
+		resources.maxTransformFeedbackInterleavedComponents = 64;
+		resources.maxCullDistances = 8;
+		resources.maxCombinedClipAndCullDistances = 8;
+		resources.maxSamples = 4;
+		resources.maxMeshOutputVerticesNV = 256;
+		resources.maxMeshOutputPrimitivesNV = 512;
+		resources.maxMeshWorkGroupSizeX_NV = 32;
+		resources.maxMeshWorkGroupSizeY_NV = 1;
+		resources.maxMeshWorkGroupSizeZ_NV = 1;
+		resources.maxTaskWorkGroupSizeX_NV = 32;
+		resources.maxTaskWorkGroupSizeY_NV = 1;
+		resources.maxTaskWorkGroupSizeZ_NV = 1;
+		resources.maxMeshViewCountNV = 4;
+		resources.limits.nonInductiveForLoops = 1;
+		resources.limits.whileLoops = 1;
+		resources.limits.doWhileLoops = 1;
+		resources.limits.generalUniformIndexing = 1;
+		resources.limits.generalAttributeMatrixVectorIndexing = 1;
+		resources.limits.generalVaryingIndexing = 1;
+		resources.limits.generalSamplerIndexing = 1;
+		resources.limits.generalVariableIndexing = 1;
+		resources.limits.generalConstantMatrixVectorIndexing = 1;
+	}
+	
+	static std::vector<char> 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");
+			return std::vector<char>{};
+		}
+		
+		std::streamsize fileSize = file.tellg();
+		std::vector<char> buffer (fileSize);
+		
+		file.seekg(0);
+		file.read(buffer.data(), fileSize);
+		file.close();
+		
+		return buffer;
+	}
+	
+	static bool writeSpirvCode(const std::filesystem::path &shaderPath, const std::vector<uint32_t>& spirv) {
+		std::ofstream file (shaderPath.string(), std::ios::out);
+		
+		if (!file.is_open()) {
+			vkcv_log(LogLevel::ERROR, "The file could not be opened");
+			return false;
+		}
+		
+		std::streamsize fileSize = static_cast<std::streamsize>(spirv.size()) * sizeof(uint32_t);
+		
+		file.seekp(0);
+		file.write(reinterpret_cast<const char*>(spirv.data()), fileSize);
+		file.close();
+		
+		return true;
+	}
+	
+	void GLSLCompiler::compile(ShaderStage shaderStage, const std::filesystem::path &shaderPath,
+							   const ShaderCompiledFunction& compiled, bool update) {
+		const EShLanguage language = findShaderLanguage(shaderStage);
+		
+		if (language == EShLangCount) {
+			vkcv_log(LogLevel::ERROR, "Shader stage not supported");
+			return;
+		}
+		
+		const std::vector<char> code = readShaderCode(shaderPath);
+		
+		glslang::TShader shader (language);
+		glslang::TProgram program;
+		
+		const char *shaderStrings [1];
+		shaderStrings[0] = code.data();
+		
+		shader.setStrings(shaderStrings, 1);
+		
+		TBuiltInResource resources = {};
+		initResources(resources);
+		
+		const auto messages = (EShMessages) (
+				EShMsgSpvRules |
+				EShMsgVulkanRules
+		);
+		
+		if (!shader.parse(&resources, 100, false, messages)) {
+			vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n}",
+					 shader.getInfoLog(), shader.getInfoDebugLog());
+			return;
+		}
+		
+		program.addShader(&shader);
+		
+		if (!program.link(messages)) {
+			vkcv_log(LogLevel::ERROR, "Shader linking failed {\n%s\n%s\n}",
+					 shader.getInfoLog(), shader.getInfoDebugLog());
+			return;
+		}
+		
+		const glslang::TIntermediate* intermediate = program.getIntermediate(language);
+		
+		if (!intermediate) {
+			vkcv_log(LogLevel::ERROR, "No valid intermediate representation");
+			return;
+		}
+		
+		std::vector<uint32_t> spirv;
+		glslang::GlslangToSpv(*intermediate, spirv);
+		
+		const std::filesystem::path tmp_path (std::tmpnam(nullptr));
+		
+		if (!writeSpirvCode(tmp_path, spirv)) {
+			vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk");
+			return;
+		}
+		
+		if (compiled) {
+			compiled(shaderStage, tmp_path);
+		}
+		
+		std::filesystem::remove(tmp_path);
+	}
+	
+}
diff --git a/projects/first_triangle/CMakeLists.txt b/projects/first_triangle/CMakeLists.txt
index e7c8373e..7e606b23 100644
--- a/projects/first_triangle/CMakeLists.txt
+++ b/projects/first_triangle/CMakeLists.txt
@@ -22,7 +22,7 @@ if(MSVC)
 endif()
 
 # including headers of dependencies and the VkCV framework
-target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include})
+target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include})
 
 # linking with libraries from all dependencies and the VkCV framework
-target_link_libraries(first_triangle vkcv vkcv_testing vkcv_camera)
+target_link_libraries(first_triangle vkcv vkcv_testing vkcv_camera vkcv_shader_compiler)
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 2ede653f..1ad49010 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -4,6 +4,8 @@
 #include <vkcv/camera/CameraManager.hpp>
 #include <chrono>
 
+#include <vkcv/shader/GLSLCompiler.hpp>
+
 int main(int argc, const char** argv) {
 	const char* applicationName = "First Triangle";
 
@@ -91,12 +93,26 @@ int main(int argc, const char** argv) {
 		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
 		return EXIT_FAILURE;
 	}
-
+	
 	vkcv::ShaderProgram triangleShaderProgram{};
-	triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/vert.spv"));
-	triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/frag.spv"));
-	triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX);
-	triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT);
+	vkcv::shader::GLSLCompiler compiler;
+	
+	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/shader.vert"),
+					 [&triangleShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		 triangleShaderProgram.addShader(shaderStage, path);
+		 triangleShaderProgram.reflectShader(shaderStage);
+	});
+	
+	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/shader.frag"),
+					 [&triangleShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		triangleShaderProgram.addShader(shaderStage, path);
+		triangleShaderProgram.reflectShader(shaderStage);
+	});
+	
+	//triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/vert.spv"));
+	//triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/frag.spv"));
+	//triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX);
+	//triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT);
 
 	const vkcv::PipelineConfig trianglePipelineDefinition(
 		triangleShaderProgram,
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 7c54c301..74383476 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -14,17 +14,21 @@ namespace vkcv {
      * @param[in] relative path to the shader code
      * @return vector of chars as a buffer for the code
      */
-	std::vector<char> readShaderCode(const std::filesystem::path &shaderPath)
-	{
-		std::ifstream file(shaderPath.string(), std::ios::ate | std::ios::binary);
+	std::vector<char> 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");
 			return std::vector<char>{};
 		}
+		
 		size_t fileSize = (size_t)file.tellg();
 		std::vector<char> buffer(fileSize);
+		
 		file.seekg(0);
 		file.read(buffer.data(), fileSize);
+		file.close();
+		
         return buffer;
 	}
 
@@ -205,7 +209,7 @@ namespace vkcv {
         return m_VertexLayout;
 	}
 
-    const std::vector<std::vector<DescriptorBinding>> ShaderProgram::getReflectedDescriptors() const {
+    const std::vector<std::vector<DescriptorBinding>>& ShaderProgram::getReflectedDescriptors() const {
         return m_DescriptorSets;
     }
 	size_t ShaderProgram::getPushConstantSize() const {
-- 
GitLab