Shader.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Copyright 2019 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoBackends/D3DCommon/Shader.h"
  4. #include <fstream>
  5. #include <optional>
  6. #include <string_view>
  7. #include <fmt/format.h>
  8. #include <wrl/client.h>
  9. #include "disassemble.h"
  10. #include "spirv_hlsl.hpp"
  11. #include "Common/Assert.h"
  12. #include "Common/FileUtil.h"
  13. #include "Common/HRWrap.h"
  14. #include "Common/Logging/Log.h"
  15. #include "Common/MsgHandler.h"
  16. #include "Common/StringUtil.h"
  17. #include "Common/Version.h"
  18. #include "VideoCommon/Spirv.h"
  19. #include "VideoCommon/VideoBackendBase.h"
  20. #include "VideoCommon/VideoConfig.h"
  21. namespace
  22. {
  23. // Regarding the UBO bind points, we subtract one from the binding index because
  24. // the OpenGL backend requires UBO #0 for non-block uniforms (at least on NV).
  25. // This allows us to share the same shaders but use bind point #0 in the D3D
  26. // backends. None of the specific shaders use UBOs, instead they use push
  27. // constants, so when/if the GL backend moves to uniform blocks completely this
  28. // subtraction can be removed.
  29. constexpr std::string_view SHADER_HEADER = R"(
  30. // Target GLSL 4.5.
  31. #version 450 core
  32. #define ATTRIBUTE_LOCATION(x) layout(location = x)
  33. #define FRAGMENT_OUTPUT_LOCATION(x) layout(location = x)
  34. #define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y) layout(location = x, index = y)
  35. #define UBO_BINDING(packing, x) layout(packing, binding = (x - 1))
  36. #define SAMPLER_BINDING(x) layout(binding = x)
  37. #define TEXEL_BUFFER_BINDING(x) layout(binding = x)
  38. #define SSBO_BINDING(x) layout(binding = (x + 2))
  39. #define VARYING_LOCATION(x) layout(location = x)
  40. #define FORCE_EARLY_Z layout(early_fragment_tests) in
  41. // hlsl to glsl function translation
  42. #define float2 vec2
  43. #define float3 vec3
  44. #define float4 vec4
  45. #define uint2 uvec2
  46. #define uint3 uvec3
  47. #define uint4 uvec4
  48. #define int2 ivec2
  49. #define int3 ivec3
  50. #define int4 ivec4
  51. #define frac fract
  52. #define lerp mix
  53. #define API_D3D 1
  54. )";
  55. constexpr std::string_view COMPUTE_SHADER_HEADER = R"(
  56. // Target GLSL 4.5.
  57. #version 450 core
  58. // All resources are packed into one descriptor set for compute.
  59. #define UBO_BINDING(packing, x) layout(packing, binding = (x - 1))
  60. #define SAMPLER_BINDING(x) layout(binding = x)
  61. #define TEXEL_BUFFER_BINDING(x) layout(binding = x)
  62. #define IMAGE_BINDING(format, x) layout(format, binding = x)
  63. // hlsl to glsl function translation
  64. #define float2 vec2
  65. #define float3 vec3
  66. #define float4 vec4
  67. #define uint2 uvec2
  68. #define uint3 uvec3
  69. #define uint4 uvec4
  70. #define int2 ivec2
  71. #define int3 ivec3
  72. #define int4 ivec4
  73. #define frac fract
  74. #define lerp mix
  75. #define API_D3D 1
  76. )";
  77. std::optional<std::string> GetHLSLFromSPIRV(SPIRV::CodeVector spv, D3D_FEATURE_LEVEL feature_level)
  78. {
  79. spirv_cross::CompilerHLSL::Options options;
  80. switch (feature_level)
  81. {
  82. case D3D_FEATURE_LEVEL_10_0:
  83. options.shader_model = 40;
  84. break;
  85. case D3D_FEATURE_LEVEL_10_1:
  86. options.shader_model = 41;
  87. break;
  88. default:
  89. options.shader_model = 50;
  90. break;
  91. };
  92. spirv_cross::CompilerHLSL compiler(std::move(spv));
  93. compiler.set_hlsl_options(options);
  94. return compiler.compile();
  95. }
  96. std::optional<SPIRV::CodeVector> GetSpirv(ShaderStage stage, std::string_view source)
  97. {
  98. switch (stage)
  99. {
  100. case ShaderStage::Vertex:
  101. {
  102. const auto full_source = fmt::format("{}{}", SHADER_HEADER, source);
  103. return SPIRV::CompileVertexShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0);
  104. }
  105. case ShaderStage::Geometry:
  106. {
  107. // Spirv cross does not currently support hlsl geometry shaders
  108. return std::nullopt;
  109. }
  110. case ShaderStage::Pixel:
  111. {
  112. const auto full_source = fmt::format("{}{}", SHADER_HEADER, source);
  113. return SPIRV::CompileFragmentShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0);
  114. }
  115. case ShaderStage::Compute:
  116. {
  117. const auto full_source = fmt::format("{}{}", COMPUTE_SHADER_HEADER, source);
  118. return SPIRV::CompileComputeShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0);
  119. }
  120. };
  121. return std::nullopt;
  122. }
  123. std::optional<std::string> GetHLSL(D3D_FEATURE_LEVEL feature_level, ShaderStage stage,
  124. std::string_view source)
  125. {
  126. if (stage == ShaderStage::Geometry)
  127. {
  128. return std::string{source};
  129. }
  130. else if (const auto spirv = GetSpirv(stage, source))
  131. {
  132. return GetHLSLFromSPIRV(std::move(*spirv), feature_level);
  133. }
  134. return std::nullopt;
  135. }
  136. } // namespace
  137. namespace D3DCommon
  138. {
  139. Shader::Shader(ShaderStage stage, BinaryData bytecode)
  140. : AbstractShader(stage), m_bytecode(std::move(bytecode))
  141. {
  142. }
  143. Shader::~Shader() = default;
  144. AbstractShader::BinaryData Shader::GetBinary() const
  145. {
  146. return m_bytecode;
  147. }
  148. static const char* GetCompileTarget(D3D_FEATURE_LEVEL feature_level, ShaderStage stage)
  149. {
  150. switch (stage)
  151. {
  152. case ShaderStage::Vertex:
  153. {
  154. switch (feature_level)
  155. {
  156. case D3D_FEATURE_LEVEL_10_0:
  157. return "vs_4_0";
  158. case D3D_FEATURE_LEVEL_10_1:
  159. return "vs_4_1";
  160. default:
  161. return "vs_5_0";
  162. }
  163. }
  164. case ShaderStage::Geometry:
  165. {
  166. switch (feature_level)
  167. {
  168. case D3D_FEATURE_LEVEL_10_0:
  169. return "gs_4_0";
  170. case D3D_FEATURE_LEVEL_10_1:
  171. return "gs_4_1";
  172. default:
  173. return "gs_5_0";
  174. }
  175. }
  176. case ShaderStage::Pixel:
  177. {
  178. switch (feature_level)
  179. {
  180. case D3D_FEATURE_LEVEL_10_0:
  181. return "ps_4_0";
  182. case D3D_FEATURE_LEVEL_10_1:
  183. return "ps_4_1";
  184. default:
  185. return "ps_5_0";
  186. }
  187. }
  188. case ShaderStage::Compute:
  189. {
  190. switch (feature_level)
  191. {
  192. case D3D_FEATURE_LEVEL_10_0:
  193. case D3D_FEATURE_LEVEL_10_1:
  194. return "";
  195. default:
  196. return "cs_5_0";
  197. }
  198. }
  199. default:
  200. return "";
  201. }
  202. }
  203. std::optional<Shader::BinaryData> Shader::CompileShader(D3D_FEATURE_LEVEL feature_level,
  204. ShaderStage stage, std::string_view source)
  205. {
  206. const auto hlsl = GetHLSL(feature_level, stage, source);
  207. if (!hlsl)
  208. return std::nullopt;
  209. static constexpr D3D_SHADER_MACRO macros[] = {{"API_D3D", "1"}, {nullptr, nullptr}};
  210. const UINT flags = g_ActiveConfig.bEnableValidationLayer ?
  211. (D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION) :
  212. (D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_SKIP_VALIDATION);
  213. const char* target = GetCompileTarget(feature_level, stage);
  214. Microsoft::WRL::ComPtr<ID3DBlob> code;
  215. Microsoft::WRL::ComPtr<ID3DBlob> errors;
  216. HRESULT hr = d3d_compile(hlsl->data(), hlsl->size(), nullptr, macros, nullptr, "main", target,
  217. flags, 0, &code, &errors);
  218. if (FAILED(hr))
  219. {
  220. static int num_failures = 0;
  221. std::string filename = VideoBackendBase::BadShaderFilename(target, num_failures++);
  222. std::ofstream file;
  223. File::OpenFStream(file, filename, std::ios_base::out);
  224. file.write(hlsl->data(), hlsl->size());
  225. file << "\n";
  226. file.write(static_cast<const char*>(errors->GetBufferPointer()), errors->GetBufferSize());
  227. file << "\n";
  228. file << "Dolphin Version: " + Common::GetScmRevStr() + "\n";
  229. file << "Video Backend: " + g_video_backend->GetDisplayName();
  230. if (const auto spirv = GetSpirv(stage, source))
  231. {
  232. file << "\nOriginal Source: \n";
  233. file << source << std::endl;
  234. file << "SPIRV: \n";
  235. spv::Disassemble(file, *spirv);
  236. }
  237. file.close();
  238. PanicAlertFmt("Failed to compile {}: {}\nDebug info ({}):\n{}", filename, Common::HRWrap(hr),
  239. target, static_cast<const char*>(errors->GetBufferPointer()));
  240. return std::nullopt;
  241. }
  242. if (errors && errors->GetBufferSize() > 0)
  243. {
  244. WARN_LOG_FMT(VIDEO, "{} compilation succeeded with warnings:\n{}", target,
  245. static_cast<const char*>(errors->GetBufferPointer()));
  246. }
  247. return CreateByteCode(code->GetBufferPointer(), code->GetBufferSize());
  248. }
  249. AbstractShader::BinaryData Shader::CreateByteCode(const void* data, size_t length)
  250. {
  251. const auto* const begin = static_cast<const u8*>(data);
  252. const auto* const end = begin + length;
  253. return {begin, end};
  254. }
  255. } // namespace D3DCommon