Render Target | The screen/window |
Component Object Model (COM) | Language-independant interface; Reference counting reference/pointer to a resource/class type. |
Swap Chain | The combination of the front and back buffer for double buffering (although more buffers can be used) |
Depth Buffer | Buffer representing the depth of each pixel; 0.0 means closest to viewer and 1.0 means farthest |
Bind | Telling the rendering pipeline that we're going to use a certain resource |
Descriptor | Contains information about a resource and references/points to said resource; is used for binding |
View | A synonym for Descriptor |
DirectX Graphics Infrastructure (DXGI) | Helper API used with DirectX |
Residency | Whether or not a resource is on the GPU memory |
Command Queue | queue of GPU commands; commands sit on the queue until the GPU gets to them for processing. |
Command List | Group of commands which are added to the command queue in order to be executed. |
Fence | A point/event in time used to synchronize the GPU and CPU. An integer incremented by the GPU once it executes a Signal command. After adding the signal command to the GPU command queue, the CPU waits until the integer reaches the expected value, which implies the GPU executed the Signal command and all other commands before the Signal have finished. |
Flush | Wait (using a fence) until all commands in the command queue have been completed by the GPU. |
Resource Transition | Changing the state of a resource, for example from read-only to write-only. Done by adding transition resource barriers to the command list. |
Smart pointer to manage COM objects. ComPtr::Get() -> returns the raw pointer to the underlying object. ComPtr::GetAddressOf() -> returns the address of the raw pointer (e.g. void**) ComPtr::Reset() -> sets the ComPtr to nullptr and decrements the reference count. You can also set the ComPtr = nullptr. COM interface names begin with captial I, e.g. ID3D12GraphicsCommandList
Swapping the front and back buffer is called 'presenting'. Uses the IDXGISwapChain interface. Functions include IDXGISwapChain::Resize and IDXGISwapChain::Present
Possible texture formats of the depth buffer: DXGI_FORMAT_D32_FLOAT_S8X24_UINT - 32bit float, 8bits reserved for spencil buffer and 24bits unused (for padding) DXGI_FORMAT_D32_FLOAT - 32bit float DXGI_FORMAT_D24_UNORM_S8_UINT - 24bit mapped to 0...1, 8bits for stencil buffer mapped to 0...255 DXGI_FORMAT_D16_UNORM - 16bit mapped to 0...1 Stencil buffer is optional and always attached to the depth buffer if used.
CBV/SRV/UAV descriptor - constant buffer view, shader resource view, and unordered access view Sample descriptor - sampler resources used for texturing RTV descriptor - render target resources DSV descriptor - depth/stencil resources Descriptor heap - array of descriptors; memory for all descriptors of a certain type. Separate heaps for each descriptor type. Multiple descriptors can reference the same resource.
IDXGIFactory - used to create swap chain and display adapter interfaces IDXGISwapChain - swap chain interface IDXGIAdapter - display adapter interface (e.g. the graphics card) IDXGIOutput - computer monitor/screen/display interface ID3D12Device - virtual adapter interface used to create command alloctors/command lists/etc. ID3D12CommandQueue - GPU command queue interface ID3D12CommandList - Command list interface ID3D12GraphicsCommandList - GPU command list; child of ID3D12CommandList ID3D12CommandAllocator - memory created and used to store commands in the command list ID3D12Fence - fence interface
ID3D12CommandList::ResourceBarrier adds a resource transition command. D3D12_RESOURCE_STATE_PRESENT - when the resource is being displayed/showed/read D3D12_RESOURCE_STATE_RENDER_TARGET - when the resource is being written/drawn to
Create the ID3D12Device (D3D12CreateDevice()) Create an ID3D12Fence Check 4X MSAA quality level support Create the command queue, command list allocator, and main command list Describe and create the swap chain Create the descriptor heaps Resize the back buffer and create a render target view to the back buffer Create the depth/stencil buffer and its associated depth/stencil view Set the viewport and scissor rectangles
Note d3dx12.h is available from Microsoft at this link
#include <windows.h>
#include <wrl.h>
#include <dxgi1_4.h>
#include <d3d12.h>
#include <D3Dcompiler.h>
#include <DirectXMath.h>
#include <DirectXPackedVector.h>
#include <DirectXColors.h>
#include <DirectXCollision.h>
#include <string>
#include <memory>
#include <algorithm>
#include <vector>
#include <array>
#include <unordered_map>
#include <cstdint>
#include <fstream>
#include <sstream>
#include <cassert>
#include "d3dx12.h"
using Microsoft::WRL::ComPtr;
using namespace DirectX;
using namespace DirectX::PackedVector;
LRESULT CALLBACK
ExampleMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// TODO!!!
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = ExampleMainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = L"MainWnd";
if (!RegisterClass(&wc))
{
MessageBox(0, L"RegisterClass Failed.", 0, 0);
return false;
}
// Compute window rectangle dimensions based on requested client area dimensions.
RECT R = { 0, 0, 800, 600 };
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
int width = R.right - R.left;
int height = R.bottom - R.top;
HWND hMainWnd = CreateWindow(L"MainWnd", L"Window Caption",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, hInstance, 0);
if (!hMainWnd)
{
MessageBox(0, L"CreateWindow Failed.", 0, 0);
return false;
}
ShowWindow(hMainWnd, SW_SHOW);
UpdateWindow(hMainWnd);
// Create the ID3D12Device (D3D12CreateDevice())
Microsoft::WRL::ComPtr<IDXGIFactory4> dxgiFactory;
CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory));
Microsoft::WRL::ComPtr<ID3D12Device> d3dDevice;
HRESULT hardwareResult = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&d3dDevice));
if (FAILED(hardwareResult)) { /* TODO */ }
// Create an ID3D12Fence and descriptor sizes
Microsoft::WRL::ComPtr<ID3D12Fence> fence;
d3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
UINT rtvDescriptorSize = 0; // render target view descriptor size
UINT dsvDescriptorSize = 0; // depth/stencil view descriptor size
UINT cbvSrvUavDescriptorSize = 0; // constant buffer/shader resource/unordered access views descriptor size
rtvDescriptorSize = d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
dsvDescriptorSize = d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
cbvSrvUavDescriptorSize = d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
// Check 4X MSAA quality level support
DXGI_FORMAT backBufferFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
msQualityLevels.Format = backBufferFormat;
msQualityLevels.SampleCount = 4;
msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
msQualityLevels.NumQualityLevels = 0;
d3dDevice->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
&msQualityLevels, sizeof(msQualityLevels));
UINT msaaQuality = msQualityLevels.NumQualityLevels;
// Create the command queue, command list allocator, and main command list
Microsoft::WRL::ComPtr<ID3D12CommandQueue> commandQueue;
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocator;
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> commandList;
{
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
d3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue));
d3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(commandAllocator.GetAddressOf()));
d3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
commandAllocator.Get(), nullptr, IID_PPV_ARGS(commandList.GetAddressOf()));
// start the list in a closed state
commandList->Close();
}
// Describe and create the swap chain
Microsoft::WRL::ComPtr<IDXGISwapChain> swapChain;
const int swapChainBufferCount = 2;
swapChain.Reset();
DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = 800; // window width
sd.BufferDesc.Height = 600; // window height
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = backBufferFormat;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
sd.SampleDesc.Count = msaaQuality > 0 ? 4 : 1;
sd.SampleDesc.Quality = msaaQuality > 0 ? msaaQuality - 1 : 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = swapChainBufferCount;
sd.OutputWindow = hMainWnd;
sd.Windowed = true;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
dxgiFactory->CreateSwapChain(commandQueue.Get(), &sd, swapChain.GetAddressOf());
// Create the descriptor heaps
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> rtvHeap; // render target view descriptors
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> dsvHeap; // depth/stencil view descriptors
{
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
rtvHeapDesc.NumDescriptors = swapChainBufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0;
d3dDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(rtvHeap.GetAddressOf()));
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
d3dDevice->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(dsvHeap.GetAddressOf()));
}
int currBackBuffer = 0;
#define DepthStencilView() \
dsvHeap->GetCPUDescriptorHandleForHeapStart()
#define CurrentBackBufferView() \
CD3DX12_CPU_DESCRIPTOR_HANDLE(rtvHeap->GetCPUDescriptorHandleForHeapStart(), currBackBuffer, rtvDescriptorSize)
// Resize the back buffer and create a render target view to the back buffer
Microsoft::WRL::ComPtr<ID3D12Resource> swapChainBuffer[swapChainBufferCount];
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(rtvHeap->GetCPUDescriptorHandleForHeapStart());
for (UINT i = 0; i < swapChainBufferCount; ++i)
{
// get the ith buffer in the swap chain
swapChain->GetBuffer(i, IID_PPV_ARGS(&swapChainBuffer[i]));
// create a Render Target View to it
d3dDevice->CreateRenderTargetView(swapChainBuffer[i].Get(), nullptr, rtvHeapHandle);
// Next entry in the heap
rtvHeapHandle.Offset(1, rtvDescriptorSize);
}
// Create the depth/stencil buffer and its associated depth/stencil view
DXGI_FORMAT depthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
Microsoft::WRL::ComPtr<ID3D12Resource> depthStencilBuffer;
{
D3D12_RESOURCE_DESC dsDesc;
dsDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
dsDesc.Alignment = 0;
dsDesc.Width = 800; // window width
dsDesc.Height = 600; // window height
dsDesc.DepthOrArraySize = 1;
dsDesc.MipLevels = 1;
dsDesc.Format = depthStencilFormat;
dsDesc.SampleDesc.Count = msaaQuality > 0 ? 4 : 1;
dsDesc.SampleDesc.Quality = msaaQuality > 0 ? msaaQuality - 1 : 0;
dsDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
dsDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
D3D12_CLEAR_VALUE optClear;
optClear.Format = depthStencilFormat;
optClear.DepthStencil.Depth = 1.0f;
optClear.DepthStencil.Stencil = 0;
d3dDevice->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, &dsDesc, D3D12_RESOURCE_STATE_COMMON,
&optClear, IID_PPV_ARGS(depthStencilBuffer.GetAddressOf()));
// create descriptor to mip level 0 of entire resource using the format of the resource
d3dDevice->CreateDepthStencilView(depthStencilBuffer.Get(), nullptr, DepthStencilView());
// transition the resource from its initial state to be used as a depth buffer
commandList->ResourceBarrier(1,
&CD3DX12_RESOURCE_BARRIER::Transition(depthStencilBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE));
}
// Set the viewport and scissor rectangles
// The viewport needs to be reset whenever the command list is reset
D3D12_VIEWPORT vp;
vp.TopLeftX = 0.0f;
vp.TopLeftY = 0.0f;
vp.Width = 800.0f;
vp.Height = 600.0f;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
commandList->RSSetViewports(1, &vp);
// example scissor rect covers the upper-left quadrant of the back buffer
tagRECT scissorRect = { 0, 0, 800/2, 600/2 };
commandList->RSSetScissorRects(1, &scissorRect);
return 0;
}