Browse Source

Examples: DirectX12: Add directx12_example

Jefferson Montgomery 9 years ago
parent
commit
f6181b3428

+ 4 - 0
examples/README.txt

@@ -58,6 +58,10 @@ directx11_example/
     DirectX11 example, Windows only.
     This is quite long and tedious, because: DirectX11.
 	
+directx12_example/
+    DirectX12 example, Windows only.
+    This is quite long and tedious, because: DirectX12.
+	
 ios_example/
     iOS example.
     Using Synergy to access keyboard/mouse data from server computer.

+ 4 - 0
examples/directx12_example/build_win32.bat

@@ -0,0 +1,4 @@
+@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler.
+mkdir Debug
+cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\..\*.cpp /FeDebug/directx12_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d12.lib d3dcompiler.lib
+

+ 164 - 0
examples/directx12_example/directx12_example.vcxproj

@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{b4cf9797-519d-4afe-a8f4-5141a6b521d3}</ProjectGuid>
+    <RootNamespace>directx12_example</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.10240.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+    <IntDir>$(ProjectDir)$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+    <IntDir>$(ProjectDir)$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+    <IntDir>$(ProjectDir)$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+    <IntDir>$(ProjectDir)$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>d3d12.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>d3d12.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>d3d12.lib;d3dcompiler.lib;dxgi.lib;imm32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>d3d12.lib;d3dcompiler.lib;dxgi.lib;imm32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\imconfig.h" />
+    <ClInclude Include="..\..\imgui.h" />
+    <ClInclude Include="..\..\imgui_internal.h" />
+    <ClInclude Include="imgui_impl_dx12.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\imgui.cpp" />
+    <ClCompile Include="..\..\imgui_demo.cpp" />
+    <ClCompile Include="..\..\imgui_draw.cpp" />
+    <ClCompile Include="imgui_impl_dx12.cpp" />
+    <ClCompile Include="main.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\README.txt" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 45 - 0
examples/directx12_example/directx12_example.vcxproj.filters

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="imgui">
+      <UniqueIdentifier>{fb3d294f-51ec-478e-a627-25831c80fefd}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="sources">
+      <UniqueIdentifier>{4f33ddea-9910-456d-b868-4267eb3c2b19}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\imconfig.h">
+      <Filter>imgui</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\imgui.h">
+      <Filter>imgui</Filter>
+    </ClInclude>
+    <ClInclude Include="imgui_impl_dx12.h">
+      <Filter>sources</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\imgui_internal.h">
+      <Filter>imgui</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\imgui.cpp">
+      <Filter>imgui</Filter>
+    </ClCompile>
+    <ClCompile Include="main.cpp">
+      <Filter>sources</Filter>
+    </ClCompile>
+    <ClCompile Include="imgui_impl_dx12.cpp">
+      <Filter>sources</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\imgui_demo.cpp">
+      <Filter>imgui</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\imgui_draw.cpp">
+      <Filter>imgui</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\README.txt" />
+  </ItemGroup>
+</Project>

+ 667 - 0
examples/directx12_example/imgui_impl_dx12.cpp

