Skip to content

Instantly share code, notes, and snippets.

@nickav
Created May 8, 2026 15:16
Show Gist options
  • Select an option

  • Save nickav/a2dcb08888d3e4ec278170961d068f93 to your computer and use it in GitHub Desktop.

Select an option

Save nickav/a2dcb08888d3e4ec278170961d068f93 to your computer and use it in GitHub Desktop.
Minimal DirectX12 Hello Triangle With Textures, MSAA
#pragma comment(lib, "d3d12")
#pragma comment(lib, "dxgi")
#pragma comment(lib, "d3dcompiler")
#pragma comment(lib, "user32")
#include <windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <d3dcompiler.h>
static const wchar_t* TITLE = L"Minimal D3D12";
#define CountOf(x) (sizeof(x) / sizeof(x[0]))
static const UINT MSAA_SAMPLE_COUNT = 4;
struct float2 { float x, y; };
struct float3 { float x, y, z; };
struct float4 { float x, y, z, w; };
struct vertex {
float3 pos;
float2 uv;
float4 color;
};
void check(HRESULT hr) {
if (FAILED(hr)) ExitProcess(hr);
}
#define Str(x) #x
const char *shader_source = Str(
struct VSInput {
float3 pos : POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
struct PSInput {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
Texture2D g_texture : register(t0);
SamplerState g_sampler : register(s0);
PSInput vert_main(VSInput input) {
PSInput output;
output.pos = float4(input.pos, 1.0f);
output.uv = input.uv;
output.color = input.color;
return output;
}
float4 frag_main(PSInput input) : SV_TARGET {
return g_texture.Sample(g_sampler, input.uv) * input.color;
}
);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
{
HMODULE user32 = LoadLibraryA("user32.dll");
typedef BOOL Win32_SetProcessDpiAwarenessContext(HANDLE);
typedef BOOL Win32_SetProcessDpiAwareness(int);
Win32_SetProcessDpiAwarenessContext *SetProcessDpiAwarenessContext = (Win32_SetProcessDpiAwarenessContext *) GetProcAddress(user32, "SetProcessDpiAwarenessContext");
Win32_SetProcessDpiAwareness *SetProcessDpiAwareness = (Win32_SetProcessDpiAwareness *) GetProcAddress(user32, "SetProcessDpiAwareness");
if (SetProcessDpiAwarenessContext) {
SetProcessDpiAwarenessContext(((HANDLE) -4));
} else if (SetProcessDpiAwareness) {
SetProcessDpiAwareness(1);
} else {
SetProcessDPIAware();
}
}
WNDCLASSW wc = {0};
wc.lpfnWndProc = DefWindowProcW;
wc.hInstance = hInstance;
wc.lpszClassName = TITLE;
wc.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
RegisterClassW(&wc);
HWND hwnd = CreateWindowExW(0, TITLE, TITLE, WS_POPUP | WS_MAXIMIZE | WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
// ----------------------------------------------------------------------------------------------------
ID3D12Debug *debug_controller = NULL;
check(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)));
debug_controller->EnableDebugLayer();
// ----------------------------------------------------------------------------------------------------
ID3D12Device *device = NULL;
check(D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&device)));
// ----------------------------------------------------------------------------------------------------
UINT msaa_quality = 0;
if (MSAA_SAMPLE_COUNT > 1) {
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaa_data = {};
msaa_data.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
msaa_data.SampleCount = MSAA_SAMPLE_COUNT;
check(device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msaa_data, sizeof(msaa_data)));
msaa_quality = msaa_data.NumQualityLevels - 1;
}
// ----------------------------------------------------------------------------------------------------
D3D12_COMMAND_QUEUE_DESC queue_desc = {0};
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
ID3D12CommandQueue *cmd_queue = NULL;
check(device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&cmd_queue)));
// ----------------------------------------------------------------------------------------------------
IDXGIFactory4 *factory = NULL;
check(CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, IID_PPV_ARGS(&factory)));
DXGI_SWAP_CHAIN_DESC1 swapchain_desc = {0};
swapchain_desc.BufferCount = 2;
swapchain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapchain_desc.SampleDesc.Count = 1;
IDXGISwapChain1 *swapchain1 = NULL;
check(factory->CreateSwapChainForHwnd(cmd_queue, hwnd, &swapchain_desc, NULL, NULL, &swapchain1));
factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
IDXGISwapChain3 *swapchain = NULL;
check(swapchain1->QueryInterface(IID_PPV_ARGS(&swapchain)));
swapchain->GetDesc1(&swapchain_desc);
// ----------------------------------------------------------------------------------------------------
// RTV heap: slots 0-1 for swapchain buffers, slot 2 for MSAA RT (when enabled)
D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc = {0};
rtv_heap_desc.NumDescriptors = 3;
rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
ID3D12DescriptorHeap *rtv_heap = NULL;
check(device->CreateDescriptorHeap(&rtv_heap_desc, IID_PPV_ARGS(&rtv_heap)));
ID3D12Resource *render_targets[2] = {0};
UINT rtv_descriptor_size = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = rtv_heap->GetCPUDescriptorHandleForHeapStart();
for (UINT i = 0; i < 2; ++i) {
check(swapchain->GetBuffer(i, IID_PPV_ARGS(&render_targets[i])));
device->CreateRenderTargetView(render_targets[i], NULL, rtv_handle);
rtv_handle.ptr = SIZE_T(INT64(rtv_handle.ptr) + INT64(rtv_descriptor_size));
}
// ----------------------------------------------------------------------------------------------------
const float clear_color[] = { 0.0f, 0.2f, 0.4f, 1.0f };
D3D12_HEAP_PROPERTIES default_heap = {0};
default_heap.Type = D3D12_HEAP_TYPE_DEFAULT;
ID3D12Resource *msaa_rt = NULL;
if (MSAA_SAMPLE_COUNT > 1) {
D3D12_CLEAR_VALUE msaa_clear_value = {0};
msaa_clear_value.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
msaa_clear_value.Color[0] = clear_color[0];
msaa_clear_value.Color[1] = clear_color[1];
msaa_clear_value.Color[2] = clear_color[2];
msaa_clear_value.Color[3] = clear_color[3];
D3D12_RESOURCE_DESC msaa_rt_desc = {0};
msaa_rt_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
msaa_rt_desc.Width = swapchain_desc.Width;
msaa_rt_desc.Height = swapchain_desc.Height;
msaa_rt_desc.DepthOrArraySize = 1;
msaa_rt_desc.MipLevels = 1;
msaa_rt_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
msaa_rt_desc.SampleDesc.Count = MSAA_SAMPLE_COUNT;
msaa_rt_desc.SampleDesc.Quality = msaa_quality;
msaa_rt_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
check(device->CreateCommittedResource(&default_heap, D3D12_HEAP_FLAG_NONE, &msaa_rt_desc,
D3D12_RESOURCE_STATE_RENDER_TARGET, &msaa_clear_value, IID_PPV_ARGS(&msaa_rt)));
D3D12_RESOURCE_DESC actual = msaa_rt->GetDesc();
if (actual.SampleDesc.Count != MSAA_SAMPLE_COUNT) ExitProcess(1);
D3D12_CPU_DESCRIPTOR_HANDLE msaa_rtv_handle = rtv_heap->GetCPUDescriptorHandleForHeapStart();
msaa_rtv_handle.ptr = SIZE_T(INT64(msaa_rtv_handle.ptr) + INT64(2) * INT64(rtv_descriptor_size));
device->CreateRenderTargetView(msaa_rt, NULL, msaa_rtv_handle);
}
// ----------------------------------------------------------------------------------------------------
ID3D12CommandAllocator *cmd_allocator = NULL;
check(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmd_allocator)));
// ----------------------------------------------------------------------------------------------------
D3D12_DESCRIPTOR_RANGE srv_range = {0};
srv_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
srv_range.NumDescriptors = 1;
srv_range.BaseShaderRegister = 0;
D3D12_ROOT_PARAMETER root_params[1] = {};
root_params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_params[0].DescriptorTable.NumDescriptorRanges = 1;
root_params[0].DescriptorTable.pDescriptorRanges = &srv_range;
root_params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC sampler_desc = {0};
sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler_desc.ShaderRegister = 0;
sampler_desc.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC root_sig_desc = {0};
root_sig_desc.NumParameters = CountOf(root_params);
root_sig_desc.pParameters = root_params;
root_sig_desc.NumStaticSamplers = 1;
root_sig_desc.pStaticSamplers = &sampler_desc;
root_sig_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
ID3DBlob *signature = NULL;
check(D3D12SerializeRootSignature(&root_sig_desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, NULL));
ID3D12RootSignature *root_signature = NULL;
check(device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&root_signature)));
// ----------------------------------------------------------------------------------------------------
ID3DBlob *vertex_shader = NULL;
check(D3DCompile(
shader_source, strlen(shader_source), "triangle.hlsl", NULL,
D3D_COMPILE_STANDARD_FILE_INCLUDE, "vert_main", "vs_5_0",
0, 0, &vertex_shader, NULL
));
ID3DBlob *fragment_shader = NULL;
check(D3DCompile(
shader_source, strlen(shader_source), "triangle.hlsl", NULL,
D3D_COMPILE_STANDARD_FILE_INCLUDE, "frag_main", "ps_5_0",
0, 0, &fragment_shader, NULL
));
// ----------------------------------------------------------------------------------------------------
D3D12_INPUT_ELEMENT_DESC input_element_descs[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
D3D12_RASTERIZER_DESC rasterizer_desc = {0};
rasterizer_desc.FillMode = D3D12_FILL_MODE_SOLID;
rasterizer_desc.CullMode = D3D12_CULL_MODE_BACK;
rasterizer_desc.DepthClipEnable = TRUE;
rasterizer_desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
rasterizer_desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
rasterizer_desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
rasterizer_desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
D3D12_BLEND_DESC blend_desc = {0};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++) {
D3D12_RENDER_TARGET_BLEND_DESC &rt = blend_desc.RenderTarget[i];
rt.BlendEnable = TRUE;
rt.SrcBlend = D3D12_BLEND_SRC_ALPHA;
rt.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
rt.BlendOp = D3D12_BLEND_OP_ADD;
rt.SrcBlendAlpha = D3D12_BLEND_ONE;
rt.DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
rt.BlendOpAlpha = D3D12_BLEND_OP_ADD;
rt.LogicOp = D3D12_LOGIC_OP_NOOP;
rt.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
}
D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_state_desc = {0};
pipeline_state_desc.InputLayout = { input_element_descs, CountOf(input_element_descs) };
pipeline_state_desc.pRootSignature = root_signature;
pipeline_state_desc.VS = { vertex_shader->GetBufferPointer(), vertex_shader->GetBufferSize() };
pipeline_state_desc.PS = { fragment_shader->GetBufferPointer(), fragment_shader->GetBufferSize() };
pipeline_state_desc.RasterizerState = rasterizer_desc;
pipeline_state_desc.BlendState = blend_desc;
pipeline_state_desc.DepthStencilState.DepthEnable = FALSE;
pipeline_state_desc.DepthStencilState.StencilEnable = FALSE;
pipeline_state_desc.SampleMask = UINT_MAX;
pipeline_state_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pipeline_state_desc.NumRenderTargets = 1;
pipeline_state_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
pipeline_state_desc.SampleDesc.Count = MSAA_SAMPLE_COUNT;
pipeline_state_desc.SampleDesc.Quality = (MSAA_SAMPLE_COUNT > 1) ? msaa_quality : 0;
ID3D12PipelineState *pipeline_state = NULL;
check(device->CreateGraphicsPipelineState(&pipeline_state_desc, IID_PPV_ARGS(&pipeline_state)));
// ----------------------------------------------------------------------------------------------------
ID3D12GraphicsCommandList *cmd_list = NULL;
check(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmd_allocator, pipeline_state, IID_PPV_ARGS(&cmd_list)));
cmd_list->Close();
// ----------------------------------------------------------------------------------------------------
ID3D12Fence *fence = NULL;
check(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)));
UINT64 fence_value = 1;
HANDLE fence_event = CreateEventW(NULL, FALSE, FALSE, NULL);
if (fence_event == NULL) ExitProcess(GetLastError());
// ----------------------------------------------------------------------------------------------------
const UINT tex_size = 2;
UINT8 tex_data[2 * 2 * 4] = {
220, 220, 220, 255,
30, 30, 30, 255,
30, 30, 30, 255,
220, 220, 220, 255,
};
D3D12_RESOURCE_DESC tex_desc = {0};
tex_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
tex_desc.Width = tex_size;
tex_desc.Height = tex_size;
tex_desc.DepthOrArraySize = 1;
tex_desc.MipLevels = 1;
tex_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
tex_desc.SampleDesc.Count = 1;
tex_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
ID3D12Resource *texture = NULL;
check(device->CreateCommittedResource(&default_heap, D3D12_HEAP_FLAG_NONE, &tex_desc,
D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&texture)));
UINT64 upload_size = 0;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = {0};
device->GetCopyableFootprints(&tex_desc, 0, 1, 0, &footprint, NULL, NULL, &upload_size);
D3D12_HEAP_PROPERTIES upload_heap = {0};
upload_heap.Type = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC upload_desc = {0};
upload_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
upload_desc.Width = upload_size;
upload_desc.Height = 1;
upload_desc.DepthOrArraySize = 1;
upload_desc.MipLevels = 1;
upload_desc.SampleDesc.Count = 1;
upload_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
ID3D12Resource *tex_upload = NULL;
check(device->CreateCommittedResource(&upload_heap, D3D12_HEAP_FLAG_NONE, &upload_desc,
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&tex_upload)));
void *upload_ptr = NULL;
D3D12_RANGE no_read = {0, 0};
check(tex_upload->Map(0, &no_read, &upload_ptr));
for (UINT y = 0; y < tex_size; y++) {
memcpy((UINT8*)upload_ptr + y * footprint.Footprint.RowPitch, tex_data + y * tex_size * 4, tex_size * 4);
}
tex_upload->Unmap(0, NULL);
cmd_allocator->Reset();
cmd_list->Reset(cmd_allocator, NULL);
D3D12_TEXTURE_COPY_LOCATION copy_dst = {0};
copy_dst.pResource = texture;
copy_dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
copy_dst.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION copy_src = {0};
copy_src.pResource = tex_upload;
copy_src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
copy_src.PlacedFootprint = footprint;
cmd_list->CopyTextureRegion(&copy_dst, 0, 0, 0, &copy_src, NULL);
D3D12_RESOURCE_BARRIER tex_barrier = {0};
tex_barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
tex_barrier.Transition.pResource = texture;
tex_barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
tex_barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
tex_barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
cmd_list->ResourceBarrier(1, &tex_barrier);
cmd_list->Close();
ID3D12CommandList *init_lists[] = { cmd_list };
cmd_queue->ExecuteCommandLists(1, init_lists);
cmd_queue->Signal(fence, fence_value);
fence->SetEventOnCompletion(fence_value, fence_event);
WaitForSingleObject(fence_event, INFINITE);
fence_value++;
// ----------------------------------------------------------------------------------------------------
D3D12_DESCRIPTOR_HEAP_DESC srv_heap_desc = {0};
srv_heap_desc.NumDescriptors = 1;
srv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ID3D12DescriptorHeap *srv_heap = NULL;
check(device->CreateDescriptorHeap(&srv_heap_desc, IID_PPV_ARGS(&srv_heap)));
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {0};
srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MipLevels = 1;
device->CreateShaderResourceView(texture, &srv_desc, srv_heap->GetCPUDescriptorHandleForHeapStart());
// ----------------------------------------------------------------------------------------------------
vertex vertices[] = {
{ { 0.0f, 0.5f, 0.0f }, { 0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
{ { 0.5f, -0.5f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
{ { -0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } },
};
D3D12_HEAP_PROPERTIES heap_props = {0};
heap_props.Type = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC buffer_desc = {0};
buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buffer_desc.Width = sizeof(vertices);
buffer_desc.Height = 1;
buffer_desc.DepthOrArraySize = 1;
buffer_desc.MipLevels = 1;
buffer_desc.SampleDesc.Count = 1;
buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
ID3D12Resource *vertex_buffer = NULL;
check(device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &buffer_desc,
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&vertex_buffer)));
void *vertex_data_begin = NULL;
D3D12_RANGE read_range = {0, 0};
check(vertex_buffer->Map(0, &read_range, &vertex_data_begin));
memcpy(vertex_data_begin, vertices, sizeof(vertices));
vertex_buffer->Unmap(0, NULL);
D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view = {0};
vertex_buffer_view.BufferLocation = vertex_buffer->GetGPUVirtualAddress();
vertex_buffer_view.StrideInBytes = sizeof(vertex);
vertex_buffer_view.SizeInBytes = sizeof(vertices);
D3D12_VIEWPORT viewport = {0};
viewport.Width = (float)swapchain_desc.Width;
viewport.Height = (float)swapchain_desc.Height;
viewport.MinDepth = D3D12_MIN_DEPTH;
viewport.MaxDepth = D3D12_MAX_DEPTH;
D3D12_RECT scissor = {0};
scissor.right = swapchain_desc.Width;
scissor.bottom = swapchain_desc.Height;
// ----------------------------------------------------------------------------------------------------
UINT frame_index = swapchain->GetCurrentBackBufferIndex();
bool is_running = true;
while (is_running) {
MSG msg = {0};
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_KEYDOWN) is_running = false;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
cmd_allocator->Reset();
cmd_list->Reset(cmd_allocator, pipeline_state);
cmd_list->SetGraphicsRootSignature(root_signature);
cmd_list->RSSetViewports(1, &viewport);
cmd_list->RSSetScissorRects(1, &scissor);
// Determine draw target — MSAA RT or directly to backbuffer
D3D12_CPU_DESCRIPTOR_HANDLE draw_rtv;
if (MSAA_SAMPLE_COUNT > 1) {
draw_rtv = rtv_heap->GetCPUDescriptorHandleForHeapStart();
draw_rtv.ptr = SIZE_T(INT64(draw_rtv.ptr) + INT64(2) * INT64(rtv_descriptor_size));
} else {
draw_rtv = rtv_heap->GetCPUDescriptorHandleForHeapStart();
draw_rtv.ptr = SIZE_T(INT64(draw_rtv.ptr) + INT64(frame_index) * INT64(rtv_descriptor_size));
D3D12_RESOURCE_BARRIER barrier = {0};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Transition.pResource = render_targets[frame_index];
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
cmd_list->ResourceBarrier(1, &barrier);
}
cmd_list->OMSetRenderTargets(1, &draw_rtv, FALSE, NULL);
cmd_list->ClearRenderTargetView(draw_rtv, clear_color, 0, NULL);
cmd_list->SetDescriptorHeaps(1, &srv_heap);
cmd_list->SetGraphicsRootDescriptorTable(0, srv_heap->GetGPUDescriptorHandleForHeapStart());
cmd_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
cmd_list->IASetVertexBuffers(0, 1, &vertex_buffer_view);
cmd_list->DrawInstanced(3, 1, 0, 0);
if (MSAA_SAMPLE_COUNT > 1) {
D3D12_RESOURCE_BARRIER resolve_barriers[2] = {};
resolve_barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
resolve_barriers[0].Transition.pResource = msaa_rt;
resolve_barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
resolve_barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
resolve_barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_SOURCE;
resolve_barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
resolve_barriers[1].Transition.pResource = render_targets[frame_index];
resolve_barriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
resolve_barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
resolve_barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_DEST;
cmd_list->ResourceBarrier(2, resolve_barriers);
cmd_list->ResolveSubresource(render_targets[frame_index], 0, msaa_rt, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
resolve_barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_SOURCE;
resolve_barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
resolve_barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_DEST;
resolve_barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
cmd_list->ResourceBarrier(2, resolve_barriers);
} else {
D3D12_RESOURCE_BARRIER barrier = {0};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Transition.pResource = render_targets[frame_index];
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
cmd_list->ResourceBarrier(1, &barrier);
}
cmd_list->Close();
ID3D12CommandList *cmd_lists[] = { cmd_list };
cmd_queue->ExecuteCommandLists(CountOf(cmd_lists), cmd_lists);
swapchain->Present(1, 0);
const UINT64 current_fence_value = fence_value;
cmd_queue->Signal(fence, current_fence_value);
fence_value++;
if (fence->GetCompletedValue() < current_fence_value) {
fence->SetEventOnCompletion(current_fence_value, fence_event);
WaitForSingleObject(fence_event, INFINITE);
}
frame_index = swapchain->GetCurrentBackBufferIndex();
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment