imgui_impl_dx12.cpp (31184B)
1 // dear imgui: Renderer Backend for DirectX12 2 // This needs to be used along with a Platform Backend (e.g. Win32) 3 4 // Implemented features: 5 // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! 6 // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. 7 8 // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. 9 // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. 10 // This define is set in the example .vcxproj file and need to be replicated in your app or by adding it to your imconfig.h file. 11 12 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 13 // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 14 // Read online: https://github.com/ocornut/imgui/tree/master/docs 15 16 // CHANGELOG 17 // (minor and older changes stripped away, please see git history for details) 18 // 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) 19 // 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer. 20 // 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically. 21 // 2020-09-16: DirectX12: Avoid rendering calls with zero-sized scissor rectangle since it generates a validation layer warning. 22 // 2020-09-08: DirectX12: Clarified support for building on 32-bit systems by redefining ImTextureID. 23 // 2019-10-18: DirectX12: *BREAKING CHANGE* Added extra ID3D12DescriptorHeap parameter to ImGui_ImplDX12_Init() function. 24 // 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. 25 // 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. 26 // 2019-03-29: Misc: Various minor tidying up. 27 // 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile(). 28 // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. 29 // 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData(). 30 // 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example. 31 // 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). 32 // 2018-02-22: Merged into master with all Win32 code synchronized to other examples. 33 34 #include "imgui.h" 35 #include "imgui_impl_dx12.h" 36 37 // DirectX 38 #include <d3d12.h> 39 #include <dxgi1_4.h> 40 #include <d3dcompiler.h> 41 #ifdef _MSC_VER 42 #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. 43 #endif 44 45 // DirectX data 46 static ID3D12Device* g_pd3dDevice = NULL; 47 static ID3D12RootSignature* g_pRootSignature = NULL; 48 static ID3D12PipelineState* g_pPipelineState = NULL; 49 static DXGI_FORMAT g_RTVFormat = DXGI_FORMAT_UNKNOWN; 50 static ID3D12Resource* g_pFontTextureResource = NULL; 51 static D3D12_CPU_DESCRIPTOR_HANDLE g_hFontSrvCpuDescHandle = {}; 52 static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {}; 53 54 struct FrameResources 55 { 56 ID3D12Resource* IndexBuffer; 57 ID3D12Resource* VertexBuffer; 58 int IndexBufferSize; 59 int VertexBufferSize; 60 }; 61 static FrameResources* g_pFrameResources = NULL; 62 static UINT g_numFramesInFlight = 0; 63 static UINT g_frameIndex = UINT_MAX; 64 65 template<typename T> 66 static void SafeRelease(T*& res) 67 { 68 if (res) 69 res->Release(); 70 res = NULL; 71 } 72 73 struct VERTEX_CONSTANT_BUFFER 74 { 75 float mvp[4][4]; 76 }; 77 78 static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, FrameResources* fr) 79 { 80 // Setup orthographic projection matrix into our constant buffer 81 // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). 82 VERTEX_CONSTANT_BUFFER vertex_constant_buffer; 83 { 84 float L = draw_data->DisplayPos.x; 85 float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; 86 float T = draw_data->DisplayPos.y; 87 float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; 88 float mvp[4][4] = 89 { 90 { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, 91 { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, 92 { 0.0f, 0.0f, 0.5f, 0.0f }, 93 { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, 94 }; 95 memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp)); 96 } 97 98 // Setup viewport 99 D3D12_VIEWPORT vp; 100 memset(&vp, 0, sizeof(D3D12_VIEWPORT)); 101 vp.Width = draw_data->DisplaySize.x; 102 vp.Height = draw_data->DisplaySize.y; 103 vp.MinDepth = 0.0f; 104 vp.MaxDepth = 1.0f; 105 vp.TopLeftX = vp.TopLeftY = 0.0f; 106 ctx->RSSetViewports(1, &vp); 107 108 // Bind shader and vertex buffers 109 unsigned int stride = sizeof(ImDrawVert); 110 unsigned int offset = 0; 111 D3D12_VERTEX_BUFFER_VIEW vbv; 112 memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW)); 113 vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset; 114 vbv.SizeInBytes = fr->VertexBufferSize * stride; 115 vbv.StrideInBytes = stride; 116 ctx->IASetVertexBuffers(0, 1, &vbv); 117 D3D12_INDEX_BUFFER_VIEW ibv; 118 memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW)); 119 ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress(); 120 ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx); 121 ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; 122 ctx->IASetIndexBuffer(&ibv); 123 ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 124 ctx->SetPipelineState(g_pPipelineState); 125 ctx->SetGraphicsRootSignature(g_pRootSignature); 126 ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0); 127 128 // Setup blend factor 129 const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; 130 ctx->OMSetBlendFactor(blend_factor); 131 } 132 133 // Render function 134 void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx) 135 { 136 // Avoid rendering when minimized 137 if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) 138 return; 139 140 // FIXME: I'm assuming that this only gets called once per frame! 141 // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. 142 g_frameIndex = g_frameIndex + 1; 143 FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight]; 144 145 // Create and grow vertex/index buffers if needed 146 if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) 147 { 148 SafeRelease(fr->VertexBuffer); 149 fr->VertexBufferSize = draw_data->TotalVtxCount + 5000; 150 D3D12_HEAP_PROPERTIES props; 151 memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); 152 props.Type = D3D12_HEAP_TYPE_UPLOAD; 153 props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 154 props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; 155 D3D12_RESOURCE_DESC desc; 156 memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC)); 157 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 158 desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert); 159 desc.Height = 1; 160 desc.DepthOrArraySize = 1; 161 desc.MipLevels = 1; 162 desc.Format = DXGI_FORMAT_UNKNOWN; 163 desc.SampleDesc.Count = 1; 164 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 165 desc.Flags = D3D12_RESOURCE_FLAG_NONE; 166 if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->VertexBuffer)) < 0) 167 return; 168 } 169 if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount) 170 { 171 SafeRelease(fr->IndexBuffer); 172 fr->IndexBufferSize = draw_data->TotalIdxCount + 10000; 173 D3D12_HEAP_PROPERTIES props; 174 memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); 175 props.Type = D3D12_HEAP_TYPE_UPLOAD; 176 props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 177 props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; 178 D3D12_RESOURCE_DESC desc; 179 memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC)); 180 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 181 desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx); 182 desc.Height = 1; 183 desc.DepthOrArraySize = 1; 184 desc.MipLevels = 1; 185 desc.Format = DXGI_FORMAT_UNKNOWN; 186 desc.SampleDesc.Count = 1; 187 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 188 desc.Flags = D3D12_RESOURCE_FLAG_NONE; 189 if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->IndexBuffer)) < 0) 190 return; 191 } 192 193 // Upload vertex/index data into a single contiguous GPU buffer 194 void* vtx_resource, *idx_resource; 195 D3D12_RANGE range; 196 memset(&range, 0, sizeof(D3D12_RANGE)); 197 if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK) 198 return; 199 if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK) 200 return; 201 ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource; 202 ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource; 203 for (int n = 0; n < draw_data->CmdListsCount; n++) 204 { 205 const ImDrawList* cmd_list = draw_data->CmdLists[n]; 206 memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); 207 memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); 208 vtx_dst += cmd_list->VtxBuffer.Size; 209 idx_dst += cmd_list->IdxBuffer.Size; 210 } 211 fr->VertexBuffer->Unmap(0, &range); 212 fr->IndexBuffer->Unmap(0, &range); 213 214 // Setup desired DX state 215 ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr); 216 217 // Render command lists 218 // (Because we merged all buffers into a single one, we maintain our own offset into them) 219 int global_vtx_offset = 0; 220 int global_idx_offset = 0; 221 ImVec2 clip_off = draw_data->DisplayPos; 222 for (int n = 0; n < draw_data->CmdListsCount; n++) 223 { 224 const ImDrawList* cmd_list = draw_data->CmdLists[n]; 225 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 226 { 227 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 228 if (pcmd->UserCallback != NULL) 229 { 230 // User callback, registered via ImDrawList::AddCallback() 231 // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) 232 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) 233 ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr); 234 else 235 pcmd->UserCallback(cmd_list, pcmd); 236 } 237 else 238 { 239 // Apply Scissor, Bind texture, Draw 240 const D3D12_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; 241 if (r.right > r.left && r.bottom > r.top) 242 { 243 D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {}; 244 texture_handle.ptr = (UINT64)(intptr_t)pcmd->GetTexID(); 245 ctx->SetGraphicsRootDescriptorTable(1, texture_handle); 246 ctx->RSSetScissorRects(1, &r); 247 ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); 248 } 249 } 250 } 251 global_idx_offset += cmd_list->IdxBuffer.Size; 252 global_vtx_offset += cmd_list->VtxBuffer.Size; 253 } 254 } 255 256 static void ImGui_ImplDX12_CreateFontsTexture() 257 { 258 // Build texture atlas 259 ImGuiIO& io = ImGui::GetIO(); 260 unsigned char* pixels; 261 int width, height; 262 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 263 264 // Upload texture to graphics system 265 { 266 D3D12_HEAP_PROPERTIES props; 267 memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); 268 props.Type = D3D12_HEAP_TYPE_DEFAULT; 269 props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 270 props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; 271 272 D3D12_RESOURCE_DESC desc; 273 ZeroMemory(&desc, sizeof(desc)); 274 desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 275 desc.Alignment = 0; 276 desc.Width = width; 277 desc.Height = height; 278 desc.DepthOrArraySize = 1; 279 desc.MipLevels = 1; 280 desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 281 desc.SampleDesc.Count = 1; 282 desc.SampleDesc.Quality = 0; 283 desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; 284 desc.Flags = D3D12_RESOURCE_FLAG_NONE; 285 286 ID3D12Resource* pTexture = NULL; 287 g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, 288 D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&pTexture)); 289 290 UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); 291 UINT uploadSize = height * uploadPitch; 292 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 293 desc.Alignment = 0; 294 desc.Width = uploadSize; 295 desc.Height = 1; 296 desc.DepthOrArraySize = 1; 297 desc.MipLevels = 1; 298 desc.Format = DXGI_FORMAT_UNKNOWN; 299 desc.SampleDesc.Count = 1; 300 desc.SampleDesc.Quality = 0; 301 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 302 desc.Flags = D3D12_RESOURCE_FLAG_NONE; 303 304 props.Type = D3D12_HEAP_TYPE_UPLOAD; 305 props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 306 props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; 307 308 ID3D12Resource* uploadBuffer = NULL; 309 HRESULT hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, 310 D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer)); 311 IM_ASSERT(SUCCEEDED(hr)); 312 313 void* mapped = NULL; 314 D3D12_RANGE range = { 0, uploadSize }; 315 hr = uploadBuffer->Map(0, &range, &mapped); 316 IM_ASSERT(SUCCEEDED(hr)); 317 for (int y = 0; y < height; y++) 318 memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4); 319 uploadBuffer->Unmap(0, &range); 320 321 D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; 322 srcLocation.pResource = uploadBuffer; 323 srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; 324 srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 325 srcLocation.PlacedFootprint.Footprint.Width = width; 326 srcLocation.PlacedFootprint.Footprint.Height = height; 327 srcLocation.PlacedFootprint.Footprint.Depth = 1; 328 srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch; 329 330 D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; 331 dstLocation.pResource = pTexture; 332 dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 333 dstLocation.SubresourceIndex = 0; 334 335 D3D12_RESOURCE_BARRIER barrier = {}; 336 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 337 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; 338 barrier.Transition.pResource = pTexture; 339 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 340 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; 341 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 342 343 ID3D12Fence* fence = NULL; 344 hr = g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); 345 IM_ASSERT(SUCCEEDED(hr)); 346 347 HANDLE event = CreateEvent(0, 0, 0, 0); 348 IM_ASSERT(event != NULL); 349 350 D3D12_COMMAND_QUEUE_DESC queueDesc = {}; 351 queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 352 queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 353 queueDesc.NodeMask = 1; 354 355 ID3D12CommandQueue* cmdQueue = NULL; 356 hr = g_pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue)); 357 IM_ASSERT(SUCCEEDED(hr)); 358 359 ID3D12CommandAllocator* cmdAlloc = NULL; 360 hr = g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); 361 IM_ASSERT(SUCCEEDED(hr)); 362 363 ID3D12GraphicsCommandList* cmdList = NULL; 364 hr = g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList)); 365 IM_ASSERT(SUCCEEDED(hr)); 366 367 cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL); 368 cmdList->ResourceBarrier(1, &barrier); 369 370 hr = cmdList->Close(); 371 IM_ASSERT(SUCCEEDED(hr)); 372 373 cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); 374 hr = cmdQueue->Signal(fence, 1); 375 IM_ASSERT(SUCCEEDED(hr)); 376 377 fence->SetEventOnCompletion(1, event); 378 WaitForSingleObject(event, INFINITE); 379 380 cmdList->Release(); 381 cmdAlloc->Release(); 382 cmdQueue->Release(); 383 CloseHandle(event); 384 fence->Release(); 385 uploadBuffer->Release(); 386 387 // Create texture view 388 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; 389 ZeroMemory(&srvDesc, sizeof(srvDesc)); 390 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 391 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; 392 srvDesc.Texture2D.MipLevels = desc.MipLevels; 393 srvDesc.Texture2D.MostDetailedMip = 0; 394 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; 395 g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, g_hFontSrvCpuDescHandle); 396 SafeRelease(g_pFontTextureResource); 397 g_pFontTextureResource = pTexture; 398 } 399 400 // Store our identifier 401 static_assert(sizeof(ImTextureID) >= sizeof(g_hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); 402 io.Fonts->SetTexID((ImTextureID)g_hFontSrvGpuDescHandle.ptr); 403 } 404 405 bool ImGui_ImplDX12_CreateDeviceObjects() 406 { 407 if (!g_pd3dDevice) 408 return false; 409 if (g_pPipelineState) 410 ImGui_ImplDX12_InvalidateDeviceObjects(); 411 412 // Create the root signature 413 { 414 D3D12_DESCRIPTOR_RANGE descRange = {}; 415 descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; 416 descRange.NumDescriptors = 1; 417 descRange.BaseShaderRegister = 0; 418 descRange.RegisterSpace = 0; 419 descRange.OffsetInDescriptorsFromTableStart = 0; 420 421 D3D12_ROOT_PARAMETER param[2] = {}; 422 423 param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; 424 param[0].Constants.ShaderRegister = 0; 425 param[0].Constants.RegisterSpace = 0; 426 param[0].Constants.Num32BitValues = 16; 427 param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; 428 429 param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; 430 param[1].DescriptorTable.NumDescriptorRanges = 1; 431 param[1].DescriptorTable.pDescriptorRanges = &descRange; 432 param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; 433 434 D3D12_STATIC_SAMPLER_DESC staticSampler = {}; 435 staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; 436 staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; 437 staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; 438 staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; 439 staticSampler.MipLODBias = 0.f; 440 staticSampler.MaxAnisotropy = 0; 441 staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; 442 staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; 443 staticSampler.MinLOD = 0.f; 444 staticSampler.MaxLOD = 0.f; 445 staticSampler.ShaderRegister = 0; 446 staticSampler.RegisterSpace = 0; 447 staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; 448 449 D3D12_ROOT_SIGNATURE_DESC desc = {}; 450 desc.NumParameters = _countof(param); 451 desc.pParameters = param; 452 desc.NumStaticSamplers = 1; 453 desc.pStaticSamplers = &staticSampler; 454 desc.Flags = 455 D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | 456 D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | 457 D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | 458 D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS; 459 460 // Load d3d12.dll and D3D12SerializeRootSignature() function address dynamically to facilitate using with D3D12On7. 461 // See if any version of d3d12.dll is already loaded in the process. If so, give preference to that. 462 static HINSTANCE d3d12_dll = ::GetModuleHandleA("d3d12.dll"); 463 if (d3d12_dll == NULL) 464 { 465 // Attempt to load d3d12.dll from local directories. This will only succeed if 466 // (1) the current OS is Windows 7, and 467 // (2) there exists a version of d3d12.dll for Windows 7 (D3D12On7) in one of the following directories. 468 // See https://github.com/ocornut/imgui/pull/3696 for details. 469 const char* localD3d12Paths[] = { ".\\d3d12.dll", ".\\d3d12on7\\d3d12.dll", ".\\12on7\\d3d12.dll" }; // A. current directory, B. used by some games, C. used in Microsoft D3D12On7 sample 470 for (int i = 0; i < IM_ARRAYSIZE(localD3d12Paths); i++) 471 if ((d3d12_dll = ::LoadLibraryA(localD3d12Paths[i])) != NULL) 472 break; 473 474 // If failed, we are on Windows >= 10. 475 if (d3d12_dll == NULL) 476 d3d12_dll = ::LoadLibraryA("d3d12.dll"); 477 478 if (d3d12_dll == NULL) 479 return false; 480 } 481 482 PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature"); 483 if (D3D12SerializeRootSignatureFn == NULL) 484 return false; 485 486 ID3DBlob* blob = NULL; 487 if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, NULL) != S_OK) 488 return false; 489 490 g_pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&g_pRootSignature)); 491 blob->Release(); 492 } 493 494 // By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) 495 // If you would like to use this DX12 sample code but remove this dependency you can: 496 // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] 497 // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. 498 // See https://github.com/ocornut/imgui/pull/638 for sources and details. 499 500 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc; 501 memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC)); 502 psoDesc.NodeMask = 1; 503 psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; 504 psoDesc.pRootSignature = g_pRootSignature; 505 psoDesc.SampleMask = UINT_MAX; 506 psoDesc.NumRenderTargets = 1; 507 psoDesc.RTVFormats[0] = g_RTVFormat; 508 psoDesc.SampleDesc.Count = 1; 509 psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; 510 511 ID3DBlob* vertexShaderBlob; 512 ID3DBlob* pixelShaderBlob; 513 514 // Create the vertex shader 515 { 516 static const char* vertexShader = 517 "cbuffer vertexBuffer : register(b0) \ 518 {\ 519 float4x4 ProjectionMatrix; \ 520 };\ 521 struct VS_INPUT\ 522 {\ 523 float2 pos : POSITION;\ 524 float4 col : COLOR0;\ 525 float2 uv : TEXCOORD0;\ 526 };\ 527 \ 528 struct PS_INPUT\ 529 {\ 530 float4 pos : SV_POSITION;\ 531 float4 col : COLOR0;\ 532 float2 uv : TEXCOORD0;\ 533 };\ 534 \ 535 PS_INPUT main(VS_INPUT input)\ 536 {\ 537 PS_INPUT output;\ 538 output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ 539 output.col = input.col;\ 540 output.uv = input.uv;\ 541 return output;\ 542 }"; 543 544 if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vertexShaderBlob, NULL))) 545 return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! 546 psoDesc.VS = { vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize() }; 547 548 // Create the input layout 549 static D3D12_INPUT_ELEMENT_DESC local_layout[] = 550 { 551 { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 552 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 553 { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 554 }; 555 psoDesc.InputLayout = { local_layout, 3 }; 556 } 557 558 // Create the pixel shader 559 { 560 static const char* pixelShader = 561 "struct PS_INPUT\ 562 {\ 563 float4 pos : SV_POSITION;\ 564 float4 col : COLOR0;\ 565 float2 uv : TEXCOORD0;\ 566 };\ 567 SamplerState sampler0 : register(s0);\ 568 Texture2D texture0 : register(t0);\ 569 \ 570 float4 main(PS_INPUT input) : SV_Target\ 571 {\ 572 float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ 573 return out_col; \ 574 }"; 575 576 if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &pixelShaderBlob, NULL))) 577 { 578 vertexShaderBlob->Release(); 579 return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! 580 } 581 psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() }; 582 } 583 584 // Create the blending setup 585 { 586 D3D12_BLEND_DESC& desc = psoDesc.BlendState; 587 desc.AlphaToCoverageEnable = false; 588 desc.RenderTarget[0].BlendEnable = true; 589 desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; 590 desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; 591 desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; 592 desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE; 593 desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; 594 desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; 595 desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; 596 } 597 598 // Create the rasterizer state 599 { 600 D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState; 601 desc.FillMode = D3D12_FILL_MODE_SOLID; 602 desc.CullMode = D3D12_CULL_MODE_NONE; 603 desc.FrontCounterClockwise = FALSE; 604 desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; 605 desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; 606 desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; 607 desc.DepthClipEnable = true; 608 desc.MultisampleEnable = FALSE; 609 desc.AntialiasedLineEnable = FALSE; 610 desc.ForcedSampleCount = 0; 611 desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; 612 } 613 614 // Create depth-stencil State 615 { 616 D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState; 617 desc.DepthEnable = false; 618 desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; 619 desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; 620 desc.StencilEnable = false; 621 desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; 622 desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; 623 desc.BackFace = desc.FrontFace; 624 } 625 626 HRESULT result_pipeline_state = g_pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&g_pPipelineState)); 627 vertexShaderBlob->Release(); 628 pixelShaderBlob->Release(); 629 if (result_pipeline_state != S_OK) 630 return false; 631 632 ImGui_ImplDX12_CreateFontsTexture(); 633 634 return true; 635 } 636 637 void ImGui_ImplDX12_InvalidateDeviceObjects() 638 { 639 if (!g_pd3dDevice) 640 return; 641 642 SafeRelease(g_pRootSignature); 643 SafeRelease(g_pPipelineState); 644 SafeRelease(g_pFontTextureResource); 645 646 ImGuiIO& io = ImGui::GetIO(); 647 io.Fonts->SetTexID(NULL); // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. 648 649 for (UINT i = 0; i < g_numFramesInFlight; i++) 650 { 651 FrameResources* fr = &g_pFrameResources[i]; 652 SafeRelease(fr->IndexBuffer); 653 SafeRelease(fr->VertexBuffer); 654 } 655 } 656 657 bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, 658 D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) 659 { 660 // Setup backend capabilities flags 661 ImGuiIO& io = ImGui::GetIO(); 662 io.BackendRendererName = "imgui_impl_dx12"; 663 io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. 664 665 g_pd3dDevice = device; 666 g_RTVFormat = rtv_format; 667 g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; 668 g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle; 669 g_pFrameResources = new FrameResources[num_frames_in_flight]; 670 g_numFramesInFlight = num_frames_in_flight; 671 g_frameIndex = UINT_MAX; 672 IM_UNUSED(cbv_srv_heap); // Unused in master branch (will be used by multi-viewports) 673 674 // Create buffers with a default size (they will later be grown as needed) 675 for (int i = 0; i < num_frames_in_flight; i++) 676 { 677 FrameResources* fr = &g_pFrameResources[i]; 678 fr->IndexBuffer = NULL; 679 fr->VertexBuffer = NULL; 680 fr->IndexBufferSize = 10000; 681 fr->VertexBufferSize = 5000; 682 } 683 684 return true; 685 } 686 687 void ImGui_ImplDX12_Shutdown() 688 { 689 ImGui_ImplDX12_InvalidateDeviceObjects(); 690 delete[] g_pFrameResources; 691 g_pFrameResources = NULL; 692 g_pd3dDevice = NULL; 693 g_hFontSrvCpuDescHandle.ptr = 0; 694 g_hFontSrvGpuDescHandle.ptr = 0; 695 g_numFramesInFlight = 0; 696 g_frameIndex = UINT_MAX; 697 } 698 699 void ImGui_ImplDX12_NewFrame() 700 { 701 if (!g_pPipelineState) 702 ImGui_ImplDX12_CreateDeviceObjects(); 703 }