@@ -0,0 +1,667 @@
+// ImGui Win32 + DirectX12 binding
+// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
+// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown().
+// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
+// https://github.com/ocornut/imgui
+
+#include <imgui.h>
+#include "imgui_impl_dx12.h"
+
+// DirectX
+#include <d3d12.h>
+#include <d3dcompiler.h>
+
+struct FrameResources
+{
+    ID3D12Resource* IB;
+    ID3D12Resource* VB;
+    int VertexBufferSize;
+    int IndexBufferSize;
+};
+
+// Data
+static INT64                    g_Time = 0;
+static INT64                    g_TicksPerSecond = 0;
+
+static HWND                     g_hWnd = 0;
+static ID3D12Device*            g_pd3dDevice = NULL;
+static ID3D12GraphicsCommandList* g_pd3dCommandList = NULL;
+static ID3D10Blob*              g_pVertexShaderBlob = NULL;
+static ID3D10Blob*              g_pPixelShaderBlob = NULL;
+static ID3D12RootSignature*     g_pRootSignature = NULL;
+static ID3D12PipelineState*     g_pPipelineState = NULL;
+static ID3D12Resource*          g_pFontTextureResource = NULL;
+static D3D12_CPU_DESCRIPTOR_HANDLE g_hFontSrvCpuDescHandle = {};
+static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {};
+
+static FrameResources*          g_pFrameResources = NULL;
+static UINT                     g_numFramesInFlight = 0;
+static UINT                     g_frameIndex = UINT_MAX;
+
+struct VERTEX_CONSTANT_BUFFER
+{
+    float        mvp[4][4];
+};
+
+// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
+// If text or lines are blurry when integrating ImGui in your engine:
+// - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
+void ImGui_ImplDX12_RenderDrawLists(ImDrawData* draw_data)
+{
+    // Create and grow vertex/index buffers if needed
+    //
+    // NOTE: I'm assuming that this only get's called once per frame!  If not,
+    // we can't just re-allocate the IB or VB, we'll have to do a proper
+    // allocator.
+    g_frameIndex = g_frameIndex + 1;
+    FrameResources* frameResources = &g_pFrameResources[g_frameIndex % g_numFramesInFlight];
+
+    D3D12_HEAP_PROPERTIES props = {};
+    props.Type = D3D12_HEAP_TYPE_UPLOAD;
+    props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+    props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+
+    D3D12_RESOURCE_DESC desc = {};
+    desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+    desc.Height = 1;
+    desc.DepthOrArraySize = 1;
+    desc.MipLevels = 1;
+    desc.Format = DXGI_FORMAT_UNKNOWN;
+    desc.SampleDesc.Count = 1;
+    desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+    desc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+    if (!frameResources->VB || frameResources->VertexBufferSize < draw_data->TotalVtxCount)
+    {
+        if (frameResources->VB) { frameResources->VB->Release(); frameResources->VB = NULL; }
+        frameResources->VertexBufferSize = draw_data->TotalVtxCount + 5000;
+        desc.Width = frameResources->VertexBufferSize * sizeof(ImDrawVert);
+        HRESULT hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&frameResources->VB));
+        assert(SUCCEEDED(hr));
+    }
+    if (!frameResources->IB || frameResources->IndexBufferSize < draw_data->TotalIdxCount)
+    {
+        if (frameResources->IB) { frameResources->IB->Release(); frameResources->IB = NULL; }
+        frameResources->IndexBufferSize = draw_data->TotalIdxCount + 10000;
+        desc.Width = frameResources->IndexBufferSize * sizeof(ImDrawIdx);
+        HRESULT hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&frameResources->IB));
+        assert(SUCCEEDED(hr));
+    }
+
+    // Copy and convert all vertices into a single contiguous buffer
+    D3D12_RANGE range = {};
+    void* vtx_resource = NULL;
+    HRESULT hr = frameResources->VB->Map(0, &range, &vtx_resource);
+    assert(SUCCEEDED(hr));
+
+    void* idx_resource = NULL;
+    hr = frameResources->IB->Map(0, &range, &idx_resource);
+    assert(SUCCEEDED(hr));
+
+    ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource;
+    ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource;
+    for (int n = 0; n < draw_data->CmdListsCount; n++)
+    {
+        const ImDrawList* cmd_list = draw_data->CmdLists[n];
+        memcpy(vtx_dst, &cmd_list->VtxBuffer[0], cmd_list->VtxBuffer.size() * sizeof(ImDrawVert));
+        memcpy(idx_dst, &cmd_list->IdxBuffer[0], cmd_list->IdxBuffer.size() * sizeof(ImDrawIdx));
+        vtx_dst += cmd_list->VtxBuffer.size();
+        idx_dst += cmd_list->IdxBuffer.size();
+    }
+    frameResources->VB->Unmap(0, &range);
+    frameResources->IB->Unmap(0, &range);
+
+    // Setup orthographic projection matrix into our constant buffer
+    VERTEX_CONSTANT_BUFFER vertexConstantBuffer = {};
+    {
+        const float L = 0.0f;
+        const float R = ImGui::GetIO().DisplaySize.x;
+        const float B = ImGui::GetIO().DisplaySize.y;
+        const float T = 0.0f;
+        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(&vertexConstantBuffer.mvp, mvp, sizeof(mvp));
+    }
+
+    // Setup viewport
+    D3D12_VIEWPORT viewport = {};
+    viewport.Width = ImGui::GetIO().DisplaySize.x;
+    viewport.Height = ImGui::GetIO().DisplaySize.y;
+    viewport.MinDepth = 0.0f;
+    viewport.MaxDepth = 1.0f;
+    viewport.TopLeftX = 0;
+    viewport.TopLeftY = 0;
+
+    // Bind shader and vertex buffers
+    D3D12_VERTEX_BUFFER_VIEW vbv = {};
+    vbv.BufferLocation = frameResources->VB->GetGPUVirtualAddress();
+    vbv.SizeInBytes = frameResources->VertexBufferSize * sizeof(ImDrawVert);
+    vbv.StrideInBytes = sizeof(ImDrawVert);
+
+    D3D12_INDEX_BUFFER_VIEW ibv = {};
+    ibv.BufferLocation = frameResources->IB->GetGPUVirtualAddress();
+    ibv.SizeInBytes = frameResources->IndexBufferSize * sizeof(ImDrawIdx);
+    ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
+
+    FLOAT blendFactor[4] = { 0.f, 0.f, 0.f, 0.f };
+
+    g_pd3dCommandList->SetPipelineState(g_pPipelineState);
+    g_pd3dCommandList->SetGraphicsRootSignature(g_pRootSignature);
+    g_pd3dCommandList->SetGraphicsRoot32BitConstants(0, 16, &vertexConstantBuffer, 0);
+    g_pd3dCommandList->IASetIndexBuffer(&ibv);
+    g_pd3dCommandList->IASetVertexBuffers(0, 1, &vbv);
+    g_pd3dCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+    g_pd3dCommandList->RSSetViewports(1, &viewport);
+    g_pd3dCommandList->OMSetBlendFactor(blendFactor);
+
+    // Render command lists
+    int vtx_offset = 0;
+    int idx_offset = 0;
+    for (int n = 0; n < draw_data->CmdListsCount; n++)
+    {
+        const ImDrawList* cmd_list = draw_data->CmdLists[n];
+        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
+            {
+                D3D12_RECT scissorRect = { (LONG)pcmd->ClipRect.x, (LONG)pcmd->ClipRect.y, (LONG)pcmd->ClipRect.z, (LONG)pcmd->ClipRect.w };
+
+                D3D12_GPU_DESCRIPTOR_HANDLE srvHandle = {};
+                srvHandle.ptr = (UINT64) pcmd->TextureId;
+
+                g_pd3dCommandList->SetGraphicsRootDescriptorTable(1, srvHandle);
+                g_pd3dCommandList->RSSetScissorRects(1, &scissorRect);
+                g_pd3dCommandList->DrawIndexedInstanced(pcmd->ElemCount, 1, idx_offset, vtx_offset, 0);
+            }
+            idx_offset += pcmd->ElemCount;
+        }
+        vtx_offset += cmd_list->VtxBuffer.size();
+    }
+}
+
+IMGUI_API 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_MBUTTONDOWN:
+        io.MouseDown[2] = true;
+        return true;
+    case WM_MBUTTONUP:
+        io.MouseDown[2] = 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 void ImGui_ImplDX12_CreateFontsTexture()
+{
+    // Build texture atlas
+    ImGuiIO& io = ImGui::GetIO();
+    unsigned char* pixels;
+    int width, height;
+    io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
+
+    // Upload texture to graphics system
+    {
+        D3D12_RESOURCE_DESC resourceDesc = {};
+        resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+        resourceDesc.Alignment = 0;
+        resourceDesc.Width = width;
+        resourceDesc.Height = height;
+        resourceDesc.DepthOrArraySize = 1;
+        resourceDesc.MipLevels = 1;
+        resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+        resourceDesc.SampleDesc.Count = 1;
+        resourceDesc.SampleDesc.Quality = 0;
+        resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+        resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+        D3D12_HEAP_PROPERTIES props = {};
+        props.Type = D3D12_HEAP_TYPE_DEFAULT;
+        props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+        props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+
+        HRESULT hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &resourceDesc,
+            D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&g_pFontTextureResource));
+        assert(SUCCEEDED(hr));
+
+        UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
+        UINT uploadSize = height * uploadPitch;
+        resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+        resourceDesc.Alignment = 0;
+        resourceDesc.Width = uploadSize;
+        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 = D3D12_RESOURCE_FLAG_NONE;
+
+        props.Type = D3D12_HEAP_TYPE_UPLOAD;
+        props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+        props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+
+        ID3D12Resource* uploadBuffer = NULL;
+        hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &resourceDesc,
+            D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer));
+        assert(SUCCEEDED(hr));
+
+        void* mapped = NULL;
+        D3D12_RANGE range = { 0, uploadSize };
+        hr = uploadBuffer->Map(0, &range, &mapped);
+        assert(SUCCEEDED(hr));
+        for (int y = 0; y < height; ++y)
+        {
+            memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4);
+        }
+        uploadBuffer->Unmap(0, &range);
+
+        D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
+        srcLocation.pResource = uploadBuffer;
+        srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+        srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+        srcLocation.PlacedFootprint.Footprint.Width = width;
+        srcLocation.PlacedFootprint.Footprint.Height = height;
+        srcLocation.PlacedFootprint.Footprint.Depth = 1;
+        srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch;
+
+        D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
+        dstLocation.pResource = g_pFontTextureResource;
+        dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+        dstLocation.SubresourceIndex = 0;
+
+        D3D12_RESOURCE_BARRIER barrier = {};
+        barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+        barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+        barrier.Transition.pResource   = g_pFontTextureResource;
+        barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+        barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
+        barrier.Transition.StateAfter  = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+
+        ID3D12Fence* fence = NULL;
+        hr = g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
+        assert(SUCCEEDED(hr));
+
+        HANDLE event = CreateEvent(0, 0, 0, 0);
+        assert(event != NULL);
+
+        D3D12_COMMAND_QUEUE_DESC queueDesc = {};
+        queueDesc.Type     = D3D12_COMMAND_LIST_TYPE_DIRECT;
+        queueDesc.Flags    = D3D12_COMMAND_QUEUE_FLAG_NONE;
+        queueDesc.NodeMask = 1;
+
+        ID3D12CommandQueue* cmdQueue = NULL;
+        hr = g_pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue));
+        assert(SUCCEEDED(hr));
+
+        ID3D12CommandAllocator* cmdAlloc = NULL;
+        hr = g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
+        assert(SUCCEEDED(hr));
+
+        ID3D12GraphicsCommandList* cmdList = NULL;
+        hr = g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList));
+        assert(SUCCEEDED(hr));
+
+        cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL);
+        cmdList->ResourceBarrier(1, &barrier);
+
+        hr = cmdList->Close();
+        assert(SUCCEEDED(hr));
+
+        cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*) &cmdList);
+        hr = cmdQueue->Signal(fence, 1);
+        assert(SUCCEEDED(hr));
+
+        fence->SetEventOnCompletion(1, event);
+        WaitForSingleObject(event, INFINITE);
+
+        cmdList->Release();
+        cmdAlloc->Release();
+        cmdQueue->Release();
+        CloseHandle(event);
+        fence->Release();
+        uploadBuffer->Release();
+    }
+
+    // Create texture view
+    g_pd3dDevice->CreateShaderResourceView(g_pFontTextureResource, NULL, g_hFontSrvCpuDescHandle);
+
+    // Store our identifier
+    static_assert(sizeof(void*) >= sizeof(g_hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID");
+    ImGui::GetIO().Fonts->TexID = (void*) g_hFontSrvGpuDescHandle.ptr;
+}
+
+bool    ImGui_ImplDX12_CreateDeviceObjects()
+{
+    if (!g_pd3dDevice)
+        return false;
+    if (g_pPipelineState)
+        ImGui_ImplDX12_InvalidateDeviceObjects();
+
+    // Create the root signature
+    {
+        D3D12_DESCRIPTOR_RANGE descRange = {};
+        descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+        descRange.NumDescriptors = 1;
+        descRange.BaseShaderRegister = 0;
+        descRange.RegisterSpace = 0;
+        descRange.OffsetInDescriptorsFromTableStart = 0;
+
+        D3D12_ROOT_PARAMETER param[2] = {};
+
+        param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
+        param[0].Constants.ShaderRegister = 0;
+        param[0].Constants.RegisterSpace = 0;
+        param[0].Constants.Num32BitValues = 16;
+        param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
+
+        param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+        param[1].DescriptorTable.NumDescriptorRanges = 1;
+        param[1].DescriptorTable.pDescriptorRanges = &descRange;
+        param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+
+        D3D12_STATIC_SAMPLER_DESC staticSampler = {};
+        staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
+        staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+        staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+        staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+        staticSampler.MipLODBias = 0.f;
+        staticSampler.MaxAnisotropy = 0;
+        staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
+        staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
+        staticSampler.MinLOD = 0.f;
+        staticSampler.MaxLOD = 0.f;
+        staticSampler.ShaderRegister = 0;
+        staticSampler.RegisterSpace = 0;
+        staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+
+        D3D12_ROOT_SIGNATURE_DESC desc = {};
+        desc.NumParameters = _countof(param);
+        desc.pParameters = param;
+        desc.NumStaticSamplers = 1;
+        desc.pStaticSamplers = &staticSampler;
+        desc.Flags =
+            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
+            D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
+            D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
+            D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
+
+        ID3DBlob* blob = NULL;
+        if (D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, NULL) != S_OK)
+            return false;
+
+        g_pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&g_pRootSignature));
+        blob->Release();
+    }
+    // Create the pipeline state object
+    {
+        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; \
+            }";
+
+        D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &g_pVertexShaderBlob, NULL);
+        if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
+            return false;
+
+        D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &g_pPixelShaderBlob, NULL);
+        if (g_pPixelShaderBlob == NULL)  // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
+            return false;
+
+        D3D12_INPUT_ELEMENT_DESC localLayout[] = {
+            { "POSITION", 0, DXGI_FORMAT_R32G32_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 },
+        };
+
+        D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = {};
+        desc.NodeMask              = 1;
+        desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+        desc.InputLayout           = { localLayout, _countof(localLayout) };
+        desc.pRootSignature        = g_pRootSignature;
+        desc.VS                    = { g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize() };
+        desc.PS                    = { g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize() };
+        desc.SampleMask            = UINT_MAX;
+        desc.NumRenderTargets      = 1;
+        desc.RTVFormats[0]         = DXGI_FORMAT_B8G8R8A8_UNORM;
+        desc.SampleDesc.Count      = 1;
+        desc.Flags                 = D3D12_PIPELINE_STATE_FLAG_NONE;
+
+        desc.RasterizerState.FillMode              = D3D12_FILL_MODE_SOLID;
+        desc.RasterizerState.CullMode              = D3D12_CULL_MODE_NONE;
+        desc.RasterizerState.FrontCounterClockwise = FALSE;
+        desc.RasterizerState.DepthBias             = D3D12_DEFAULT_DEPTH_BIAS;
+        desc.RasterizerState.DepthBiasClamp        = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
+        desc.RasterizerState.SlopeScaledDepthBias  = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
+        desc.RasterizerState.DepthClipEnable       = TRUE;
+        desc.RasterizerState.MultisampleEnable     = FALSE;
+        desc.RasterizerState.AntialiasedLineEnable = FALSE;
+        desc.RasterizerState.ForcedSampleCount     = 0;
+        desc.RasterizerState.ConservativeRaster    = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
+
+        desc.BlendState.AlphaToCoverageEnable = FALSE;
+        desc.BlendState.RenderTarget[0].BlendEnable = TRUE;
+        desc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
+        desc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
+        desc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
+        desc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
+        desc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO;
+        desc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
+        desc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
+
+        desc.DepthStencilState.DepthEnable   = FALSE;
+        desc.DepthStencilState.StencilEnable = FALSE;
+
+        HRESULT hr = g_pd3dDevice->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(&g_pPipelineState));
+        assert(SUCCEEDED(hr));
+    }
+
+    ImGui_ImplDX12_CreateFontsTexture();
+
+    return true;
+}
+
+void    ImGui_ImplDX12_InvalidateDeviceObjects()
+{
+    if (!g_pd3dDevice)
+        return;
+
+    if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
+    if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
+    if (g_pRootSignature) { g_pRootSignature->Release(); g_pRootSignature = NULL; }
+    if (g_pPipelineState) { g_pPipelineState->Release(); g_pPipelineState = NULL; }
+    if (g_pFontTextureResource) { g_pFontTextureResource->Release(); g_pFontTextureResource = NULL; }
+    for (UINT i = 0; i < g_numFramesInFlight; ++i)
+    {
+        if (g_pFrameResources[i].IB) { g_pFrameResources[i].IB->Release(); g_pFrameResources[i].IB = NULL; }
+        if (g_pFrameResources[i].VB) { g_pFrameResources[i].VB->Release(); g_pFrameResources[i].VB = NULL; }
+    }
+    ImGui::GetIO().Fonts->TexID = 0;
+}
+
+bool    ImGui_ImplDX12_Init(void* hwnd, int numFramesInFlight,
+                            ID3D12Device* device, ID3D12GraphicsCommandList* cmdList,
+                            D3D12_CPU_DESCRIPTOR_HANDLE fontSrvCpuDescHandle,
+                            D3D12_GPU_DESCRIPTOR_HANDLE fontSrvGpuDescHandle)
+{
+    g_hWnd = (HWND)hwnd;
+    g_pd3dDevice = device;
+    g_pd3dCommandList = cmdList;
+    g_hFontSrvCpuDescHandle = fontSrvCpuDescHandle;
+    g_hFontSrvGpuDescHandle = fontSrvGpuDescHandle;
+    g_pFrameResources = new FrameResources [numFramesInFlight];
+    g_numFramesInFlight = numFramesInFlight;
+    g_frameIndex = UINT_MAX;
+
+    for (int i = 0; i < numFramesInFlight; ++i)
+    {
+        g_pFrameResources[i].IB = NULL;
+        g_pFrameResources[i].VB = NULL;
+        g_pFrameResources[i].VertexBufferSize = 5000;
+        g_pFrameResources[i].IndexBufferSize = 10000;
+    }
+
+    if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
+        return false;
+    if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
+        return false;
+
+    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;  // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer.
+    io.ImeWindowHandle = g_hWnd;
+
+    return true;
+}
+
+void ImGui_ImplDX12_Shutdown()
+{
+    ImGui_ImplDX12_InvalidateDeviceObjects();
+    ImGui::Shutdown();
+    delete[] g_pFrameResources;
+    g_pd3dDevice = NULL;
+    g_hWnd = (HWND)0;
+    g_pd3dCommandList = NULL;
+    g_hFontSrvCpuDescHandle.ptr = 0;
+    g_hFontSrvGpuDescHandle.ptr = 0;
+    g_pFrameResources = NULL;
+    g_numFramesInFlight = 0;
+    g_frameIndex = UINT_MAX;
+}
+
+void ImGui_ImplDX12_NewFrame()
+{
+    if (!g_pPipelineState)
+        ImGui_ImplDX12_CreateDeviceObjects();
+
+    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 *)&current_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();
+}

