要介紹 Graphics Pipeline 前,就得先把 Micorsoft 官方的圖片拿出來

從圖可以看到,要實際讓 DX 可以把資料繪製出來,會需要至少以下的流程
其中 Shader 的部分又可以細拆成以下的部分
其中有些階段是可程式化的 Shader Stage,有些則是由 GPU 硬體執行的 Fixed-function Stage。
文章會拆成六篇,邊介紹功能與設定、邊把範例程式展現出來。
此範例會先以輸出三角形為目標,介紹最基本的 Graphics Pipeline 流程。
簡稱為 PSO,是 DX 用來保存固定狀態的物件,PSO 建立後是無法改變的,需要套不同設定的話就必須另外再建一套 PSO。
typedef struct D3D12_GRAPHICS_PIPELINE_STATE_DESC
{
ID3D12RootSignature* pRootSignature; // 指定此 Graphics Pipeline 使用的 Root Signature
D3D12_SHADER_BYTECODE VS; // 指定 Vertex Shader 的編譯後位元碼
D3D12_SHADER_BYTECODE PS; // 指定 Pixel Shader 的編譯後位元碼
D3D12_SHADER_BYTECODE DS; // 指定 Tessellation 使用的 Domain Shader 位元碼
D3D12_SHADER_BYTECODE HS; // 指定 Tessellation 使用的 Hull Shader 位元碼
D3D12_SHADER_BYTECODE GS; // 指定 Geometry Shader 的編譯後位元碼
D3D12_STREAM_OUTPUT_DESC StreamOutput; // 設定是否將 Shader 輸出的頂點資料寫回 Buffer
D3D12_BLEND_DESC BlendState; // 設定 Render Target 的顏色混合方式
UINT SampleMask; // 設定 Multisampling 中允許被寫入的 Sample
D3D12_RASTERIZER_DESC RasterizerState; // 設定 Rasterizer 的填充模式 剔除模式與 Depth Bias
D3D12_DEPTH_STENCIL_DESC DepthStencilState; // 設定 Depth Test 與 Stencil Test 的行為
D3D12_INPUT_LAYOUT_DESC InputLayout; // 定義 Vertex Buffer 傳入 Vertex Shader 的資料格式
D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue; // 設定 Triangle Strip 或 Line Strip 中用來切斷 Strip 的 Index 值
D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType; // 指定 Pipeline 使用的 Primitive 類型
UINT NumRenderTargets; // 指定同時使用的 Render Target 數量 最大為 8 個
DXGI_FORMAT RTVFormats[8]; // 指定每個 Render Target View 對應的像素格式
DXGI_FORMAT DSVFormat; // 指定 Depth Stencil View 使用的資料格式
DXGI_SAMPLE_DESC SampleDesc; // 設定 MSAA 的 Sample 數量與品質等級
UINT NodeMask; // 指定 Multi GPU 環境中此 Pipeline State 所屬的 GPU Node
D3D12_CACHED_PIPELINE_STATE CachedPSO; // 指定可以重複使用的已快取 Pipeline State 資料
D3D12_PIPELINE_STATE_FLAGS Flags; // 設定 Pipeline State 的額外控制 Flag
} D3D12_GRAPHICS_PIPELINE_STATE_DESC;
其中常用的部份為以下幾個,之後的介紹可以更加理解其作用
pRootSignature
VS
PS
BlendState
SampleMask
RasterizerState
DepthStencilState
InputLayout
PrimitiveTopologyType
NumRenderTargets
RTVFormats
DSVFormat
SampleDesc
最後,為此專案中的相關的程式設定
//pipeline_state.h
#pragma once
#include <directx/d3d12.h>
#include <wrl/client.h>
class PipelineState
{
public:
// 初始化 Graphics Pipeline State
void init(D3D12_GRAPHICS_PIPELINE_STATE_DESC decs);
[[nodiscard]]
ID3D12PipelineState* get() const
{
return m_pipelineState.Get();
}
private:
Microsoft::WRL::ComPtr<ID3D12PipelineState> m_pipelineState;
};
//pipeline_state.cpp
#include "pipeline_state.h"
#include <stdexcept>
#include "graphics_engine.h"
void PipelineState::init(D3D12_GRAPHICS_PIPELINE_STATE_DESC desc)
{
if (g_graphicsEngine == nullptr || g_graphicsEngine->getD3DDevice() == nullptr)
throw std::runtime_error("PipelineState: Graphics device is not initialized.");
const auto d3dDevice = g_graphicsEngine->getD3DDevice();
if (auto hr = d3dDevice->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(&m_pipelineState)); FAILED(hr))
{
throw std::runtime_error("PipelineState: Failed to create the graphics pipeline state.");
}
}
Pipelines and Shaders with Direct3D 12