// dear imgui: Renderer Backend for DirectX12 // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. // To build this on 32-bit systems: // - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file) // - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. // - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) // - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file) // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer. // 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically. // 2020-09-16: DirectX12: Avoid rendering calls with zero-sized scissor rectangle since it generates a validation layer warning. // 2020-09-08: DirectX12: Clarified support for building on 32-bit systems by redefining ImTextureID. // 2019-10-18: DirectX12: *BREAKING CHANGE* Added extra ID3D12DescriptorHeap parameter to ImGui_ImplDX12_Init() function. // 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. // 2019-03-29: Misc: Various minor tidying up. // 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile(). // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData(). // 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example. // 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport). // 2018-02-22: Merged into master with all Win32 code synchronized to other examples. #include "common/windows_headers.h" #include "common/assert.h" #include "common/d3d12/context.h" #include "common/d3d12/texture.h" #include "common/d3d12/stream_buffer.h" #include "imgui.h" #include "imgui_impl_dx12.h" // DirectX #include #include #include #ifdef _MSC_VER #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif // If we're doing more than this... wtf? static constexpr u32 VERTEX_BUFFER_SIZE = 8 * 1024 * 1024; static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024; struct ImGui_ImplDX12_Data { D3D12::StreamBuffer VertexStreamBuffer; D3D12::StreamBuffer IndexStreamBuffer; D3D12::Texture FontTexture; ID3D12RootSignature* pRootSignature = nullptr; ID3D12PipelineState* pPipelineState = nullptr; DXGI_FORMAT RTVFormat = DXGI_FORMAT_UNKNOWN; }; struct VERTEX_CONSTANT_BUFFER { float mvp[4][4]; }; // Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts // It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : NULL; } // Functions static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); // Setup orthographic projection matrix into our constant buffer // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). VERTEX_CONSTANT_BUFFER vertex_constant_buffer; { float L = draw_data->DisplayPos.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; float T = draw_data->DisplayPos.y; float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; 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(&vertex_constant_buffer.mvp, mvp, sizeof(mvp)); } // Setup viewport D3D12_VIEWPORT vp; memset(&vp, 0, sizeof(D3D12_VIEWPORT)); vp.Width = draw_data->DisplaySize.x; vp.Height = draw_data->DisplaySize.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0.0f; ctx->RSSetViewports(1, &vp); // Bind shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); D3D12_VERTEX_BUFFER_VIEW vbv; memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW)); vbv.BufferLocation = bd->VertexStreamBuffer.GetCurrentGPUPointer(); vbv.SizeInBytes = bd->VertexStreamBuffer.GetCurrentSpace(); vbv.StrideInBytes = stride; ctx->IASetVertexBuffers(0, 1, &vbv); D3D12_INDEX_BUFFER_VIEW ibv; memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW)); ibv.BufferLocation = bd->IndexStreamBuffer.GetCurrentGPUPointer(); ibv.SizeInBytes = bd->IndexStreamBuffer.GetCurrentSpace(); ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; ctx->IASetIndexBuffer(&ibv); ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); ctx->SetPipelineState(bd->pPipelineState); ctx->SetGraphicsRootSignature(bd->pRootSignature); ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0); // Setup blend factor const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; ctx->OMSetBlendFactor(blend_factor); } template static inline void SafeRelease(T*& res) { if (res) res->Release(); res = NULL; } // Render function void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; // FIXME: I'm assuming that this only gets called once per frame! // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); const u32 needed_vb = draw_data->TotalVtxCount * sizeof(ImDrawVert); const u32 needed_ib = draw_data->TotalIdxCount * sizeof(ImDrawIdx); if (!bd->VertexStreamBuffer.ReserveMemory(needed_vb, sizeof(ImDrawVert)) || !bd->IndexStreamBuffer.ReserveMemory(needed_ib, sizeof(ImDrawIdx))) { g_d3d12_context->ExecuteCommandList(false); if (!bd->VertexStreamBuffer.ReserveMemory(needed_vb, sizeof(ImDrawVert)) || !bd->IndexStreamBuffer.ReserveMemory(needed_ib, sizeof(ImDrawIdx))) { Panic("Failed to allocate space for imgui vertices/indices"); } } // Upload vertex/index data into a single contiguous GPU buffer ImDrawVert* vtx_dst = (ImDrawVert*)bd->VertexStreamBuffer.GetCurrentHostPointer(); ImDrawIdx* idx_dst = (ImDrawIdx*)bd->IndexStreamBuffer.GetCurrentHostPointer(); for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); vtx_dst += cmd_list->VtxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size; } // Setup desired DX state (must happen before commit, because it uses the offsets) ID3D12GraphicsCommandList* ctx = g_d3d12_context->GetCommandList(); ImGui_ImplDX12_SetupRenderState(draw_data, ctx); bd->VertexStreamBuffer.CommitMemory(needed_vb); bd->IndexStreamBuffer.CommitMemory(needed_ib); // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) int global_vtx_offset = 0; int global_idx_offset = 0; ImVec2 clip_off = draw_data->DisplayPos; const D3D12::Texture* last_texture = nullptr; 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 != NULL) { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) ImGui_ImplDX12_SetupRenderState(draw_data, ctx); else pcmd->UserCallback(cmd_list, pcmd); } else { // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply Scissor/clipping rectangle, Bind texture, Draw const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; const D3D12::Texture* tex = (D3D12::Texture*)pcmd->GetTexID(); if (tex && last_texture != tex) { #if 0 // for when we redo the descriptor stuff D3D12::DescriptorHandle handle; if (!g_d3d12_context->GetDescriptorAllocator().Allocate(1, &handle)) { // ugh. g_d3d12_context->ExecuteCommandList(false); ctx = g_d3d12_context->GetCommandList(); ImGui_ImplDX12_SetupRenderState(draw_data, ctx); if (!g_d3d12_context->GetDescriptorAllocator().Allocate(1, &handle)) Panic("Failed to allocate descriptor after cmdlist kick"); } g_d3d12_context->GetDevice()->CopyDescriptorsSimple(1, handle, tex->GetSRVDescriptor(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); ctx->SetGraphicsRootDescriptorTable(1, handle); #else ctx->SetGraphicsRootDescriptorTable(1, tex->GetSRVDescriptor()); #endif last_texture = tex; } ctx->RSSetScissorRects(1, &r); ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } } global_idx_offset += cmd_list->IdxBuffer.Size; global_vtx_offset += cmd_list->VtxBuffer.Size; } } bool ImGui_ImplDX12_CreateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Upload texture to graphics system if (bd->FontTexture.GetWidth() != static_cast(width) || bd->FontTexture.GetHeight() != static_cast(height)) { if (!bd->FontTexture.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE)) { return false; } } #if 0 if (!bd->FontTexture.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, pixels, width * sizeof(u32))) return false; #else if (!bd->FontTexture.LoadData(0, 0, width, height, pixels, width * sizeof(u32))) return false; #endif io.Fonts->SetTexID((ImTextureID)&bd->FontTexture); return true; } bool ImGui_ImplDX12_CreateDeviceObjects() { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); if (bd->pPipelineState) ImGui_ImplDX12_DestroyDeviceObjects(); // 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; // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. D3D12_STATIC_SAMPLER_DESC staticSampler = {}; staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; 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; auto blob = g_d3d12_context->SerializeRootSignature(&desc); if (!blob) return false; g_d3d12_context->GetDevice()->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature)); } // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) // If you would like to use this DX12 sample code but remove this dependency you can: // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. // See https://github.com/ocornut/imgui/pull/638 for sources and details. D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc; memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC)); psoDesc.NodeMask = 1; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; psoDesc.pRootSignature = bd->pRootSignature; psoDesc.SampleMask = UINT_MAX; psoDesc.NumRenderTargets = 1; psoDesc.RTVFormats[0] = bd->RTVFormat; psoDesc.SampleDesc.Count = 1; psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; ID3DBlob* vertexShaderBlob; ID3DBlob* pixelShaderBlob; // Create the vertex shader { 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;\ }"; if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vertexShaderBlob, NULL))) return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! psoDesc.VS = { vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize() }; // Create the input layout static D3D12_INPUT_ELEMENT_DESC local_layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, }; psoDesc.InputLayout = { local_layout, 3 }; } // Create the pixel shader { 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; \ }"; if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &pixelShaderBlob, NULL))) { vertexShaderBlob->Release(); return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! } psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() }; } // Create the blending setup { D3D12_BLEND_DESC& desc = psoDesc.BlendState; desc.AlphaToCoverageEnable = false; desc.RenderTarget[0].BlendEnable = true; desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE; desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; } // Create the rasterizer state { D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState; desc.FillMode = D3D12_FILL_MODE_SOLID; desc.CullMode = D3D12_CULL_MODE_NONE; desc.FrontCounterClockwise = FALSE; desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; desc.DepthClipEnable = true; desc.MultisampleEnable = FALSE; desc.AntialiasedLineEnable = FALSE; desc.ForcedSampleCount = 0; desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; } // Create depth-stencil State { D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState; desc.DepthEnable = false; desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; desc.StencilEnable = false; desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; desc.BackFace = desc.FrontFace; } HRESULT result_pipeline_state = g_d3d12_context->GetDevice()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState)); vertexShaderBlob->Release(); pixelShaderBlob->Release(); if (result_pipeline_state != S_OK) return false; return true; } void ImGui_ImplDX12_DestroyDeviceObjects() { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); if (!bd) return; ImGuiIO& io = ImGui::GetIO(); SafeRelease(bd->pRootSignature); SafeRelease(bd->pPipelineState); bd->FontTexture.Destroy(false); bd->VertexStreamBuffer.Destroy(false); bd->IndexStreamBuffer.Destroy(false); io.Fonts->SetTexID(NULL); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. } bool ImGui_ImplDX12_Init(DXGI_FORMAT rtv_format) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); // Setup backend capabilities flags ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)(); io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx12"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. bd->RTVFormat = rtv_format; if (!bd->VertexStreamBuffer.Create(VERTEX_BUFFER_SIZE) || !bd->IndexStreamBuffer.Create(INDEX_BUFFER_SIZE)) return false; return ImGui_ImplDX12_CreateDeviceObjects(); } void ImGui_ImplDX12_Shutdown() { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX12_DestroyDeviceObjects(); io.BackendRendererName = NULL; io.BackendRendererUserData = NULL; IM_DELETE(bd); } void ImGui_ImplDX12_NewFrame() { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX12_Init()?"); if (!bd->pPipelineState) ImGui_ImplDX12_CreateDeviceObjects(); }