diff --git a/.idea/.name b/.idea/.name index d8662b5..f081644 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -OpenVulkano +openVulkanoCpp \ No newline at end of file diff --git a/cmake/SetupVulkan.cmake b/cmake/SetupVulkan.cmake index a880584..24ed595 100644 --- a/cmake/SetupVulkan.cmake +++ b/cmake/SetupVulkan.cmake @@ -5,6 +5,11 @@ function(SetupVulkan TARGET) else () find_package(Vulkan REQUIRED) target_link_libraries(${TARGET} PRIVATE Vulkan::Vulkan) + find_package(Vulkan OPTIONAL_COMPONENTS shaderc_combined) + if (Vulkan_shaderc_combined_FOUND) + target_link_libraries(${TARGET} PRIVATE Vulkan::shaderc_combined) + target_compile_definitions(${TARGET} PRIVATE HAS_SHADERC) + endif () endif () target_include_directories(${TARGET} PUBLIC ${Vulkan_INCLUDE_DIR}) @@ -17,4 +22,5 @@ function(SetupVulkan TARGET) find_package(XCB REQUIRED) target_link_libraries(${TARGET} PRIVATE ${XCB_LIBRARIES}) endif() -endfunction() \ No newline at end of file +endfunction() + diff --git a/openVulkanoCpp/Base/Utils.cpp b/openVulkanoCpp/Base/Utils.cpp index d3f8fd1..4987888 100644 --- a/openVulkanoCpp/Base/Utils.cpp +++ b/openVulkanoCpp/Base/Utils.cpp @@ -7,9 +7,9 @@ #include "Utils.hpp" #ifdef _MSC_VER -#include + #include #else -#include + #include #endif #include #include @@ -57,11 +57,11 @@ namespace OpenVulkano #ifdef _MSC_VER return (uint64_t)::GetThreadId(::GetCurrentThread()); #else - return (uint64_t)pthread_self(); + return (uint64_t) pthread_self(); #endif } - Array Utils::ReadFile(const std::string& filePath, bool emptyOnMissing) + Array Utils::ReadFile(const std::string& filePath, bool emptyOnMissing, bool nullTerminateString) { std::ifstream file(filePath, std::ios::ate | std::ios::binary); if (!file.is_open()) @@ -70,9 +70,10 @@ namespace OpenVulkano throw std::runtime_error("Failed to open file '" + filePath + "'!"); } const size_t fileSize = static_cast(file.tellg()); - Array data(fileSize); + Array data(fileSize + nullTerminateString); file.seekg(0); file.read(data.Data(), fileSize); + if (nullTerminateString) data[fileSize] = '\0'; file.close(); return data; } diff --git a/openVulkanoCpp/Base/Utils.hpp b/openVulkanoCpp/Base/Utils.hpp index a29ea03..391a322 100644 --- a/openVulkanoCpp/Base/Utils.hpp +++ b/openVulkanoCpp/Base/Utils.hpp @@ -159,7 +159,8 @@ namespace OpenVulkano return subs; } - static Array ReadFile(const std::string& filePath, bool emptyOnMissing = false); + static Array ReadFile(const std::string& filePath, bool emptyOnMissing = false, + bool nullTerminateString = false); template static int GetUniqueTypeId() diff --git a/openVulkanoCpp/Shader/ShaderCompiler.cpp b/openVulkanoCpp/Shader/ShaderCompiler.cpp new file mode 100644 index 0000000..e70a3f2 --- /dev/null +++ b/openVulkanoCpp/Shader/ShaderCompiler.cpp @@ -0,0 +1,131 @@ +#if defined(HAS_SHADERC) + +#include "ShaderCompiler.hpp" + +#include "Base/Logger.hpp" +#include + +#include +#include + +namespace OpenVulkano +{ + + class ShaderIncluder : public shaderc::CompileOptions::IncluderInterface + { + struct IncludeData + { + shaderc_include_result result = {}; + std::string m_fullPath; + Array m_content; + }; + + public: + ShaderIncluder(const std::string& path); + ~ShaderIncluder() override = default; + + shaderc_include_result* GetInclude(const char* requestedSource, shaderc_include_type type, + const char* requestingSource, uint64_t includeDepth) override; + void ReleaseInclude(shaderc_include_result* data) override; + + private: + std::string ResolveInclude(const std::string& requestedSource) const; + + private: + std::string m_includePath; + }; + + shaderc_shader_kind CheckStage(const std::string& extensionName) + { + static std::map stageMap = { + { "vert", shaderc_glsl_vertex_shader }, { "frag", shaderc_glsl_fragment_shader }, + { "comp", shaderc_glsl_compute_shader }, { "geom", shaderc_glsl_geometry_shader }, + { "tesc", shaderc_glsl_tess_control_shader }, { "tese", shaderc_glsl_tess_evaluation_shader }, + { "rgen", shaderc_glsl_raygen_shader }, { "rint", shaderc_glsl_intersection_shader }, + { "rahit", shaderc_glsl_anyhit_shader }, { "rchit", shaderc_glsl_closesthit_shader }, + { "rmiss", shaderc_glsl_miss_shader }, { "rcall", shaderc_glsl_callable_shader }, + { "task", shaderc_glsl_task_shader }, { "mesh", shaderc_glsl_mesh_shader } + }; + + auto it = stageMap.find(extensionName); + if (it != stageMap.end()) return it->second; + else throw std::runtime_error("Failed to find shader stage for extension '" + extensionName + "'!"); + } + + Array ShaderCompiler::CompileGLSLToSpirv(const std::string& absPath, bool hasIncludes, + const std::string& incPath, const std::string& entryPoint) + { + Array file = Utils::ReadFile(absPath, false, true); + + shaderc::Compiler shaderCompiler; + shaderc::CompileOptions options; + + if (hasIncludes && incPath.size() > 0) { options.SetIncluder(std::make_unique(incPath)); } + options.SetSourceLanguage(shaderc_source_language_glsl); + options.SetSuppressWarnings(); + + auto extensionSplit = Utils::SplitAtLastOccurrence(absPath, '.'); + + shaderc::PreprocessedSourceCompilationResult preResult = + shaderCompiler.PreprocessGlsl(file.Data(), CheckStage(extensionSplit.second), entryPoint.c_str(), options); + + #if defined(_DEBUG) + std::string test = static_cast(preResult.begin(), preResult.end()); + // printf("Preprocessed shader: %s\n\n\n", test.c_str()); // Works fine + // Logger::APP->info("Preprocessed shader: {0}", test); gives error + #endif + + if (preResult.GetCompilationStatus() != shaderc_compilation_status_success) + { + throw std::runtime_error("Failed preprocessing shader. Reason: " + preResult.GetErrorMessage()); + } + + shaderc::CompilationResult result = shaderCompiler.CompileGlslToSpv( + static_cast(preResult.begin()), CheckStage(extensionSplit.second), "", options); + + if (result.GetCompilationStatus() != shaderc_compilation_status_success) + { + throw std::runtime_error("Failed compiling shader. Reason: " + result.GetErrorMessage()); + } + + Array returnResult(result.cend() - result.cbegin()); + // Copy the data to the return array + std::copy(result.begin(), result.end(), returnResult.Data()); + return returnResult; + } + + ShaderIncluder::ShaderIncluder(const std::string& path) : m_includePath(path) {} + + shaderc_include_result* ShaderIncluder::GetInclude(const char* requestedSource, shaderc_include_type type, + const char* requestingSource, uint64_t includeDepth) + { + IncludeData* includeData = new IncludeData(); + includeData->m_fullPath = ResolveInclude(requestedSource); + + // Do not null terminate the string + includeData->m_content = Utils::ReadFile(includeData->m_fullPath, false, false); + + shaderc_include_result* result = &includeData->result; + result->content = includeData->m_content.Data(); + result->content_length = includeData->m_content.Size(); + result->source_name = includeData->m_fullPath.data(); + result->source_name_length = includeData->m_fullPath.size(); + result->user_data = includeData; + + return result; + } + + void ShaderIncluder::ReleaseInclude(shaderc_include_result* data) + { + delete static_cast(data->user_data); + } + + std::string ShaderIncluder::ResolveInclude(const std::string& requestedSource) const + { + // Check if the file exists in the include path + std::string path = m_includePath + requestedSource; + if (std::filesystem::exists(path)) return path; + else throw std::runtime_error("Failed to resolve include '" + requestedSource + "'!"); + } +} +#endif diff --git a/openVulkanoCpp/Shader/ShaderCompiler.hpp b/openVulkanoCpp/Shader/ShaderCompiler.hpp new file mode 100644 index 0000000..4f42997 --- /dev/null +++ b/openVulkanoCpp/Shader/ShaderCompiler.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "Base/Utils.hpp" +#include "Base/Wrapper.hpp" + +namespace OpenVulkano +{ + class ShaderCompiler + { + public: + /** + * @param absPath - absolute path of the shader that needs to be compiled + * @param incPath - include path that will be used to resolve includes + * @param entryPoint - the name of the void function in the shader + * @param shaderStage - type of the shader that needs to be compiled + */ + static Array CompileGLSLToSpirv(const std::string& absPath, bool hasIncludes, + const std::string& incPath = std::string(), const std::string& entryPoint = "main") +#if defined(HAS_SHADERC) + ; +#else + { throw std::runtime_error("Shader compiler is not available on this platform"); } +#endif + }; +} \ No newline at end of file