diff --git a/examples/directx12_example/directx12_example.vcxproj b/examples/directx12_example/directx12_example.vcxproj new file mode 100644 index 000000000000..418959bab6a2 --- /dev/null +++ b/examples/directx12_example/directx12_example.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {DD502977-4B3C-459F-9CA0-A0E75937EA6E} + Win32Proj + directx12_example + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ..\..;%(AdditionalIncludeDirectories);C:\Program Files (x86)\Windows Kits\10\Include\10.0.10158.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10158.0\shared + + + Console + true + d3dcompiler.lib;dxgi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ..\..;%(AdditionalIncludeDirectories);C:\Program Files (x86)\Windows Kits\10\Include\10.0.10158.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10158.0\shared + + + Console + true + true + true + d3dcompiler.lib;dxgi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/directx12_example/directx12_example.vcxproj.filters b/examples/directx12_example/directx12_example.vcxproj.filters new file mode 100644 index 000000000000..9b32ceebfdb2 --- /dev/null +++ b/examples/directx12_example/directx12_example.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/examples/directx12_example/imgui_impl_dx12.cpp b/examples/directx12_example/imgui_impl_dx12.cpp new file mode 100644 index 000000000000..ffba2a99d1a0 --- /dev/null +++ b/examples/directx12_example/imgui_impl_dx12.cpp @@ -0,0 +1,472 @@ +#include "imgui.h" +#include "imgui_impl_dx12.h" + +#include + +#include +#include +#include + +ID3D12GraphicsCommandList* g_commandList; +D3D12_CPU_DESCRIPTOR_HANDLE g_renderTarget; +ID3D12Resource* g_uploadBuffer; +ID3D12PipelineState* g_pipelineState; + +// Data +static INT64 g_Time = 0; +static INT64 g_TicksPerSecond = 0; +static HWND g_hwnd; + + +// dxgi, device, cmd queue, cmd allocator +// command lists genereation with vsh,psh +#define IMGUI_GPU_BUFFER_SIZE 1024*1024 + +// For creation of resources in the GPU +struct HeapProperty +{ + enum Enum + { + Default, + Upload, + ReadBack, + + Count + }; + + D3D12_HEAP_PROPERTIES m_properties; + D3D12_RESOURCE_STATES m_state; +}; + +static const HeapProperty s_heapProperties[] = +{ + { { D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 1, 1 }, D3D12_RESOURCE_STATE_COMMON }, + { { D3D12_HEAP_TYPE_UPLOAD, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 1, 1 }, D3D12_RESOURCE_STATE_GENERIC_READ }, + { { D3D12_HEAP_TYPE_READBACK, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 1, 1 }, D3D12_RESOURCE_STATE_COPY_DEST }, +}; + +static ID3D12Resource* createCommittedResource(ID3D12Device* _device, HeapProperty::Enum _heapProperty, D3D12_RESOURCE_DESC* _resourceDesc, D3D12_CLEAR_VALUE* _clearValue) +{ + const HeapProperty& heapProperty = s_heapProperties[_heapProperty]; + ID3D12Resource* resource; + _device->CreateCommittedResource(&heapProperty.m_properties + , D3D12_HEAP_FLAG_NONE + , _resourceDesc + , heapProperty.m_state + , _clearValue + , __uuidof(ID3D12Resource) + , (void**)&resource + ); + + return resource; +} + +static ID3D12Resource* createCommittedResource(ID3D12Device* _device, HeapProperty::Enum _heapProperty, uint64_t _size, D3D12_RESOURCE_FLAGS _flags = D3D12_RESOURCE_FLAG_NONE) +{ + D3D12_RESOURCE_DESC resourceDesc; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = _size; + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc.Count = 1; + resourceDesc.SampleDesc.Quality = 0; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = _flags; + + return createCommittedResource(_device, _heapProperty, &resourceDesc, NULL); +} + +static void setResourceBarrier(ID3D12GraphicsCommandList* _commandList, ID3D12Resource* _resource, D3D12_RESOURCE_STATES _stateBefore, D3D12_RESOURCE_STATES _stateAfter) +{ + D3D12_RESOURCE_BARRIER barrier; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = _resource; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = _stateBefore; + barrier.Transition.StateAfter = _stateAfter; + _commandList->ResourceBarrier(1, &barrier); +} + +void ImGui_ImplDX12_CreateDeviceObjects(ID3D12Device* _device + , ID3D12GraphicsCommandList* _commandList + , ID3D12CommandQueue* _commandQueue + , ID3D12Fence* _fence + , D3D12_CPU_DESCRIPTOR_HANDLE _srvHandle + , ID3D12RootSignature* _rootSignature) + +{ + static const char* vertexShader = + "cbuffer vertexBuffer : register(b0) \ + {\ + float4x4 ProjectionMatrix; \ + };\ + struct VS_INPUT\ + {\ + float2 pos : POSITION;\ + float4 col : COLOR0;\ + float2 uv : TEXCOORD0;\ + };\ + \ + struct PS_INPUT\ + {\ + float4 pos : SV_POSITION;\ + float4 col : COLOR0;\ + float2 uv : TEXCOORD0;\ + };\ + \ + PS_INPUT main(VS_INPUT input)\ + {\ + PS_INPUT output;\ + output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ + output.col = input.col;\ + output.uv = input.uv;\ + return output;\ + }"; + + static const char* pixelShader = + "struct PS_INPUT\ + {\ + float4 pos : SV_POSITION;\ + float4 col : COLOR0;\ + float2 uv : TEXCOORD0;\ + };\ + SamplerState sampler0 : register(s0);\ + Texture2D texture0 : register(t0);\ + \ + float4 main(PS_INPUT input) : SV_Target\ + {\ + float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ + return out_col; \ + }"; + + ID3DBlob* vshBlob = 0; + ID3DBlob* pshBlob = 0; + ID3DBlob* errorBlob = 0; + + // Define the vertex input layout. + D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; + + + D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", D3DCOMPILE_DEBUG, 0, &vshBlob, &errorBlob); + D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", D3DCOMPILE_DEBUG, 0, &pshBlob, &errorBlob); + + D3D12_RASTERIZER_DESC rasterizerDesc = {}; + rasterizerDesc.FillMode = D3D12_FILL_MODE_SOLID; + rasterizerDesc.CullMode = D3D12_CULL_MODE_NONE; + rasterizerDesc.FrontCounterClockwise = true; + rasterizerDesc.DepthBias = 0; + rasterizerDesc.DepthBiasClamp = 0.f; + rasterizerDesc.SlopeScaledDepthBias = 0.f; + rasterizerDesc.DepthClipEnable = true; + rasterizerDesc.MultisampleEnable = false; + rasterizerDesc.AntialiasedLineEnable = true; + rasterizerDesc.ForcedSampleCount = 1; + rasterizerDesc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + + D3D12_BLEND_DESC blendDesc; + blendDesc.AlphaToCoverageEnable = false; + blendDesc.IndependentBlendEnable = false; + blendDesc.RenderTarget[0].BlendEnable = true; + blendDesc.RenderTarget[0].LogicOpEnable = false; + blendDesc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; + blendDesc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; + blendDesc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; + blendDesc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; + blendDesc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; + blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + + // Create the graphics pipeline state object (PSO). + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; + psoDesc.pRootSignature = _rootSignature; + psoDesc.VS = { (uint8_t*)(vshBlob->GetBufferPointer()), vshBlob->GetBufferSize() }; + psoDesc.PS = { (uint8_t*)(pshBlob->GetBufferPointer()), pshBlob->GetBufferSize() }; + psoDesc.RasterizerState = rasterizerDesc; + psoDesc.BlendState = blendDesc; + 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; + _device->CreateGraphicsPipelineState(&psoDesc, __uuidof(ID3D12PipelineState), (void**)&g_pipelineState); + + // Create upload ring buffer, which we'll also use as staging buffer for the texture + g_uploadBuffer = createCommittedResource(_device, HeapProperty::Upload, IMGUI_GPU_BUFFER_SIZE * 8); + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = 1; + srvDesc.Texture2D.PlaneSlice = 0; + srvDesc.Texture2D.ResourceMinLODClamp = 0.f; + + // Create fonts texture and SRV descriptor for it + unsigned char* pixels = 0; + int width, height; + + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + + // Create fonts texture + D3D12_RESOURCE_DESC desc; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.Alignment = 0; + desc.Width = width; + desc.Height = height; + desc.DepthOrArraySize = 1; + desc.MipLevels = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + desc.Flags = D3D12_RESOURCE_FLAG_NONE; + ID3D12Resource* fontResource = createCommittedResource(_device, HeapProperty::Default, &desc, 0); + + // Upload the fonts texture + uint32_t subres = 0; + uint32_t numRows; + uint64_t rowPitch; + uint64_t totalBytes; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout; + _device->GetCopyableFootprints(&desc + , subres + , 1 + , 0 + , &layout + , &numRows + , &rowPitch + , &totalBytes + ); + + uint8_t* mappedBuffer; + + // Upload the font + g_uploadBuffer->Map(0, NULL, (void**)&mappedBuffer); + memcpy(mappedBuffer, pixels, (size_t)totalBytes); + g_uploadBuffer->Unmap(0, NULL); + + D3D12_BOX box; + box.left = 0; + box.top = 0; + box.right = (UINT)desc.Width; + box.bottom = (UINT)desc.Height; + box.front = 0; + box.back = 1; + + D3D12_TEXTURE_COPY_LOCATION dst = { fontResource, D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, { subres } }; + D3D12_TEXTURE_COPY_LOCATION src = { g_uploadBuffer, D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, layout }; + _commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, &box); + _commandList->Close(); + + ID3D12CommandList* ppCommandLists[] = { _commandList }; + _commandQueue->ExecuteCommandLists(1, ppCommandLists); + _commandQueue->Signal(_fence, 0); + + _device->CreateShaderResourceView(fontResource, &srvDesc, _srvHandle); +} + +void ImGui_ImplDX12_SetRenderData(ID3D12GraphicsCommandList* _commandList, D3D12_CPU_DESCRIPTOR_HANDLE _renderTarget) +{ + g_commandList = _commandList; + g_renderTarget = _renderTarget; +} + +void ImGui_ImplDX12_RenderDrawLists(ImDrawData* _draw_data) +{ + // Range CPU will read from mapping the upload buffer + // End < Begin specifies CPU will not read the mapped buffer + D3D12_RANGE readRange; + readRange.End = 0; + readRange.Begin = 1; + + char* mappedBuffer = 0; + g_uploadBuffer->Map(0, &readRange, (void**)&mappedBuffer); + if (mappedBuffer == NULL) + return; + + char* writeCursor = mappedBuffer; + + // Copy the projection matrix at the beginning of the buffer + { + float translate = -0.5f * 2.f; + const float L = 0.f; + const float R = ImGui::GetIO().DisplaySize.x; + const float B = ImGui::GetIO().DisplaySize.y; + const float T = 0.f; + const float mvp[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f}, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f,}, + { 0.0f, 0.0f, 0.5f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + }; + + memcpy(writeCursor, &mvp[0], sizeof(mvp)); + writeCursor += sizeof(mvp); + } + + // Copy the vertices and indices for each command list + for (int n = 0; n < _draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = _draw_data->CmdLists[n]; + size_t verticesCount = cmd_list->VtxBuffer.size(); + size_t indicesCount = cmd_list->IdxBuffer.size(); + size_t verticesSize = verticesCount * sizeof(ImDrawVert); + size_t indicesSize = indicesCount * sizeof(ImDrawIdx); + + // Copy the vertex data + memcpy(writeCursor, &cmd_list->VtxBuffer[0], verticesSize); + writeCursor += verticesSize; + + // Copy the index data + memcpy(writeCursor, &cmd_list->IdxBuffer[0], indicesSize); + writeCursor += indicesSize; + } + + ID3D12GraphicsCommandList* commandList = g_commandList; + + D3D12_VIEWPORT viewport; + viewport.Width = ImGui::GetIO().DisplaySize.x; + viewport.Height = ImGui::GetIO().DisplaySize.y; + viewport.MinDepth = 0.f; + viewport.MaxDepth = 1.f; + viewport.TopLeftX = 0.f; + viewport.TopLeftY = 0.f; + + commandList->RSSetViewports(1, &viewport); + commandList->OMSetRenderTargets(1, &g_renderTarget, FALSE, nullptr); + commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + commandList->SetPipelineState(g_pipelineState); + + D3D12_GPU_VIRTUAL_ADDRESS bufferAddress = g_uploadBuffer->GetGPUVirtualAddress(); + commandList->SetGraphicsRootConstantBufferView(1, bufferAddress); + + uint64_t readCursor = 64; // Our constant buffer takes 64 bytes - one mat4x4 + + for (int n = 0; n < _draw_data->CmdListsCount; n++) + { + // Render command lists + int vtx_offset = 0; + int idx_offset = 0; + + const ImDrawList* cmd_list = _draw_data->CmdLists[n]; + size_t verticesCount = cmd_list->VtxBuffer.size(); + size_t indicesCount = cmd_list->IdxBuffer.size(); + size_t verticesSize = verticesCount * sizeof(ImDrawVert); + size_t indicesSize = indicesCount * sizeof(ImDrawIdx); + + D3D12_VERTEX_BUFFER_VIEW vertexBufferView; + vertexBufferView.BufferLocation = bufferAddress + readCursor; + vertexBufferView.StrideInBytes = sizeof(ImDrawVert); + vertexBufferView.SizeInBytes = verticesSize; + readCursor += verticesSize; + + D3D12_INDEX_BUFFER_VIEW indexBufferView; + indexBufferView.BufferLocation = bufferAddress + readCursor; + indexBufferView.SizeInBytes = indicesSize; + indexBufferView.Format = DXGI_FORMAT_R16_UINT; + readCursor += indicesSize; + + commandList->IASetVertexBuffers(0, 1, &vertexBufferView); + commandList->IASetIndexBuffer(&indexBufferView); + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.size(); cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + const D3D12_RECT r = { (LONG)pcmd->ClipRect.x, (LONG)pcmd->ClipRect.y, (LONG)pcmd->ClipRect.z, (LONG)pcmd->ClipRect.w }; + commandList->RSSetScissorRects(1, &r); + commandList->DrawIndexedInstanced(pcmd->ElemCount, 1, idx_offset, vtx_offset, 0); + } + idx_offset += pcmd->ElemCount; + } + vtx_offset += verticesCount; + } +} + + +void ImGui_ImplDX12_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + + // Setup display size (every frame to accommodate for window resizing) + RECT rect; + GetClientRect(g_hwnd, &rect); + io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); + + // Setup time step + INT64 current_time; + QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); + io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; + g_Time = current_time; + + // Read keyboard modifiers inputs + io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; + io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; + io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; + // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events + // io.MousePos : filled by WM_MOUSEMOVE events + // io.MouseDown : filled by WM_*BUTTON* events + // io.MouseWheel : filled by WM_MOUSEWHEEL events + + // Hide OS mouse cursor if ImGui is drawing it + SetCursor(io.MouseDrawCursor ? NULL : LoadCursor(NULL, IDC_ARROW)); + + // Start the frame + ImGui::NewFrame(); +} + +bool ImGui_ImplDX12_Init(void* _hwnd) +{ + ImGuiIO& io = ImGui::GetIO(); + io.KeyMap[ImGuiKey_Tab] = VK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. + io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; + io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; + io.KeyMap[ImGuiKey_UpArrow] = VK_UP; + io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; + io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; + io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; + io.KeyMap[ImGuiKey_Home] = VK_HOME; + io.KeyMap[ImGuiKey_End] = VK_END; + io.KeyMap[ImGuiKey_Delete] = VK_DELETE; + io.KeyMap[ImGuiKey_Backspace] = VK_BACK; + io.KeyMap[ImGuiKey_Enter] = VK_RETURN; + io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; + io.KeyMap[ImGuiKey_A] = 'A'; + io.KeyMap[ImGuiKey_C] = 'C'; + io.KeyMap[ImGuiKey_V] = 'V'; + io.KeyMap[ImGuiKey_X] = 'X'; + io.KeyMap[ImGuiKey_Y] = 'Y'; + io.KeyMap[ImGuiKey_Z] = 'Z'; + + io.RenderDrawListsFn = ImGui_ImplDX12_RenderDrawLists; + io.ImeWindowHandle = _hwnd; + + g_hwnd = (HWND)_hwnd; + + if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) + return false; + if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) + return false; + + return true; +} \ No newline at end of file diff --git a/examples/directx12_example/imgui_impl_dx12.h b/examples/directx12_example/imgui_impl_dx12.h new file mode 100644 index 000000000000..385e79a2b76e --- /dev/null +++ b/examples/directx12_example/imgui_impl_dx12.h @@ -0,0 +1,23 @@ +struct ID3D12Device; +struct ID3D12GraphicsCommandList; +struct ID3D12CommandQueue; +struct ID3D12Fence; +struct D3D12_CPU_DESCRIPTOR_HANDLE; +struct ID3D12RootSignature; + +#define FRAME_COUNT 3 + +bool ImGui_ImplDX12_Init(void* _hwnd); +void ImGui_ImplDX12_RenderDrawLists(ImDrawData* _draw_data); + +void ImGui_ImplDX12_CreateDeviceObjects(ID3D12Device* _device + , ID3D12GraphicsCommandList* _commandList + , ID3D12CommandQueue* _commandQueue + , ID3D12Fence* _fence + , D3D12_CPU_DESCRIPTOR_HANDLE _srvHandle + , ID3D12RootSignature* _rootSignature); + +void ImGui_ImplDX12_SetRenderData(ID3D12GraphicsCommandList* _commandList + , D3D12_CPU_DESCRIPTOR_HANDLE _renderTarget); + +void ImGui_ImplDX12_NewFrame(); \ No newline at end of file diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp new file mode 100644 index 000000000000..44d661c01624 --- /dev/null +++ b/examples/directx12_example/main.cpp @@ -0,0 +1,520 @@ +/* + * Copyright 2015 Onat Turkcuoglu. All rights reserved. + * License: http://www.opensource.org/licenses/BSD-2-Clause + */ + +#include + +#include "imgui.cpp" +#include "imgui_demo.cpp" +#include "imgui_draw.cpp" + +#include +#include +#include + +#include "imgui_impl_dx12.h" + +bool InitD3D12(ID3D12Device** _device + , ID3D12CommandQueue** _commandQueue + , ID3D12CommandAllocator** _commandAllocator + , IDXGISwapChain3** _swapChain + , ID3D12DescriptorHeap** _rtvHeap + , ID3D12DescriptorHeap** _srvHeap + , ID3D12RootSignature** _rootSignature + , uint32_t _width + , uint32_t _height + , HWND _hwnd) +{ + // Load the DLLs and lookup function pointers + HMODULE d3d12dll = LoadLibraryA("d3d12.dll"); + if (NULL == d3d12dll) + { + return false; + } + + PFN_D3D12_CREATE_DEVICE D3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)GetProcAddress(d3d12dll, "D3D12CreateDevice"); + PFN_D3D12_GET_DEBUG_INTERFACE D3D12GetDebugInterface = (PFN_D3D12_GET_DEBUG_INTERFACE)GetProcAddress(d3d12dll, "D3D12GetDebugInterface"); + PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)GetProcAddress(d3d12dll, "D3D12SerializeRootSignature"); + if (NULL == D3D12CreateDevice + || NULL == D3D12GetDebugInterface + || NULL == D3D12SerializeRootSignature) + { + return false; + } + + ID3D12Debug* debug; + HRESULT hr = D3D12GetDebugInterface(__uuidof(ID3D12Debug), (void**)&debug); + + if (SUCCEEDED(hr) ) + { + debug->EnableDebugLayer(); + } + + IDXGIFactory2* dxgiFactory; + // Create DXGI factory to create the swap chain later + hr = CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, __uuidof(IDXGIFactory2), (void**)&dxgiFactory); + if (FAILED(hr) ) + { + return false; + } + + // Create device + ID3D12Device* device = 0; + hr = D3D12CreateDevice( + NULL // NULL => Default adapter + , D3D_FEATURE_LEVEL_11_0 // Feature level + , __uuidof(ID3D12Device) + , (void**)&device + ); + + *_device = device; + + // Create command queue + ID3D12CommandQueue* commandQueue = 0; + D3D12_COMMAND_QUEUE_DESC queueDesc; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queueDesc.Priority = 0; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.NodeMask = 0; // Single GPU operation + device->CreateCommandQueue(&queueDesc + , __uuidof(ID3D12CommandQueue) + , (void**)_commandQueue + ); + + // Create command allocator + ID3D12CommandAllocator* commandAllocator = 0; + device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT + , __uuidof(ID3D12CommandAllocator) + , (void**)_commandAllocator + ); + + // Describe and create the swap chain. + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferCount = FRAME_COUNT; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferDesc.Width = _width; + swapChainDesc.BufferDesc.Height = _height; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; + swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapChainDesc.OutputWindow = _hwnd; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.Windowed = TRUE; + swapChainDesc.Flags = 0; + + hr = dxgiFactory->CreateSwapChain(*_commandQueue + , &swapChainDesc + , (IDXGISwapChain**)_swapChain + ); + + // Create #FRAME_COUNT render target descriptors (stored in descriptor heap) + D3D12_DESCRIPTOR_HEAP_DESC rtvDescHeap; + rtvDescHeap.NumDescriptors = FRAME_COUNT; + rtvDescHeap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + rtvDescHeap.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + rtvDescHeap.NodeMask = 0; + device->CreateDescriptorHeap(&rtvDescHeap + , __uuidof(ID3D12DescriptorHeap) + , (void**)_rtvHeap + ); + + // Create one shader resource descriptor for the font texture + D3D12_DESCRIPTOR_HEAP_DESC srvDescHeap; + srvDescHeap.NumDescriptors = 1; + srvDescHeap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + srvDescHeap.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + srvDescHeap.NodeMask = 0; + device->CreateDescriptorHeap(&srvDescHeap + , __uuidof(ID3D12DescriptorHeap) + , (void**)_srvHeap + ); + + // A root parameter describes one slot of a root signature + // Parameter types are: DESCRIPTOR_TABLE, 32BIT_CONSTANTS, CBV, SRV, UAV + // + // Root Descriptor Table: { uint NumDescriptorRanges, const DescriptorRange* pDescriptorRanges } + // Root Descriptor: { uint ShaderRegister, uint RegisterSpace } + // Root Constants: { uint ShaderRegister, uint RegisterSpace, uint Num32BitValues + + D3D12_DESCRIPTOR_RANGE descRange[] = + { + { D3D12_DESCRIPTOR_RANGE_TYPE_SRV, // Range Type + 1, // Number of descriptors + 0, // Base shader register + 0, // Register space + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND }, // Offset in descriptors from the start of the root signature + }; + + // This root signature will have two parameters, one descriptor table for SRVs and one constant buffer descriptor + D3D12_ROOT_PARAMETER rootParameter[] = + { + { D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, {1,descRange}, D3D12_SHADER_VISIBILITY_ALL }, + { D3D12_ROOT_PARAMETER_TYPE_CBV, {0,0}, D3D12_SHADER_VISIBILITY_VERTEX }, + }; + + rootParameter[1].Descriptor.RegisterSpace = 0; + rootParameter[1].Descriptor.ShaderRegister = 0; + + // Include a statci sampler + D3D12_STATIC_SAMPLER_DESC samplerDesc; + memset(&samplerDesc, 0, sizeof(samplerDesc)); + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplerDesc.MipLODBias = 0.f; + samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; + samplerDesc.MinLOD = 0.f; + samplerDesc.MaxLOD = 0.f; + samplerDesc.ShaderRegister = 0; + samplerDesc.RegisterSpace = 0; + samplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + // Root signature description + D3D12_ROOT_SIGNATURE_DESC descRootSignature; + descRootSignature.NumParameters = 2; + descRootSignature.pParameters = rootParameter; + descRootSignature.NumStaticSamplers = 1; + descRootSignature.pStaticSamplers = &samplerDesc; + descRootSignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + + // Serialise the root signature into memory + ID3DBlob* outBlob; + ID3DBlob* errorBlob; + D3D12SerializeRootSignature(&descRootSignature + , D3D_ROOT_SIGNATURE_VERSION_1 + , &outBlob + , &errorBlob + ); + + // Create the root signature using the binary blob + device->CreateRootSignature(0 + , outBlob->GetBufferPointer() + , outBlob->GetBufferSize() + , __uuidof(ID3D12RootSignature) + , (void**)_rootSignature + ); + + outBlob->Release(); + return true; +} + +LRESULT ImGui_ImplDX12_WndProcHandler(HWND, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ImGuiIO& io = ImGui::GetIO(); + switch (msg) + { + case WM_LBUTTONDOWN: + io.MouseDown[0] = true; + return true; + case WM_LBUTTONUP: + io.MouseDown[0] = false; + return true; + case WM_RBUTTONDOWN: + io.MouseDown[1] = true; + return true; + case WM_RBUTTONUP: + io.MouseDown[1] = false; + return true; + case WM_MOUSEWHEEL: + io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; + return true; + case WM_MOUSEMOVE: + io.MousePos.x = (signed short)(lParam); + io.MousePos.y = (signed short)(lParam >> 16); + return true; + case WM_KEYDOWN: + if (wParam < 256) + io.KeysDown[wParam] = 1; + return true; + case WM_KEYUP: + if (wParam < 256) + io.KeysDown[wParam] = 0; + return true; + case WM_CHAR: + // You can also use ToAscii()+GetKeyboardState() to retrieve characters. + if (wParam > 0 && wParam < 0x10000) + io.AddInputCharacter((unsigned short)wParam); + return true; + } + return 0; +} + +static bool s_resizeRenderTargets = false; +static UINT s_resizedWidth; +static UINT s_resizedHeight; + +LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (ImGui_ImplDX12_WndProcHandler(hWnd, msg, wParam, lParam)) + return true; + + switch (msg) + { + case WM_SIZE: + // Resize + s_resizedWidth = (UINT)LOWORD(lParam); + s_resizedHeight = (UINT)HIWORD(lParam); + s_resizeRenderTargets = true; + return 0; + case WM_SYSCOMMAND: + if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu + return 0; + break; + case WM_DESTROY: + PostQuitMessage(0); + return 0; + case WM_LBUTTONDOWN: + return 1; + } + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +static void setResourceBarrier(ID3D12GraphicsCommandList* _commandList, ID3D12Resource* _resource, D3D12_RESOURCE_STATES _stateBefore, D3D12_RESOURCE_STATES _stateAfter) +{ + D3D12_RESOURCE_BARRIER barrier; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = _resource; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = _stateBefore; + barrier.Transition.StateAfter = _stateAfter; + _commandList->ResourceBarrier(1, &barrier); +} + +int main(int, char**) +{ + // Create application window + WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, LoadCursor(NULL, IDC_ARROW), NULL, NULL, L"ImGui Example", NULL }; + RegisterClassEx(&wc); + HWND hwnd = CreateWindow(L"ImGui Example", L"ImGui DirectX12 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL); + + // D3D12 interfaces + ID3D12Device* device = 0; + ID3D12CommandQueue* commandQueue = 0; + ID3D12CommandAllocator* commandAllocator = 0; + IDXGISwapChain3* swapChain = 0; + ID3D12DescriptorHeap* rtvHeap = 0; + ID3D12DescriptorHeap* srvHeap = 0; + ID3D12GraphicsCommandList* commandList = 0; + ID3D12RootSignature* rootSignature = 0; + ID3D12Resource* renderTargets[FRAME_COUNT]; + + // Show the window + ShowWindow(hwnd, SW_SHOWDEFAULT); + UpdateWindow(hwnd); + + ImGui_ImplDX12_Init(hwnd); + + RECT rect; + GetClientRect(hwnd, &rect); + + InitD3D12(&device + , &commandQueue + , &commandAllocator + , &swapChain + , &rtvHeap + , &srvHeap + , &rootSignature + , rect.right - rect.left + , rect.bottom - rect.top + , hwnd); + + // Initialize render targets + // Create one RTV for each frame + uint32_t rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap->GetCPUDescriptorHandleForHeapStart(); + for (uint32_t n = 0; n < FRAME_COUNT; n++) + { + swapChain->GetBuffer(n + , __uuidof(ID3D12Resource) + , (void**)&renderTargets[n]); + device->CreateRenderTargetView(renderTargets[n] + , nullptr + , rtvHandle); + rtvHandle.ptr += rtvDescriptorSize; + } + + // Create the command list for uploading the font texture and rendering + device->CreateCommandList(0 + , D3D12_COMMAND_LIST_TYPE_DIRECT + , commandAllocator + , NULL + , __uuidof(ID3D12GraphicsCommandList) + , (void**)&commandList + ); + + // Data for sync + ID3D12Fence* fence; + uint32_t frameIndex = 0; + uint32_t fenceValue = 1; + + // Create an event handle to use for frame synchronization. + HANDLE fenceEvent = CreateEventEx(nullptr, FALSE, FALSE, EVENT_ALL_ACCESS); + device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); + + D3D12_CPU_DESCRIPTOR_HANDLE srvHandle = srvHeap->GetCPUDescriptorHandleForHeapStart(); + ImGui_ImplDX12_CreateDeviceObjects(device, commandList, commandQueue, fence, srvHandle, rootSignature); + + // The fence will be signalled when the command queue finishes uploading the fonts texture (see CreateDeviceObjects) + // which will signal the fenceEvent and execution continues + fence->SetEventOnCompletion(0, fenceEvent); + WaitForSingleObject(fenceEvent, INFINITE); + + // Load Fonts + // (see extra_fonts/README.txt for more details) + //ImGuiIO& io = ImGui::GetIO(); + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("../../extra_fonts/Cousine-Regular.ttf", 15.0f); + //io.Fonts->AddFontFromFileTTF("../../extra_fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../extra_fonts/ProggyClean.ttf", 13.0f); + //io.Fonts->AddFontFromFileTTF("../../extra_fonts/ProggyTiny.ttf", 10.0f); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + + // Merge glyphs from multiple fonts into one (e.g. combine default font with another with Chinese glyphs, or add icons) + //ImWchar icons_ranges[] = { 0xf000, 0xf3ff, 0 }; + //ImFontConfig icons_config; icons_config.MergeMode = true; icons_config.PixelSnapH = true; + //io.Fonts->AddFontFromFileTTF("../../extra_fonts/DroidSans.ttf", 18.0f); + //io.Fonts->AddFontFromFileTTF("../../extra_fonts/fontawesome-webfont.ttf", 18.0f, &icons_config, icons_ranges); + + bool show_test_window = true; + bool show_another_window = false; + ImVec4 clear_col = ImColor(114, 144, 154); + + // Main loop + MSG msg; + ZeroMemory(&msg, sizeof(msg)); + while (msg.message != WM_QUIT) + { + if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + continue; + } + + // Resizing logic + if (s_resizeRenderTargets) + { + for (uint32_t n = 0; n < FRAME_COUNT; n++) + { + renderTargets[n]->Release(); // Have to release the resources before calling ResizeBuffers + } + swapChain->ResizeBuffers(0, s_resizedWidth, s_resizedHeight, DXGI_FORMAT_UNKNOWN, 0); + + rtvHandle = rtvHeap->GetCPUDescriptorHandleForHeapStart(); + for (uint32_t n = 0; n < FRAME_COUNT; n++) + { + swapChain->GetBuffer(n + , __uuidof(ID3D12Resource) + , (void**)&renderTargets[n]); + device->CreateRenderTargetView(renderTargets[n] + , nullptr + , rtvHandle); + rtvHandle.ptr += rtvDescriptorSize; + } + + s_resizeRenderTargets = false; + frameIndex = 0; + } + + + ImGui_ImplDX12_NewFrame(); + + // 1. Show a simple window + // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets appears in a window automatically called "Debug" + { + static float f = 0.0f; + ImGui::Text("Hello, world!"); + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); + ImGui::ColorEdit3("clear color", (float*)&clear_col); + if (ImGui::Button("Test Window")) show_test_window ^= 1; + if (ImGui::Button("Another Window")) show_another_window ^= 1; + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + } + + // 2. Show another simple window, this time using an explicit Begin/End pair + if (show_another_window) + { + ImGui::SetNextWindowSize(ImVec2(200, 100), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Another Window", &show_another_window); + ImGui::Text("Hello"); + ImGui::End(); + } + + // 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowTestWindow() + if (show_test_window) + { + ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiSetCond_FirstUseEver); // Normally user code doesn't need/want to call it because positions are saved in .ini file anyway. Here we just want to make the demo initial state a bit more friendly! + ImGui::ShowTestWindow(&show_test_window); + } + + // This is where rendering starts + commandAllocator->Reset(); + commandList->Reset(commandAllocator, 0); + ID3D12CommandList* ppCommandLists[] = { commandList }; + + // Clear render target + D3D12_CPU_DESCRIPTOR_HANDLE renderTarget = rtvHeap->GetCPUDescriptorHandleForHeapStart(); + { + renderTarget.ptr += frameIndex * rtvDescriptorSize; + + setResourceBarrier(commandList + , renderTargets[frameIndex] + , D3D12_RESOURCE_STATE_PRESENT + , D3D12_RESOURCE_STATE_RENDER_TARGET); + + // Set necessary state. + const float clearColor[] = { 0.8f, 0.8f, 0.8f, 0.8f }; + commandList->ClearRenderTargetView(renderTarget, clearColor, 0, nullptr); + commandList->Close(); + + commandQueue->ExecuteCommandLists(1, ppCommandLists); + } + + // Setup imgui cmd list and render + { + commandList->Reset(commandAllocator, 0); + commandList->SetGraphicsRootSignature(rootSignature); + commandList->SetDescriptorHeaps(1, &srvHeap); + D3D12_GPU_DESCRIPTOR_HANDLE srvHandle = srvHeap->GetGPUDescriptorHandleForHeapStart(); + commandList->SetGraphicsRootDescriptorTable(0, srvHandle); + + // Pass command queue, command list and CPU descriptor handle to the render target + // to ImGui before rendering + ImGui_ImplDX12_SetRenderData(commandList, renderTarget); + ImGui::Render(); + + // Closing the command list and executing it is the apps responsibility + // ImGui only fills the command list + } + + setResourceBarrier(commandList + , renderTargets[frameIndex] + , D3D12_RESOURCE_STATE_RENDER_TARGET + , D3D12_RESOURCE_STATE_PRESENT); + commandList->Close(); + + commandQueue->ExecuteCommandLists(1, ppCommandLists); + commandQueue->Signal(fence, fenceValue); + + // Wait until the resource transition to present is completed + if (fence->GetCompletedValue() < fenceValue) + { + fence->SetEventOnCompletion(fenceValue, fenceEvent); + WaitForSingleObject(fenceEvent, INFINITE); + } + + // Present + swapChain->Present(0, DXGI_PRESENT_RESTART); + frameIndex = swapChain->GetCurrentBackBufferIndex(); + fenceValue++; + } + + // Shutdown!! + // + UnregisterClass(L"ImGui Example", wc.hInstance); + return 0; +}