前言
此篇将展示如何利用DX12绘制一个静态的三角形
渲染流程与必备组件
shader
//cpu端
struct PSInput
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
//GPU端
PSInput VSMain(float4 pos : POSITION, float4 color : COLOR)
{
PSInput result;
result.position = pos;
result.color = color;
return result;
}
float4 PSMain(PSInput input) : SV_TARGET //This SV is used for pixel shader output color
{
return input.color;
}
流程
初始化
LoadPipeline
启动调试层
//.cpp
UINT dxgiFactoryFlags = 0;
UINT dxgiFactoryFlags = 0;
/* Enables the debug layer */
//_DEBUG。Runtime selection when compiling
#if defined(_DEBUG)
ComPtr<ID3D12Debug> debugController;
ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)));
debugController->EnableDebugLayer();
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
#endif
创建设备
//.h
ComPtr<ID3D12Device> m_device;
//.cpp
/* create the device */
ComPtr<IDXGIFactory4> factory;
ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
if (m_useWarpDevice)
{
ComPtr<IDXGIAdapter> warpAdapter;
ThrowIfFailed(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));
ThrowIfFailed(D3D12CreateDevice(
warpAdapter.Get(),
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&m_device)
));
}
else
{
ComPtr<IDXGIAdapter1> hardwareAdapter;
GetHardwareAdapter(factory.Get(), &hardwareAdapter);
ThrowIfFailed(D3D12CreateDevice(
hardwareAdapter.Get(),
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&m_device)
));
}
创建命令队列
//.h
ComPtr<ID3D12CommandQueue> m_commandQueue;
//.cpp
/*Describe and create the command queue*/
D3D12_COMMAND_QUEUE_DESC queueDesc{};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.NodeMask = 0;
ThrowIfFailed(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)));
创建交换链
//.h
ComPtr<IDXGISwapChain3> m_swapChain;
UINT m_frameIndex;
//.cpp
/*Describe and create the swap chain*/
/*Describe and create the swap chain*/
DXGI_SWAP_CHAIN_DESC1 swapChainDesc{};
swapChainDesc.Width = m_width;
swapChainDesc.Height = m_height;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; //unsigned-normalized-integer
swapChainDesc.Stereo = FALSE; //Don't need the stereo
swapChainDesc.SampleDesc.Count = 1; //multi-sampling descriptor
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; //describes the surface usage and CPU access options for the back buffer
swapChainDesc.BufferCount = FrameCount;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH; //make the back-buffer contents scale to fit the presentation target size
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; //discard the contents of the back buffer
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; //identifies the transparency behavior of the swap-chain back buffer
ComPtr<IDXGISwapChain1> swapChain;
ThrowIfFailed(factory->CreateSwapChainForHwnd(
m_commandQueue.Get(),
Win32Application::GetHwnd(),
&swapChainDesc,
nullptr,
nullptr,
&swapChain //IDXGISwapChain1。So it needs to use IDXGISwapChain1 first
));
//ALT_ENTER switch fullscreen
ThrowIfFailed(factory->MakeWindowAssociation(Win32Application::GetHwnd(), DXGI_MWA_NO_ALT_ENTER));
ThrowIfFailed(swapChain.As(&m_swapChain));
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
创建RTV描述符堆
//.h
ComPtr<ID3D12DescriptorHeap> m_rtvHeap;
//.cpp
// Describe and create a render target view (RTV) descriptor heap
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = FrameCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ThrowIfFailed(m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap)));
创建帧资源
//.h
ComPtr<ID3D12Resource> m_renderTargets[FrameCount];
static const UINT FrameCount = 2;
//.cpp
m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());
// Create a RTV for each frame
for (UINT n = 0; n < FrameCount; n++)
{
ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
rtvHandle.Offset(1, m_rtvDescriptorSize);
}
创建命令分配器
//.h
ComPtr<ID3D12CommandAllocator> m_commandAllocator;
//.cpp
/*create command allocator*/
ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator)));
LoadAssets
创建空的根签名
//.h
ComPtr<ID3D12RootSignature> m_rootSignature;
//.cpp
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
ThrowIfFailed(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
创建顶点输入布局
//.cpp
/*create and load the vertex buffer*/
//Define the geometry for a triangle.
Vertex triangleVertices[] =
{
{{0.0f, 0.25f * m_aspectRatio, 0.0f}, {1.0f, 0.0f, 0.0f, 1.0f}},
{{0.25f, -0.25f * m_aspectRatio, 0.0f},{0.0f, 1.0f, 0.0f, 1.0f}},
{{-0.25f, -0.25f * m_aspectRatio, 0.0f},{0.0f, 0.0f, 1.0f, 1.0f}}
};
创建管线对象描述符并创建管线对象
//.h
ComPtr<ID3D12PipelineState> m_pipelineState;
//.cpp
ComPtr<ID3DBlob> vertexShader;
ComPtr<ID3DBlob> pixelShader;
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr));
ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr));
// Describe and create the graphics pipeline state object (PSO).
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
psoDesc.pRootSignature = m_rootSignature.Get();
psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get());
psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get());
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
psoDesc.DepthStencilState.DepthEnable = FALSE;
psoDesc.DepthStencilState.StencilEnable = FALSE;
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
psoDesc.SampleDesc.Count = 1;
ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState)));
创建命令列表
//.h
ComPtr<ID3D12GraphicsCommandList> m_commandList;
//.cpp
/*create the command list*/
ThrowIfFailed(m_device->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
m_commandAllocator.Get(),
m_pipelineState.Get(),
IID_PPV_ARGS(&m_commandList)
));
关闭命令列表
/*close the command list*/
ThrowIfFailed(m_commandList->Close());
创建并加载顶点缓冲区
//.h
ComPtr<ID3D12Resource> m_vertexBuffer;
//.cpp
/*create and load the vertex buffer*/
//Define the geometry for a triangle.
Vertex triangleVertices[] =
{
{{0.0f, 0.25f * m_aspectRatio, 0.0f}, {1.0f, 0.0f, 0.0f, 1.0f}},
{{0.25f, -0.25f * m_aspectRatio, 0.0f},{0.0f, 1.0f, 0.0f, 1.0f}},
{{-0.25f, -0.25f * m_aspectRatio, 0.0f},{0.0f, 0.0f, 1.0f, 1.0f}}
};
const UINT vertexBufferSize = sizeof(triangleVertices);
//Creates a resource and an heap.the resource is mapped to the heap
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_vertexBuffer)
));
//Copy the triangle data to the vertex buffer
UINT8* pVertexDataBegin;
CD3DX12_RANGE readRange(0, 0); //Describes a memory range
ThrowIfFailed(m_vertexBuffer->Map(
0,
&readRange,
reinterpret_cast<void**>(&pVertexDataBegin) ////pVertexDataBegin指向m_vertexBuffer
));
memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
m_vertexBuffer->Unmap(0, nullptr); //null pointer indicates the entire subresource might have been modified by the CPU
创建顶点缓冲区视图
//.h
D3D12_VERTEX_BUFFER_VIEW m_vertexBufferView;
//.cpp
/*create the vertex buffer view*/
m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
m_vertexBufferView.SizeInBytes = vertexBufferSize;
m_vertexBufferView.StrideInBytes = sizeof(Vertex);
创建围栏
//.h
ComPtr<ID3D12Fence> m_fence;
UINT64 m_fenceValue;
//.cpp
ThrowIfFailed(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
m_fenceValue = 1;
创建事件句柄
//.h
HANDLE m_fenceEvent;
//.cpp
// Create an event handle to use for frame synchronization.
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (m_fenceEvent == nullptr)
{
ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
}
等待GPU完成
//.cpp
WaitForPreviousFrame();
渲染
向命令列表加入命令
重置命令列表分配器
//.cpp
ThrowIfFailed(m_commandAllocator->Reset());
重置命令列表
//.cpp
//reset the command list
ThrowIfFailed(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get()));
设置图像根签名
//.cpp
m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
设置视图和裁剪矩阵
//.h
CD3DX12_VIEWPORT m_viewport;
CD3DX12_RECT m_scissorRect;
//.cpp
m_commandList->RSSetViewports(1, &m_viewport);
m_commandList->RSSetScissorRects(1, &m_scissorRect);
设置资源屏障,将后台缓冲区用作渲染目标
//.cpp
// Indicate that the back buffer will be used as a render target.
m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
向命令列表添加命令
//.cpp
// Record commands.
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
m_commandList->DrawInstanced(3, 1, 0, 0);
指出当命令列表的命令被执行后后台缓冲区被用于呈现
//.cpp
// Indicate that the back buffer will now be used to present.
m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
关闭命令列表
ThrowIfFailed(m_commandList->Close());
执行命令列表
//.cpp
// Execute the command list
ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
呈现帧画面
//.cpp
// Present the frame
ThrowIfFailed(m_swapChain->Present(1, 0));
等待GPU完成
//.cpp
WaitForPreviousFrame();
销毁
等待GPU完成
WaitForPreviousFrame();
const UINT64 fence = m_fenceValue;
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), fence));
m_fenceValue++;
// Wait until the previous frame is finished.
if (m_fence->GetCompletedValue() < fence)
{
ThrowIfFailed(m_fence->SetEventOnCompletion(fence, m_fenceEvent));
WaitForSingleObject(m_fenceEvent, INFINITE);
}
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
关闭事件句柄
//.cpp
CloseHandle(m_fenceEvent);
报错
-
hlsl报错(X3501)
这是因为hlsl没有写主函数
解决方案:
右键".hlsl"文件->属性->配置属性->常规->项类型。选择"不参与生产"
-
左值报错(C2102)
解决方案:
右键项目->属性->配置属性->c/c++->语言->"符合模式"改为否