前面有個舉例顏色格子的例子,這邊就用這例子來介紹 StructuredBuffer
基本上來說,一般傳遞資料都是一筆一筆的,不過這樣傳的問題就會像是前面的例子那樣,如果有一百萬筆資料要傳你會讓 CPU 傳一百萬次才完成。如果這裡面的資料內容其實是相同的那就可以全放在一起後一口氣傳進 GPU 裡。
而 C++ 裡的 struct 大小要與 HLSL 裡的對應
// 這邊 C++ 是 32 bytes
struct ColorBlockData
{
Vector2 center;
Vector2 halfSize;
Vector4 color;
};
// 這邊 HLSL 也要是 32 bytes
struct ColorBlockData
{
float2 center;
float2 halfSize;
float4 color;
};
各家引擎傳遞的方法都不太一樣,這邊就使用相對簡陋的方式去設定 StructuredBuffer 的傳遞
render_state.h
#pragma once
#include <array>
#include <cstdint>
#include <directx/d3d12.h>
#include <wrl/client.h>
class ConstantBuffer;
class IShaderResource;
class IUnorderAccessResource;
class RenderContext;
enum class ShaderStage
{
Graphics,
Compute
};
class RenderState
{
public:
static constexpr UINT FrameCount = 2;
static constexpr UINT ConstantBufferCount = 4;
static constexpr UINT ShaderResourceCount = 32;
static constexpr UINT UnorderedAccessCount = 8;
static constexpr UINT ResourceTableRootParameter = 4;
void init(ID3D12Device* device);
void setStructuredBuffer(UINT slot, IShaderResource& buffer);
void setRWStructuredBuffer(
UINT shaderResourceSlot,
UINT unorderedAccessSlot,
IUnorderAccessResource& buffer);
void clearStructuredBuffer(UINT slot);
void clearRWStructuredBuffer(UINT shaderResourceSlot, UINT unorderedAccessSlot);
void clearStructuredBuffers();
void setConstantBuffer(
RenderContext& renderContext,
ShaderStage stage,
UINT slot,
ConstantBuffer& buffer) const;
void applyGraphics(RenderContext& renderContext, UINT frameIndex) const;
void applyCompute(RenderContext& renderContext, UINT frameIndex) const;
private:
static constexpr UINT ShaderResourceOffset = ConstantBufferCount;
static constexpr UINT UnorderedAccessOffset = ShaderResourceOffset + ShaderResourceCount;
static constexpr UINT DescriptorCount = UnorderedAccessOffset + UnorderedAccessCount;
D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(UINT frameIndex, UINT descriptorIndex) const;
D3D12_GPU_DESCRIPTOR_HANDLE getGpuTableStart(UINT frameIndex) const;
void writeNullConstantBuffer(UINT frameIndex, UINT slot);
void writeNullShaderResource(UINT frameIndex, UINT slot);
void writeNullUnorderedAccess(UINT frameIndex, UINT slot);
void requireInitialized() const;
ID3D12Device* m_device = nullptr;
std::array<Microsoft::WRL::ComPtr<ID3D12DescriptorHeap>, FrameCount> m_descriptorHeaps;
UINT m_descriptorSize = 0;
bool m_initialized = false;
};
render_state.cpp
#include "render_state.h"
#include <stdexcept>
#include "constant_buffer.h"
#include "render_context.h"
#include "shader_resource.h"
#include "unorder_access_resource.h"
// 初始化 RenderState 使用的 Descriptor Heap 與相關狀態
void RenderState::init(ID3D12Device* device)
{
// 避免重複初始化
if (m_initialized)
// 已初始化時拋出邏輯錯誤
throw std::logic_error("RenderState: Render state is already initialized.");
// 確認傳入的 D3D12 Device 有效
if (device == nullptr)
// Device 無效時拋出參數錯誤
throw std::invalid_argument("RenderState: A graphics device is required.");
// 保存 D3D12 Device 供後續建立 Descriptor 使用
m_device = device;
// 取得 CBV、SRV、UAV Descriptor 在 Heap 中的間隔大小
m_descriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
// 建立並零初始化 Descriptor Heap 設定
D3D12_DESCRIPTOR_HEAP_DESC description{};
// 指定 Heap 用來存放 CBV、SRV 與 UAV
description.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
// 設定 Heap 內總 Descriptor 數量
description.NumDescriptors = DescriptorCount;
// 設定 Heap 可由 Shader 存取
description.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
// 為每個 Frame 建立獨立的 Descriptor Heap
for (UINT frameIndex = 0; frameIndex < FrameCount; ++frameIndex)
{
// 建立目前 Frame 使用的 Shader-visible Descriptor Heap
if (FAILED(device->CreateDescriptorHeap(&description,
IID_PPV_ARGS(m_descriptorHeaps[frameIndex].ReleaseAndGetAddressOf()))))
// Descriptor Heap 建立失敗時拋出執行期錯誤
throw std::runtime_error("RenderState: Failed to create a shader-visible descriptor heap.");
}
// 標記 RenderState 已完成初始化
m_initialized = true;
// 將所有資源 Descriptor 初始化成 Null Descriptor
clearStructuredBuffers();
}
// 將 Structured Buffer 設定到指定 SRV slot
void RenderState::setStructuredBuffer(UINT slot, IShaderResource& buffer)
{
// 確認 RenderState 已完成初始化
requireInitialized();
// 確認 SRV slot 沒有超出允許範圍
if (slot >= ShaderResourceCount)
// slot 超出範圍時拋出錯誤
throw std::out_of_range("RenderState: Shader-resource slot is out of range.");
// 為每個 Frame 註冊對應的 SRV
for (UINT frameIndex = 0; frameIndex < FrameCount; ++frameIndex)
// 將 Buffer 的 SRV 寫入目前 Frame 對應的 Descriptor
buffer.registerShaderResourceView(
// 取得指定 SRV slot 的 CPU Descriptor Handle
getCpuHandle(frameIndex, ShaderResourceOffset + slot),
// 將 Frame index 傳給資源以取得對應 Frame 的內容
static_cast<int>(frameIndex));
}
// 同時將 Structured Buffer 設定成 SRV 與 UAV
void RenderState::setRWStructuredBuffer(UINT shaderResourceSlot, UINT unorderedAccessSlot,
IUnorderAccessResource& buffer)
{
// 將 Buffer 註冊到指定的 SRV slot
setStructuredBuffer(shaderResourceSlot, buffer);
// 確認 UAV slot 沒有超出允許範圍
if (unorderedAccessSlot >= UnorderedAccessCount)
// slot 超出範圍時拋出錯誤
throw std::out_of_range("RenderState: Unordered-access slot is out of range.");
// 為每個 Frame 註冊對應的 UAV
for (UINT frameIndex = 0; frameIndex < FrameCount; ++frameIndex)
// 將 Buffer 的 UAV 寫入目前 Frame 對應的 Descriptor
buffer.RegisterUnorderAccessView(
// 取得指定 UAV slot 的 CPU Descriptor Handle
getCpuHandle(frameIndex, UnorderedAccessOffset + unorderedAccessSlot),
// 將 Frame index 傳給資源以取得對應 Frame 的內容
static_cast<int>(frameIndex));
}
// 清除指定 SRV slot
void RenderState::clearStructuredBuffer(UINT slot)
{
// 確認 RenderState 已完成初始化
requireInitialized();
// 確認 SRV slot 沒有超出允許範圍
if (slot >= ShaderResourceCount)
// slot 超出範圍時拋出錯誤
throw std::out_of_range("RenderState: Shader-resource slot is out of range.");
// 清除每個 Frame 中指定的 SRV slot
for (UINT frameIndex = 0; frameIndex < FrameCount; ++frameIndex)
// 寫入 Null SRV
writeNullShaderResource(frameIndex, slot);
}
// 清除指定 Buffer 對應的 SRV 與 UAV slot
void RenderState::clearRWStructuredBuffer(UINT shaderResourceSlot, UINT unorderedAccessSlot)
{
// 清除指定的 SRV slot
clearStructuredBuffer(shaderResourceSlot);
// 確認 UAV slot 沒有超出允許範圍
if (unorderedAccessSlot >= UnorderedAccessCount)
// slot 超出範圍時拋出錯誤
throw std::out_of_range("RenderState: Unordered-access slot is out of range.");
// 清除每個 Frame 中指定的 UAV slot
for (UINT frameIndex = 0; frameIndex < FrameCount; ++frameIndex)
// 寫入 Null UAV
writeNullUnorderedAccess(frameIndex, unorderedAccessSlot);
}
// 清除所有 Frame 中的 CBV、SRV 與 UAV Descriptor
void RenderState::clearStructuredBuffers()
{
// 確認 RenderState 已完成初始化
requireInitialized();
// 逐一處理所有 Frame
for (UINT frameIndex = 0; frameIndex < FrameCount; ++frameIndex)
{
// 清除目前 Frame 的所有 Constant Buffer slot
for (UINT slot = 0; slot < ConstantBufferCount; ++slot)
// 寫入 Null CBV
writeNullConstantBuffer(frameIndex, slot);
// 清除目前 Frame 的所有 Shader Resource slot
for (UINT slot = 0; slot < ShaderResourceCount; ++slot)
// 寫入 Null SRV
writeNullShaderResource(frameIndex, slot);
// 清除目前 Frame 的所有 Unordered Access slot
for (UINT slot = 0; slot < UnorderedAccessCount; ++slot)
// 寫入 Null UAV
writeNullUnorderedAccess(frameIndex, slot);
}
}
// 將 Constant Buffer 綁定到指定 Shader Stage 的 Root Parameter
void RenderState::setConstantBuffer(RenderContext& renderContext, ShaderStage stage, UINT slot,
ConstantBuffer& buffer) const
{
// 確認 RenderState 已完成初始化
requireInitialized();
// 確認 Constant Buffer slot 沒有超出允許範圍
if (slot >= ConstantBufferCount)
// slot 超出範圍時拋出錯誤
throw std::out_of_range("RenderState: Constant-buffer slot is out of range.");
// 取得 Constant Buffer 的 GPU Virtual Address
const D3D12_GPU_VIRTUAL_ADDRESS address = buffer.getGPUVirtualAddress();
// 判斷 Constant Buffer 要綁定到 Graphics 或 Compute Pipeline
if (stage == ShaderStage::Graphics)
// 設定 Graphics Root CBV
renderContext.setGraphicsRootConstantBufferView(slot, address);
else
// 設定 Compute Root CBV
renderContext.setComputeRootConstantBufferView(slot, address);
}
// 套用 Graphics Pipeline 所需的 Descriptor Heap 與 Descriptor Table
void RenderState::applyGraphics(RenderContext& renderContext, UINT frameIndex) const
{
// 確認 RenderState 已完成初始化
requireInitialized();
// 確認 Frame index 沒有超出範圍
if (frameIndex >= FrameCount)
// Frame index 超出範圍時拋出錯誤
throw std::out_of_range("RenderState: Frame index is out of range.");
// 將目前 Frame 的 Descriptor Heap 綁定到 Command List
renderContext.setDescriptorHeap(m_descriptorHeaps[frameIndex].Get());
// 將目前 Frame 的 Descriptor Table 綁定到 Graphics Root Signature
renderContext.setGraphicsRootDescriptorTable(ResourceTableRootParameter, getGpuTableStart(frameIndex));
}
// 套用 Compute Pipeline 所需的 Descriptor Heap 與 Descriptor Table
void RenderState::applyCompute(RenderContext& renderContext, UINT frameIndex) const
{
// 確認 RenderState 已完成初始化
requireInitialized();
// 確認 Frame index 沒有超出範圍
if (frameIndex >= FrameCount)
// Frame index 超出範圍時拋出錯誤
throw std::out_of_range("RenderState: Frame index is out of range.");
// 將目前 Frame 的 Descriptor Heap 綁定到 Command List
renderContext.setDescriptorHeap(m_descriptorHeaps[frameIndex].Get());
// 將目前 Frame 的 Descriptor Table 綁定到 Compute Root Signature
renderContext.setComputeRootDescriptorTable(ResourceTableRootParameter, getGpuTableStart(frameIndex));
}
// 取得指定 Descriptor 的 CPU Handle
D3D12_CPU_DESCRIPTOR_HANDLE RenderState::getCpuHandle(UINT frameIndex, UINT descriptorIndex) const
{
// 取得目前 Frame Descriptor Heap 起始位置的 CPU Handle
D3D12_CPU_DESCRIPTOR_HANDLE handle = m_descriptorHeaps[frameIndex]->GetCPUDescriptorHandleForHeapStart();
// 根據 Descriptor index 與 Descriptor 大小計算目標位置
handle.ptr += static_cast<SIZE_T>(descriptorIndex) * m_descriptorSize;
// 回傳計算完成的 CPU Descriptor Handle
return handle;
}
// 取得指定 Frame Descriptor Heap 的 GPU 起始 Handle
D3D12_GPU_DESCRIPTOR_HANDLE RenderState::getGpuTableStart(UINT frameIndex) const
{
// 回傳 Descriptor Heap 起始位置的 GPU Handle
return m_descriptorHeaps[frameIndex]->GetGPUDescriptorHandleForHeapStart();
}
// 在指定 Constant Buffer slot 建立 Null CBV
void RenderState::writeNullConstantBuffer(UINT frameIndex, UINT slot)
{
// 使用空 Resource 建立 Null Constant Buffer View
m_device->CreateConstantBufferView(nullptr, getCpuHandle(frameIndex, slot));
}
// 在指定 Shader Resource slot 建立 Null SRV
void RenderState::writeNullShaderResource(UINT frameIndex, UINT slot)
{
// 建立並零初始化 SRV 設定
D3D12_SHADER_RESOURCE_VIEW_DESC description{};
// Structured Buffer 不使用特定像素格式
description.Format = DXGI_FORMAT_UNKNOWN;
// 將 SRV 類型設定成 Buffer
description.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
// 使用 D3D12 預設的 Shader Component Mapping
description.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
// Null SRV 使用一個虛擬元素描述 Buffer
description.Buffer.NumElements = 1;
// 將虛擬元素大小設定成 32-bit
description.Buffer.StructureByteStride = sizeof(std::uint32_t);
// 使用空 Resource 建立 Null Shader Resource View
m_device->CreateShaderResourceView(nullptr, &description,
// 將 SRV 寫入指定 Frame 與 slot 的 Descriptor
getCpuHandle(frameIndex, ShaderResourceOffset + slot));
}
// 在指定 Unordered Access slot 建立 Null UAV
void RenderState::writeNullUnorderedAccess(UINT frameIndex, UINT slot)
{
// 建立並零初始化 UAV 設定
D3D12_UNORDERED_ACCESS_VIEW_DESC description{};
// Structured Buffer 不使用特定像素格式
description.Format = DXGI_FORMAT_UNKNOWN;
// 將 UAV 類型設定成 Buffer
description.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
// Null UAV 使用一個虛擬元素描述 Buffer
description.Buffer.NumElements = 1;
// 將虛擬元素大小設定成 32-bit
description.Buffer.StructureByteStride = sizeof(std::uint32_t);
// 使用空 Resource 與空 Counter Resource 建立 Null UAV
m_device->CreateUnorderedAccessView(nullptr, nullptr, &description,
// 將 UAV 寫入指定 Frame 與 slot 的 Descriptor
getCpuHandle(frameIndex, UnorderedAccessOffset + slot));
}
// 確認 RenderState 已完成初始化
void RenderState::requireInitialized() const
{
// 尚未初始化時禁止使用 RenderState
if (!m_initialized)
// 拋出錯誤提醒必須先呼叫 init
throw std::logic_error("RenderState: init must be called before use.");
}
剩下為實作的程式
//color_block.hlsl
// 定義單一色塊在 Structured Buffer 中的資料格式
struct ColorBlockData
{
// 儲存色塊中心位置
float2 center;
// 儲存色塊寬高的一半
float2 halfSize;
// 儲存色塊的 RGBA 顏色
float4 color;
};
// 將色塊資料綁定到 t0 Shader Resource slot
StructuredBuffer<ColorBlockData> colorBlocks : register(t0);
// 定義 Vertex Shader 的輸入資料
struct VSInput
{
// 接收 Vertex Buffer 傳入的頂點位置
float4 position : POSITION;
// 接收目前 instance 的 index
uint instanceId : SV_InstanceID;
};
// 定義 Vertex Shader 傳給 Pixel Shader 的資料
struct VSOutput
{
// 儲存轉換後的裁切空間位置
float4 position : SV_Position;
// 傳遞目前色塊的顏色
float4 color : COLOR0;
};
// 執行每個頂點的 Vertex Shader
VSOutput VSMain(VSInput input)
{
// 根據目前 instance index 取得對應的色塊資料
ColorBlockData block = colorBlocks[input.instanceId];
// 建立 Vertex Shader 輸出資料
VSOutput output;
// 將共用四邊形頂點縮放至色塊大小,再平移至色塊中心
output.position = float4(block.center + input.position.xy * block.halfSize, 0.0f, 1.0f);
// 將目前色塊顏色傳給 Pixel Shader
output.color = block.color;
// 回傳 Vertex Shader 輸出
return output;
}
// 執行每個 Pixel 的 Pixel Shader
float4 PSMain(VSOutput input) : SV_Target0
{
// 將 Vertex Shader 傳入的顏色直接輸出到 Render Target
return input.color;
}
//main.cpp
#include <array>
#include <cstdint>
#include <cstdlib>
#include <directx/d3dx12_core.h>
#include <exception>
#include <stdexcept>
#include "graphics_engine.h"
#include "math/vector.h"
#include "my_engine.h"
#include "render_state.h"
#include "skyline_debugger.h"
#include "system.h"
namespace
{
// 定義單一頂點只包含二維位置資料
struct SimpleVertex
{
// 儲存頂點在區域座標中的二維位置
Vector2 position;
};
// 定義傳給 Shader 的單一色塊資料
struct ColorBlockData
{
// 儲存色塊中心位置
Vector2 center;
// 儲存色塊寬高的一半
Vector2 halfSize;
// 儲存色塊的 RGBA 顏色
Vector4 color;
};
static_assert(sizeof(ColorBlockData) == sizeof(float) * 8,
"ColorBlockData must match the HLSL structured-buffer layout.");
// 設定水平方向的色塊數量
constexpr std::uint32_t ColumnCount = 10;
// 設定垂直方向的色塊數量
constexpr std::uint32_t RowCount = 10;
// 計算畫面上的色塊總數
constexpr std::uint32_t BlockCount = ColumnCount * RowCount;
// 建立所有色塊的位置、大小與顏色資料
std::array<ColorBlockData, BlockCount> createColorBlocks()
{
// 定義色塊使用的顏色表
static const std::array<Vector4, 20> palette = {
Vector4{1.00f, 0.05f, 0.03f, 1.0f}, Vector4{0.05f, 0.08f, 1.00f, 1.0f}, Vector4{1.00f, 0.88f, 0.03f, 1.0f},
Vector4{0.12f, 0.92f, 0.10f, 1.0f}, Vector4{0.95f, 0.20f, 0.68f, 1.0f}, Vector4{0.02f, 0.85f, 0.88f, 1.0f},
Vector4{1.00f, 0.43f, 0.02f, 1.0f}, Vector4{0.45f, 0.05f, 0.62f, 1.0f}, Vector4{0.98f, 0.67f, 0.82f, 1.0f},
Vector4{0.02f, 0.45f, 0.13f, 1.0f}, Vector4{0.33f, 0.27f, 0.92f, 1.0f}, Vector4{0.22f, 0.90f, 0.67f, 1.0f},
Vector4{1.00f, 0.30f, 0.32f, 1.0f}, Vector4{0.04f, 0.09f, 0.48f, 1.0f}, Vector4{0.98f, 0.96f, 0.39f, 1.0f},
Vector4{0.83f, 0.02f, 0.59f, 1.0f}, Vector4{0.02f, 0.59f, 0.56f, 1.0f}, Vector4{1.00f, 0.62f, 0.42f, 1.0f},
Vector4{0.57f, 0.05f, 0.98f, 1.0f}, Vector4{0.54f, 0.63f, 0.12f, 1.0f},
};
// 根據欄數計算每個色塊在 NDC 中的寬度
constexpr float blockWidth = 2.0f / static_cast<float>(ColumnCount);
// 根據列數計算每個色塊在 NDC 中的高度
constexpr float blockHeight = 2.0f / static_cast<float>(RowCount);
// 計算每個色塊從中心到邊界的距離
const Vector2 halfSize{blockWidth * 0.5f, blockHeight * 0.5f};
// 建立並零初始化所有色塊資料
std::array<ColorBlockData, BlockCount> blocks{};
// 逐列建立色塊
for (std::uint32_t row = 0; row < RowCount; ++row)
{
// 逐欄建立目前列中的色塊
for (std::uint32_t column = 0; column < ColumnCount; ++column)
{
// 將二維列欄座標轉換成一維陣列 index
const std::uint32_t blockIndex = row * ColumnCount + column;
// 依照色塊位置產生分散的顏色表 index
const std::uint32_t colorIndex = (blockIndex * 7 + row * 3) % palette.size();
// 計算目前色塊在 NDC 中的中心位置
const Vector2 center{-1.0f + blockWidth * (static_cast<float>(column) + 0.5f),
1.0f - blockHeight * (static_cast<float>(row) + 0.5f)};
// 寫入目前色塊的位置、大小與顏色
blocks[blockIndex] = {center, halfSize, palette[colorIndex]};
}
}
// 回傳建立完成的色塊資料
return blocks;
}
// 初始化 Root Signature
void initRootSignature(RootSignature& rootSignature)
{
// 使用線性取樣與 Wrap addressing mode 建立 Root Signature
rootSignature.init(D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE_WRAP);
}
// 建立繪製色塊使用的 Graphics Pipeline State 設定
D3D12_GRAPHICS_PIPELINE_STATE_DESC createColorBlockPipelineStateDescription(ID3D12RootSignature* rootSignature,
ID3DBlob* vertexShader,
ID3DBlob* pixelShader)
{
if (rootSignature == nullptr)
throw std::invalid_argument("ColorBlockPipeline: Root signature is required.");
if (vertexShader == nullptr)
throw std::invalid_argument("ColorBlockPipeline: Vertex shader is required.");
if (pixelShader == nullptr)
throw std::invalid_argument("ColorBlockPipeline: Pixel shader is required.");
// 定義 Vertex Buffer 中 POSITION attribute 的格式
static constexpr D3D12_INPUT_ELEMENT_DESC inputElementDescriptions[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
// 建立並零初始化 Graphics Pipeline State 設定
D3D12_GRAPHICS_PIPELINE_STATE_DESC description{};
// 設定 Vertex Shader 的 Input Layout
description.InputLayout = {inputElementDescriptions, _countof(inputElementDescriptions)};
// 指定 Pipeline 使用的 Root Signature
description.pRootSignature = rootSignature;
// 設定 Vertex Shader bytecode
description.VS = CD3DX12_SHADER_BYTECODE(vertexShader);
// 設定 Pixel Shader bytecode
description.PS = CD3DX12_SHADER_BYTECODE(pixelShader);
// 使用 D3D12 預設 Rasterizer State
description.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
// 關閉背面剔除以繪製所有三角形方向
description.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
// 使用 D3D12 預設 Blend State
description.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
// 使用 D3D12 預設 Depth Stencil State
description.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
// 關閉 Depth Test
description.DepthStencilState.DepthEnable = FALSE;
// 禁止寫入 Depth Buffer
description.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
// 關閉 Stencil Test
description.DepthStencilState.StencilEnable = FALSE;
// 啟用所有 sample
description.SampleMask = UINT_MAX;
// 指定 Pipeline 接收 Triangle primitive
description.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
// 設定使用一個 Render Target
description.NumRenderTargets = 1;
// 設定 Render Target 的像素格式
description.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
// 設定 Depth Stencil Buffer 的像素格式
description.DSVFormat = DXGI_FORMAT_D32_FLOAT;
// 關閉 MSAA 並使用單一 sample
description.SampleDesc.Count = 1;
// 回傳完成的 Pipeline State 設定
return description;
}
// 建立色塊繪製使用的 Pipeline State
void initPipelineState(PipelineState& pipelineState, RootSignature& rootSignature, Shader& vertexShader,
Shader& pixelShader)
{
// 根據 Root Signature 與 Shader 建立 Pipeline State 描述
const auto description = createColorBlockPipelineStateDescription(
rootSignature.get(), vertexShader.getCompiledBlob(), pixelShader.getCompiledBlob());
// 使用描述資料建立 Pipeline State Object
pipelineState.init(description);
}
} // namespace
// Windows GUI 應用程式的進入點
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
// 捕捉初始化與執行期間可能發生的例外
try
{
// 初始化 Skyline Debugger 並在建立 D3D12 Device 前啟用 Debug Layer
SkylineDebugger::Initialize("DayX_ColorBlock");
// 建立應用程式視窗
initWindow(hInstance, hPrevInstance, lpCmdLine, nCmdShow, TEXT("Color Block"));
// 確認視窗已成功建立
if (g_hWnd == nullptr)
throw std::runtime_error("DayX_ColorBlock: Failed to create the application window.");
// 建立 Graphics Engine
GraphicsEngine graphicsEngine;
// 初始化 D3D12 Device、Swap Chain 與相關圖形資源
graphicsEngine.init(g_hWnd, FRAME_BUFFER_W, FRAME_BUFFER_H);
// 將建立完成的 D3D12 Device 交給 Skyline Debugger 設定
SkylineDebugger::ConfigureD3D12(graphicsEngine.getD3DDevice());
// 建立 Root Signature
RootSignature rootSignature;
// 初始化繪圖使用的 Root Signature
initRootSignature(rootSignature);
// 建立 Vertex Shader 與 Pixel Shader
Shader vertexShader, pixelShader;
// 編譯並載入 Vertex Shader
vertexShader.loadVS("assets/shaders/color_block.hlsl", "VSMain");
// 編譯並載入 Pixel Shader
pixelShader.loadPS("assets/shaders/color_block.hlsl", "PSMain");
// 建立 Graphics Pipeline State
PipelineState pipelineState;
// 使用 Shader 與 Root Signature 初始化 Pipeline State
initPipelineState(pipelineState, rootSignature, vertexShader, pixelShader);
// 定義所有色塊共用的單位四邊形頂點
const SimpleVertex vertices[] = {{{-1.0f, -1.0f}}, {{-1.0f, 1.0f}}, {{1.0f, 1.0f}}, {{1.0f, -1.0f}}};
// 建立四邊形的 Vertex Buffer
VertexBuffer blockVertexBuffer;
// 配置可容納所有頂點的 Vertex Buffer
blockVertexBuffer.init(_countof(vertices), sizeof(vertices[0]));
// 將頂點資料複製進 Vertex Buffer
blockVertexBuffer.copy(vertices);
// 定義組成四邊形兩個三角形的 index
const std::uint16_t indices[] = {0, 1, 2, 0, 2, 3};
// 建立四邊形的 Index Buffer
IndexBuffer blockIndexBuffer;
// 配置可容納所有 index 的 Index Buffer
blockIndexBuffer.init(_countof(indices), sizeof(indices[0]));
// 將 index 資料複製進 Index Buffer
blockIndexBuffer.copy(indices);
// 產生所有色塊的 instance 資料
auto blocks = createColorBlocks();
// 建立存放所有色塊資料的 Structured Buffer
StructuredBuffer colorBlockBuffer;
// 將色塊資料上傳到 Structured Buffer
colorBlockBuffer.init(sizeof(ColorBlockData), static_cast<int>(blocks.size()), blocks.data());
// 建立管理 Descriptor 與資源綁定狀態的 RenderState
RenderState renderState;
// 初始化 RenderState 使用的 Descriptor Heap
renderState.init(graphicsEngine.getD3DDevice());
// 將色塊 Structured Buffer 綁定到 SRV slot 0
renderState.setStructuredBuffer(0, colorBlockBuffer);
// 取得 Graphics Engine 使用的 RenderContext
RenderContext& renderContext = graphicsEngine.getRenderContext();
// 將目前的 Command List 設定到 RenderContext
renderContext.SetCommandList(graphicsEngine.getCommandList());
// 持續處理視窗訊息直到應用程式結束
while (dispatchWindowMessage())
{
// 開始目前 Frame 的繪製流程
graphicsEngine.beginRender();
// 綁定目前繪圖使用的 Root Signature
renderContext.setRootSignature(rootSignature);
// 綁定色塊繪製使用的 Pipeline State
renderContext.setPipelineState(pipelineState);
// 綁定目前 Back Buffer 對應的 Descriptor Heap 與資源表
renderState.applyGraphics(renderContext, graphicsEngine.getBackBufferIndex());
// 指定輸入的 index 以 Triangle List 方式組成三角形
renderContext.setPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 綁定四邊形的 Vertex Buffer
renderContext.setVertexBuffer(blockVertexBuffer);
// 綁定四邊形的 Index Buffer
renderContext.setIndexBuffer(blockIndexBuffer);
// 使用同一組四邊形 geometry 執行 BlockCount 次 instanced drawing
renderContext.drawIndexedInstanced(_countof(indices), BlockCount);
// 結束目前 Frame 並提交繪圖命令
graphicsEngine.endRender();
// 輸出 D3D12 Debug Layer 產生的訊息
SkylineDebugger::LogD3D12Messages(graphicsEngine.getD3DDevice());
}
SkylineDebugger::Shutdown();
return EXIT_SUCCESS;
}
catch (const std::exception& exception)
{
SkylineDebugger::ShowFatalError("DayX_ColorBlock initialization failed", exception);
SkylineDebugger::Shutdown();
return EXIT_FAILURE;
}
catch (...)
{
SkylineDebugger::ShowFatalError("DayX_ColorBlock initialization failed", "An unknown fatal error occurred.");
SkylineDebugger::Shutdown();
return EXIT_FAILURE;
}
}
