Created
May 8, 2026 15:16
-
-
Save nickav/a2dcb08888d3e4ec278170961d068f93 to your computer and use it in GitHub Desktop.
Minimal DirectX12 Hello Triangle With Textures, MSAA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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(©_dst, 0, 0, 0, ©_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