+ 34 - 0
examples/directx12_example/imgui_impl_dx12.h

@@ -0,0 +1,34 @@
+// ImGui Win32 + DirectX12 binding
+// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
+// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown().
+// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
+// https://github.com/ocornut/imgui
+
+#include <d3d12.h>
+
+// cmdList is the command list that the implementation will use to render the
+// GUI.
+//
+// Before calling ImGui::Render(), caller must prepare cmdList by resetting it
+// and setting the appropriate render target and descriptor heap that contains
+// fontSrv*puDescHandle.
+//
+// fontSrvCpuDescHandle and fontSrvGpuDescHandle are handles to a single SRV
+// descriptor to use for the internal font texture.
+IMGUI_API bool        ImGui_ImplDX12_Init(void* hwnd, int numFramesInFlight,
+                                          ID3D12Device* device, ID3D12GraphicsCommandList* cmdList,
+                                          D3D12_CPU_DESCRIPTOR_HANDLE fontSrvCpuDescHandle,
+                                          D3D12_GPU_DESCRIPTOR_HANDLE fontSrvGpuDescHandle);
+IMGUI_API void        ImGui_ImplDX12_Shutdown();
+IMGUI_API void        ImGui_ImplDX12_NewFrame();
+
+// Use if you want to reset your rendering device without losing ImGui state.
+IMGUI_API void        ImGui_ImplDX12_InvalidateDeviceObjects();
+IMGUI_API bool        ImGui_ImplDX12_CreateDeviceObjects();
+
+// Handler for Win32 messages, update mouse/keyboard data.
+// You may or not need this for your implementation, but it can serve as reference for handling inputs.
+// Commented out to avoid dragging dependencies on <windows.h> types. You can copy the extern declaration in your code.
+/*
+IMGUI_API LRESULT   ImGui_ImplDX12_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+*/

