diff --git a/.gitmodules b/.gitmodules index 668c908712a7f5cf54c29d304aa9308f22b305a5..10aee941292cb1d425327427502af7f733cd0eaf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -70,3 +70,12 @@ path = modules/asset_loader/lib/wuffs-mirror-release-c url = https://github.com/google/wuffs-mirror-release-c.git branch = main +[submodule "modules/shader_compiler/lib/shady"] + path = modules/shader_compiler/lib/shady + url = https://github.com/TheJackiMonster/shady.git +[submodule "modules/shader_compiler/lib/SPIRV-Headers"] + path = modules/shader_compiler/lib/SPIRV-Headers + url = https://github.com/KhronosGroup/SPIRV-Headers.git +[submodule "modules/shader_compiler/lib/murmur3"] + path = modules/shader_compiler/lib/murmur3 + url = https://github.com/PeterScott/murmur3.git diff --git a/modules/shader_compiler/CMakeLists.txt b/modules/shader_compiler/CMakeLists.txt index bd85c7d8e235591273d205a7c7314181b831a401..960eb65cb17121087c2f5b32b2a6abeecd7ff2a5 100644 --- a/modules/shader_compiler/CMakeLists.txt +++ b/modules/shader_compiler/CMakeLists.txt @@ -21,6 +21,15 @@ set(vkcv_shader_compiler_sources ${vkcv_shader_compiler_include}/vkcv/shader/HLSLCompiler.hpp ${vkcv_shader_compiler_source}/vkcv/shader/HLSLCompiler.cpp + + ${vkcv_shader_compiler_include}/vkcv/shader/ShadyCompiler.hpp + ${vkcv_shader_compiler_source}/vkcv/shader/ShadyCompiler.cpp + + ${vkcv_shader_compiler_include}/vkcv/shader/SlimCompiler.hpp + ${vkcv_shader_compiler_source}/vkcv/shader/SlimCompiler.cpp + + ${vkcv_shader_compiler_include}/vkcv/shader/LLVMCompiler.hpp + ${vkcv_shader_compiler_source}/vkcv/shader/LLVMCompiler.cpp ) filter_headers(vkcv_shader_compiler_sources ${vkcv_shader_compiler_include} vkcv_shader_compiler_headers) @@ -33,9 +42,16 @@ set_target_properties(vkcv_shader_compiler PROPERTIES PUBLIC_HEADER "${vkcv_shad set(vkcv_shader_compiler_lib lib) set(vkcv_shader_compiler_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_shader_compiler_lib}) +# Load SPIRV-Headers +include(config/SPIRV-Headers.cmake) + # Check and load GLSLANG include(config/GLSLANG.cmake) +# Check and load Shady (with murmur3) +include(config/Murmur3.cmake) +include(config/Shady.cmake) + # link the required libraries to the module target_link_libraries(vkcv_shader_compiler ${vkcv_shader_compiler_libraries} vkcv) diff --git a/modules/shader_compiler/config/Murmur3.cmake b/modules/shader_compiler/config/Murmur3.cmake new file mode 100644 index 0000000000000000000000000000000000000000..d220b52cd015b38c6ef198b455de193c7759745d --- /dev/null +++ b/modules/shader_compiler/config/Murmur3.cmake @@ -0,0 +1,9 @@ + +use_git_submodule("${vkcv_shader_compiler_lib_path}/murmur3" murmur3_status) + +if (${murmur3_status}) + add_library(murmur3 STATIC ${vkcv_shader_compiler_lib}/murmur3/murmur3.c) + + target_include_directories(murmur3 INTERFACE ${vkcv_shader_compiler_lib}/murmur3) + set_target_properties(murmur3 PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif () diff --git a/modules/shader_compiler/config/SPIRV-Headers.cmake b/modules/shader_compiler/config/SPIRV-Headers.cmake new file mode 100644 index 0000000000000000000000000000000000000000..b5f47dbe91d3da73f585a55765514bcb3a7412c9 --- /dev/null +++ b/modules/shader_compiler/config/SPIRV-Headers.cmake @@ -0,0 +1,9 @@ + +use_git_submodule("${vkcv_shader_compiler_lib_path}/SPIRV-Headers" spriv_headers_status) + +if (${spriv_headers_status}) + add_subdirectory(${vkcv_shader_compiler_lib}/SPIRV-Headers) + + list(APPEND vkcv_shader_compiler_libraries SPIRV-Headers) + list(APPEND vkcv_shader_compiler_includes ${vkcv_shader_compiler_lib}/SPIRV-Headers/include) +endif () diff --git a/modules/shader_compiler/config/Shady.cmake b/modules/shader_compiler/config/Shady.cmake new file mode 100644 index 0000000000000000000000000000000000000000..88e1f7b32de08c233901111fa89b371e86d1f0de --- /dev/null +++ b/modules/shader_compiler/config/Shady.cmake @@ -0,0 +1,17 @@ + +use_git_submodule("${vkcv_shader_compiler_lib_path}/shady" shady_status) + +if (${shady_status}) + set(EXTERNAL_SPIRV_HEADERS ON CACHE INTERNAL "") + set(EXTERNAL_MURMUR3 ON CACHE INTERNAL "") + + add_subdirectory(${vkcv_shader_compiler_lib}/shady) + + if (vkcv_build_attribute EQUAL "SHARED") + list(APPEND vkcv_shader_compiler_libraries shady runtime) + else () + list(APPEND vkcv_shader_compiler_libraries common driver) + endif () + + list(APPEND vkcv_shader_compiler_includes ${vkcv_shader_compiler_lib}/shady/include) +endif () diff --git a/modules/shader_compiler/include/vkcv/shader/GlslangCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/GlslangCompiler.hpp index 837b9b1dd5aea34386ac4060068ff61b22823f91..161c796ec8cd7fbb1a74ad673c25fc07804b9c97 100644 --- a/modules/shader_compiler/include/vkcv/shader/GlslangCompiler.hpp +++ b/modules/shader_compiler/include/vkcv/shader/GlslangCompiler.hpp @@ -19,8 +19,6 @@ namespace vkcv::shader { public: /** * The constructor of a runtime Glslang shader compiler instance. - * - * @param[in] target Compile target (optional) */ GlslangCompiler(); diff --git a/modules/shader_compiler/include/vkcv/shader/LLVMCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/LLVMCompiler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2d075d7b7b1b62161f769d4b7eb296cd36589ea6 --- /dev/null +++ b/modules/shader_compiler/include/vkcv/shader/LLVMCompiler.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <filesystem> + +#include <vkcv/ShaderStage.hpp> +#include "ShadyCompiler.hpp" + +namespace vkcv::shader { + + /** + * @addtogroup vkcv_shader + * @{ + */ + + enum class LLVMCompileTarget { + UNKNOWN + }; + + /** + * A class to handle LLVM runtime shader compilation. + */ + class LLVMCompiler : public ShadyCompiler { + private: + LLVMCompileTarget m_target; + + public: + /** + * The constructor of a runtime LLVM shader compiler instance. + * + * @param[in] target Compile target (optional) + */ + explicit LLVMCompiler(LLVMCompileTarget target = LLVMCompileTarget::UNKNOWN); + + /** + * Compile a LLVM shader from source for a target stage with a custom shader + * include path and an event function called if the compilation completes. + * + * @param[in] shaderStage Shader pipeline stage + * @param[in] shaderSource Source of shader + * @param[in] compiled Shader compilation event + * @param[in] includePath Include path for shaders + * @return Result if the compilation succeeds + */ + bool compileSource(ShaderStage shaderStage, + const std::string& shaderSource, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath = "") override; + + }; + + /** @} */ + +} diff --git a/modules/shader_compiler/include/vkcv/shader/ShadyCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/ShadyCompiler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..65e732f021d80d60a653962c82b51e44b4ab887b --- /dev/null +++ b/modules/shader_compiler/include/vkcv/shader/ShadyCompiler.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include <filesystem> + +#include <vkcv/ShaderStage.hpp> +#include "Compiler.hpp" + +namespace vkcv::shader { + + /** + * @addtogroup vkcv_shader + * @{ + */ + + /** + * An abstract class to handle Shady runtime shader compilation. + */ + class ShadyCompiler : public Compiler { + public: + /** + * The constructor of a runtime Shady shader compiler instance. + */ + ShadyCompiler(); + + /** + * The copy-constructor of a runtime Shady shader compiler instance. + * + * @param[in] other Other instance of a Shady shader compiler instance + */ + ShadyCompiler(const ShadyCompiler& other) = default; + + /** + * The move-constructor of a runtime Shady shader compiler instance. + * + * @param[out] other Other instance of a Shady shader compiler instance + */ + ShadyCompiler(ShadyCompiler&& other) = default; + + /** + * The destructor of a runtime Shady shader compiler instance. + */ + ~ShadyCompiler() = default; + + /** + * The copy-operator of a runtime Shady shader compiler instance. + * + * @param[in] other Other instance of a Shady shader compiler instance + * @return Reference to this instance + */ + ShadyCompiler& operator=(const ShadyCompiler& other) = default; + + /** + * The copy-operator of a runtime Shady shader compiler instance. + * + * @param[out] other Other instance of a Shady shader compiler instance + * @return Reference to this instance + */ + ShadyCompiler& operator=(ShadyCompiler&& other) = default; + + /** + * Compile a shader from a specific file path for a target stage with + * a custom shader include path and an event function called if the + * compilation completes. + * + * @param[in] shaderStage Shader pipeline stage + * @param[in] shaderPath Filepath of shader + * @param[in] compiled Shader compilation event + * @param[in] includePath Include path for shaders + * @param[in] update Flag to update shaders during runtime + */ + void compile(ShaderStage shaderStage, + const std::filesystem::path& shaderPath, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath = "", + bool update = false) override; + + }; + + /** @} */ + +} diff --git a/modules/shader_compiler/include/vkcv/shader/SlimCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/SlimCompiler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..47d9596e327ff1db1a8d7c9638df27e109776e68 --- /dev/null +++ b/modules/shader_compiler/include/vkcv/shader/SlimCompiler.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <filesystem> + +#include <vkcv/ShaderStage.hpp> +#include "ShadyCompiler.hpp" + +namespace vkcv::shader { + + /** + * @addtogroup vkcv_shader + * @{ + */ + + enum class SlimCompileTarget { + UNKNOWN + }; + + /** + * A class to handle Slim runtime shader compilation. + */ + class SlimCompiler : public ShadyCompiler { + private: + SlimCompileTarget m_target; + + public: + /** + * The constructor of a runtime Slim shader compiler instance. + * + * @param[in] target Compile target (optional) + */ + explicit SlimCompiler(SlimCompileTarget target = SlimCompileTarget::UNKNOWN); + + /** + * Compile a Slim shader from source for a target stage with a custom shader + * include path and an event function called if the compilation completes. + * + * @param[in] shaderStage Shader pipeline stage + * @param[in] shaderSource Source of shader + * @param[in] compiled Shader compilation event + * @param[in] includePath Include path for shaders + * @return Result if the compilation succeeds + */ + bool compileSource(ShaderStage shaderStage, + const std::string& shaderSource, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath = "") override; + + }; + + /** @} */ + +} diff --git a/modules/shader_compiler/lib/SPIRV-Headers b/modules/shader_compiler/lib/SPIRV-Headers new file mode 160000 index 0000000000000000000000000000000000000000..bdd1b2ab1f03e616047bbcf8971157dccd50c792 --- /dev/null +++ b/modules/shader_compiler/lib/SPIRV-Headers @@ -0,0 +1 @@ +Subproject commit bdd1b2ab1f03e616047bbcf8971157dccd50c792 diff --git a/modules/shader_compiler/lib/murmur3 b/modules/shader_compiler/lib/murmur3 new file mode 160000 index 0000000000000000000000000000000000000000..dae94be0c0f54a399d23ea6cbe54bca5a4e93ce4 --- /dev/null +++ b/modules/shader_compiler/lib/murmur3 @@ -0,0 +1 @@ +Subproject commit dae94be0c0f54a399d23ea6cbe54bca5a4e93ce4 diff --git a/modules/shader_compiler/lib/shady b/modules/shader_compiler/lib/shady new file mode 160000 index 0000000000000000000000000000000000000000..0a92cf7ff4081d21db0724a8e66ab868b878df7d --- /dev/null +++ b/modules/shader_compiler/lib/shady @@ -0,0 +1 @@ +Subproject commit 0a92cf7ff4081d21db0724a8e66ab868b878df7d diff --git a/modules/shader_compiler/src/vkcv/shader/LLVMCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/LLVMCompiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c53904d402efd7117b6fc73356284624445c4f5c --- /dev/null +++ b/modules/shader_compiler/src/vkcv/shader/LLVMCompiler.cpp @@ -0,0 +1,118 @@ + +#include "vkcv/shader/LLVMCompiler.hpp" + +#include <vkcv/File.hpp> +#include <vkcv/Logger.hpp> + +extern "C" { + #include <shady/driver.h> +} + +namespace vkcv::shader { + + LLVMCompiler::LLVMCompiler(LLVMCompileTarget target) + : ShadyCompiler(), m_target(target) {} + + static bool shadyCompileModule(Module* module, + ShaderStage shaderStage, + const std::string& shaderSource, + const ShaderCompiledFunction &compiled, + const std::filesystem::path &includePath) { + ShadyErrorCodes codes = driver_load_source_file( + SrcLLVM, + shaderSource.length(), + shaderSource.c_str(), + module + ); + + switch (codes) { + case NoError: + break; + case MissingInputArg: + case MissingOutputArg: + case InputFileDoesNotExist: + case InputFileIOError: + case MissingDumpCfgArg: + case MissingDumpIrArg: + case IncorrectLogLevel: + case InvalidTarget: + case ClangInvocationFailed: + default: + vkcv_log(LogLevel::ERROR, "Unknown error while loading shader"); + return false; + } + + const std::filesystem::path tmp_path = generateTemporaryFilePath(); + + DriverConfig config = default_driver_config(); + + config.target = TgtSPV; + config.output_filename = tmp_path.c_str(); + + codes = driver_compile(&config, module); + destroy_driver_config(&config); + + switch (codes) { + case NoError: + break; + case MissingInputArg: + case MissingOutputArg: + case InputFileDoesNotExist: + case InputFileIOError: + case MissingDumpCfgArg: + case MissingDumpIrArg: + case IncorrectLogLevel: + case InvalidTarget: + case ClangInvocationFailed: + default: + vkcv_log(LogLevel::ERROR, "Unknown error while compiling shader"); + return false; + } + + if (compiled) { + compiled(shaderStage, tmp_path); + } + + std::filesystem::remove(tmp_path); + return true; + } + + static bool shadyCompileArena(IrArena* arena, + ShaderStage shaderStage, + const std::string& shaderSource, + const ShaderCompiledFunction &compiled, + const std::filesystem::path &includePath) { + Module* module = new_module(arena, "slim_module"); + + if (nullptr == module) { + vkcv_log(LogLevel::ERROR, "Module could not be created"); + return false; + } + + return shadyCompileModule(module, shaderStage, shaderSource, compiled, includePath); + } + + bool LLVMCompiler::compileSource(ShaderStage shaderStage, + const std::string& shaderSource, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath) { + if (ShaderStage::COMPUTE != shaderStage) { + vkcv_log(LogLevel::ERROR, "Shader stage not supported"); + return false; + } + + ArenaConfig config = default_arena_config(); + IrArena* arena = new_ir_arena(config); + + if (nullptr == arena) { + vkcv_log(LogLevel::ERROR, "IR Arena could not be created"); + return false; + } + + bool result = shadyCompileArena(arena, shaderStage, shaderSource, compiled, includePath); + + destroy_ir_arena(arena); + return result; + } + +} diff --git a/modules/shader_compiler/src/vkcv/shader/ShadyCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/ShadyCompiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6168fcfa2baa04b8a7f261db8828bfab8c2ed018 --- /dev/null +++ b/modules/shader_compiler/src/vkcv/shader/ShadyCompiler.cpp @@ -0,0 +1,39 @@ + +#include "vkcv/shader/ShadyCompiler.hpp" + +#include <vkcv/File.hpp> +#include <vkcv/Logger.hpp> + +namespace vkcv::shader { + + ShadyCompiler::ShadyCompiler() + : Compiler() {} + + void ShadyCompiler::compile(ShaderStage shaderStage, + const std::filesystem::path &shaderPath, + const ShaderCompiledFunction &compiled, + const std::filesystem::path &includePath, + bool update) { + 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, shaderCode, compiled, includePath); + } else { + result = compileSource(shaderStage, shaderCode, compiled, shaderPath.parent_path()); + } + + if (!result) { + vkcv_log(LogLevel::ERROR, "Shader compilation failed: (%s)", shaderPath.string().c_str()); + } + + if (update) { + // TODO: Shader hot compilation during runtime + } + } + +} diff --git a/modules/shader_compiler/src/vkcv/shader/SlimCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/SlimCompiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..475d867b883d82a5c811e7ea1081b8fc45dfa0c6 --- /dev/null +++ b/modules/shader_compiler/src/vkcv/shader/SlimCompiler.cpp @@ -0,0 +1,118 @@ + +#include "vkcv/shader/SlimCompiler.hpp" + +#include <vkcv/File.hpp> +#include <vkcv/Logger.hpp> + +extern "C" { + #include <shady/driver.h> +} + +namespace vkcv::shader { + + SlimCompiler::SlimCompiler(SlimCompileTarget target) + : ShadyCompiler(), m_target(target) {} + + static bool shadyCompileModule(Module* module, + ShaderStage shaderStage, + const std::string& shaderSource, + const ShaderCompiledFunction &compiled, + const std::filesystem::path &includePath) { + ShadyErrorCodes codes = driver_load_source_file( + SrcSlim, + shaderSource.length(), + shaderSource.c_str(), + module + ); + + switch (codes) { + case NoError: + break; + case MissingInputArg: + case MissingOutputArg: + case InputFileDoesNotExist: + case InputFileIOError: + case MissingDumpCfgArg: + case MissingDumpIrArg: + case IncorrectLogLevel: + case InvalidTarget: + case ClangInvocationFailed: + default: + vkcv_log(LogLevel::ERROR, "Unknown error while loading shader"); + return false; + } + + const std::filesystem::path tmp_path = generateTemporaryFilePath(); + + DriverConfig config = default_driver_config(); + + config.target = TgtSPV; + config.output_filename = tmp_path.c_str(); + + codes = driver_compile(&config, module); + destroy_driver_config(&config); + + switch (codes) { + case NoError: + break; + case MissingInputArg: + case MissingOutputArg: + case InputFileDoesNotExist: + case InputFileIOError: + case MissingDumpCfgArg: + case MissingDumpIrArg: + case IncorrectLogLevel: + case InvalidTarget: + case ClangInvocationFailed: + default: + vkcv_log(LogLevel::ERROR, "Unknown error while compiling shader"); + return false; + } + + if (compiled) { + compiled(shaderStage, tmp_path); + } + + std::filesystem::remove(tmp_path); + return true; + } + + static bool shadyCompileArena(IrArena* arena, + ShaderStage shaderStage, + const std::string& shaderSource, + const ShaderCompiledFunction &compiled, + const std::filesystem::path &includePath) { + Module* module = new_module(arena, "slim_module"); + + if (nullptr == module) { + vkcv_log(LogLevel::ERROR, "Module could not be created"); + return false; + } + + return shadyCompileModule(module, shaderStage, shaderSource, compiled, includePath); + } + + bool SlimCompiler::compileSource(ShaderStage shaderStage, + const std::string& shaderSource, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath) { + if (ShaderStage::COMPUTE != shaderStage) { + vkcv_log(LogLevel::ERROR, "Shader stage not supported"); + return false; + } + + ArenaConfig config = default_arena_config(); + IrArena* arena = new_ir_arena(config); + + if (nullptr == arena) { + vkcv_log(LogLevel::ERROR, "IR Arena could not be created"); + return false; + } + + bool result = shadyCompileArena(arena, shaderStage, shaderSource, compiled, includePath); + + destroy_ir_arena(arena); + return result; + } + +} diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index aae046da7f84bcf26017f5fe609732aa2e0e4df7..995c6aa34a359aa83e0f838cdb8e3226a3e80cfd 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -2,6 +2,7 @@ #include <vkcv/Core.hpp> #include <vkcv/Pass.hpp> #include <GLFW/glfw3.h> +#include <vkcv/ShaderProgram.hpp> #include <vkcv/camera/CameraManager.hpp> #include <vkcv/shader/GLSLCompiler.hpp> @@ -38,7 +39,7 @@ int main(int argc, const char** argv) { vkcv::shader::GLSLCompiler compiler; compiler.compileProgram(triangleShaderProgram, { - {vkcv::ShaderStage::VERTEX, "shaders/shader.vert"}, + { vkcv::ShaderStage::VERTEX, "shaders/shader.vert" }, { vkcv::ShaderStage::FRAGMENT, "shaders/shader.frag" } }, nullptr);