diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 35d170a0d54..5049ac785f6 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -749,6 +749,7 @@ + @@ -1396,6 +1397,7 @@ + diff --git a/Source/Core/VideoBackends/D3D/D3DGfx.cpp b/Source/Core/VideoBackends/D3D/D3DGfx.cpp index a58293ef6bd..dc7ee37908e 100644 --- a/Source/Core/VideoBackends/D3D/D3DGfx.cpp +++ b/Source/Core/VideoBackends/D3D/D3DGfx.cpp @@ -71,9 +71,10 @@ Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth } std::unique_ptr -Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) +Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name) { - auto bytecode = DXShader::CompileShader(D3D::feature_level, stage, source); + auto bytecode = DXShader::CompileShader(D3D::feature_level, stage, source, shader_includer); if (!bytecode) return nullptr; diff --git a/Source/Core/VideoBackends/D3D/D3DGfx.h b/Source/Core/VideoBackends/D3D/D3DGfx.h index 96c5f316be0..1c65485c952 100644 --- a/Source/Core/VideoBackends/D3D/D3DGfx.h +++ b/Source/Core/VideoBackends/D3D/D3DGfx.h @@ -31,8 +31,10 @@ public: std::string_view name) override; std::unique_ptr CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; - std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, - std::string_view name) override; + std::unique_ptr + CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, + std::string_view name) override; std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) override; diff --git a/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp index 265ec06e6c1..cc89b492e68 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp @@ -68,9 +68,10 @@ Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth } std::unique_ptr -Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) +Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name) { - return DXShader::CreateFromSource(stage, source, name); + return DXShader::CreateFromSource(stage, source, shader_includer, name); } std::unique_ptr Gfx::CreateShaderFromBinary(ShaderStage stage, const void* data, diff --git a/Source/Core/VideoBackends/D3D12/D3D12Gfx.h b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h index ce574a36901..478fa7f5c0b 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Gfx.h +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h @@ -36,8 +36,10 @@ public: CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment, std::vector additional_color_attachments) override; - std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, - std::string_view name) override; + std::unique_ptr + CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, + std::string_view name) override; std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) override; diff --git a/Source/Core/VideoBackends/D3D12/DX12Shader.cpp b/Source/Core/VideoBackends/D3D12/DX12Shader.cpp index 61550d2a686..e437533510b 100644 --- a/Source/Core/VideoBackends/D3D12/DX12Shader.cpp +++ b/Source/Core/VideoBackends/D3D12/DX12Shader.cpp @@ -29,9 +29,10 @@ std::unique_ptr DXShader::CreateFromBytecode(ShaderStage stage, Binary } std::unique_ptr DXShader::CreateFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name) { - auto bytecode = CompileShader(g_dx_context->GetFeatureLevel(), stage, source); + auto bytecode = CompileShader(g_dx_context->GetFeatureLevel(), stage, source, shader_includer); if (!bytecode) return nullptr; diff --git a/Source/Core/VideoBackends/D3D12/DX12Shader.h b/Source/Core/VideoBackends/D3D12/DX12Shader.h index 8c4c9292b41..d5090e57405 100644 --- a/Source/Core/VideoBackends/D3D12/DX12Shader.h +++ b/Source/Core/VideoBackends/D3D12/DX12Shader.h @@ -23,6 +23,7 @@ public: static std::unique_ptr CreateFromBytecode(ShaderStage stage, BinaryData bytecode, std::string_view name); static std::unique_ptr CreateFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name); private: diff --git a/Source/Core/VideoBackends/D3DCommon/Shader.cpp b/Source/Core/VideoBackends/D3DCommon/Shader.cpp index 369af7c8a13..bf4af33b954 100644 --- a/Source/Core/VideoBackends/D3DCommon/Shader.cpp +++ b/Source/Core/VideoBackends/D3DCommon/Shader.cpp @@ -20,6 +20,7 @@ #include "Common/StringUtil.h" #include "Common/Version.h" +#include "VideoCommon/ShaderCompileUtils.h" #include "VideoCommon/Spirv.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" @@ -35,6 +36,9 @@ namespace constexpr std::string_view SHADER_HEADER = R"( // Target GLSL 4.5. #version 450 core + + #extension GL_ARB_shading_language_include : enable + #define ATTRIBUTE_LOCATION(x) layout(location = x) #define FRAGMENT_OUTPUT_LOCATION(x) layout(location = x) #define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y) layout(location = x, index = y) @@ -107,14 +111,16 @@ std::optional GetHLSLFromSPIRV(SPIRV::CodeVector spv, D3D_FEATURE_L return compiler.compile(); } -std::optional GetSpirv(ShaderStage stage, std::string_view source) +std::optional GetSpirv(ShaderStage stage, std::string_view source, + glslang::TShader::Includer* shader_includer) { switch (stage) { case ShaderStage::Vertex: { const auto full_source = fmt::format("{}{}", SHADER_HEADER, source); - return SPIRV::CompileVertexShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0); + return SPIRV::CompileVertexShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0, + shader_includer); } case ShaderStage::Geometry: @@ -126,13 +132,15 @@ std::optional GetSpirv(ShaderStage stage, std::string_view so case ShaderStage::Pixel: { const auto full_source = fmt::format("{}{}", SHADER_HEADER, source); - return SPIRV::CompileFragmentShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0); + return SPIRV::CompileFragmentShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0, + shader_includer); } case ShaderStage::Compute: { const auto full_source = fmt::format("{}{}", COMPUTE_SHADER_HEADER, source); - return SPIRV::CompileComputeShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0); + return SPIRV::CompileComputeShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0, + shader_includer); } }; @@ -140,13 +148,14 @@ std::optional GetSpirv(ShaderStage stage, std::string_view so } std::optional GetHLSL(D3D_FEATURE_LEVEL feature_level, ShaderStage stage, - std::string_view source) + std::string_view source, + VideoCommon::ShaderIncluder* shader_includer) { if (stage == ShaderStage::Geometry) { return std::string{source}; } - else if (const auto spirv = GetSpirv(stage, source)) + else if (const auto spirv = GetSpirv(stage, source, shader_includer)) { return GetHLSLFromSPIRV(std::move(*spirv), feature_level); } @@ -230,10 +239,11 @@ static const char* GetCompileTarget(D3D_FEATURE_LEVEL feature_level, ShaderStage } } -std::optional Shader::CompileShader(D3D_FEATURE_LEVEL feature_level, - ShaderStage stage, std::string_view source) +std::optional +Shader::CompileShader(D3D_FEATURE_LEVEL feature_level, ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer) { - const auto hlsl = GetHLSL(feature_level, stage, source); + const auto hlsl = GetHLSL(feature_level, stage, source, shader_includer); if (!hlsl) return std::nullopt; @@ -260,7 +270,7 @@ std::optional Shader::CompileShader(D3D_FEATURE_LEVEL featur file << "Dolphin Version: " + Common::GetScmRevStr() + "\n"; file << "Video Backend: " + g_video_backend->GetDisplayName(); - if (const auto spirv = GetSpirv(stage, source)) + if (const auto spirv = GetSpirv(stage, source, shader_includer)) { file << "\nOriginal Source: \n"; file << source << std::endl; diff --git a/Source/Core/VideoBackends/D3DCommon/Shader.h b/Source/Core/VideoBackends/D3DCommon/Shader.h index c450bf74cb8..0018534f1df 100644 --- a/Source/Core/VideoBackends/D3DCommon/Shader.h +++ b/Source/Core/VideoBackends/D3DCommon/Shader.h @@ -20,7 +20,8 @@ public: BinaryData GetBinary() const override; static std::optional CompileShader(D3D_FEATURE_LEVEL feature_level, ShaderStage stage, - std::string_view source); + std::string_view source, + VideoCommon::ShaderIncluder* shader_includer); static BinaryData CreateByteCode(const void* data, size_t length); diff --git a/Source/Core/VideoBackends/Metal/MTLGfx.h b/Source/Core/VideoBackends/Metal/MTLGfx.h index f1916766a6d..fc9fca0c68e 100644 --- a/Source/Core/VideoBackends/Metal/MTLGfx.h +++ b/Source/Core/VideoBackends/Metal/MTLGfx.h @@ -31,8 +31,10 @@ public: CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment, std::vector additional_color_attachments) override; - std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, - std::string_view name) override; + std::unique_ptr + CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, + std::string_view name) override; std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) override; diff --git a/Source/Core/VideoBackends/Metal/MTLGfx.mm b/Source/Core/VideoBackends/Metal/MTLGfx.mm index d4236bd5032..4b744fe5111 100644 --- a/Source/Core/VideoBackends/Metal/MTLGfx.mm +++ b/Source/Core/VideoBackends/Metal/MTLGfx.mm @@ -120,11 +120,12 @@ Metal::Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture // MARK: Pipeline Creation -std::unique_ptr Metal::Gfx::CreateShaderFromSource(ShaderStage stage, - std::string_view source, - std::string_view name) +std::unique_ptr +Metal::Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, + std::string_view name) { - std::optional msl = Util::TranslateShaderToMSL(stage, source); + std::optional msl = Util::TranslateShaderToMSL(stage, source, shader_includer); if (!msl.has_value()) { PanicAlertFmt("Failed to convert shader {} to MSL", name); diff --git a/Source/Core/VideoBackends/Metal/MTLUtil.h b/Source/Core/VideoBackends/Metal/MTLUtil.h index 812ce39e5a4..1752c82aa5c 100644 --- a/Source/Core/VideoBackends/Metal/MTLUtil.h +++ b/Source/Core/VideoBackends/Metal/MTLUtil.h @@ -54,7 +54,8 @@ static inline bool HasStencil(AbstractTextureFormat format) return format == AbstractTextureFormat::D24_S8 || format == AbstractTextureFormat::D32F_S8; } -std::optional TranslateShaderToMSL(ShaderStage stage, std::string_view source); +std::optional TranslateShaderToMSL(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer); } // namespace Util } // namespace Metal diff --git a/Source/Core/VideoBackends/Metal/MTLUtil.mm b/Source/Core/VideoBackends/Metal/MTLUtil.mm index 05e2115ab31..ad52653ee3d 100644 --- a/Source/Core/VideoBackends/Metal/MTLUtil.mm +++ b/Source/Core/VideoBackends/Metal/MTLUtil.mm @@ -13,6 +13,7 @@ #include "VideoCommon/Constants.h" #include "VideoCommon/DriverDetails.h" +#include "VideoCommon/ShaderCompileUtils.h" #include "VideoCommon/Spirv.h" Metal::DeviceFeatures Metal::g_features; @@ -490,8 +491,9 @@ MakeResourceBinding(spv::ExecutionModel stage, u32 set, u32 binding, // return resource; } -std::optional Metal::Util::TranslateShaderToMSL(ShaderStage stage, - std::string_view source) +std::optional +Metal::Util::TranslateShaderToMSL(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer) { std::string full_source; @@ -514,16 +516,19 @@ std::optional Metal::Util::TranslateShaderToMSL(ShaderStage stage, switch (stage) { case ShaderStage::Vertex: - code = SPIRV::CompileVertexShader(full_source, APIType::Metal, glslang::EShTargetSpv_1_5); + code = SPIRV::CompileVertexShader(full_source, APIType::Metal, glslang::EShTargetSpv_1_5, + shader_includer); break; case ShaderStage::Geometry: PanicAlertFmt("Tried to compile geometry shader for Metal, but Metal doesn't support them!"); break; case ShaderStage::Pixel: - code = SPIRV::CompileFragmentShader(full_source, APIType::Metal, glslang::EShTargetSpv_1_5); + code = SPIRV::CompileFragmentShader(full_source, APIType::Metal, glslang::EShTargetSpv_1_5, + shader_includer); break; case ShaderStage::Compute: - code = SPIRV::CompileComputeShader(full_source, APIType::Metal, glslang::EShTargetSpv_1_5); + code = SPIRV::CompileComputeShader(full_source, APIType::Metal, glslang::EShTargetSpv_1_5, + shader_includer); break; } if (!code.has_value()) diff --git a/Source/Core/VideoBackends/Null/NullGfx.cpp b/Source/Core/VideoBackends/Null/NullGfx.cpp index 61822d3c83b..09c605ee18e 100644 --- a/Source/Core/VideoBackends/Null/NullGfx.cpp +++ b/Source/Core/VideoBackends/Null/NullGfx.cpp @@ -53,6 +53,7 @@ public: std::unique_ptr NullGfx::CreateShaderFromSource(ShaderStage stage, [[maybe_unused]] std::string_view source, + [[maybe_unused]] VideoCommon::ShaderIncluder* shader_includer, [[maybe_unused]] std::string_view name) { return std::make_unique(stage); diff --git a/Source/Core/VideoBackends/Null/NullGfx.h b/Source/Core/VideoBackends/Null/NullGfx.h index 5e1a25fef74..d811a48cbc4 100644 --- a/Source/Core/VideoBackends/Null/NullGfx.h +++ b/Source/Core/VideoBackends/Null/NullGfx.h @@ -25,8 +25,10 @@ public: CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment, std::vector additional_color_attachments) override; - std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, - std::string_view name) override; + std::unique_ptr + CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, + std::string_view name) override; std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) override; diff --git a/Source/Core/VideoBackends/OGL/OGLGfx.cpp b/Source/Core/VideoBackends/OGL/OGLGfx.cpp index 015bb719940..064856804f4 100644 --- a/Source/Core/VideoBackends/OGL/OGLGfx.cpp +++ b/Source/Core/VideoBackends/OGL/OGLGfx.cpp @@ -231,9 +231,10 @@ OGLGfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* de } std::unique_ptr -OGLGfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) +OGLGfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name) { - return OGLShader::CreateFromSource(stage, source, name); + return OGLShader::CreateFromSource(stage, source, shader_includer, name); } std::unique_ptr diff --git a/Source/Core/VideoBackends/OGL/OGLGfx.h b/Source/Core/VideoBackends/OGL/OGLGfx.h index 79144880134..a76008fdc96 100644 --- a/Source/Core/VideoBackends/OGL/OGLGfx.h +++ b/Source/Core/VideoBackends/OGL/OGLGfx.h @@ -25,8 +25,10 @@ public: std::string_view name) override; std::unique_ptr CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; - std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, - std::string_view name) override; + std::unique_ptr + CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, + std::string_view name) override; std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) override; diff --git a/Source/Core/VideoBackends/OGL/OGLShader.cpp b/Source/Core/VideoBackends/OGL/OGLShader.cpp index 9c100a5e30e..3d6c4ed860f 100644 --- a/Source/Core/VideoBackends/OGL/OGLShader.cpp +++ b/Source/Core/VideoBackends/OGL/OGLShader.cpp @@ -3,12 +3,110 @@ #include "VideoBackends/OGL/OGLShader.h" +#include +#include + #include "VideoBackends/OGL/ProgramShaderCache.h" +#include "VideoCommon/ShaderCompileUtils.h" +#include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/VideoConfig.h" namespace OGL { +namespace +{ +std::string ResolveIncludeStatements(glslang::TShader::Includer* shader_includer, + std::string_view source, const char* includer_name = "", + std::size_t depth = 1) +{ + if (!shader_includer) + { + return std::string{source}; + } + + std::string source_str(source); + std::istringstream iss(source_str); + + ShaderCode output; + std::string line; + while (std::getline(iss, line)) + { + if (line.empty()) + { + output.Write("{}\n", line); + continue; + } + + std::string_view include_preprocessor = "#include"; + if (!line.starts_with(include_preprocessor)) + { + output.Write("{}\n", line); + continue; + } + + const std::string after_include = line.substr(include_preprocessor.size()); + + bool local_include = true; + std::string_view filename; + // First non-whitespace character after include + const std::string::const_iterator non_whitespace_iter = std::find_if_not( + after_include.begin(), after_include.end(), [](char ch) { return std::isspace(ch); }); + if (*non_whitespace_iter == '<') + { + const auto after_less = std::next(non_whitespace_iter); + if (after_less == after_include.end()) + { + // Found less-than at the end, malformed + output.Write("{}\n", line); + continue; + } + const auto end_iter = std::find(after_less, after_include.end(), '>'); + if (end_iter == after_include.end()) + { + // Include spans multiple lines or is malformed, just pass it along + output.Write("{}\n", line); + continue; + } + filename = std::string_view(after_less, end_iter); + local_include = false; + } + else if (*non_whitespace_iter == '"') + { + const auto after_quote = std::next(non_whitespace_iter); + if (after_quote == after_include.end()) + { + // Found quote at the end, malformed + output.Write("{}\n", line); + continue; + } + const auto end_iter = std::find(after_quote, after_include.end(), '"'); + if (end_iter == after_include.end()) + { + // Include spans multiple lines or is malformed, just pass it along + output.Write("{}\n", line); + continue; + } + filename = std::string_view(after_quote, end_iter); + } + + const std::string header_path = std::string{filename}; + auto include_result = + local_include ? shader_includer->includeLocal(header_path.c_str(), includer_name, depth) : + shader_includer->includeSystem(header_path.c_str(), includer_name, depth); + if (!include_result) + { + output.Write("{}\n", line); + continue; + } + + output.Write("{}", ResolveIncludeStatements(shader_includer, include_result->headerData, + header_path.c_str(), ++depth)); + } + + return output.GetBuffer(); +} +} // namespace static GLenum GetGLShaderTypeForStage(ShaderStage stage) { switch (stage) @@ -57,9 +155,12 @@ OGLShader::~OGLShader() } std::unique_ptr OGLShader::CreateFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name) { - std::string source_str(source); + // Note: while the source will all be available, any errors will not + // reference the correct paths + std::string source_str = ResolveIncludeStatements(shader_includer, source); std::string name_str(name); if (stage != ShaderStage::Compute) { diff --git a/Source/Core/VideoBackends/OGL/OGLShader.h b/Source/Core/VideoBackends/OGL/OGLShader.h index 06b9449ac63..2554c744b85 100644 --- a/Source/Core/VideoBackends/OGL/OGLShader.h +++ b/Source/Core/VideoBackends/OGL/OGLShader.h @@ -29,6 +29,7 @@ public: const std::string& GetSource() const { return m_source; } static std::unique_ptr CreateFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name); private: diff --git a/Source/Core/VideoBackends/Software/SWGfx.cpp b/Source/Core/VideoBackends/Software/SWGfx.cpp index c758d1819eb..fba9faf7e5e 100644 --- a/Source/Core/VideoBackends/Software/SWGfx.cpp +++ b/Source/Core/VideoBackends/Software/SWGfx.cpp @@ -78,6 +78,7 @@ public: std::unique_ptr SWGfx::CreateShaderFromSource(ShaderStage stage, [[maybe_unused]] std::string_view source, + [[maybe_unused]] VideoCommon::ShaderIncluder* shader_includer, [[maybe_unused]] std::string_view name) { return std::make_unique(stage); diff --git a/Source/Core/VideoBackends/Software/SWGfx.h b/Source/Core/VideoBackends/Software/SWGfx.h index 2ea36de5d8a..f901f2936d9 100644 --- a/Source/Core/VideoBackends/Software/SWGfx.h +++ b/Source/Core/VideoBackends/Software/SWGfx.h @@ -28,8 +28,10 @@ public: bool BindBackbuffer(const ClearColor& clear_color = {}) override; - std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, - std::string_view name) override; + std::unique_ptr + CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, + std::string_view name) override; std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) override; diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp index 86826f72801..13dbe0e0b74 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp +++ b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp @@ -8,6 +8,7 @@ #include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/DriverDetails.h" +#include "VideoCommon/ShaderCompileUtils.h" #include "VideoCommon/Spirv.h" namespace Vulkan::ShaderCompiler @@ -21,6 +22,9 @@ namespace Vulkan::ShaderCompiler static const char SHADER_HEADER[] = R"( // Target GLSL 4.5. #version 450 core + + #extension GL_ARB_shading_language_include : enable + #define ATTRIBUTE_LOCATION(x) layout(location = x) #define FRAGMENT_OUTPUT_LOCATION(x) layout(location = x) #define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y) layout(location = x, index = y) @@ -121,27 +125,31 @@ static glslang::EShTargetLanguageVersion GetLanguageVersion() return glslang::EShTargetSpv_1_0; } -std::optional CompileVertexShader(std::string_view source_code) +std::optional CompileVertexShader(std::string_view source_code, + VideoCommon::ShaderIncluder* shader_includer) { return SPIRV::CompileVertexShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan, - GetLanguageVersion()); + GetLanguageVersion(), shader_includer); } -std::optional CompileGeometryShader(std::string_view source_code) +std::optional CompileGeometryShader(std::string_view source_code, + VideoCommon::ShaderIncluder* shader_includer) { return SPIRV::CompileGeometryShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan, - GetLanguageVersion()); + GetLanguageVersion(), shader_includer); } -std::optional CompileFragmentShader(std::string_view source_code) +std::optional CompileFragmentShader(std::string_view source_code, + VideoCommon::ShaderIncluder* shader_includer) { return SPIRV::CompileFragmentShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan, - GetLanguageVersion()); + GetLanguageVersion(), shader_includer); } -std::optional CompileComputeShader(std::string_view source_code) +std::optional CompileComputeShader(std::string_view source_code, + VideoCommon::ShaderIncluder* shader_includer) { return SPIRV::CompileComputeShader(GetShaderCode(source_code, COMPUTE_SHADER_HEADER), - APIType::Vulkan, GetLanguageVersion()); + APIType::Vulkan, GetLanguageVersion(), shader_includer); } } // namespace Vulkan::ShaderCompiler diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.h b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.h index ff1ea64b979..eab0a04cb5d 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.h +++ b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.h @@ -10,6 +10,11 @@ #include "Common/CommonTypes.h" +namespace VideoCommon +{ +class ShaderIncluder; +} + namespace Vulkan::ShaderCompiler { // SPIR-V compiled code type @@ -17,14 +22,18 @@ using SPIRVCodeType = u32; using SPIRVCodeVector = std::vector; // Compile a vertex shader to SPIR-V. -std::optional CompileVertexShader(std::string_view source_code); +std::optional CompileVertexShader(std::string_view source_code, + VideoCommon::ShaderIncluder* shader_includer); // Compile a geometry shader to SPIR-V. -std::optional CompileGeometryShader(std::string_view source_code); +std::optional CompileGeometryShader(std::string_view source_code, + VideoCommon::ShaderIncluder* shader_includer); // Compile a fragment shader to SPIR-V. -std::optional CompileFragmentShader(std::string_view source_code); +std::optional CompileFragmentShader(std::string_view source_code, + VideoCommon::ShaderIncluder* shader_includer); // Compile a compute shader to SPIR-V. -std::optional CompileComputeShader(std::string_view source_code); +std::optional CompileComputeShader(std::string_view source_code, + VideoCommon::ShaderIncluder* shader_includer); } // namespace Vulkan::ShaderCompiler diff --git a/Source/Core/VideoBackends/Vulkan/VKGfx.cpp b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp index a3258a92747..c071d99357a 100644 --- a/Source/Core/VideoBackends/Vulkan/VKGfx.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp @@ -60,9 +60,10 @@ std::unique_ptr VKGfx::CreateStagingTexture(StagingTextu } std::unique_ptr -VKGfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) +VKGfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name) { - return VKShader::CreateFromSource(stage, source, name); + return VKShader::CreateFromSource(stage, source, shader_includer, name); } std::unique_ptr VKGfx::CreateShaderFromBinary(ShaderStage stage, const void* data, diff --git a/Source/Core/VideoBackends/Vulkan/VKGfx.h b/Source/Core/VideoBackends/Vulkan/VKGfx.h index 209ea929134..e044bf59ca7 100644 --- a/Source/Core/VideoBackends/Vulkan/VKGfx.h +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.h @@ -38,8 +38,10 @@ public: CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment, std::vector additional_color_attachments) override; - std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, - std::string_view name) override; + std::unique_ptr + CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, + std::string_view name) override; std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) override; diff --git a/Source/Core/VideoBackends/Vulkan/VKShader.cpp b/Source/Core/VideoBackends/Vulkan/VKShader.cpp index f0848f1c924..17e13fa7952 100644 --- a/Source/Core/VideoBackends/Vulkan/VKShader.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKShader.cpp @@ -108,22 +108,23 @@ CreateShaderObject(ShaderStage stage, ShaderCompiler::SPIRVCodeVector spv, std:: } std::unique_ptr VKShader::CreateFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name) { std::optional spv; switch (stage) { case ShaderStage::Vertex: - spv = ShaderCompiler::CompileVertexShader(source); + spv = ShaderCompiler::CompileVertexShader(source, shader_includer); break; case ShaderStage::Geometry: - spv = ShaderCompiler::CompileGeometryShader(source); + spv = ShaderCompiler::CompileGeometryShader(source, shader_includer); break; case ShaderStage::Pixel: - spv = ShaderCompiler::CompileFragmentShader(source); + spv = ShaderCompiler::CompileFragmentShader(source, shader_includer); break; case ShaderStage::Compute: - spv = ShaderCompiler::CompileComputeShader(source); + spv = ShaderCompiler::CompileComputeShader(source, shader_includer); break; default: break; diff --git a/Source/Core/VideoBackends/Vulkan/VKShader.h b/Source/Core/VideoBackends/Vulkan/VKShader.h index 8414983aaaf..9cf63f94950 100644 --- a/Source/Core/VideoBackends/Vulkan/VKShader.h +++ b/Source/Core/VideoBackends/Vulkan/VKShader.h @@ -27,6 +27,7 @@ public: BinaryData GetBinary() const override; static std::unique_ptr CreateFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer, std::string_view name); static std::unique_ptr CreateFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name); diff --git a/Source/Core/VideoCommon/AbstractGfx.h b/Source/Core/VideoCommon/AbstractGfx.h index d739c3f5faa..d9d14e1a120 100644 --- a/Source/Core/VideoCommon/AbstractGfx.h +++ b/Source/Core/VideoCommon/AbstractGfx.h @@ -37,7 +37,8 @@ struct SurfaceInfo namespace VideoCommon { class AsyncShaderCompiler; -} +class ShaderIncluder; +} // namespace VideoCommon using ClearColor = std::array; @@ -110,9 +111,10 @@ public: virtual void PresentBackbuffer() {} // Shader modules/objects. - virtual std::unique_ptr CreateShaderFromSource(ShaderStage stage, - std::string_view source, - std::string_view name = "") = 0; + virtual std::unique_ptr + CreateShaderFromSource(ShaderStage stage, std::string_view source, + VideoCommon::ShaderIncluder* shader_includer = nullptr, + std::string_view name = "") = 0; virtual std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name = "") = 0; diff --git a/Source/Core/VideoCommon/AbstractShader.h b/Source/Core/VideoCommon/AbstractShader.h index 3395c44a312..249cc852c16 100644 --- a/Source/Core/VideoCommon/AbstractShader.h +++ b/Source/Core/VideoCommon/AbstractShader.h @@ -9,6 +9,14 @@ #include "Common/CommonTypes.h" +// Note: not directly used in this file +// but done here to avoid specifying in +// every derived class that needs it +namespace VideoCommon +{ +class ShaderIncluder; +} + enum class ShaderStage { Vertex, diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index fb9cba4fa4b..73b9221c4b5 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -147,6 +147,8 @@ add_library(videocommon RenderState.h ShaderCache.cpp ShaderCache.h + ShaderCompileUtils.cpp + ShaderCompileUtils.h ShaderGenCommon.cpp ShaderGenCommon.h Spirv.cpp diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index f3f6d3968ed..9d2382c190c 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -412,7 +412,7 @@ bool FramebufferManager::CompileConversionPipelines() EFBReinterpretType convtype = static_cast(i); std::unique_ptr pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, - FramebufferShaderGen::GenerateFormatConversionShader(convtype, GetEFBSamples()), + FramebufferShaderGen::GenerateFormatConversionShader(convtype, GetEFBSamples()), nullptr, fmt::format("Framebuffer conversion pixel shader {}", convtype)); if (!pixel_shader) return false; @@ -644,7 +644,7 @@ bool FramebufferManager::CompileReadbackPipelines() { auto depth_resolve_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateResolveDepthPixelShader(GetEFBSamples()), - "Depth resolve pixel shader"); + nullptr, "Depth resolve pixel shader"); if (!depth_resolve_shader) return false; @@ -658,7 +658,7 @@ bool FramebufferManager::CompileReadbackPipelines() config.framebuffer_state.color_texture_format = GetEFBColorFormat(); auto color_resolve_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, - FramebufferShaderGen::GenerateResolveColorPixelShader(GetEFBSamples()), + FramebufferShaderGen::GenerateResolveColorPixelShader(GetEFBSamples()), nullptr, "Color resolve pixel shader"); if (!color_resolve_shader) return false; @@ -672,7 +672,7 @@ bool FramebufferManager::CompileReadbackPipelines() // EFB restore pipeline auto restore_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Pixel, FramebufferShaderGen::GenerateEFBRestorePixelShader(), + ShaderStage::Pixel, FramebufferShaderGen::GenerateEFBRestorePixelShader(), nullptr, "EFB restore pixel shader"); if (!restore_shader) return false; @@ -888,7 +888,7 @@ void FramebufferManager::ClearEFB(const MathUtil::Rectangle& rc, bool color bool FramebufferManager::CompileClearPipelines() { auto vertex_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Vertex, FramebufferShaderGen::GenerateClearVertexShader(), + ShaderStage::Vertex, FramebufferShaderGen::GenerateClearVertexShader(), nullptr, "Clear vertex shader"); if (!vertex_shader) return false; @@ -1063,7 +1063,7 @@ bool FramebufferManager::CompilePokePipelines() return false; auto poke_vertex_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Vertex, FramebufferShaderGen::GenerateEFBPokeVertexShader(), + ShaderStage::Vertex, FramebufferShaderGen::GenerateEFBPokeVertexShader(), nullptr, "EFB poke vertex shader"); if (!poke_vertex_shader) return false; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp index 56f8ec1b3b4..a0dca15d382 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp @@ -348,7 +348,7 @@ CustomShaderCache::CompilePixelShader(const PixelShaderUid& uid, { const ShaderCode source_code = GeneratePixelShaderCode(m_api_type, m_host_config, uid.GetUidData(), {}); - return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), + return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), nullptr, "Custom Pixel Shader"); } @@ -357,7 +357,7 @@ CustomShaderCache::CompilePixelShader(const UberShader::PixelShaderUid& uid, const CustomShaderInstance& custom_shaders) const { const ShaderCode source_code = GenPixelShader(m_api_type, m_host_config, uid.GetUidData()); - return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), + return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), nullptr, "Custom Uber Pixel Shader"); } diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index bca12bccddb..8cbd7ca4f04 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -113,11 +113,11 @@ bool OnScreenUI::RecompileImGuiPipeline() g_presenter->GetBackbufferFormat() == AbstractTextureFormat::RGBA16F; std::unique_ptr vertex_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Vertex, FramebufferShaderGen::GenerateImGuiVertexShader(), + ShaderStage::Vertex, FramebufferShaderGen::GenerateImGuiVertexShader(), nullptr, "ImGui vertex shader"); std::unique_ptr pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateImGuiPixelShader(linear_space_output), - "ImGui pixel shader"); + nullptr, "ImGui pixel shader"); if (!vertex_shader || !pixel_shader) { PanicAlertFmt("Failed to compile ImGui shaders"); @@ -130,7 +130,7 @@ bool OnScreenUI::RecompileImGuiPipeline() { geometry_shader = g_gfx->CreateShaderFromSource( ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 1), - "ImGui passthrough geometry shader"); + nullptr, "ImGui passthrough geometry shader"); if (!geometry_shader) { PanicAlertFmt("Failed to compile ImGui geometry shader"); diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index ada1513d1f9..e1cc6e0e60c 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -27,6 +27,7 @@ #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/Present.h" #include "VideoCommon/ShaderCache.h" +#include "VideoCommon/ShaderCompileUtils.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -97,6 +98,9 @@ void PostProcessingConfiguration::LoadShader(const std::string& shader) // might have set in the settings LoadOptionsConfiguration(); m_current_shader_code = code; + m_shader_includer = + std::make_unique(File::GetUserPath(D_SHADERS_IDX) + sub_dir, + File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir); } void PostProcessingConfiguration::LoadDefaultShader() @@ -809,14 +813,14 @@ bool PostProcessing::CompileVertexShader() std::ostringstream ss_default; ss_default << GetUniformBufferHeader(false); ss_default << GetVertexShaderBody(); - m_default_vertex_shader = g_gfx->CreateShaderFromSource(ShaderStage::Vertex, ss_default.str(), - "Default post-processing vertex shader"); + m_default_vertex_shader = g_gfx->CreateShaderFromSource( + ShaderStage::Vertex, ss_default.str(), nullptr, "Default post-processing vertex shader"); std::ostringstream ss; ss << GetUniformBufferHeader(true); ss << GetVertexShaderBody(); - m_vertex_shader = - g_gfx->CreateShaderFromSource(ShaderStage::Vertex, ss.str(), "Post-processing vertex shader"); + m_vertex_shader = g_gfx->CreateShaderFromSource(ShaderStage::Vertex, ss.str(), nullptr, + "Post-processing vertex shader"); if (!m_default_vertex_shader || !m_vertex_shader) { @@ -966,7 +970,7 @@ bool PostProcessing::CompilePixelShader() if (LoadShaderFromFile(s_default_pixel_shader_name, "", default_pixel_shader_code)) { m_default_pixel_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Pixel, GetHeader(false) + default_pixel_shader_code + GetFooter(), + ShaderStage::Pixel, GetHeader(false) + default_pixel_shader_code + GetFooter(), nullptr, "Default post-processing pixel shader"); // We continue even if all of this failed, it doesn't matter m_default_uniform_staging_buffer.resize(CalculateUniformsSize(false)); @@ -979,6 +983,7 @@ bool PostProcessing::CompilePixelShader() m_config.LoadShader(g_ActiveConfig.sPostProcessingShader); m_pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, GetHeader(true) + m_config.GetShaderCode() + GetFooter(), + m_config.GetShaderIncluder(), fmt::format("User post-processing pixel shader: {}", m_config.GetShader())); if (!m_pixel_shader) { @@ -987,7 +992,7 @@ bool PostProcessing::CompilePixelShader() // Use default shader. m_config.LoadDefaultShader(); m_pixel_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Pixel, GetHeader(true) + m_config.GetShaderCode() + GetFooter(), + ShaderStage::Pixel, GetHeader(true) + m_config.GetShaderCode() + GetFooter(), nullptr, "Default user post-processing pixel shader"); if (!m_pixel_shader) { diff --git a/Source/Core/VideoCommon/PostProcessing.h b/Source/Core/VideoCommon/PostProcessing.h index b636f3b1332..30a0cac2c27 100644 --- a/Source/Core/VideoCommon/PostProcessing.h +++ b/Source/Core/VideoCommon/PostProcessing.h @@ -20,6 +20,8 @@ class AbstractFramebuffer; namespace VideoCommon { +class ShaderIncluder; + class PostProcessingConfiguration { public: @@ -68,6 +70,7 @@ public: void SaveOptionsConfiguration(); const std::string& GetShader() const { return m_current_shader; } const std::string& GetShaderCode() const { return m_current_shader_code; } + ShaderIncluder* GetShaderIncluder() { return m_shader_includer.get(); } bool IsDirty() const { return m_any_options_dirty; } void SetDirty(bool dirty) { m_any_options_dirty = dirty; } bool HasOptions() const { return m_options.size() > 0; } @@ -81,6 +84,7 @@ public: private: bool m_any_options_dirty = false; + std::unique_ptr m_shader_includer; std::string m_current_shader; std::string m_current_shader_code; ConfigMap m_options; diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 74903506905..44868808ddc 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -442,7 +442,7 @@ ShaderCache::CompileVertexUberShader(const UberShader::VertexShaderUid& uid) con { const ShaderCode source_code = UberShader::GenVertexShader(m_api_type, m_host_config, uid.GetUidData()); - return g_gfx->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer(), + return g_gfx->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer(), nullptr, fmt::to_string(*uid.GetUidData())); } @@ -458,7 +458,7 @@ ShaderCache::CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const { const ShaderCode source_code = UberShader::GenPixelShader(m_api_type, m_host_config, uid.GetUidData()); - return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), + return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), nullptr, fmt::to_string(*uid.GetUidData())); } @@ -555,7 +555,7 @@ const AbstractShader* ShaderCache::CreateGeometryShader(const GeometryShaderUid& const ShaderCode source_code = GenerateGeometryShaderCode(m_api_type, m_host_config, uid.GetUidData()); std::unique_ptr shader = - g_gfx->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), + g_gfx->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), nullptr, fmt::format("Geometry shader: {}", *uid.GetUidData())); auto& entry = m_gs_cache.shader_map[uid]; @@ -1393,7 +1393,7 @@ ShaderCache::GetEFBCopyToVRAMPipeline(const TextureConversionShaderGen::TCShader auto shader_code = TextureConversionShaderGen::GeneratePixelShader(m_api_type, uid.GetUidData()); auto shader = g_gfx->CreateShaderFromSource( - ShaderStage::Pixel, shader_code.GetBuffer(), + ShaderStage::Pixel, shader_code.GetBuffer(), nullptr, fmt::format("EFB copy to VRAM pixel shader: {}", *uid.GetUidData())); if (!shader) { @@ -1424,8 +1424,9 @@ const AbstractPipeline* ShaderCache::GetEFBCopyToRAMPipeline(const EFBCopyParams const std::string shader_code = TextureConversionShaderTiled::GenerateEncodingShader(uid, m_api_type); - const auto shader = g_gfx->CreateShaderFromSource( - ShaderStage::Pixel, shader_code, fmt::format("EFB copy to RAM pixel shader: {}", uid)); + const auto shader = + g_gfx->CreateShaderFromSource(ShaderStage::Pixel, shader_code, nullptr, + fmt::format("EFB copy to RAM pixel shader: {}", uid)); if (!shader) { m_efb_copy_to_ram_pipelines.emplace(uid, nullptr); @@ -1447,14 +1448,14 @@ const AbstractPipeline* ShaderCache::GetEFBCopyToRAMPipeline(const EFBCopyParams bool ShaderCache::CompileSharedPipelines() { m_screen_quad_vertex_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Vertex, FramebufferShaderGen::GenerateScreenQuadVertexShader(), + ShaderStage::Vertex, FramebufferShaderGen::GenerateScreenQuadVertexShader(), nullptr, "Screen quad vertex shader"); m_texture_copy_vertex_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Vertex, FramebufferShaderGen::GenerateTextureCopyVertexShader(), + ShaderStage::Vertex, FramebufferShaderGen::GenerateTextureCopyVertexShader(), nullptr, "Texture copy vertex shader"); m_efb_copy_vertex_shader = g_gfx->CreateShaderFromSource( ShaderStage::Vertex, TextureConversionShaderGen::GenerateVertexShader(m_api_type).GetBuffer(), - "EFB copy vertex shader"); + nullptr, "EFB copy vertex shader"); if (!m_screen_quad_vertex_shader || !m_texture_copy_vertex_shader || !m_efb_copy_vertex_shader) return false; @@ -1462,19 +1463,20 @@ bool ShaderCache::CompileSharedPipelines() { m_texcoord_geometry_shader = g_gfx->CreateShaderFromSource( ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 0), - "Texcoord passthrough geometry shader"); + nullptr, "Texcoord passthrough geometry shader"); m_color_geometry_shader = g_gfx->CreateShaderFromSource( ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(0, 1), - "Color passthrough geometry shader"); + nullptr, "Color passthrough geometry shader"); if (!m_texcoord_geometry_shader || !m_color_geometry_shader) return false; } m_texture_copy_pixel_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Pixel, FramebufferShaderGen::GenerateTextureCopyPixelShader(), + ShaderStage::Pixel, FramebufferShaderGen::GenerateTextureCopyPixelShader(), nullptr, "Texture copy pixel shader"); m_color_pixel_shader = g_gfx->CreateShaderFromSource( - ShaderStage::Pixel, FramebufferShaderGen::GenerateColorPixelShader(), "Color pixel shader"); + ShaderStage::Pixel, FramebufferShaderGen::GenerateColorPixelShader(), nullptr, + "Color pixel shader"); if (!m_texture_copy_pixel_shader || !m_color_pixel_shader) return false; @@ -1511,7 +1513,7 @@ bool ShaderCache::CompileSharedPipelines() auto shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, TextureConversionShaderTiled::GeneratePaletteConversionShader(format, m_api_type), - fmt::format("Palette conversion pixel shader: {}", format)); + nullptr, fmt::format("Palette conversion pixel shader: {}", format)); if (!shader) return false; @@ -1546,7 +1548,7 @@ const AbstractPipeline* ShaderCache::GetTextureReinterpretPipeline(TextureFormat return nullptr; std::unique_ptr shader = g_gfx->CreateShaderFromSource( - ShaderStage::Pixel, shader_source, + ShaderStage::Pixel, shader_source, nullptr, fmt::format("Texture reinterpret pixel shader: {} to {}", from_format, to_format)); if (!shader) return nullptr; @@ -1586,7 +1588,7 @@ ShaderCache::GetTextureDecodingShader(TextureFormat format, fmt::format("Texture decoding compute shader: {}", format); std::unique_ptr shader = - g_gfx->CreateShaderFromSource(ShaderStage::Compute, shader_source, name); + g_gfx->CreateShaderFromSource(ShaderStage::Compute, shader_source, nullptr, name); if (!shader) return nullptr; diff --git a/Source/Core/VideoCommon/ShaderCompileUtils.cpp b/Source/Core/VideoCommon/ShaderCompileUtils.cpp new file mode 100644 index 00000000000..ea17162ce9b --- /dev/null +++ b/Source/Core/VideoCommon/ShaderCompileUtils.cpp @@ -0,0 +1,93 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/ShaderCompileUtils.h" + +#include +#include + +#include "Common/FileUtil.h" +#include "Common/StringUtil.h" + +namespace VideoCommon +{ +ShaderIncluder::ShaderIncluder(const std::string& user_path, const std::string& system_path) + : m_root_user_path(user_path), m_root_system_path(system_path) +{ +} + +std::vector ShaderIncluder::GetIncludes() const +{ + const auto keys = std::views::keys(m_include_results); + return {keys.begin(), keys.end()}; +} + +ShaderIncluder::IncludeResult* +ShaderIncluder::includeLocal(const char* header_name, const char* includer_name, std::size_t depth) +{ + return ProcessInclude(m_root_user_path, header_name, includer_name, depth); +} + +ShaderIncluder::IncludeResult* +ShaderIncluder::includeSystem(const char* header_name, const char* includer_name, std::size_t depth) +{ + return ProcessInclude(m_root_system_path, header_name, includer_name, depth); +} + +void ShaderIncluder::releaseInclude(IncludeResult* result) +{ + m_include_results.erase(result->headerName); +} + +ShaderIncluder::IncludeResult* ShaderIncluder::ProcessInclude(const std::string& root, + const char* header_name, + const char* includer_name, + std::size_t depth) +{ + m_dirs.resize(depth); + if (depth == 1) + { + const auto includer_dir = GetDirectory(includer_name); + if (includer_dir == ".") + { + m_dirs.push_back(root); + } + else + { + m_dirs.push_back(std::string{includer_dir}); + } + } + + // Search through directories back to front + for (auto iter = m_dirs.rbegin(); iter != m_dirs.rend(); iter++) + { + const std::string full_path = WithUnifiedPathSeparators(*iter + '/' + header_name); + + std::string file_data; + if (File::ReadFileToString(full_path, file_data)) + { + m_dirs.push_back(std::string{GetDirectory(full_path)}); + + const char* file_bytes = file_data.data(); + const std::size_t file_size = file_data.size(); + IncludeResultData result_data{ + .result = std::make_unique(full_path, file_bytes, file_size, nullptr), + .file_data = std::move(file_data)}; + + auto ptr = result_data.result.get(); + m_include_results[full_path] = std::move(result_data); + return ptr; + } + } + return nullptr; +} + +std::string_view ShaderIncluder::GetDirectory(std::string_view path) const +{ + const auto last_pos = path.find_last_of('/'); + if (last_pos == std::string_view::npos) + return "."; + + return path.substr(0, last_pos); +} +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/ShaderCompileUtils.h b/Source/Core/VideoCommon/ShaderCompileUtils.h new file mode 100644 index 00000000000..c3023922483 --- /dev/null +++ b/Source/Core/VideoCommon/ShaderCompileUtils.h @@ -0,0 +1,46 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "ShaderLang.h" + +namespace VideoCommon +{ +class ShaderIncluder final : public glslang::TShader::Includer +{ +public: + ShaderIncluder(const std::string& user_path, const std::string& system_path); + ~ShaderIncluder() override = default; + + std::vector GetIncludes() const; + +private: + IncludeResult* includeLocal(const char* header_name, const char* includer_name, + std::size_t depth) override; + IncludeResult* includeSystem(const char* header_name, const char* includer_name, + std::size_t depth) override; + void releaseInclude(IncludeResult* result) override; + + IncludeResult* ProcessInclude(const std::string& root, const char* header_name, + const char* includer_name, std::size_t depth); + std::string_view GetDirectory(std::string_view path) const; + + std::string m_root_user_path; + std::string m_root_system_path; + std::vector m_dirs; + + struct IncludeResultData + { + std::unique_ptr result; + std::string file_data; + }; + std::map> m_include_results; +}; + +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Spirv.cpp b/Source/Core/VideoCommon/Spirv.cpp index 59bc872f931..fce9b272bee 100644 --- a/Source/Core/VideoCommon/Spirv.cpp +++ b/Source/Core/VideoCommon/Spirv.cpp @@ -159,14 +159,14 @@ const TBuiltInResource* GetCompilerResourceLimits() std::optional CompileShaderToSPV(EShLanguage stage, APIType api_type, glslang::EShTargetLanguageVersion language_version, const char* stage_filename, - std::string_view source) + std::string_view source, glslang::TShader::Includer* shader_includer) { if (!InitializeGlslang()) return std::nullopt; std::unique_ptr shader = std::make_unique(stage); std::unique_ptr program; - glslang::TShader::ForbidIncluder includer; + glslang::TShader::ForbidIncluder forbid_includer; EProfile profile = ECoreProfile; EShMessages messages = static_cast(EShMsgDefault | EShMsgSpvRules); if (api_type == APIType::Vulkan || api_type == APIType::Metal) @@ -209,7 +209,7 @@ CompileShaderToSPV(EShLanguage stage, APIType api_type, }; if (!shader->parse(GetCompilerResourceLimits(), default_version, profile, false, true, messages, - includer)) + shader_includer ? *shader_includer : forbid_includer)) { DumpBadShader("Failed to parse shader"); return std::nullopt; @@ -277,26 +277,34 @@ CompileShaderToSPV(EShLanguage stage, APIType api_type, namespace SPIRV { std::optional CompileVertexShader(std::string_view source_code, APIType api_type, - glslang::EShTargetLanguageVersion language_version) + glslang::EShTargetLanguageVersion language_version, + glslang::TShader::Includer* shader_includer) { - return CompileShaderToSPV(EShLangVertex, api_type, language_version, "vs", source_code); + return CompileShaderToSPV(EShLangVertex, api_type, language_version, "vs", source_code, + shader_includer); } std::optional CompileGeometryShader(std::string_view source_code, APIType api_type, - glslang::EShTargetLanguageVersion language_version) + glslang::EShTargetLanguageVersion language_version, + glslang::TShader::Includer* shader_includer) { - return CompileShaderToSPV(EShLangGeometry, api_type, language_version, "gs", source_code); + return CompileShaderToSPV(EShLangGeometry, api_type, language_version, "gs", source_code, + shader_includer); } std::optional CompileFragmentShader(std::string_view source_code, APIType api_type, - glslang::EShTargetLanguageVersion language_version) + glslang::EShTargetLanguageVersion language_version, + glslang::TShader::Includer* shader_includer) { - return CompileShaderToSPV(EShLangFragment, api_type, language_version, "ps", source_code); + return CompileShaderToSPV(EShLangFragment, api_type, language_version, "ps", source_code, + shader_includer); } std::optional CompileComputeShader(std::string_view source_code, APIType api_type, - glslang::EShTargetLanguageVersion language_version) + glslang::EShTargetLanguageVersion language_version, + glslang::TShader::Includer* shader_includer) { - return CompileShaderToSPV(EShLangCompute, api_type, language_version, "cs", source_code); + return CompileShaderToSPV(EShLangCompute, api_type, language_version, "cs", source_code, + shader_includer); } } // namespace SPIRV diff --git a/Source/Core/VideoCommon/Spirv.h b/Source/Core/VideoCommon/Spirv.h index 4da7b01c178..8a18d5bcc7f 100644 --- a/Source/Core/VideoCommon/Spirv.h +++ b/Source/Core/VideoCommon/Spirv.h @@ -21,17 +21,21 @@ using CodeVector = std::vector; // Compile a vertex shader to SPIR-V. std::optional CompileVertexShader(std::string_view source_code, APIType api_type, - glslang::EShTargetLanguageVersion language_version); + glslang::EShTargetLanguageVersion language_version, + glslang::TShader::Includer* shader_includer); // Compile a geometry shader to SPIR-V. std::optional CompileGeometryShader(std::string_view source_code, APIType api_type, - glslang::EShTargetLanguageVersion language_version); + glslang::EShTargetLanguageVersion language_version, + glslang::TShader::Includer* shader_includer); // Compile a fragment shader to SPIR-V. std::optional CompileFragmentShader(std::string_view source_code, APIType api_type, - glslang::EShTargetLanguageVersion language_version); + glslang::EShTargetLanguageVersion language_version, + glslang::TShader::Includer* shader_includer); // Compile a compute shader to SPIR-V. std::optional CompileComputeShader(std::string_view source_code, APIType api_type, - glslang::EShTargetLanguageVersion language_version); + glslang::EShTargetLanguageVersion language_version, + glslang::TShader::Includer* shader_includer); } // namespace SPIRV