duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

d3d12_pipeline.cpp (12825B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #include "d3d12_pipeline.h"
      5 #include "d3d12_builders.h"
      6 #include "d3d12_device.h"
      7 #include "d3d_common.h"
      8 
      9 #include "common/assert.h"
     10 #include "common/bitutils.h"
     11 #include "common/log.h"
     12 #include "common/sha1_digest.h"
     13 #include "common/string_util.h"
     14 
     15 #include <d3dcompiler.h>
     16 
     17 Log_SetChannel(D3D12Device);
     18 
     19 D3D12Shader::D3D12Shader(GPUShaderStage stage, Bytecode bytecode) : GPUShader(stage), m_bytecode(std::move(bytecode))
     20 {
     21 }
     22 
     23 D3D12Shader::~D3D12Shader() = default;
     24 
     25 void D3D12Shader::SetDebugName(std::string_view name)
     26 {
     27 }
     28 
     29 std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
     30                                                                Error* error)
     31 {
     32   // Can't do much at this point.
     33   std::vector bytecode(data.begin(), data.end());
     34   return std::unique_ptr<GPUShader>(new D3D12Shader(stage, std::move(bytecode)));
     35 }
     36 
     37 std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
     38                                                                std::string_view source, const char* entry_point,
     39                                                                DynamicHeapArray<u8>* out_binary, Error* error)
     40 {
     41   const u32 shader_model = D3DCommon::GetShaderModelForFeatureLevel(m_feature_level);
     42   if (language != GPUShaderLanguage::HLSL)
     43   {
     44     return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL,
     45                                               shader_model, out_binary, error);
     46   }
     47 
     48   std::optional<DynamicHeapArray<u8>> bytecode =
     49     D3DCommon::CompileShader(shader_model, m_debug_device, stage, source, entry_point, error);
     50   if (!bytecode.has_value())
     51     return {};
     52 
     53   std::unique_ptr<GPUShader> ret = CreateShaderFromBinary(stage, bytecode.value(), error);
     54   if (ret && out_binary)
     55     *out_binary = std::move(bytecode.value());
     56 
     57   return ret;
     58 }
     59 
     60 //////////////////////////////////////////////////////////////////////////
     61 
     62 D3D12Pipeline::D3D12Pipeline(Microsoft::WRL::ComPtr<ID3D12PipelineState> pipeline, Layout layout,
     63                              D3D12_PRIMITIVE_TOPOLOGY topology, u32 vertex_stride, u32 blend_constants)
     64   : GPUPipeline(), m_pipeline(std::move(pipeline)), m_layout(layout), m_topology(topology),
     65     m_vertex_stride(vertex_stride), m_blend_constants(blend_constants),
     66     m_blend_constants_f(GPUDevice::RGBA8ToFloat(blend_constants))
     67 {
     68 }
     69 
     70 D3D12Pipeline::~D3D12Pipeline()
     71 {
     72   D3D12Device::GetInstance().DeferObjectDestruction(std::move(m_pipeline));
     73 }
     74 
     75 void D3D12Pipeline::SetDebugName(std::string_view name)
     76 {
     77   D3D12::SetObjectName(m_pipeline.Get(), name);
     78 }
     79 
     80 std::string D3D12Pipeline::GetPipelineName(const GraphicsConfig& config)
     81 {
     82   SHA1Digest hash;
     83   hash.Update(&config.layout, sizeof(config.layout));
     84   hash.Update(&config.primitive, sizeof(config.primitive));
     85   if (!config.input_layout.vertex_attributes.empty())
     86   {
     87     hash.Update(config.input_layout.vertex_attributes.data(),
     88                 sizeof(VertexAttribute) * static_cast<u32>(config.input_layout.vertex_attributes.size()));
     89     hash.Update(&config.input_layout.vertex_stride, sizeof(config.input_layout.vertex_stride));
     90   }
     91   hash.Update(&config.rasterization.key, sizeof(config.rasterization.key));
     92   hash.Update(&config.depth.key, sizeof(config.depth.key));
     93   hash.Update(&config.blend.key, sizeof(config.blend.key));
     94   if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.vertex_shader))
     95     hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
     96   if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.fragment_shader))
     97     hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
     98   if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.geometry_shader))
     99     hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
    100   hash.Update(&config.color_formats, sizeof(config.color_formats));
    101   hash.Update(&config.depth_format, sizeof(config.depth_format));
    102   hash.Update(&config.samples, sizeof(config.samples));
    103   hash.Update(&config.per_sample_shading, sizeof(config.per_sample_shading));
    104 
    105   u8 digest[SHA1Digest::DIGEST_SIZE];
    106   hash.Final(digest);
    107   return SHA1Digest::DigestToString(digest);
    108 }
    109 
    110 std::unique_ptr<GPUPipeline> D3D12Device::CreatePipeline(const GPUPipeline::GraphicsConfig& config, Error* error)
    111 {
    112   static constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY, static_cast<u32>(GPUPipeline::Primitive::MaxCount)> primitives =
    113     {{
    114       D3D_PRIMITIVE_TOPOLOGY_POINTLIST,     // Points
    115       D3D_PRIMITIVE_TOPOLOGY_LINELIST,      // Lines
    116       D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,  // Triangles
    117       D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, // TriangleStrips
    118     }};
    119   static constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY_TYPE, static_cast<u32>(GPUPipeline::Primitive::MaxCount)>
    120     primitive_types = {{
    121       D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT,    // Points
    122       D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE,     // Lines
    123       D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // Triangles
    124       D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // TriangleStrips
    125     }};
    126 
    127   static constexpr u32 MAX_COMPONENTS = 4;
    128   static constexpr const DXGI_FORMAT
    129     format_mapping[static_cast<u8>(GPUPipeline::VertexAttribute::Type::MaxCount)][MAX_COMPONENTS] = {
    130       {DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32B32_FLOAT,
    131        DXGI_FORMAT_R32G32B32A32_FLOAT},                                                                       // Float
    132       {DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UINT},           // UInt8
    133       {DXGI_FORMAT_R8_SINT, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_SINT},           // SInt8
    134       {DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM},        // UNorm8
    135       {DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UINT},    // UInt16
    136       {DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_SINT},    // SInt16
    137       {DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UNORM}, // UNorm16
    138       {DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_UINT},    // UInt32
    139       {DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32G32_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_SINT},    // SInt32
    140     };
    141 
    142   static constexpr std::array<D3D12_CULL_MODE, static_cast<u32>(GPUPipeline::CullMode::MaxCount)> cull_mapping = {{
    143     D3D12_CULL_MODE_NONE,  // None
    144     D3D12_CULL_MODE_FRONT, // Front
    145     D3D12_CULL_MODE_BACK,  // Back
    146   }};
    147 
    148   static constexpr std::array<D3D12_COMPARISON_FUNC, static_cast<u32>(GPUPipeline::DepthFunc::MaxCount)>
    149     compare_mapping = {{
    150       D3D12_COMPARISON_FUNC_NEVER,         // Never
    151       D3D12_COMPARISON_FUNC_ALWAYS,        // Always
    152       D3D12_COMPARISON_FUNC_LESS,          // Less
    153       D3D12_COMPARISON_FUNC_LESS_EQUAL,    // LessEqual
    154       D3D12_COMPARISON_FUNC_GREATER,       // Greater
    155       D3D12_COMPARISON_FUNC_GREATER_EQUAL, // GreaterEqual
    156       D3D12_COMPARISON_FUNC_EQUAL,         // Equal
    157     }};
    158 
    159   static constexpr std::array<D3D12_BLEND, static_cast<u32>(GPUPipeline::BlendFunc::MaxCount)> blend_mapping = {{
    160     D3D12_BLEND_ZERO,             // Zero
    161     D3D12_BLEND_ONE,              // One
    162     D3D12_BLEND_SRC_COLOR,        // SrcColor
    163     D3D12_BLEND_INV_SRC_COLOR,    // InvSrcColor
    164     D3D12_BLEND_DEST_COLOR,       // DstColor
    165     D3D12_BLEND_INV_DEST_COLOR,   // InvDstColor
    166     D3D12_BLEND_SRC_ALPHA,        // SrcAlpha
    167     D3D12_BLEND_INV_SRC_ALPHA,    // InvSrcAlpha
    168     D3D12_BLEND_SRC1_ALPHA,       // SrcAlpha1
    169     D3D12_BLEND_INV_SRC1_ALPHA,   // InvSrcAlpha1
    170     D3D12_BLEND_DEST_ALPHA,       // DstAlpha
    171     D3D12_BLEND_INV_DEST_ALPHA,   // InvDstAlpha
    172     D3D12_BLEND_BLEND_FACTOR,     // ConstantColor
    173     D3D12_BLEND_INV_BLEND_FACTOR, // InvConstantColor
    174   }};
    175 
    176   static constexpr std::array<D3D12_BLEND_OP, static_cast<u32>(GPUPipeline::BlendOp::MaxCount)> op_mapping = {{
    177     D3D12_BLEND_OP_ADD,          // Add
    178     D3D12_BLEND_OP_SUBTRACT,     // Subtract
    179     D3D12_BLEND_OP_REV_SUBTRACT, // ReverseSubtract
    180     D3D12_BLEND_OP_MIN,          // Min
    181     D3D12_BLEND_OP_MAX,          // Max
    182   }};
    183 
    184   if (config.render_pass_flags & GPUPipeline::BindRenderTargetsAsImages && !m_features.raster_order_views)
    185   {
    186     ERROR_LOG("Attempting to create ROV pipeline without ROV feature.");
    187     return {};
    188   }
    189 
    190   D3D12::GraphicsPipelineBuilder gpb;
    191   gpb.SetRootSignature(m_root_signatures[BoolToUInt8(
    192     (config.render_pass_flags & GPUPipeline::BindRenderTargetsAsImages))][static_cast<u8>(config.layout)]
    193                          .Get());
    194   gpb.SetVertexShader(static_cast<const D3D12Shader*>(config.vertex_shader)->GetBytecodeData(),
    195                       static_cast<const D3D12Shader*>(config.vertex_shader)->GetBytecodeSize());
    196   gpb.SetPixelShader(static_cast<const D3D12Shader*>(config.fragment_shader)->GetBytecodeData(),
    197                      static_cast<const D3D12Shader*>(config.fragment_shader)->GetBytecodeSize());
    198   if (config.geometry_shader)
    199   {
    200     gpb.SetGeometryShader(static_cast<const D3D12Shader*>(config.geometry_shader)->GetBytecodeData(),
    201                           static_cast<const D3D12Shader*>(config.geometry_shader)->GetBytecodeSize());
    202   }
    203   gpb.SetPrimitiveTopologyType(primitive_types[static_cast<u8>(config.primitive)]);
    204 
    205   if (!config.input_layout.vertex_attributes.empty())
    206   {
    207     for (u32 i = 0; i < static_cast<u32>(config.input_layout.vertex_attributes.size()); i++)
    208     {
    209       const GPUPipeline::VertexAttribute& va = config.input_layout.vertex_attributes[i];
    210       DebugAssert(va.components > 0 && va.components <= MAX_COMPONENTS);
    211       gpb.AddVertexAttribute(
    212         "ATTR", i, format_mapping[static_cast<u8>(va.type.GetValue())][static_cast<u8>(va.components.GetValue() - 1)],
    213         0, va.offset);
    214     }
    215   }
    216 
    217   gpb.SetRasterizationState(D3D12_FILL_MODE_SOLID,
    218                             cull_mapping[static_cast<u8>(config.rasterization.cull_mode.GetValue())], false);
    219   if (config.samples > 1)
    220     gpb.SetMultisamples(config.samples);
    221   gpb.SetDepthState(config.depth.depth_test != GPUPipeline::DepthFunc::Always || config.depth.depth_write,
    222                     config.depth.depth_write, compare_mapping[static_cast<u8>(config.depth.depth_test.GetValue())]);
    223   gpb.SetNoStencilState();
    224 
    225   gpb.SetBlendState(0, config.blend.enable, blend_mapping[static_cast<u8>(config.blend.src_blend.GetValue())],
    226                     blend_mapping[static_cast<u8>(config.blend.dst_blend.GetValue())],
    227                     op_mapping[static_cast<u8>(config.blend.blend_op.GetValue())],
    228                     blend_mapping[static_cast<u8>(config.blend.src_alpha_blend.GetValue())],
    229                     blend_mapping[static_cast<u8>(config.blend.dst_alpha_blend.GetValue())],
    230                     op_mapping[static_cast<u8>(config.blend.alpha_blend_op.GetValue())], config.blend.write_mask);
    231 
    232   for (u32 i = 0; i < MAX_RENDER_TARGETS; i++)
    233   {
    234     if (config.color_formats[i] != GPUTexture::Format::Unknown)
    235       gpb.SetRenderTarget(i, D3DCommon::GetFormatMapping(config.color_formats[i]).rtv_format);
    236   }
    237 
    238   if (config.depth_format != GPUTexture::Format::Unknown)
    239     gpb.SetDepthStencilFormat(D3DCommon::GetFormatMapping(config.depth_format).dsv_format);
    240 
    241   ComPtr<ID3D12PipelineState> pipeline;
    242   if (m_pipeline_library)
    243   {
    244     const std::wstring name = StringUtil::UTF8StringToWideString(D3D12Pipeline::GetPipelineName(config));
    245     HRESULT hr =
    246       m_pipeline_library->LoadGraphicsPipeline(name.c_str(), gpb.GetDesc(), IID_PPV_ARGS(pipeline.GetAddressOf()));
    247     if (FAILED(hr))
    248     {
    249       // E_INVALIDARG = not found.
    250       if (hr != E_INVALIDARG)
    251         ERROR_LOG("LoadGraphicsPipeline() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
    252 
    253       // Need to create it normally.
    254       pipeline = gpb.Create(m_device.Get(), error, false);
    255 
    256       // Store if it wasn't an OOM or something else.
    257       if (pipeline && hr == E_INVALIDARG)
    258       {
    259         hr = m_pipeline_library->StorePipeline(name.c_str(), pipeline.Get());
    260         if (FAILED(hr))
    261           ERROR_LOG("StorePipeline() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
    262       }
    263     }
    264   }
    265   else
    266   {
    267     pipeline = gpb.Create(m_device.Get(), error, false);
    268   }
    269 
    270   if (!pipeline)
    271     return {};
    272 
    273   return std::unique_ptr<GPUPipeline>(new D3D12Pipeline(
    274     pipeline, config.layout, primitives[static_cast<u8>(config.primitive)],
    275     config.input_layout.vertex_attributes.empty() ? 0 : config.input_layout.vertex_stride, config.blend.constant));
    276 }