Duckstation/src/util/d3d12_pipeline.cpp
2023-09-05 22:10:50 +10:00

257 lines
12 KiB
C++

// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "d3d12_pipeline.h"
#include "d3d12_builders.h"
#include "d3d12_device.h"
#include "d3d_common.h"
#include "common/assert.h"
#include "common/log.h"
#include "common/sha1_digest.h"
#include "common/string_util.h"
#include <d3dcompiler.h>
Log_SetChannel(D3D12Device);
D3D12Shader::D3D12Shader(GPUShaderStage stage, Bytecode bytecode) : GPUShader(stage), m_bytecode(std::move(bytecode))
{
}
D3D12Shader::~D3D12Shader() = default;
void D3D12Shader::SetDebugName(const std::string_view& name)
{
}
std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data)
{
// Can't do much at this point.
std::vector bytecode(data.begin(), data.end());
return std::unique_ptr<GPUShader>(new D3D12Shader(stage, std::move(bytecode)));
}
std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromSource(GPUShaderStage stage, const std::string_view& source,
const char* entry_point,
DynamicHeapArray<u8>* out_binary)
{
std::optional<DynamicHeapArray<u8>> bytecode =
D3DCommon::CompileShader(m_feature_level, m_debug_device, stage, source, entry_point);
if (!bytecode.has_value())
return {};
std::unique_ptr<GPUShader> ret = CreateShaderFromBinary(stage, bytecode.value());
if (ret && out_binary)
*out_binary = std::move(bytecode.value());
return ret;
}
//////////////////////////////////////////////////////////////////////////
D3D12Pipeline::D3D12Pipeline(Microsoft::WRL::ComPtr<ID3D12PipelineState> pipeline, Layout layout,
D3D12_PRIMITIVE_TOPOLOGY topology, u32 vertex_stride, u32 blend_constants)
: GPUPipeline(), m_pipeline(std::move(pipeline)), m_layout(layout), m_topology(topology),
m_vertex_stride(vertex_stride), m_blend_constants(blend_constants),
m_blend_constants_f(GPUDevice::RGBA8ToFloat(blend_constants))
{
}
D3D12Pipeline::~D3D12Pipeline()
{
D3D12Device::GetInstance().DeferObjectDestruction(std::move(m_pipeline));
}
void D3D12Pipeline::SetDebugName(const std::string_view& name)
{
D3D12::SetObjectName(m_pipeline.Get(), name);
}
std::string D3D12Pipeline::GetPipelineName(const GraphicsConfig& config)
{
SHA1Digest hash;
hash.Update(&config.layout, sizeof(config.layout));
hash.Update(&config.primitive, sizeof(config.primitive));
if (!config.input_layout.vertex_attributes.empty())
{
hash.Update(config.input_layout.vertex_attributes.data(),
sizeof(VertexAttribute) * static_cast<u32>(config.input_layout.vertex_attributes.size()));
hash.Update(&config.input_layout.vertex_stride, sizeof(config.input_layout.vertex_stride));
}
hash.Update(&config.rasterization.key, sizeof(config.rasterization.key));
hash.Update(&config.depth.key, sizeof(config.depth.key));
hash.Update(&config.blend.key, sizeof(config.blend.key));
if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.vertex_shader))
hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.fragment_shader))
hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.geometry_shader))
hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
hash.Update(&config.color_format, sizeof(config.color_format));
hash.Update(&config.depth_format, sizeof(config.depth_format));
hash.Update(&config.samples, sizeof(config.samples));
hash.Update(&config.per_sample_shading, sizeof(config.per_sample_shading));
u8 digest[SHA1Digest::DIGEST_SIZE];
hash.Final(digest);
return SHA1Digest::DigestToString(digest);
}
std::unique_ptr<GPUPipeline> D3D12Device::CreatePipeline(const GPUPipeline::GraphicsConfig& config)
{
static constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY, static_cast<u32>(GPUPipeline::Primitive::MaxCount)> primitives =
{{
D3D_PRIMITIVE_TOPOLOGY_POINTLIST, // Points
D3D_PRIMITIVE_TOPOLOGY_LINELIST, // Lines
D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // Triangles
D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, // TriangleStrips
}};
static constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY_TYPE, static_cast<u32>(GPUPipeline::Primitive::MaxCount)>
primitive_types = {{
D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, // Points
D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, // Lines
D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // Triangles
D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // TriangleStrips
}};
static constexpr u32 MAX_COMPONENTS = 4;
static constexpr const DXGI_FORMAT
format_mapping[static_cast<u8>(GPUPipeline::VertexAttribute::Type::MaxCount)][MAX_COMPONENTS] = {
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32B32_FLOAT,
DXGI_FORMAT_R32G32B32A32_FLOAT}, // Float
{DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UINT}, // UInt8
{DXGI_FORMAT_R8_SINT, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_SINT}, // SInt8
{DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM}, // UNorm8
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UINT}, // UInt16
{DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_SINT}, // SInt16
{DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UNORM}, // UNorm16
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_UINT}, // UInt32
{DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32G32_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_SINT}, // SInt32
};
static constexpr std::array<D3D12_CULL_MODE, static_cast<u32>(GPUPipeline::CullMode::MaxCount)> cull_mapping = {{
D3D12_CULL_MODE_NONE, // None
D3D12_CULL_MODE_FRONT, // Front
D3D12_CULL_MODE_BACK, // Back
}};
static constexpr std::array<D3D12_COMPARISON_FUNC, static_cast<u32>(GPUPipeline::DepthFunc::MaxCount)>
compare_mapping = {{
D3D12_COMPARISON_FUNC_NEVER, // Never
D3D12_COMPARISON_FUNC_ALWAYS, // Always
D3D12_COMPARISON_FUNC_LESS, // Less
D3D12_COMPARISON_FUNC_LESS_EQUAL, // LessEqual
D3D12_COMPARISON_FUNC_GREATER, // Greater
D3D12_COMPARISON_FUNC_GREATER_EQUAL, // GreaterEqual
D3D12_COMPARISON_FUNC_EQUAL, // Equal
}};
static constexpr std::array<D3D12_BLEND, static_cast<u32>(GPUPipeline::BlendFunc::MaxCount)> blend_mapping = {{
D3D12_BLEND_ZERO, // Zero
D3D12_BLEND_ONE, // One
D3D12_BLEND_SRC_COLOR, // SrcColor
D3D12_BLEND_INV_SRC_COLOR, // InvSrcColor
D3D12_BLEND_DEST_COLOR, // DstColor
D3D12_BLEND_INV_DEST_COLOR, // InvDstColor
D3D12_BLEND_SRC_ALPHA, // SrcAlpha
D3D12_BLEND_INV_SRC_ALPHA, // InvSrcAlpha
D3D12_BLEND_SRC1_ALPHA, // SrcAlpha1
D3D12_BLEND_INV_SRC1_ALPHA, // InvSrcAlpha1
D3D12_BLEND_DEST_ALPHA, // DstAlpha
D3D12_BLEND_INV_DEST_ALPHA, // InvDstAlpha
D3D12_BLEND_BLEND_FACTOR, // ConstantColor
D3D12_BLEND_INV_BLEND_FACTOR, // InvConstantColor
}};
static constexpr std::array<D3D12_BLEND_OP, static_cast<u32>(GPUPipeline::BlendOp::MaxCount)> op_mapping = {{
D3D12_BLEND_OP_ADD, // Add
D3D12_BLEND_OP_SUBTRACT, // Subtract
D3D12_BLEND_OP_REV_SUBTRACT, // ReverseSubtract
D3D12_BLEND_OP_MIN, // Min
D3D12_BLEND_OP_MAX, // Max
}};
D3D12::GraphicsPipelineBuilder gpb;
gpb.SetRootSignature(m_root_signatures[static_cast<u8>(config.layout)].Get());
gpb.SetVertexShader(static_cast<const D3D12Shader*>(config.vertex_shader)->GetBytecodeData(),
static_cast<const D3D12Shader*>(config.vertex_shader)->GetBytecodeSize());
gpb.SetPixelShader(static_cast<const D3D12Shader*>(config.fragment_shader)->GetBytecodeData(),
static_cast<const D3D12Shader*>(config.fragment_shader)->GetBytecodeSize());
if (config.geometry_shader)
{
gpb.SetGeometryShader(static_cast<const D3D12Shader*>(config.geometry_shader)->GetBytecodeData(),
static_cast<const D3D12Shader*>(config.geometry_shader)->GetBytecodeSize());
}
gpb.SetPrimitiveTopologyType(primitive_types[static_cast<u8>(config.primitive)]);
if (!config.input_layout.vertex_attributes.empty())
{
for (u32 i = 0; i < static_cast<u32>(config.input_layout.vertex_attributes.size()); i++)
{
const GPUPipeline::VertexAttribute& va = config.input_layout.vertex_attributes[i];
DebugAssert(va.components > 0 && va.components <= MAX_COMPONENTS);
gpb.AddVertexAttribute(
"ATTR", i, format_mapping[static_cast<u8>(va.type.GetValue())][static_cast<u8>(va.components.GetValue() - 1)],
0, va.offset);
}
}
gpb.SetRasterizationState(D3D12_FILL_MODE_SOLID,
cull_mapping[static_cast<u8>(config.rasterization.cull_mode.GetValue())], false);
if (config.samples > 1)
gpb.SetMultisamples(config.samples);
gpb.SetDepthState(config.depth.depth_test != GPUPipeline::DepthFunc::Always || config.depth.depth_write,
config.depth.depth_write, compare_mapping[static_cast<u8>(config.depth.depth_test.GetValue())]);
gpb.SetNoStencilState();
gpb.SetBlendState(0, config.blend.enable, blend_mapping[static_cast<u8>(config.blend.src_blend.GetValue())],
blend_mapping[static_cast<u8>(config.blend.dst_blend.GetValue())],
op_mapping[static_cast<u8>(config.blend.blend_op.GetValue())],
blend_mapping[static_cast<u8>(config.blend.src_alpha_blend.GetValue())],
blend_mapping[static_cast<u8>(config.blend.dst_alpha_blend.GetValue())],
op_mapping[static_cast<u8>(config.blend.alpha_blend_op.GetValue())], config.blend.write_mask);
if (config.color_format != GPUTexture::Format::Unknown)
gpb.SetRenderTarget(0, D3DCommon::GetFormatMapping(config.color_format).rtv_format);
if (config.depth_format != GPUTexture::Format::Unknown)
gpb.SetDepthStencilFormat(D3DCommon::GetFormatMapping(config.depth_format).dsv_format);
ComPtr<ID3D12PipelineState> pipeline;
if (m_pipeline_library)
{
const std::wstring name = StringUtil::UTF8StringToWideString(D3D12Pipeline::GetPipelineName(config));
HRESULT hr =
m_pipeline_library->LoadGraphicsPipeline(name.c_str(), gpb.GetDesc(), IID_PPV_ARGS(pipeline.GetAddressOf()));
if (FAILED(hr))
{
// E_INVALIDARG = not found.
if (hr != E_INVALIDARG)
Log_ErrorPrintf("LoadGraphicsPipeline() failed with HRESULT %08X", hr);
// Need to create it normally.
pipeline = gpb.Create(m_device.Get(), false);
// Store if it wasn't an OOM or something else.
if (pipeline && hr == E_INVALIDARG)
{
hr = m_pipeline_library->StorePipeline(name.c_str(), pipeline.Get());
if (FAILED(hr))
Log_ErrorPrintf("StorePipeline() failed with HRESULT %08X", hr);
}
}
}
else
{
pipeline = gpb.Create(m_device.Get(), false);
}
if (!pipeline)
return {};
return std::unique_ptr<GPUPipeline>(new D3D12Pipeline(
pipeline, config.layout, primitives[static_cast<u8>(config.primitive)],
config.input_layout.vertex_attributes.empty() ? 0 : config.input_layout.vertex_stride, config.blend.constant));
}