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 }