summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/D3DCommon/Shader.cpp
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2022-07-08 15:29:58 -0400
committerGitHub <noreply@github.com>2022-07-08 15:29:58 -0400
commit828afc67356c2299c6056d618eb6b7afe4c843b9 (patch)
treecb7809cf9aad689968ec209e8d886b5457ca68dc /Source/Core/VideoBackends/D3DCommon/Shader.cpp
parent7cc89f5642cab3b91aef1c8255af857a1607ddf6 (diff)
parenta8b52663479effc0f404f7c0f8b52bc0ee37b742 (diff)
Merge pull request #10673 from iwubcode/spirv-backends
D3D: Generate HLSL from SPIRV*
Diffstat (limited to 'Source/Core/VideoBackends/D3DCommon/Shader.cpp')
-rw-r--r--Source/Core/VideoBackends/D3DCommon/Shader.cpp154
1 files changed, 152 insertions, 2 deletions
diff --git a/Source/Core/VideoBackends/D3DCommon/Shader.cpp b/Source/Core/VideoBackends/D3DCommon/Shader.cpp
index 0d7c84b8e0..1be4b2298e 100644
--- a/Source/Core/VideoBackends/D3DCommon/Shader.cpp
+++ b/Source/Core/VideoBackends/D3DCommon/Shader.cpp
@@ -4,7 +4,13 @@
#include "VideoBackends/D3DCommon/Shader.h"
#include <fstream>
+#include <optional>
+#include <string_view>
+
+#include <fmt/format.h>
#include <wrl/client.h>
+#include "disassemble.h"
+#include "spirv_hlsl.hpp"
#include "Common/Assert.h"
#include "Common/FileUtil.h"
@@ -14,9 +20,141 @@
#include "Common/StringUtil.h"
#include "Common/Version.h"
+#include "VideoCommon/Spirv.h"
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"
+namespace
+{
+// Regarding the UBO bind points, we subtract one from the binding index because
+// the OpenGL backend requires UBO #0 for non-block uniforms (at least on NV).
+// This allows us to share the same shaders but use bind point #0 in the D3D
+// backends. None of the specific shaders use UBOs, instead they use push
+// constants, so when/if the GL backend moves to uniform blocks completely this
+// subtraction can be removed.
+constexpr std::string_view SHADER_HEADER = R"(
+ // Target GLSL 4.5.
+ #version 450 core
+ #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)
+ #define UBO_BINDING(packing, x) layout(packing, binding = (x - 1))
+ #define SAMPLER_BINDING(x) layout(binding = x)
+ #define TEXEL_BUFFER_BINDING(x) layout(binding = x)
+ #define SSBO_BINDING(x) layout(binding = (x + 2))
+ #define VARYING_LOCATION(x) layout(location = x)
+ #define FORCE_EARLY_Z layout(early_fragment_tests) in
+
+ // hlsl to glsl function translation
+ #define float2 vec2
+ #define float3 vec3
+ #define float4 vec4
+ #define uint2 uvec2
+ #define uint3 uvec3
+ #define uint4 uvec4
+ #define int2 ivec2
+ #define int3 ivec3
+ #define int4 ivec4
+ #define frac fract
+ #define lerp mix
+
+ #define API_D3D 1
+)";
+constexpr std::string_view COMPUTE_SHADER_HEADER = R"(
+ // Target GLSL 4.5.
+ #version 450 core
+ // All resources are packed into one descriptor set for compute.
+ #define UBO_BINDING(packing, x) layout(packing, binding = (x - 1))
+ #define SAMPLER_BINDING(x) layout(binding = x)
+ #define TEXEL_BUFFER_BINDING(x) layout(binding = x)
+ #define IMAGE_BINDING(format, x) layout(format, binding = x)
+
+ // hlsl to glsl function translation
+ #define float2 vec2
+ #define float3 vec3
+ #define float4 vec4
+ #define uint2 uvec2
+ #define uint3 uvec3
+ #define uint4 uvec4
+ #define int2 ivec2
+ #define int3 ivec3
+ #define int4 ivec4
+ #define frac fract
+ #define lerp mix
+
+ #define API_D3D 1
+)";
+
+std::optional<std::string> GetHLSLFromSPIRV(SPIRV::CodeVector spv, D3D_FEATURE_LEVEL feature_level)
+{
+ spirv_cross::CompilerHLSL::Options options;
+ switch (feature_level)
+ {
+ case D3D_FEATURE_LEVEL_10_0:
+ options.shader_model = 40;
+ break;
+ case D3D_FEATURE_LEVEL_10_1:
+ options.shader_model = 41;
+ break;
+ default:
+ options.shader_model = 50;
+ break;
+ };
+
+ spirv_cross::CompilerHLSL compiler(std::move(spv));
+ compiler.set_hlsl_options(options);
+
+ return compiler.compile();
+}
+
+std::optional<SPIRV::CodeVector> GetSpirv(ShaderStage stage, std::string_view source)
+{
+ 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);
+ }
+
+ case ShaderStage::Geometry:
+ {
+ // Spirv cross does not currently support hlsl geometry shaders
+ return std::nullopt;
+ }
+
+ case ShaderStage::Pixel:
+ {
+ const auto full_source = fmt::format("{}{}", SHADER_HEADER, source);
+ return SPIRV::CompileFragmentShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0);
+ }
+
+ 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 std::nullopt;
+}
+
+std::optional<std::string> GetHLSL(D3D_FEATURE_LEVEL feature_level, ShaderStage stage,
+ std::string_view source)
+{
+ if (stage == ShaderStage::Geometry)
+ {
+ return std::string{source};
+ }
+ else if (const auto spirv = GetSpirv(stage, source))
+ {
+ return GetHLSLFromSPIRV(std::move(*spirv), feature_level);
+ }
+
+ return std::nullopt;
+}
+} // namespace
+
namespace D3DCommon
{
Shader::Shader(ShaderStage stage, BinaryData bytecode)
@@ -95,6 +233,10 @@ static const char* GetCompileTarget(D3D_FEATURE_LEVEL feature_level, ShaderStage
std::optional<Shader::BinaryData> Shader::CompileShader(D3D_FEATURE_LEVEL feature_level,
ShaderStage stage, std::string_view source)
{
+ const auto hlsl = GetHLSL(feature_level, stage, source);
+ if (!hlsl)
+ return std::nullopt;
+
static constexpr D3D_SHADER_MACRO macros[] = {{"API_D3D", "1"}, {nullptr, nullptr}};
const UINT flags = g_ActiveConfig.bEnableValidationLayer ?
(D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION) :
@@ -103,7 +245,7 @@ std::optional<Shader::BinaryData> Shader::CompileShader(D3D_FEATURE_LEVEL featur
Microsoft::WRL::ComPtr<ID3DBlob> code;
Microsoft::WRL::ComPtr<ID3DBlob> errors;
- HRESULT hr = d3d_compile(source.data(), source.size(), nullptr, macros, nullptr, "main", target,
+ HRESULT hr = d3d_compile(hlsl->data(), hlsl->size(), nullptr, macros, nullptr, "main", target,
flags, 0, &code, &errors);
if (FAILED(hr))
{
@@ -111,12 +253,20 @@ std::optional<Shader::BinaryData> Shader::CompileShader(D3D_FEATURE_LEVEL featur
std::string filename = VideoBackendBase::BadShaderFilename(target, num_failures++);
std::ofstream file;
File::OpenFStream(file, filename, std::ios_base::out);
- file.write(source.data(), source.size());
+ file.write(hlsl->data(), hlsl->size());
file << "\n";
file.write(static_cast<const char*>(errors->GetBufferPointer()), errors->GetBufferSize());
file << "\n";
file << "Dolphin Version: " + Common::GetScmRevStr() + "\n";
file << "Video Backend: " + g_video_backend->GetDisplayName();
+
+ if (const auto spirv = GetSpirv(stage, source))
+ {
+ file << "\nOriginal Source: \n";
+ file << source << std::endl;
+ file << "SPIRV: \n";
+ spv::Disassemble(file, *spirv);
+ }
file.close();
PanicAlertFmt("Failed to compile {}: {}\nDebug info ({}):\n{}", filename, Common::HRWrap(hr),