+ 418 - 0
examples/directx12_example/main.cpp

@@ -0,0 +1,418 @@
+// ImGui - standalone example application for DirectX 11
+// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
+
+#include <assert.h>
+#include <imgui.h>
+#include "imgui_impl_dx12.h"
+#include <d3d12.h>
+#include <dxgi1_4.h>
+#include <tchar.h>
+
+struct FrameContext
+{
+    ID3D12CommandAllocator* CommandAllocator;
+    UINT64                  FenceValue;
+    bool                    FenceSignalled;
+};
+
+// Data
+static int const                    NUM_FRAMES_IN_FLIGHT = 3;
+static FrameContext                 g_frameContext[NUM_FRAMES_IN_FLIGHT] = {};
+static UINT                         g_frameIndex = 0;
+
+static int const                    NUM_BACK_BUFFERS = 3;
+static IDXGIFactory4*               g_pdxgiFactory = NULL;
+static ID3D12Device*                g_pd3dDevice = NULL;
+static ID3D12DescriptorHeap*        g_pd3dRtvDescHeap = NULL;
+static ID3D12DescriptorHeap*        g_pd3dSrvDescHeap = NULL;
+static ID3D12CommandQueue*          g_pd3dCommandQueue = NULL;
+static ID3D12GraphicsCommandList*   g_pd3dCommandList = NULL;
+static ID3D12Fence*                 g_fence = NULL;
+static HANDLE                       g_fenceEvent = NULL;
+static UINT64                       g_fenceLastSignalledValue = 0;
+static IDXGISwapChain3*             g_pSwapChain = NULL;
+static HANDLE                       g_hSwapChainWaitableObject = NULL;
+static ID3D12Resource*              g_mainRenderTargetResource[NUM_BACK_BUFFERS] = {};
+static D3D12_CPU_DESCRIPTOR_HANDLE  g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS] = {};
+
+static void WaitForLastSubmittedFrame()
+{
+    FrameContext* frameCtxt = &g_frameContext[g_frameIndex % NUM_FRAMES_IN_FLIGHT];
+
+    UINT64 fenceValue = frameCtxt->FenceValue;
+    if (fenceValue == 0) { // means no fence was signalled
+        return;
+    }
+
+    frameCtxt->FenceValue = 0;
+    if (g_fence->GetCompletedValue() >= fenceValue) {
+        return;
+    }
+
+    HRESULT hr = g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
+    assert(SUCCEEDED(hr));
+
+    WaitForSingleObject(g_fenceEvent, INFINITE);
+}
+
+static FrameContext* WaitForNextFrameResources()
+{
+    UINT nextFrameIndex = g_frameIndex + 1;
+    g_frameIndex = nextFrameIndex;
+
+    HANDLE waitableObjects[] = {
+        g_hSwapChainWaitableObject,
+        NULL,
+    };
+    DWORD numWaitableObjects = 1;
+
+    FrameContext* frameCtxt = &g_frameContext[nextFrameIndex % NUM_FRAMES_IN_FLIGHT];
+    UINT64 fenceValue = frameCtxt->FenceValue;
+    if (fenceValue != 0) { // means no fence was signalled
+        frameCtxt->FenceValue = 0;
+
+        HRESULT hr = g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
+        assert(SUCCEEDED(hr));
+
+        waitableObjects[1] = g_fenceEvent;
+        numWaitableObjects = 2;
+    }
+
+    HRESULT hr = WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);
+    assert(SUCCEEDED(hr));
+
+    return frameCtxt;
+}
+
+void CreateRenderTarget(HWND hWnd, int width, int height)
+{
+    DXGI_SWAP_CHAIN_DESC1 desc = {};
+    desc.Width              = width;
+    desc.Height             = height;
+    desc.Format             = DXGI_FORMAT_B8G8R8A8_UNORM;
+    desc.Stereo             = FALSE;
+    desc.SampleDesc.Count   = 1;
+    desc.SampleDesc.Quality = 0;
+    desc.BufferUsage        = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    desc.BufferCount        = NUM_BACK_BUFFERS;
+    desc.Scaling            = DXGI_SCALING_STRETCH;
+    desc.SwapEffect         = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+    desc.AlphaMode          = DXGI_ALPHA_MODE_UNSPECIFIED;
+    desc.Flags              = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
+
+    IDXGISwapChain1* swapChain1 = NULL;
+    HRESULT hr = g_pdxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &desc, NULL, NULL, &swapChain1);
+    assert(SUCCEEDED(hr));
+
+    hr = swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain));
+    assert(SUCCEEDED(hr));
+
+    swapChain1->Release();
+
+    hr = g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);
+    assert(SUCCEEDED(hr));
+
+    g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject();
+    assert(g_hSwapChainWaitableObject != NULL);
+
+    for (UINT i = 0; i < NUM_BACK_BUFFERS; ++i)
+    {
+        hr = g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&g_mainRenderTargetResource[i]));
+        assert(SUCCEEDED(hr));
+
+        g_pd3dDevice->CreateRenderTargetView(g_mainRenderTargetResource[i], NULL, g_mainRenderTargetDescriptor[i]);
+    }
+}
+
+void CleanupRenderTarget()
+{
+    WaitForLastSubmittedFrame();
+
+    if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = NULL; }
+    for (UINT i = 0; i < NUM_BACK_BUFFERS; ++i)
+    {
+        if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = NULL; }
+    }
+    if (g_hSwapChainWaitableObject != NULL) { CloseHandle(g_hSwapChainWaitableObject); }
+}
+
+HRESULT CreateDeviceD3D(HWND hWnd)
+{
+    (void) hWnd;
+
+#ifdef _DEBUG
+    {
+        ID3D12Debug* debugController = NULL;
+        if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
+        {
+            debugController->EnableDebugLayer();
+            debugController->Release();
+        }
+    }
+#endif
+
+    if (CreateDXGIFactory1(IID_PPV_ARGS(&g_pdxgiFactory)) != S_OK)
+        return E_FAIL;
+
+    IDXGIAdapter1* adapter = NULL;
+    for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != g_pdxgiFactory->EnumAdapters1(adapterIndex, &adapter); ++adapterIndex)
+    {
+        DXGI_ADAPTER_DESC1 desc = {};
+        adapter->GetDesc1(&desc);
+
+        if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
+        {
+            adapter->Release();
+            continue;
+        }
+
+        if (FAILED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), NULL)))
+        {
+            adapter->Release();
+            continue;
+        }
+
+        break;
+    }
+
+    HRESULT hr = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&g_pd3dDevice));
+
+    if (adapter != NULL)
+    {
+        adapter->Release();
+    }
+
+    if (FAILED(hr))
+        return hr;
+
+    {
+        D3D12_DESCRIPTOR_HEAP_DESC desc = {};
+        desc.Type           = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+        desc.NumDescriptors = NUM_BACK_BUFFERS;
+        desc.Flags          = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+        desc.NodeMask       = 1;
+        if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
+            return E_FAIL;
+
+        SIZE_T rtvDescriptorSize = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+        D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
+        for (UINT i = 0; i < NUM_BACK_BUFFERS; ++i) {
+            g_mainRenderTargetDescriptor[i] = rtvHandle;
+            rtvHandle.ptr += rtvDescriptorSize;
+        }
+    }
+
+    {
+        D3D12_DESCRIPTOR_HEAP_DESC desc = {};
+        desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+        desc.NumDescriptors = 1;
+        desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+        if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
+            return E_FAIL;
+    }
+
+    {
+        D3D12_COMMAND_QUEUE_DESC desc = {};
+        desc.Type     = D3D12_COMMAND_LIST_TYPE_DIRECT;
+        desc.Flags    = D3D12_COMMAND_QUEUE_FLAG_NONE;
+        desc.NodeMask = 1;
+        if (g_pd3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK)
+            return E_FAIL;
+    }
+
+    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; ++i)
+    {
+        if (g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK)
+            return E_FAIL;
+    }
+
+    if (g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_frameContext[0].CommandAllocator, NULL, IID_PPV_ARGS(&g_pd3dCommandList)) != S_OK ||
+        g_pd3dCommandList->Close() != S_OK)
+        return E_FAIL;
+
+    if (g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK)
+        return E_FAIL;
+
+    g_fenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (g_fenceEvent == NULL)
+        return E_FAIL;
+
+    return S_OK;
+}
+
+void CleanupDeviceD3D()
+{
+    CleanupRenderTarget();
+    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; ++i)
+    {
+        if (g_frameContext[i].CommandAllocator) { g_frameContext[i].CommandAllocator->Release(); g_frameContext[i].CommandAllocator = NULL; }
+    }
+    if (g_pd3dCommandQueue) { g_pd3dCommandQueue->Release(); g_pd3dCommandQueue = NULL; }
+    if (g_pd3dCommandList) { g_pd3dCommandList->Release(); g_pd3dCommandList = NULL; }
+    if (g_pd3dRtvDescHeap) { g_pd3dRtvDescHeap->Release(); g_pd3dRtvDescHeap = NULL; }
+    if (g_pd3dSrvDescHeap) { g_pd3dSrvDescHeap->Release(); g_pd3dSrvDescHeap = NULL; }
+    if (g_fence) { g_fence->Release(); g_fence = NULL; }
+    if (g_fenceEvent) { CloseHandle(g_fenceEvent); g_fenceEvent = NULL; }
+    if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
+    if (g_pdxgiFactory) { g_pdxgiFactory->Release(); g_pdxgiFactory = NULL; }
+}
+
+extern LRESULT ImGui_ImplDX12_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+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:
+        if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED)
+        {
+            ImGui_ImplDX12_InvalidateDeviceObjects();
+            CleanupRenderTarget();
+            CreateRenderTarget(hWnd, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam));
+            ImGui_ImplDX12_CreateDeviceObjects();
+        }
+        return 0;
+    case WM_SYSCOMMAND:
+        if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
+            return 0;
+        break;
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        return 0;
+    }
+    return DefWindowProc(hWnd, msg, wParam, lParam);
+}
+
+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, _T("ImGui Example"), NULL };
+    RegisterClassEx(&wc);
+    HWND hwnd = CreateWindow(_T("ImGui Example"), _T("ImGui DirectX12 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
+
+    // Initialize Direct3D
+    if (CreateDeviceD3D(hwnd) < 0)
+    {
+        CleanupDeviceD3D();
+        UnregisterClass(_T("ImGui Example"), wc.hInstance);
+        return 1;
+    }
+
+    // Show the window
+    ShowWindow(hwnd, SW_SHOWDEFAULT);
+    UpdateWindow(hwnd);
+
+    // Setup ImGui binding
+    ImGui_ImplDX12_Init(hwnd, NUM_FRAMES_IN_FLIGHT, g_pd3dDevice, g_pd3dCommandList,
+        g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(),
+        g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
+
+    // Load Fonts
+    // (there is a default font, this is only if you want to change it. 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());
+
+    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;
+        }
+
+        // Wait for frame resources to be available
+
+        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);
+        }
+
+        // Rendering
+        FrameContext* frameCtxt = WaitForNextFrameResources();
+        UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
+
+        HRESULT hr = frameCtxt->CommandAllocator->Reset();
+        assert(SUCCEEDED(hr));
+
+        hr = g_pd3dCommandList->Reset(frameCtxt->CommandAllocator, NULL);
+        assert(SUCCEEDED(hr));
+
+        D3D12_RESOURCE_BARRIER barrier = {};
+        barrier.Type                   = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+        barrier.Flags                  = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+        barrier.Transition.pResource   = g_mainRenderTargetResource[backBufferIdx];
+        barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+        barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
+        barrier.Transition.StateAfter  = D3D12_RESOURCE_STATE_RENDER_TARGET;
+        g_pd3dCommandList->ResourceBarrier(1, &barrier);
+
+        g_pd3dCommandList->ClearRenderTargetView(g_mainRenderTargetDescriptor[backBufferIdx], (float*)&clear_col, 0, NULL);
+        g_pd3dCommandList->OMSetRenderTargets(1, &g_mainRenderTargetDescriptor[backBufferIdx], FALSE, NULL);
+        g_pd3dCommandList->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap);
+        ImGui::Render();
+
+        barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+        barrier.Transition.StateAfter  = D3D12_RESOURCE_STATE_PRESENT;
+        g_pd3dCommandList->ResourceBarrier(1, &barrier);
+
+        hr = g_pd3dCommandList->Close();
+        assert(SUCCEEDED(hr));
+
+        g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*) &g_pd3dCommandList);
+
+        hr = g_pSwapChain->Present(1, 0);
+        assert(SUCCEEDED(hr));
+
+        auto fenceValue = g_fenceLastSignalledValue + 1;
+        hr = g_pd3dCommandQueue->Signal(g_fence, fenceValue);
+        assert(SUCCEEDED(hr));
+        g_fenceLastSignalledValue = fenceValue;
+        frameCtxt->FenceValue = fenceValue;
+    }
+
+    ImGui_ImplDX12_Shutdown();
+    CleanupDeviceD3D();
+    UnregisterClass(_T("ImGui Example"), wc.hInstance);
+
+    return 0;
+}

+ 10 - 0
examples/imgui_examples_msvc2010.sln

@@ -7,6 +7,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx9_example", "directx
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx11_example", "directx11_example\directx11_example.vcxproj", "{9F316E83-5AE5-4939-A723-305A94F48005}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx12_example", "directx12_example\directx12_example.vcxproj", "{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}"
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opengl3_example", "opengl3_example\opengl3_example.vcxproj", "{4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx10_example", "directx10_example\directx10_example.vcxproj", "{345A953E-A004-4648-B442-DC5F9F11068C}"
@@ -43,6 +45,14 @@ Global
 		{9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32
 		{9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64
 		{9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64
+		{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.ActiveCfg = Debug|Win32
+		{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.Build.0 = Debug|Win32
+		{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.ActiveCfg = Debug|x64
+		{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.Build.0 = Debug|x64
+		{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.ActiveCfg = Release|Win32
+		{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.Build.0 = Release|Win32
+		{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.ActiveCfg = Release|x64
+		{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.Build.0 = Release|x64
 		{4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32
 		{4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32
 		{4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64