diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index dd06e0e7f..4dc5f884e 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -15,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-06-11: DirectX10: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. // 2025-05-07: DirectX10: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows). // 2025-01-06: DirectX10: Expose selected render state in ImGui_ImplDX10_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-10-07: DirectX10: Changed default texture sampler to Clamp instead of Repeat/Wrap. @@ -49,6 +51,12 @@ #endif // DirectX10 data +struct ImGui_ImplDX10_Texture +{ + ID3D10Texture2D* pTexture; + ID3D10ShaderResourceView* pTextureView; +}; + struct ImGui_ImplDX10_Data { ID3D10Device* pd3dDevice; @@ -60,7 +68,6 @@ struct ImGui_ImplDX10_Data ID3D10Buffer* pVertexConstantBuffer; ID3D10PixelShader* pPixelShader; ID3D10SamplerState* pFontSampler; - ID3D10ShaderResourceView* pFontTextureView; ID3D10RasterizerState* pRasterizerState; ID3D10BlendState* pBlendState; ID3D10DepthStencilState* pDepthStencilState; @@ -147,6 +154,13 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); ID3D10Device* device = bd->pd3dDevice; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplDX10_UpdateTexture(tex); + // Create and grow vertex/index buffers if needed if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { @@ -304,21 +318,39 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); } -static void ImGui_ImplDX10_CreateFontsTexture() +static void ImGui_ImplDX10_DestroyTexture(ImTextureData* tex) { - // Build texture atlas - ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + ImGui_ImplDX10_Texture* backend_tex = (ImGui_ImplDX10_Texture*)tex->BackendUserData; + if (backend_tex == nullptr) + return; + IM_ASSERT(backend_tex->pTextureView == (ID3D10ShaderResourceView*)(intptr_t)tex->TexID); + backend_tex->pTexture->Release(); + backend_tex->pTextureView->Release(); + IM_DELETE(backend_tex); - // Upload texture to graphics system + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); + tex->BackendUserData = nullptr; +} + +void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex) +{ + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + if (tex->Status == ImTextureStatus_WantCreate) { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + unsigned int* pixels = (unsigned int*)tex->GetPixels(); + ImGui_ImplDX10_Texture* backend_tex = IM_NEW(ImGui_ImplDX10_Texture)(); + + // Create texture D3D10_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Width = width; - desc.Height = height; + desc.Width = (UINT)tex->Width; + desc.Height = (UINT)tex->Height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -327,13 +359,12 @@ static void ImGui_ImplDX10_CreateFontsTexture() desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = 0; - ID3D10Texture2D* pTexture = nullptr; D3D10_SUBRESOURCE_DATA subResource; subResource.pSysMem = pixels; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); - IM_ASSERT(pTexture != nullptr); + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &backend_tex->pTexture); + IM_ASSERT(backend_tex->pTexture != nullptr && "Backend failed to create texture!"); // Create texture view D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc; @@ -342,23 +373,29 @@ static void ImGui_ImplDX10_CreateFontsTexture() srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; srv_desc.Texture2D.MipLevels = desc.MipLevels; srv_desc.Texture2D.MostDetailedMip = 0; - bd->pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &bd->pFontTextureView); - pTexture->Release(); + bd->pd3dDevice->CreateShaderResourceView(backend_tex->pTexture, &srv_desc, &backend_tex->pTextureView); + IM_ASSERT(backend_tex->pTextureView != nullptr && "Backend failed to create texture!"); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)backend_tex->pTextureView); + tex->SetStatus(ImTextureStatus_OK); + tex->BackendUserData = backend_tex; } - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView); -} - -static void ImGui_ImplDX10_DestroyFontsTexture() -{ - ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); - if (bd->pFontTextureView) + else if (tex->Status == ImTextureStatus_WantUpdates) { - bd->pFontTextureView->Release(); - bd->pFontTextureView = nullptr; - ImGui::GetIO().Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + ImGui_ImplDX10_Texture* backend_tex = (ImGui_ImplDX10_Texture*)tex->BackendUserData; + IM_ASSERT(backend_tex->pTextureView == (ID3D10ShaderResourceView*)(intptr_t)tex->TexID); + for (ImTextureRect& r : tex->Updates) + { + D3D10_BOX box = { (UINT)r.x, (UINT)r.y, (UINT)0, (UINT)(r.x + r.w), (UINT)(r.y + r.h), (UINT)1 }; + bd->pd3dDevice->UpdateSubresource(backend_tex->pTexture, 0, &box, tex->GetPixelsAt(r.x, r.y), (UINT)tex->GetPitch(), 0); + } + tex->SetStatus(ImTextureStatus_OK); } + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplDX10_DestroyTexture(tex); } bool ImGui_ImplDX10_CreateDeviceObjects() @@ -366,8 +403,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); if (!bd->pd3dDevice) return false; - if (bd->pFontSampler) - ImGui_ImplDX10_InvalidateDeviceObjects(); + ImGui_ImplDX10_InvalidateDeviceObjects(); // 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 DX10 sample code but remove this dependency you can: @@ -526,8 +562,6 @@ bool ImGui_ImplDX10_CreateDeviceObjects() bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); } - ImGui_ImplDX10_CreateFontsTexture(); - return true; } @@ -537,8 +571,10 @@ void ImGui_ImplDX10_InvalidateDeviceObjects() if (!bd->pd3dDevice) return; - ImGui_ImplDX10_DestroyFontsTexture(); - + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplDX10_DestroyTexture(tex); if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } @@ -562,6 +598,10 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx10"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; @@ -592,7 +632,7 @@ void ImGui_ImplDX10_Shutdown() if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); IM_DELETE(bd); } diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h index d9f29987d..38ecbdfe3 100644 --- a/backends/imgui_impl_dx10.h +++ b/backends/imgui_impl_dx10.h @@ -2,8 +2,9 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -31,6 +32,9 @@ IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data); IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. +IMGUI_IMPL_API void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex); + // [BETA] Selected render state data shared with callbacks. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX10_RenderDrawData() call. // (Please open an issue if you feel you need access to more data)