// Copyright 2019 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "VideoBackends/D3D12/DX12Pipeline.h" #include "Common/Assert.h" #include "Common/MsgHandler.h" #include "VideoBackends/D3D12/Common.h" #include "VideoBackends/D3D12/DX12Context.h" #include "VideoBackends/D3D12/DX12Shader.h" #include "VideoBackends/D3D12/DX12Texture.h" #include "VideoBackends/D3D12/DX12VertexFormat.h" namespace DX12 { DXPipeline::DXPipeline(ID3D12PipelineState* pipeline, ID3D12RootSignature* root_signature, AbstractPipelineUsage usage, D3D12_PRIMITIVE_TOPOLOGY primitive_topology, bool use_integer_rtv) : m_pipeline(pipeline), m_root_signature(root_signature), m_usage(usage), m_primitive_topology(primitive_topology), m_use_integer_rtv(use_integer_rtv) { } DXPipeline::~DXPipeline() { m_pipeline->Release(); } static D3D12_PRIMITIVE_TOPOLOGY GetD3DTopology(const RasterizationState& state) { switch (state.primitive) { case PrimitiveType::Points: return D3D_PRIMITIVE_TOPOLOGY_POINTLIST; case PrimitiveType::Lines: return D3D_PRIMITIVE_TOPOLOGY_LINELIST; case PrimitiveType::Triangles: return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; case PrimitiveType::TriangleStrip: default: return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; } } static D3D12_PRIMITIVE_TOPOLOGY_TYPE GetD3DTopologyType(const RasterizationState& state) { switch (state.primitive) { case PrimitiveType::Points: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; case PrimitiveType::Lines: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; case PrimitiveType::Triangles: case PrimitiveType::TriangleStrip: default: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; } } static void GetD3DRasterizerDesc(D3D12_RASTERIZER_DESC* desc, const RasterizationState& rs_state, const FramebufferState& fb_state) { // No CULL_ALL here. static constexpr std::array cull_modes = { {D3D12_CULL_MODE_NONE, D3D12_CULL_MODE_BACK, D3D12_CULL_MODE_FRONT, D3D12_CULL_MODE_FRONT}}; desc->FillMode = D3D12_FILL_MODE_SOLID; desc->CullMode = cull_modes[u32(rs_state.cullmode.Value())]; desc->MultisampleEnable = fb_state.samples > 1; } static void GetD3DDepthDesc(D3D12_DEPTH_STENCIL_DESC* desc, const DepthState& state) { // Less/greater are swapped due to inverted depth. static constexpr std::array compare_funcs = { {D3D12_COMPARISON_FUNC_NEVER, D3D12_COMPARISON_FUNC_GREATER, D3D12_COMPARISON_FUNC_EQUAL, D3D12_COMPARISON_FUNC_GREATER_EQUAL, D3D12_COMPARISON_FUNC_LESS, D3D12_COMPARISON_FUNC_NOT_EQUAL, D3D12_COMPARISON_FUNC_LESS_EQUAL, D3D12_COMPARISON_FUNC_ALWAYS}}; desc->DepthEnable = state.testenable; desc->DepthFunc = compare_funcs[u32(state.func.Value())]; desc->DepthWriteMask = state.updateenable ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; } static void GetD3DBlendDesc(D3D12_BLEND_DESC* desc, const BlendingState& state) { static constexpr std::array src_dual_src_factors = { {D3D12_BLEND_ZERO, D3D12_BLEND_ONE, D3D12_BLEND_DEST_COLOR, D3D12_BLEND_INV_DEST_COLOR, D3D12_BLEND_SRC1_ALPHA, D3D12_BLEND_INV_SRC1_ALPHA, D3D12_BLEND_DEST_ALPHA, D3D12_BLEND_INV_DEST_ALPHA}}; static constexpr std::array dst_dual_src_factors = { {D3D12_BLEND_ZERO, D3D12_BLEND_ONE, D3D12_BLEND_SRC_COLOR, D3D12_BLEND_INV_SRC_COLOR, D3D12_BLEND_SRC1_ALPHA, D3D12_BLEND_INV_SRC1_ALPHA, D3D12_BLEND_DEST_ALPHA, D3D12_BLEND_INV_DEST_ALPHA}}; static constexpr std::array src_factors = { {D3D12_BLEND_ZERO, D3D12_BLEND_ONE, D3D12_BLEND_DEST_COLOR, D3D12_BLEND_INV_DEST_COLOR, D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_DEST_ALPHA, D3D12_BLEND_INV_DEST_ALPHA}}; static constexpr std::array dst_factors = { {D3D12_BLEND_ZERO, D3D12_BLEND_ONE, D3D12_BLEND_SRC_COLOR, D3D12_BLEND_INV_SRC_COLOR, D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_DEST_ALPHA, D3D12_BLEND_INV_DEST_ALPHA}}; static constexpr std::array logic_ops = { {D3D12_LOGIC_OP_CLEAR, D3D12_LOGIC_OP_AND, D3D12_LOGIC_OP_AND_REVERSE, D3D12_LOGIC_OP_COPY, D3D12_LOGIC_OP_AND_INVERTED, D3D12_LOGIC_OP_NOOP, D3D12_LOGIC_OP_XOR, D3D12_LOGIC_OP_OR, D3D12_LOGIC_OP_NOR, D3D12_LOGIC_OP_EQUIV, D3D12_LOGIC_OP_INVERT, D3D12_LOGIC_OP_OR_REVERSE, D3D12_LOGIC_OP_COPY_INVERTED, D3D12_LOGIC_OP_OR_INVERTED, D3D12_LOGIC_OP_NAND, D3D12_LOGIC_OP_SET}}; desc->AlphaToCoverageEnable = FALSE; desc->IndependentBlendEnable = FALSE; D3D12_RENDER_TARGET_BLEND_DESC* rtblend = &desc->RenderTarget[0]; if (state.colorupdate) { rtblend->RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_RED | D3D12_COLOR_WRITE_ENABLE_GREEN | D3D12_COLOR_WRITE_ENABLE_BLUE; } if (state.alphaupdate) { rtblend->RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_ALPHA; } // blend takes precedence over logic op rtblend->BlendEnable = state.blendenable; if (state.blendenable) { rtblend->BlendOp = state.subtract ? D3D12_BLEND_OP_REV_SUBTRACT : D3D12_BLEND_OP_ADD; rtblend->BlendOpAlpha = state.subtractAlpha ? D3D12_BLEND_OP_REV_SUBTRACT : D3D12_BLEND_OP_ADD; if (state.usedualsrc) { rtblend->SrcBlend = src_dual_src_factors[u32(state.srcfactor.Value())]; rtblend->SrcBlendAlpha = src_dual_src_factors[u32(state.srcfactoralpha.Value())]; rtblend->DestBlend = dst_dual_src_factors[u32(state.dstfactor.Value())]; rtblend->DestBlendAlpha = dst_dual_src_factors[u32(state.dstfactoralpha.Value())]; } else { rtblend->SrcBlend = src_factors[u32(state.srcfactor.Value())]; rtblend->SrcBlendAlpha = src_factors[u32(state.srcfactoralpha.Value())]; rtblend->DestBlend = dst_factors[u32(state.dstfactor.Value())]; rtblend->DestBlendAlpha = dst_factors[u32(state.dstfactoralpha.Value())]; } } else { rtblend->LogicOpEnable = state.logicopenable; if (state.logicopenable) rtblend->LogicOp = logic_ops[u32(state.logicmode.Value())]; } } std::unique_ptr DXPipeline::Create(const AbstractPipelineConfig& config, const void* cache_data, size_t cache_data_size) { DEBUG_ASSERT(config.vertex_shader && config.pixel_shader); D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = {}; switch (config.usage) { case AbstractPipelineUsage::GX: case AbstractPipelineUsage::GXUber: desc.pRootSignature = g_dx_context->GetGXRootSignature(); break; case AbstractPipelineUsage::Utility: desc.pRootSignature = g_dx_context->GetUtilityRootSignature(); break; default: PanicAlertFmt("Unknown pipeline layout."); return nullptr; } if (config.vertex_shader) desc.VS = static_cast(config.vertex_shader)->GetD3DByteCode(); if (config.geometry_shader) desc.GS = static_cast(config.geometry_shader)->GetD3DByteCode(); if (config.pixel_shader) desc.PS = static_cast(config.pixel_shader)->GetD3DByteCode(); GetD3DBlendDesc(&desc.BlendState, config.blending_state); desc.SampleMask = 0xFFFFFFFF; GetD3DRasterizerDesc(&desc.RasterizerState, config.rasterization_state, config.framebuffer_state); GetD3DDepthDesc(&desc.DepthStencilState, config.depth_state); if (config.vertex_format) static_cast(config.vertex_format)->GetInputLayoutDesc(&desc.InputLayout); desc.IBStripCutValue = config.rasterization_state.primitive == PrimitiveType::TriangleStrip ? D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF : D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED; desc.PrimitiveTopologyType = GetD3DTopologyType(config.rasterization_state); if (config.framebuffer_state.color_texture_format != AbstractTextureFormat::Undefined) { desc.NumRenderTargets = 1; desc.RTVFormats[0] = D3DCommon::GetRTVFormatForAbstractFormat( config.framebuffer_state.color_texture_format, config.blending_state.logicopenable); } if (config.framebuffer_state.depth_texture_format != AbstractTextureFormat::Undefined) desc.DSVFormat = D3DCommon::GetDSVFormatForAbstractFormat(config.framebuffer_state.depth_texture_format); desc.SampleDesc.Count = config.framebuffer_state.samples; desc.NodeMask = 1; desc.CachedPSO.pCachedBlob = cache_data; desc.CachedPSO.CachedBlobSizeInBytes = cache_data_size; ID3D12PipelineState* pso; HRESULT hr = g_dx_context->GetDevice()->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(&pso)); if (FAILED(hr)) { WARN_LOG_FMT(VIDEO, "CreateGraphicsPipelineState() {}failed: {}", cache_data ? "with cache data " : "", DX12HRWrap(hr)); return nullptr; } const bool use_integer_rtv = !config.blending_state.blendenable && config.blending_state.logicopenable; return std::make_unique(pso, desc.pRootSignature, config.usage, GetD3DTopology(config.rasterization_state), use_integer_rtv); } AbstractPipeline::CacheData DXPipeline::GetCacheData() const { ComPtr blob; HRESULT hr = m_pipeline->GetCachedBlob(&blob); if (FAILED(hr)) { WARN_LOG_FMT(VIDEO, "ID3D12Pipeline::GetCachedBlob() failed: {}", DX12HRWrap(hr)); return {}; } CacheData data(blob->GetBufferSize()); std::memcpy(data.data(), blob->GetBufferPointer(), blob->GetBufferSize()); return data; } } // namespace DX12