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