#include "ShaderCompiler.hpp" #include "Base/Logger.hpp" #include #include namespace OpenVulkano { #if defined(HAS_SHADERC) 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) { 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 }