diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index bf1019e2c952..7769c37d0700 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-XX-XX: Renderer: Add support for ImGuiBackendFlags_RendererHasTexReload. // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn @@ -227,7 +228,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) al_use_projection_transform(&last_projection_transform); } -bool ImGui_ImplAllegro5_CreateDeviceObjects() +static bool ImGui_ImplAllegro5_UpdateFontsTexture() { // Build texture atlas ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); @@ -235,6 +236,7 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects() unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + io.Fonts->MarkClean(); // Create texture // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) @@ -263,10 +265,27 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects() if (!cloned_img) return false; + if (bd->Texture) + { + al_destroy_bitmap(bd->Texture); + io.Fonts->SetTexID(0); + bd->Texture = nullptr; + } + // Store our identifier io.Fonts->SetTexID((ImTextureID)(intptr_t)cloned_img); bd->Texture = cloned_img; + return true; +} + +bool ImGui_ImplAllegro5_CreateDeviceObjects() +{ + if (!ImGui_ImplAllegro5_UpdateFontsTexture()) + return false; + + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + // Create an invisible mouse cursor // Because al_hide_mouse_cursor() seems to mess up with the actual inputs.. ALLEGRO_BITMAP* mouse_cursor = al_create_bitmap(8, 8); @@ -435,6 +454,7 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) io.BackendPlatformUserData = (void*)bd; io.BackendPlatformName = io.BackendRendererName = "imgui_impl_allegro5"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload; // We can update font atlas textures (optional) bd->Display = display; @@ -599,6 +619,9 @@ void ImGui_ImplAllegro5_NewFrame() ImGuiIO& io = ImGui::GetIO(); + if (io.Fonts->IsDirty()) + ImGui_ImplAllegro5_UpdateFontsTexture(); + // Setup display size (every frame to accommodate for window resizing) int w, h; w = al_get_display_width(bd->Display); diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 9691b49fdff4..a051181e62a4 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -15,6 +15,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-XX-XX: DirectX10: Add support for ImGuiBackendFlags_RendererHasTexReload. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 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: DirectX10: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) @@ -57,6 +58,9 @@ struct ImGui_ImplDX10_Data ID3D10Buffer* pVertexConstantBuffer; ID3D10PixelShader* pPixelShader; ID3D10SamplerState* pFontSampler; + ID3D10Texture2D* pFontTexture; + int FontTextureWidth; + int FontTextureHeight; ID3D10ShaderResourceView* pFontTextureView; ID3D10RasterizerState* pRasterizerState; ID3D10BlendState* pBlendState; @@ -296,46 +300,73 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); } -static void ImGui_ImplDX10_CreateFontsTexture() +static void ImGui_ImplDX10_UpdateFontsTexture() { // 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); + io.Fonts->MarkClean(); - // Upload texture to graphics system + if ((!bd->pFontTextureView) || (bd->FontTextureWidth != width) || (bd->FontTextureHeight != height)) { - D3D10_TEXTURE2D_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.Width = width; - desc.Height = height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.SampleDesc.Count = 1; - desc.Usage = D3D10_USAGE_DEFAULT; - 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); - - // Create texture view - D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc; - ZeroMemory(&srv_desc, sizeof(srv_desc)); - srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - 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(); + // Either we have no texture or the size has changed, so (re-)create the texture + + // Release old texture + if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pFontTexture) { bd->pFontTexture->Release(); bd->pFontTexture = nullptr; } + + // Create new texture + { + D3D10_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.Usage = D3D10_USAGE_DEFAULT; + desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + + D3D10_SUBRESOURCE_DATA subResource; + subResource.pSysMem = pixels; + subResource.SysMemPitch = desc.Width * 4; + subResource.SysMemSlicePitch = 0; + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &bd->pFontTexture); + IM_ASSERT(bd->pFontTexture != nullptr); + + // Create texture view + D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc; + ZeroMemory(&srv_desc, sizeof(srv_desc)); + srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; + srv_desc.Texture2D.MipLevels = desc.MipLevels; + srv_desc.Texture2D.MostDetailedMip = 0; + bd->pd3dDevice->CreateShaderResourceView(bd->pFontTexture, &srv_desc, &bd->pFontTextureView); + } + + // Store size + bd->FontTextureWidth = width; + bd->FontTextureHeight = height; + } + else + { + // Upload new atlas data + + D3D10_BOX box; + box.left = 0; + box.right = width; + box.top = 0; + box.bottom = height; + box.front = 0; + box.back = 1; + + bd->pd3dDevice->UpdateSubresource(bd->pFontTexture, 0, &box, pixels, bd->FontTextureWidth * 4, 0); } // Store our identifier @@ -343,6 +374,7 @@ static void ImGui_ImplDX10_CreateFontsTexture() // Create texture sampler // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + if (!bd->pFontSampler) { D3D10_SAMPLER_DESC desc; ZeroMemory(&desc, sizeof(desc)); @@ -507,7 +539,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } - ImGui_ImplDX10_CreateFontsTexture(); + ImGui_ImplDX10_UpdateFontsTexture(); return true; } @@ -520,6 +552,7 @@ void ImGui_ImplDX10_InvalidateDeviceObjects() if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; } if (bd->pFontTextureView) { 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. + if (bd->pFontTexture) { bd->pFontTexture->Release(); bd->pFontTexture = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; } @@ -542,6 +575,7 @@ 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_RendererHasTexReload; // We support font atlas texture reloading (IsDirty() check in ImGui_ImplDX10_NewFrame) // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; @@ -583,6 +617,10 @@ void ImGui_ImplDX10_NewFrame() if (!bd->pFontSampler) ImGui_ImplDX10_CreateDeviceObjects(); + + ImGuiIO& io = ImGui::GetIO(); + if (io.Fonts->IsDirty()) + ImGui_ImplDX10_UpdateFontsTexture(); } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index 167bda67380d..ba439b7e374b 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -15,6 +15,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-XX-XX: DirectX11: Add support for ImGuiBackendFlags_RendererHasTexReload. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 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: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) @@ -58,6 +59,9 @@ struct ImGui_ImplDX11_Data ID3D11Buffer* pVertexConstantBuffer; ID3D11PixelShader* pPixelShader; ID3D11SamplerState* pFontSampler; + ID3D11Texture2D* pFontTexture; + int FontTextureWidth; + int FontTextureHeight; ID3D11ShaderResourceView* pFontTextureView; ID3D11RasterizerState* pRasterizerState; ID3D11BlendState* pBlendState; @@ -308,46 +312,73 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); } -static void ImGui_ImplDX11_CreateFontsTexture() +static void ImGui_ImplDX11_UpdateFontsTexture() { - // Build texture atlas ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + + // Build texture atlas unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + io.Fonts->MarkClean(); - // Upload texture to graphics system + if ((!bd->pFontTextureView) || (bd->FontTextureWidth != width) || (bd->FontTextureHeight != height)) { - D3D11_TEXTURE2D_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.Width = width; - desc.Height = height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - desc.CPUAccessFlags = 0; - - ID3D11Texture2D* pTexture = nullptr; - D3D11_SUBRESOURCE_DATA subResource; - subResource.pSysMem = pixels; - subResource.SysMemPitch = desc.Width * 4; - subResource.SysMemSlicePitch = 0; - bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); - IM_ASSERT(pTexture != nullptr); - - // Create texture view - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; - ZeroMemory(&srvDesc, sizeof(srvDesc)); - srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MipLevels = desc.MipLevels; - srvDesc.Texture2D.MostDetailedMip = 0; - bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView); - pTexture->Release(); + // Either we have no texture or the size has changed, so (re-)create the texture + + // Release old texture + if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pFontTexture) { bd->pFontTexture->Release(); bd->pFontTexture = nullptr; } + + // Create new texture + { + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA subResource; + subResource.pSysMem = pixels; + subResource.SysMemPitch = desc.Width * 4; + subResource.SysMemSlicePitch = 0; + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &bd->pFontTexture); + IM_ASSERT(bd->pFontTexture != nullptr); + + // Create texture view + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + ZeroMemory(&srvDesc, sizeof(srvDesc)); + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = desc.MipLevels; + srvDesc.Texture2D.MostDetailedMip = 0; + bd->pd3dDevice->CreateShaderResourceView(bd->pFontTexture, &srvDesc, &bd->pFontTextureView); + } + + // Store size + bd->FontTextureWidth = width; + bd->FontTextureHeight = height; + } + else + { + // Upload new atlas data + + D3D11_BOX box; + box.left = 0; + box.right = width; + box.top = 0; + box.bottom = height; + box.front = 0; + box.back = 1; + + bd->pd3dDeviceContext->UpdateSubresource(bd->pFontTexture, 0, &box, pixels, bd->FontTextureWidth * 4, 0); } // Store our identifier @@ -355,6 +386,7 @@ static void ImGui_ImplDX11_CreateFontsTexture() // Create texture sampler // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + if (!bd->pFontSampler) { D3D11_SAMPLER_DESC desc; ZeroMemory(&desc, sizeof(desc)); @@ -519,7 +551,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } - ImGui_ImplDX11_CreateFontsTexture(); + ImGui_ImplDX11_UpdateFontsTexture(); return true; } @@ -532,6 +564,7 @@ void ImGui_ImplDX11_InvalidateDeviceObjects() if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; } if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pFontTexture) { bd->pFontTexture->Release(); bd->pFontTexture = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; } @@ -554,6 +587,7 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx11"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload; // We support font atlas texture reloading (IsDirty() check in ImGui_ImplDX11_NewFrame) // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; @@ -599,6 +633,10 @@ void ImGui_ImplDX11_NewFrame() if (!bd->pFontSampler) ImGui_ImplDX11_CreateDeviceObjects(); + + ImGuiIO& io = ImGui::GetIO(); + if (io.Fonts->IsDirty()) + ImGui_ImplDX11_UpdateFontsTexture(); } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 32f1622cd46d..d5264f22e4a0 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-XX-XX: *BREAKING CHANGE*: DirectX12: Changed ImGui_ImplDX12_CreateFontsTexture() to ImGui_ImplDX12_UpdateFontsTexture(), which should be called at the start of the frame when ImGui::GetIO().Fonts->IsDirty() is true to reupload the font texture. ImGuiBackendFlags_RendererHasTexReload should be set once this is implemented. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 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) @@ -62,6 +63,8 @@ struct ImGui_ImplDX12_Data ID3D12PipelineState* pPipelineState; DXGI_FORMAT RTVFormat; ID3D12Resource* pFontTextureResource; + int FontTextureWidth; + int FontTextureHeight; D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle; D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle; ID3D12DescriptorHeap* pd3dSrvDescHeap; @@ -287,7 +290,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL } } -static void ImGui_ImplDX12_CreateFontsTexture() +void ImGui_ImplDX12_UpdateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); @@ -295,9 +298,13 @@ static void ImGui_ImplDX12_CreateFontsTexture() unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + io.Fonts->MarkClean(); - // Upload texture to graphics system + bool need_barrier_before_copy = true; // Do we need a resource barrier before we copy new data in? + + if ((!bd->pFontTextureResource) || (bd->FontTextureWidth != width) || (bd->FontTextureHeight != height)) { + // Either we have no texture or the size has changed, so (re-)create the texture D3D12_HEAP_PROPERTIES props; memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); props.Type = D3D12_HEAP_TYPE_DEFAULT; @@ -322,8 +329,42 @@ static void ImGui_ImplDX12_CreateFontsTexture() bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pTexture)); + // Create SRV + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; + ZeroMemory(&srvDesc, sizeof(srvDesc)); + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = desc.MipLevels; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, bd->hFontSrvCpuDescHandle); + SafeRelease(bd->pFontTextureResource); + bd->pFontTextureResource = pTexture; + + bd->FontTextureWidth = width; + bd->FontTextureHeight = height; + + need_barrier_before_copy = false; // Because this is a newly-created texture it will be in D3D12_RESOURCE_STATE_COMMON and thus we don't need a barrier + } + + // Store our identifier + // READ THIS IF THE STATIC_ASSERT() TRIGGERS: + // - 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*. + // [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) + static_assert(sizeof(ImTextureID) >= sizeof(bd->hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); + io.Fonts->SetTexID((ImTextureID)bd->hFontSrvGpuDescHandle.ptr); + + // Upload texture + { UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); UINT uploadSize = height * uploadPitch; + + D3D12_RESOURCE_DESC desc; + ZeroMemory(&desc, sizeof(desc)); desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Alignment = 0; desc.Width = uploadSize; @@ -336,6 +377,8 @@ static void ImGui_ImplDX12_CreateFontsTexture() desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.Flags = D3D12_RESOURCE_FLAG_NONE; + D3D12_HEAP_PROPERTIES props; + memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); props.Type = D3D12_HEAP_TYPE_UPLOAD; props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; @@ -363,17 +406,11 @@ static void ImGui_ImplDX12_CreateFontsTexture() srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch; D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; - dstLocation.pResource = pTexture; + dstLocation.pResource = bd->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 = pTexture; - 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; + // Create temporary command list and execute immediately ID3D12Fence* fence = nullptr; hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); @@ -399,8 +436,30 @@ static void ImGui_ImplDX12_CreateFontsTexture() hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList)); IM_ASSERT(SUCCEEDED(hr)); + if (need_barrier_before_copy) + { + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = bd->pFontTextureResource; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + cmdList->ResourceBarrier(1, &barrier); + } + cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr); - cmdList->ResourceBarrier(1, &barrier); + + { + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = bd->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; + cmdList->ResourceBarrier(1, &barrier); + } hr = cmdList->Close(); IM_ASSERT(SUCCEEDED(hr)); @@ -418,30 +477,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() CloseHandle(event); fence->Release(); uploadBuffer->Release(); - - // Create texture view - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - ZeroMemory(&srvDesc, sizeof(srvDesc)); - srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MipLevels = desc.MipLevels; - srvDesc.Texture2D.MostDetailedMip = 0; - srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, bd->hFontSrvCpuDescHandle); - SafeRelease(bd->pFontTextureResource); - bd->pFontTextureResource = pTexture; } - - // Store our identifier - // READ THIS IF THE STATIC_ASSERT() TRIGGERS: - // - 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*. - // [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) - static_assert(sizeof(ImTextureID) >= sizeof(bd->hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); - io.Fonts->SetTexID((ImTextureID)bd->hFontSrvGpuDescHandle.ptr); } bool ImGui_ImplDX12_CreateDeviceObjects() @@ -673,7 +709,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() if (result_pipeline_state != S_OK) return false; - ImGui_ImplDX12_CreateFontsTexture(); + ImGui_ImplDX12_UpdateFontsTexture(); return true; } @@ -710,6 +746,7 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx12"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + // Note that we explicitly do *not* set ImGuiBackendFlags_RendererHasTexReload here, because in DX12 that requires support in the caller as well, so we leave setting it (or not) up to that code. bd->pd3dDevice = device; bd->RTVFormat = rtv_format; diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index e1fd07d6d4e1..42ed90b4129e 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -38,6 +38,7 @@ IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame(); IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list); +IMGUI_IMPL_API void ImGui_ImplDX12_UpdateFontsTexture(); // This should be called when the font texture needs updating (if ImGuiBackendFlags_RendererHasTexReload was set), with the GPU idle. It will perform the update using a temporary command list, and block internally until it completes. // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 3eff9ab5da9f..e1be4e11b23f 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -15,6 +15,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// XXXX-XX-XX: DirectX9: Add support for ImGuiBackendFlags_RendererHasTexReload. // 2024-02-12: DirectX9: Using RGBA format when supported by the driver to avoid CPU side conversion. (#6575) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 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). @@ -48,11 +49,12 @@ struct ImGui_ImplDX9_Data LPDIRECT3DDEVICE9 pd3dDevice; LPDIRECT3DVERTEXBUFFER9 pVB; LPDIRECT3DINDEXBUFFER9 pIB; - LPDIRECT3DTEXTURE9 FontTexture; + ImVectorTextures; + int TexturesUpdateFrame; int VertexBufferSize; int IndexBufferSize; - ImGui_ImplDX9_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } + ImGui_ImplDX9_Data() { memset((void*)this, 0, sizeof(*this)); TexturesUpdateFrame = -1; VertexBufferSize = 5000; IndexBufferSize = 10000; } }; struct CUSTOMVERTEX @@ -76,7 +78,6 @@ static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplDX9_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } -// Functions static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); @@ -153,8 +154,12 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - // Create and grow buffers if needed + // Update textures if not done already ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + if (bd->TexturesUpdateFrame != ImGui::GetFrameCount()) + ImGui_ImplDX9_UpdateTextures(); + + // Create and grow buffers if needed if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } @@ -293,6 +298,7 @@ bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx9"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload; // We support font atlas texture reloading (IsDirty() check in ImGui_ImplDX11_NewFrame) bd->pd3dDevice = device; bd->pd3dDevice->AddRef(); @@ -332,20 +338,19 @@ static bool ImGui_ImplDX9_CheckFormatSupport(IDirect3DDevice9* pDevice, D3DFORMA return support; } -static bool ImGui_ImplDX9_CreateFontsTexture() +static LPDIRECT3DTEXTURE9 ImGui_ImplDX9_UpdateTexture(LPDIRECT3DTEXTURE9 gpu_tex, const ImTextureData* in_tex_data) { - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); - unsigned char* pixels; - int width, height, bytes_per_pixel; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel); + unsigned char* pixels = (unsigned char*)in_tex_data->TexPixels; + int width = in_tex_data->TexWidth; + int height = in_tex_data->TexHeight; // Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices) #ifndef IMGUI_USE_BGRA_PACKED_COLOR const bool rgba_support = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8); - if (!rgba_support && io.Fonts->TexPixelsUseColors) + if (!rgba_support && in_tex_data->TexFormat == ImTextureFormat_RGBA32) { + int bytes_per_pixel = 4; ImU32* dst_start = (ImU32*)ImGui::MemAlloc((size_t)width * height * bytes_per_pixel); for (ImU32* src = (ImU32*)pixels, *dst = dst_start, *dst_end = dst_start + (size_t)width * height; dst < dst_end; src++, dst++) *dst = IMGUI_COL_TO_DX9_ARGB(*src); @@ -355,26 +360,62 @@ static bool ImGui_ImplDX9_CreateFontsTexture() const bool rgba_support = false; #endif - // Upload texture to graphics system - bd->FontTexture = nullptr; - if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, rgba_support ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture, nullptr) < 0) - return false; - D3DLOCKED_RECT tex_locked_rect; - if (bd->FontTexture->LockRect(0, &tex_locked_rect, nullptr, 0) != D3D_OK) - return false; - for (int y = 0; y < height; y++) - memcpy((unsigned char*)tex_locked_rect.pBits + (size_t)tex_locked_rect.Pitch * y, pixels + (size_t)width * bytes_per_pixel * y, (size_t)width * bytes_per_pixel); - bd->FontTexture->UnlockRect(0); + // Create or recreate texture if needed + D3DSURFACE_DESC surface_desc = {}; + if (gpu_tex != nullptr) + gpu_tex->GetLevelDesc(0, &surface_desc); + if (gpu_tex == nullptr || surface_desc.Width != (UINT)width || surface_desc.Height != (UINT)height) + { + // Create texture + gpu_tex = nullptr; + if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, rgba_support ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &gpu_tex, nullptr) < 0) + return false; + + // Store size + surface_desc.Width = width; + surface_desc.Height = height; + } - // Store our identifier - io.Fonts->SetTexID((ImTextureID)bd->FontTexture); + // Update pixels + { + D3DLOCKED_RECT tex_locked_rect; + RECT dirty_rect; + dirty_rect.left = 0; + dirty_rect.right = width; + dirty_rect.top = 0; + dirty_rect.bottom = height; + if (gpu_tex->LockRect(0, &tex_locked_rect, &dirty_rect, 0) != D3D_OK) + return false; + + if (tex_locked_rect.Pitch == (width * 4)) + { + memcpy(tex_locked_rect.pBits, pixels, (size_t)width * height * 4); // Fast path for full image upload + } + else + { + // Sub-region upload + const int src_stride = width * 4; + const int dest_stride = tex_locked_rect.Pitch; + const int copy_bytes = width * 4; // Bytes to copy for each line + unsigned char* read_ptr = pixels; + char* write_ptr = (char*)tex_locked_rect.pBits; + + for (int y = 0; y < height; y++) + { + memcpy(write_ptr, read_ptr, copy_bytes); + write_ptr += dest_stride; + read_ptr += src_stride; + } + } + gpu_tex->UnlockRect(0); + } #ifndef IMGUI_USE_BGRA_PACKED_COLOR - if (!rgba_support && io.Fonts->TexPixelsUseColors) + if (!rgba_support && in_tex_data->TexFormat == ImTextureFormat_RGBA32) ImGui::MemFree(pixels); #endif - return true; + return gpu_tex; } bool ImGui_ImplDX9_CreateDeviceObjects() @@ -382,8 +423,7 @@ bool ImGui_ImplDX9_CreateDeviceObjects() ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); if (!bd || !bd->pd3dDevice) return false; - if (!ImGui_ImplDX9_CreateFontsTexture()) - return false; + return true; } @@ -394,16 +434,87 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() return; if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } - if (bd->FontTexture) { bd->FontTexture->Release(); bd->FontTexture = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. + for (int i = 0; i < bd->Textures.Size; ++i) + bd->Textures[i]->Release(); + bd->Textures.resize(0); } void ImGui_ImplDX9_NewFrame() { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX9_Init()?"); + IM_UNUSED(bd); +} + +// FIXME-TEXUPDATE: Can we somehow allow multiple "observer" for the dirty data? (local + remote client).... means that we don't store IsDirty() in texture but instead maybe counter? +// FIXME-TEXUPDATE: Can we somehow design the update data so that "no update" result in a trivial and obvious early out? +// FIXME-TEXUPDATE: Aim to make this function less complex, so it's more evident for custom backend implementers what to do +// - why do we need the "recreate_all" flag? +// - could imgui track past requested textures and automatically submit the "destroy list" ? that would remove complexity on backend side +// - similarly texture that don't need an update may not need to be in the list (would fulfill the earlier point to allow for trivial early out) +// FIXME-TEXUPDATE: [advanced] Make sure the design of backend functions can allow advanced user to update for multiple atlas? +void ImGui_ImplDX9_UpdateTextures() +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + + // We automatically call this from _RenderDrawData() but allow user to call it explicitely earlier is desired + const int frame_count = ImGui::GetFrameCount(); + if (bd->TexturesUpdateFrame == frame_count) + return; + bd->TexturesUpdateFrame = frame_count; + + // Get texture list + ImVector& textures = bd->Textures; + ImTextureUpdateData* update_data = ImGui::GetTextureUpdateData(); + ImVector discarded = textures; // FIXME-TEXUPDATE: This would allocate every frame, try to avoid it (holding on a static ImVector<> buffer?) + + bool recreate_all = (textures.Size == 0); + for (int i = 0; i < update_data->Textures.Size; ++i) + { + ImTextureData* in_tex_data = update_data->Textures[i]; - if (!bd->FontTexture) - ImGui_ImplDX9_CreateDeviceObjects(); + LPDIRECT3DTEXTURE9 current_texture = (LPDIRECT3DTEXTURE9)in_tex_data->GetTexID(); + if (current_texture == nullptr || recreate_all || in_tex_data->IsDirty()) + { + in_tex_data->EnsureFormat(ImTextureFormat_RGBA32); + + // Make sure we do not access textures released by ImGui_ImplDX9_InvalidateDeviceObjects() + // FIXME-TEXUPDATE: this can be break! + // g_TexturesID = { 0x0001, 0x0002 } + // ImGui_ImplDX9_InvalidateDeviceObjects() -> destroy 0x0001, 0x0002, but values are kept in TexID fields + // ImGui_ImplDX9_UpdateTextures() + // - update_data contains two textures + // - first one create a new texture, DX9 return 0x0002 (UNLIKELY BUT TECHNICALLY POSSIBLE) + // - second one has TexID = 0x0002 but it is contained in textures array so we don't NULL it <-- ERROR + // Conclusion: we should invalidate tex id! maybe add bool TexIDValid, SetTexID() can set it to true.... + // ImGui_ImplDX9_InvalidateDeviceObjects() could clear it... maybe helper call in ImFontAtlas... + if (current_texture != NULL && !textures.contains(current_texture)) + current_texture = NULL; + + LPDIRECT3DTEXTURE9 new_texture = ImGui_ImplDX9_UpdateTexture(current_texture, in_tex_data); + if (current_texture != nullptr && new_texture != current_texture) + textures.find_erase_unsorted(current_texture); + + if (new_texture != nullptr && new_texture != current_texture) + textures.push_back(new_texture); + + discarded.find_erase_unsorted(new_texture); + + in_tex_data->SetTexID(new_texture); + in_tex_data->MarkClean(); + } + else if (current_texture) + { + discarded.find_erase_unsorted(current_texture); + } + } + + // Remove unused textures + for (int i = 0; i < discarded.Size; ++i) + { + discarded[i]->Release(); + textures.find_erase_unsorted(discarded[i]); + } } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_dx9.h b/backends/imgui_impl_dx9.h index df6a0a012479..6c0bcb654bd4 100644 --- a/backends/imgui_impl_dx9.h +++ b/backends/imgui_impl_dx9.h @@ -23,6 +23,7 @@ struct IDirect3DDevice9; IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device); IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplDX9_UpdateTextures(); IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing Dear ImGui state. diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index f9fd1087e849..f41791ff70c4 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -15,6 +15,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-XX-XX: Metal: Add support for ImGuiBackendFlags_RendererHasTexReload. // 2022-08-23: Metal: Update deprecated property 'sampleCount'->'rasterSampleCount'. // 2022-07-05: Metal: Add dispatch synchronization. // 2022-06-30: Metal: Use __bridge for ARC based systems. @@ -68,6 +69,8 @@ @interface MetalContext : NSObject @property (nonatomic, strong, nullable) id fontTexture; @property (nonatomic, strong) NSMutableArray* bufferCache; @property (nonatomic, assign) double lastBufferCachePurge; +- (void)makeFontTextureWithDevice:(id)device; +- (void)updateFontTexture; - (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id)device; - (id)renderPipelineStateForFramebufferDescriptor:(FramebufferDescriptor*)descriptor device:(id)device; @end @@ -132,6 +135,7 @@ bool ImGui_ImplMetal_Init(id device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_metal"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload; bd->SharedMetalContext = [[MetalContext alloc] init]; bd->SharedMetalContext.device = device; @@ -160,6 +164,13 @@ void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor) if (bd->SharedMetalContext.depthStencilState == nil) ImGui_ImplMetal_CreateDeviceObjects(bd->SharedMetalContext.device); + + ImGuiIO& io = ImGui::GetIO(); + if (io.Fonts->IsDirty()) + { + [bd->SharedMetalContext updateFontTexture]; + io.Fonts->SetTexID((__bridge void *)bd->SharedMetalContext.fontTexture); // ImTextureID == void* + } } static void ImGui_ImplMetal_SetupRenderState(ImDrawData* drawData, id commandBuffer, @@ -325,29 +336,10 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c bool ImGui_ImplMetal_CreateFontsTexture(id device) { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); + [bd->SharedMetalContext makeFontTextureWithDevice:device]; - // We are retrieving and uploading the font atlas as a 4-channels RGBA texture here. - // In theory we could call GetTexDataAsAlpha8() and upload a 1-channel texture to save on memory access bandwidth. - // However, using a shader designed for 1-channel texture would make it less obvious to use the ImTextureID facility to render users own textures. - // You can make that change in your implementation. - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm - width:(NSUInteger)width - height:(NSUInteger)height - mipmapped:NO]; - textureDescriptor.usage = MTLTextureUsageShaderRead; -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST - textureDescriptor.storageMode = MTLStorageModeManaged; -#else - textureDescriptor.storageMode = MTLStorageModeShared; -#endif - id texture = [device newTextureWithDescriptor:textureDescriptor]; - [texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)width, (NSUInteger)height) mipmapLevel:0 withBytes:pixels bytesPerRow:(NSUInteger)width * 4]; - bd->SharedMetalContext.fontTexture = texture; - io.Fonts->SetTexID((__bridge void*)bd->SharedMetalContext.fontTexture); // ImTextureID == void* + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->SetTexID((__bridge void *)bd->SharedMetalContext.fontTexture); // ImTextureID == void* return (bd->SharedMetalContext.fontTexture != nil); } @@ -455,6 +447,47 @@ - (instancetype)init return self; } +- (void)makeFontTextureWithDevice:(id)device +{ + ImGuiIO &io = ImGui::GetIO(); + + // We are retrieving and uploading the font atlas as a 4-channels RGBA texture here. + // In theory we could call GetTexDataAsAlpha8() and upload a 1-channel texture to save on memory access bandwidth. + // However, using a shader designed for 1-channel texture would make it less obvious to use the ImTextureID facility to render users own textures. + // You can make that change in your implementation. + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm + width:(NSUInteger)width + height:(NSUInteger)height + mipmapped:NO]; + textureDescriptor.usage = MTLTextureUsageShaderRead; +#if TARGET_OS_OSX || TARGET_OS_MACCATALYST + textureDescriptor.storageMode = MTLStorageModeManaged; +#else + textureDescriptor.storageMode = MTLStorageModeShared; +#endif + id texture = [device newTextureWithDescriptor:textureDescriptor]; + self.fontTexture = texture; + + [self updateFontTexture]; +} + +- (void)updateFontTexture +{ + ImGuiIO &io = ImGui::GetIO(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + io.Fonts->MarkClean(); + + if (!self.fontTexture || width != self.fontTexture.width || height != self.fontTexture.height) + [self makeFontTextureWithDevice:self.fontTexture.device]; + + [self.fontTexture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)width, (NSUInteger)height) mipmapLevel:0 withBytes:pixels bytesPerRow:(NSUInteger)width * 4]; +} + - (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id)device { uint64_t now = GetMachAbsoluteTimeInSeconds(); diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 7f1ea325aa87..ecb4552309c4 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -22,6 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-XX-XX: OpenGL: Add support for ImGuiBackendFlags_RendererHasTexReload. // 2024-06-28: OpenGL: ImGui_ImplOpenGL2_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL2_DestroyFontsTexture(). (#7748) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2021-12-08: OpenGL: Fixed mishandling of the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. @@ -80,6 +81,8 @@ static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL2_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } +static void ImGui_ImplOpenGL2_UpdateFontsTexture(); + // Functions bool ImGui_ImplOpenGL2_Init() { @@ -91,6 +94,7 @@ bool ImGui_ImplOpenGL2_Init() ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)(); io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_opengl2"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload; // We can update font atlas texture when requested. return true; } @@ -114,8 +118,8 @@ void ImGui_ImplOpenGL2_NewFrame() if (!bd->FontTexture) ImGui_ImplOpenGL2_CreateDeviceObjects(); - if (!bd->FontTexture) - ImGui_ImplOpenGL2_CreateFontsTexture(); + if (ImGui::GetIO().Fonts->IsDirty()) + ImGui_ImplOpenGL2_UpdateFontsTexture(); } static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height) @@ -246,11 +250,13 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, last_tex_env_mode); } -bool ImGui_ImplOpenGL2_CreateFontsTexture() +static void ImGui_ImplOpenGL2_UpdateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); + + IM_ASSERT(bd->FontTexture != 0); // Update expect texture to already been created unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. @@ -259,7 +265,6 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture() // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &bd->FontTexture); glBindTexture(GL_TEXTURE_2D, bd->FontTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -271,6 +276,14 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture() // Restore state glBindTexture(GL_TEXTURE_2D, last_texture); +} + +bool ImGui_ImplOpenGL2_CreateFontsTexture() +{ + ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); + IM_ASSERT(bd->FontTexture == 0); + glGenTextures(1, &bd->FontTexture); + ImGui_ImplOpenGL2_UpdateFontsTexture(); return true; } diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 7087a7c5706d..e75e82e449f4 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -25,6 +25,7 @@ // 2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) // 2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562) // 2024-04-16: OpenGL: Detect ES3 contexts on desktop based on version string, to e.g. avoid calling glPolygonMode() on them. (#7447) +// XXXX-XX-XX: OpenGL: Add support for ImGuiBackendFlags_RendererHasTexReload. // 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink. // 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983) // 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445) @@ -249,6 +250,8 @@ static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } +static void ImGui_ImplOpenGL3_UpdateFontsTexture(); + // OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only) #ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY struct ImGui_ImplOpenGL3_VtxAttribState @@ -341,6 +344,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) if (bd->GlVersion >= 320) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. #endif + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload; // We can update font atlas texture when requested. // Store GLSL version string so we can refer to it later in case we recreate shaders. // Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure. @@ -404,8 +408,8 @@ void ImGui_ImplOpenGL3_NewFrame() if (!bd->ShaderHandle) ImGui_ImplOpenGL3_CreateDeviceObjects(); - if (!bd->FontTexture) - ImGui_ImplOpenGL3_CreateFontsTexture(); + if (ImGui::GetIO().Fonts->IsDirty()) + ImGui_ImplOpenGL3_UpdateFontsTexture(); } static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) @@ -663,21 +667,23 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) (void)bd; // Not all compilation paths use this } -bool ImGui_ImplOpenGL3_CreateFontsTexture() +static void ImGui_ImplOpenGL3_UpdateFontsTexture() { + // Build texture atlas ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + IM_ASSERT(bd->FontTexture != 0); // Update expect texture to already been created // Build texture atlas unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + io.Fonts->MarkClean(); // Upload texture to graphics system // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) GLint last_texture; GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); - GL_CALL(glGenTextures(1, &bd->FontTexture)); GL_CALL(glBindTexture(GL_TEXTURE_2D, bd->FontTexture)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); @@ -691,6 +697,15 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() // Restore state GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); +} + +bool ImGui_ImplOpenGL3_CreateFontsTexture() +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + + IM_ASSERT(bd->FontTexture == 0); + GL_CALL(glGenTextures(1, &bd->FontTexture)); + ImGui_ImplOpenGL3_UpdateFontsTexture(); return true; } diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 3b757eb6670c..d717fb7eaaaf 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -46,6 +46,7 @@ // ImGui_ImplVulkan_CreateFontsTexture() is automatically called by NewFrame() the first time. // You can call ImGui_ImplVulkan_CreateFontsTexture() again to recreate the font atlas texture. // Added ImGui_ImplVulkan_DestroyFontsTexture() but you probably never need to call this. +// 2023-XX-XX: *BREAKING CHANGE*: Vulkan: Changed ImGui_ImplVulkan_CreateFontsTexture() to ImGui_ImplVulkan_UpdateFontsTexture(), which should be called at the start of the frame when ImGui::GetIO().Fonts->IsDirty() is true to reupload the font texture. ImGuiBackendFlags_RendererHasTexReload should be set once this is implemented. // 2023-07-04: Vulkan: Added optional support for VK_KHR_dynamic_rendering. User needs to set init_info->UseDynamicRendering = true and init_info->ColorAttachmentFormat. // 2023-01-02: Vulkan: Fixed sampler passed to ImGui_ImplVulkan_AddTexture() not being honored + removed a bunch of duplicate code. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -232,8 +233,11 @@ struct ImGui_ImplVulkan_Data VkImage FontImage; VkImageView FontView; VkDescriptorSet FontDescriptorSet; + VkCommandPool FontCommandPool; VkCommandBuffer FontCommandBuffer; + int FontTextureWidth; + int FontTextureHeight; // Render buffers for main window ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers; @@ -586,11 +590,11 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm vkCmdSetScissor(command_buffer, 0, 1, &scissor); // Bind DescriptorSet with font or user texture - VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; + VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->Texture.GetID() }; if (sizeof(ImTextureID) < sizeof(ImU64)) { // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used. - IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet); + IM_ASSERT(pcmd->Texture.GetID() == (ImTextureID)bd->FontDescriptorSet); desc_set[0] = bd->FontDescriptorSet; } vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr); @@ -614,7 +618,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm vkCmdSetScissor(command_buffer, 0, 1, &scissor); } -bool ImGui_ImplVulkan_CreateFontsTexture() +bool ImGui_ImplVulkan_UpdateFontsTexture() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); @@ -661,54 +665,79 @@ bool ImGui_ImplVulkan_CreateFontsTexture() unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - size_t upload_size = width * height * 4 * sizeof(char); + io.Fonts->MarkClean(); - // Create the Image: + if ((!bd->FontView) || (bd->FontTextureWidth != width) || (bd->FontTextureHeight != height)) { - VkImageCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - info.imageType = VK_IMAGE_TYPE_2D; - info.format = VK_FORMAT_R8G8B8A8_UNORM; - info.extent.width = width; - info.extent.height = height; - info.extent.depth = 1; - info.mipLevels = 1; - info.arrayLayers = 1; - info.samples = VK_SAMPLE_COUNT_1_BIT; - info.tiling = VK_IMAGE_TILING_OPTIMAL; - info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage); - check_vk_result(err); - VkMemoryRequirements req; - vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req); - VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); - alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory); - check_vk_result(err); - err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0); - check_vk_result(err); - } + // Either we have no texture or the size has changed, so (re-)create the texture + //if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; } + //if (bd->FontImage) { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; } + //if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; } - // Create the Image View: - { - VkImageViewCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.image = bd->FontImage; - info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = VK_FORMAT_R8G8B8A8_UNORM; - info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - info.subresourceRange.levelCount = 1; - info.subresourceRange.layerCount = 1; - err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView); - check_vk_result(err); + // Create the Image: + { + VkImageCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.extent.width = width; + info.extent.height = height; + info.extent.depth = 1; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage); + check_vk_result(err); + VkMemoryRequirements req; + vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req); + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory); + check_vk_result(err); + err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0); + check_vk_result(err); + } + + // Create the Image View: + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.image = bd->FontImage; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + info.subresourceRange.levelCount = 1; + info.subresourceRange.layerCount = 1; + err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView); + check_vk_result(err); + } + + // Create the Descriptor Set: + bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + // Update the Descriptor Set: + { + VkDescriptorImageInfo desc_image[1] = {}; + desc_image[0].sampler = bd->FontSampler; + desc_image[0].imageView = bd->FontView; + desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkWriteDescriptorSet write_desc[1] = {}; + write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_desc[0].dstSet = bd->FontDescriptorSet; + write_desc[0].descriptorCount = 1; + write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_desc[0].pImageInfo = desc_image; + vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, nullptr); + } } - // Create the Descriptor Set: - bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + size_t upload_size = width * height * 4 * sizeof(char); // Create the Upload Buffer: VkDeviceMemory upload_buffer_memory; @@ -739,7 +768,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture() char* map = nullptr; err = vkMapMemory(v->Device, upload_buffer_memory, 0, upload_size, 0, (void**)(&map)); check_vk_result(err); - memcpy(map, pixels, upload_size); + memcpy(map, pixels, upload_size); // Fast path for full image upload VkMappedMemoryRange range[1] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[0].memory = upload_buffer_memory; @@ -767,9 +796,14 @@ bool ImGui_ImplVulkan_CreateFontsTexture() VkBufferImageCopy region = {}; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.layerCount = 1; + region.imageOffset.x = 0; + region.imageOffset.y = 0; region.imageExtent.width = width; region.imageExtent.height = height; region.imageExtent.depth = 1; + region.bufferOffset = 0; + region.bufferRowLength = width; + region.bufferImageHeight = height; vkCmdCopyBufferToImage(bd->FontCommandBuffer, upload_buffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); VkImageMemoryBarrier use_barrier[1] = {}; @@ -789,6 +823,8 @@ bool ImGui_ImplVulkan_CreateFontsTexture() // Store our identifier io.Fonts->SetTexID((ImTextureID)bd->FontDescriptorSet); + bd->FontTextureWidth = width; + bd->FontTextureHeight = height; // End command buffer VkSubmitInfo end_info = {}; @@ -1100,6 +1136,7 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_vulkan"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + // Note that we explicitly do *not* set ImGuiBackendFlags_RendererHasTexReload here, because in Vulkan that requires support in the caller as well, so we leave setting it (or not) up to that code. IM_ASSERT(info->Instance != VK_NULL_HANDLE); IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); @@ -1136,8 +1173,10 @@ void ImGui_ImplVulkan_NewFrame() ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplVulkan_Init()?"); - if (!bd->FontDescriptorSet) - ImGui_ImplVulkan_CreateFontsTexture(); + // Upload Fonts + ImGuiIO& io = ImGui::GetIO(); + if (!bd->FontDescriptorSet || io.Fonts->IsDirty()) + ImGui_ImplVulkan_UpdateFontsTexture(); } void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 1c7f7695183f..c887a755ca2d 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -103,7 +103,7 @@ IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* inf IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); -IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplVulkan_UpdateFontsTexture(); IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture(); IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 901b46c4cfd7..958dcddf7c2b 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -453,6 +453,8 @@ int main(int, char**) init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info); + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload; // Set flag to indicate that we can reload textures when requested. + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. @@ -500,6 +502,19 @@ int main(int, char**) continue; } +#if 1 + if (ImGui::IsKeyPressed(ImGuiKey_KeypadAdd)) + { + static float size = 13.0f; + size += 1.0f; + ImFontConfig cfg; + cfg.SizePixels = size; + io.Fonts->Clear(); + io.Fonts->AddFontDefault(&cfg); + io.Fonts->MarkDirty(); + } +#endif + // Start the Dear ImGui frame ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index cb116b76eb29..0bc3d95520fc 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -429,6 +429,7 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload;// ??? // Setup Dear ImGui style ImGui::StyleColorsDark(); @@ -509,6 +510,19 @@ int main(int, char**) g_SwapChainRebuild = false; } +#if 1 + if (ImGui::IsKeyPressed(ImGuiKey_KeypadAdd)) + { + static float size = 13.0f; + size += 1.0f; + ImFontConfig cfg; + cfg.SizePixels = size; + io.Fonts->Clear(); + io.Fonts->AddFontDefault(&cfg); + io.Fonts->MarkDirty(); + } +#endif + // Start the Dear ImGui frame ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(); diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 57a481c710fc..6a3ba3548425 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -102,6 +102,8 @@ int main(int, char**) g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); + io.BackendFlags |= ImGuiBackendFlags_RendererHasTexReload; // Set flag to indicate that we can reload textures when requested. + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. @@ -151,6 +153,14 @@ int main(int, char**) // Start the Dear ImGui frame ImGui_ImplDX12_NewFrame(); ImGui_ImplWin32_NewFrame(); + + // Upload Fonts + if (io.Fonts->IsDirty()) + { + WaitForLastSubmittedFrame(); + ImGui_ImplDX12_UpdateFontsTexture(); + } + ImGui::NewFrame(); // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 422248bcbf63..903d87668ef6 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -174,6 +174,7 @@ int main(int, char**) if (g_pd3dDevice->BeginScene() >= 0) { ImGui::Render(); + //ImGui_ImplDX9_UpdateTextures(); ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); g_pd3dDevice->EndScene(); } diff --git a/imgui.cpp b/imgui.cpp index 3bbaf018f182..5fc5d7c9a958 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3729,13 +3729,13 @@ void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCurso if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale))) continue; ImDrawList* draw_list = GetForegroundDrawList(viewport); - ImTextureID tex_id = font_atlas->TexID; - draw_list->PushTextureID(tex_id); - draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border); - draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill); - draw_list->PopTextureID(); + draw_list->PushTexture(ImTexture(font_atlas)); + ImTexture tex = draw_list->_CmdHeader.Texture; + draw_list->AddImage(tex, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex, pos, pos + size * scale, uv[2], uv[3], col_border); + draw_list->AddImage(tex, pos, pos + size * scale, uv[0], uv[1], col_fill); + draw_list->PopTexture(); } } @@ -4740,6 +4740,12 @@ ImDrawData* ImGui::GetDrawData() return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL; } +ImTextureUpdateData* ImGui::GetTextureUpdateData() +{ + ImGuiContext& g = *GImGui; + return g.IO.Fonts->GetTextureUpdateData(); +} + double ImGui::GetTime() { return GImGui->Time; @@ -4767,7 +4773,7 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount) { draw_list->_ResetForNewFrame(); - draw_list->PushTextureID(g.IO.Fonts->TexID); + draw_list->PushTexture(ImTexture(g.IO.Fonts)); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount; } @@ -5034,6 +5040,11 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePre); + // Check that font atlas was built or backend support texture reload in which case we can build now + ImFontAtlas* atlas = g.IO.Fonts; + if (!atlas->IsBuilt() && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTexReload)) + atlas->Build(); + // Check and assert for various common IO and Configuration mistakes ErrorCheckNewFrameSanityChecks(); @@ -5066,6 +5077,7 @@ void ImGui::NewFrame() SetupDrawListSharedData(); SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); + g.IO.Fonts->ClearTransientTextures(); // Mark rendering data as invalid to prevent user who may have a handle on it to use it. for (ImGuiViewportP* viewport : g.Viewports) @@ -7435,7 +7447,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Setup draw list and outer clipping rectangle IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0); - window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); + window->DrawList->PushTexture(ImTexture(g.Font->ContainerAtlas)); PushClipRect(host_rect.Min, host_rect.Max, false); // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) @@ -7937,7 +7949,7 @@ void ImGui::PushFont(ImFont* font) font = GetDefaultFont(); g.FontStack.push_back(font); SetCurrentFont(font); - g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); + g.CurrentWindow->DrawList->_SetTexture(ImTexture(font->ContainerAtlas)); } void ImGui::PopFont() @@ -7951,7 +7963,7 @@ void ImGui::PopFont() g.FontStack.pop_back(); ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back(); SetCurrentFont(font); - g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); + g.CurrentWindow->DrawList->_SetTexture(ImTexture(font->ContainerAtlas)); } void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) @@ -15239,14 +15251,14 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) DebugNodeFont(font); PopID(); } - if (TreeNode("Font Atlas", "Font Atlas (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + if (TreeNode("Font Atlas", "Font Atlas (%dx%d pixels)", atlas->TexData.TexWidth, atlas->TexData.TexHeight)) { ImGuiContext& g = *GImGui; ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; Checkbox("Tint with Text Color", &cfg->ShowAtlasTintedWithTextColor); // Using text color ensure visibility of core atlas data, but will alter custom colored icons ImVec4 tint_col = cfg->ShowAtlasTintedWithTextColor ? GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); ImVec4 border_col = GetStyleColorVec4(ImGuiCol_Border); - Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col); + Image(atlas->TexData.TexID, ImVec2((float)atlas->TexData.TexWidth, (float)atlas->TexData.TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col); TreePop(); } } @@ -15959,7 +15971,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con } char texid_desc[20]; - FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TextureId); + FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->GetTexID()); char buf[300]; ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); diff --git a/imgui.h b/imgui.h index 77ca8e04f7f0..1b9eea1565c5 100644 --- a/imgui.h +++ b/imgui.h @@ -193,6 +193,8 @@ struct ImGuiTableColumnSortSpecs; // Sorting specification for one column of a struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder) struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor +struct ImTexture; +struct ImTextureUpdateData; // Enumerations // - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration) @@ -333,6 +335,7 @@ namespace ImGui IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all! IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData(). IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. + IMGUI_API ImTextureUpdateData* GetTextureUpdateData(); // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! @@ -346,6 +349,8 @@ namespace ImGui IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as an end-user (mouse/keyboard controls). IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.80 WIP" (essentially the value for IMGUI_VERSION from the compiled version of imgui.cpp) + IMGUI_API void ShowFontDemoWindow(); + // Styles IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font @@ -561,7 +566,9 @@ namespace ImGui // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. // - Note that Image() may add +2.0f to provided size if a border is visible, ImageButton() adds style.FramePadding*2.0f to provided size. + IMGUI_API void Image(ImTexture texture, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0)); IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0)); + IMGUI_API bool ImageButton(const char* str_id, ImTexture texture, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Widgets: Combo Box (Dropdown) @@ -1614,6 +1621,7 @@ enum ImGuiBackendFlags_ ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape. ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set). ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3, // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. + ImGuiBackendFlags_RendererHasTexReload = 1 << 4 // Backend Renderer checks IsDirty() on the font atlas texture after EndFrame()/Render() and reupload the texture to GPU if required. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -2943,10 +2951,41 @@ typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* c // this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices. // Backends made for <1.71. will typically ignore the VtxOffset fields. // - The ClipRect/TextureId/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for). +typedef unsigned short ImTextureType; + +enum ImTextureType_ +{ + ImTextureType_Atlas, + ImTextureType_UserID +}; + +struct ImTexture +{ + union + { + ImTextureID TextureId; + ImFontAtlas* FontAtlas; + }; + ImTextureType Type; + unsigned short FontAtlasPage; + + // FIXME-TEXUPDATE: Explain why explicit + ImTexture() {} + explicit ImTexture(ImFontAtlas* font_atlas); + explicit ImTexture(ImTextureID texture_id); + + // memcpy() compare also padding bytes on x64 archs which lead to incorrect compareison result + // inline friend bool operator==(const ImTexture& lhs, const ImTexture& rhs) { return memcmp(&lhs, &rhs, sizeof(ImTexture)) == 0; } + inline friend bool operator==(const ImTexture& lhs, const ImTexture& rhs) { return lhs.TextureId == rhs.TextureId && lhs.Type == rhs.Type && lhs.FontAtlasPage == rhs.FontAtlasPage; } + inline friend bool operator!=(const ImTexture& lhs, const ImTexture& rhs) { return !(lhs == rhs); } + + inline ImTextureID GetID() const; +}; + struct ImDrawCmd { ImVec4 ClipRect; // 4*4 // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates - ImTextureID TextureId; // 4-8 // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. + ImTexture Texture; // 8-12 // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. unsigned int VtxOffset; // 4 // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices. unsigned int IdxOffset; // 4 // Start offset in index buffer. unsigned int ElemCount; // 4 // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. @@ -2955,8 +2994,9 @@ struct ImDrawCmd ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed + inline void SetTexID(ImTextureID tex_id) { Texture.TextureId = tex_id; Texture.Type = ImTextureType_UserID; } // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) - inline ImTextureID GetTexID() const { return TextureId; } + inline ImTextureID GetTexID() const { return Texture.GetID(); } }; // Vertex layout @@ -2979,7 +3019,7 @@ IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; struct ImDrawCmdHeader { ImVec4 ClipRect; - ImTextureID TextureId; + ImTexture Texture; unsigned int VtxOffset; }; @@ -3065,7 +3105,7 @@ struct ImDrawList ImDrawCmdHeader _CmdHeader; // [Internal] template of active commands. Fields should match those of CmdBuffer.back(). ImDrawListSplitter _Splitter; // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!) ImVector _ClipRectStack; // [Internal] - ImVector _TextureIdStack; // [Internal] + ImVector _TextureStack; // [Internal] float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content const char* _OwnerName; // Pointer to owner window's name for debugging @@ -3076,8 +3116,10 @@ struct ImDrawList IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) IMGUI_API void PushClipRectFullScreen(); IMGUI_API void PopClipRect(); - IMGUI_API void PushTextureID(ImTextureID texture_id); - IMGUI_API void PopTextureID(); + IMGUI_API void PushTexture(ImTexture texture); + IMGUI_API void PopTexture(); + IMGUI_API void PushTextureID(ImTextureID texture_id); // DEPRECATED??? + IMGUI_API void PopTextureID(); // DEPRECATED??? inline ImVec2 GetClipRectMin() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.x, cr.y); } inline ImVec2 GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); } @@ -3118,8 +3160,11 @@ struct ImDrawList // - Read FAQ to understand what ImTextureID is. // - "p_min" and "p_max" represent the upper-left and lower-right corners of the rectangle. // - "uv_min" and "uv_max" represent the normalized texture coordinates to use for those corners. Using (0,0)->(1,1) texture coordinates will generally display the entire texture. + IMGUI_API void AddImage(ImTexture texture, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min = ImVec2(0, 0), const ImVec2& uv_max = ImVec2(1, 1), ImU32 col = IM_COL32_WHITE); IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min = ImVec2(0, 0), const ImVec2& uv_max = ImVec2(1, 1), ImU32 col = IM_COL32_WHITE); + IMGUI_API void AddImageQuad(ImTexture texture, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE); IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE); + IMGUI_API void AddImageRounded(ImTexture texture, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0); IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0); // Stateful path API, add points then finish with PathFillConvex() or PathStroke() @@ -3178,9 +3223,9 @@ struct ImDrawList IMGUI_API void _PopUnusedDrawCmd(); IMGUI_API void _TryMergeDrawCmds(); IMGUI_API void _OnChangedClipRect(); - IMGUI_API void _OnChangedTextureID(); + IMGUI_API void _OnChangedTexture(); IMGUI_API void _OnChangedVtxOffset(); - IMGUI_API void _SetTextureID(ImTextureID texture_id); + IMGUI_API void _SetTexture(ImTexture texture); IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const; IMGUI_API void _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step); IMGUI_API void _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments); @@ -3308,6 +3353,43 @@ enum ImFontAtlasFlags_ // You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed, // - Even though many functions are suffixed with "TTF", OTF data is supported just as well. // - This is an old API and it is currently awkward for those and various other reasons! We will address them in the future! +typedef unsigned int ImTextureFormat; + +enum ImTextureFormat_ +{ + ImTextureFormat_Alpha8, // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight + ImTextureFormat_RGBA32 // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 +}; + +struct ImTextureData +{ + IMGUI_API ImTextureData(); + IMGUI_API ~ImTextureData(); + IMGUI_API void Reset(); + IMGUI_API void AllocatePixels(int width, int height, ImTextureFormat format, bool clear = true); + IMGUI_API void DiscardPixels(); + IMGUI_API void EnsureFormat(ImTextureFormat format); + + void SetTexID(ImTextureID tex_id) { TexID = tex_id; } + ImTextureID GetTexID() const { return TexID; } + + void MarkDirty() { TexDirty = true; } + void MarkClean() { TexDirty = false; } + bool IsDirty() const { return TexDirty; } + + ImTextureID TexID; + ImTextureFormat TexFormat; + void* TexPixels; + int TexWidth; + int TexHeight; + bool TexDirty; +}; + +struct ImTextureUpdateData +{ + ImVector Textures; +}; + struct ImFontAtlas { IMGUI_API ImFontAtlas(); @@ -3329,10 +3411,13 @@ struct ImFontAtlas // Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste. IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. + IMGUI_API bool IsDirty(); // Returns true if the font needs to be (re-)built. User code should call GetTexData*** and update either the whole texture or dirty region as required. IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel - bool IsBuilt() const { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't build texture but effectively we should check TexID != 0 except that would be backend dependent... - void SetTexID(ImTextureID id) { TexID = id; } + IMGUI_API ImTextureUpdateData* GetTextureUpdateData(); + bool IsBuilt() const { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't built texture but effectively we should check TexID != 0 except that would be backend dependent... + void SetTexID(ImTextureID id) { TexData.TexID = id; } + ImTextureID GetTexID() const { return TexData.TexID; } //------------------------------------------- // Glyph Ranges @@ -3370,13 +3455,16 @@ struct ImFontAtlas // [Internal] IMGUI_API void CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const; IMGUI_API bool GetMouseCursorTexData(ImGuiMouseCursor cursor, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]); + IMGUI_API void MarkDirty(); // Mark atlas texture as dirty + IMGUI_API void MarkClean(); // Mark the whole texture as clean. Should be called after uploading texture. + IMGUI_API void PushTexPage(); + IMGUI_API void ClearTransientTextures(); //------------------------------------------- // Members //------------------------------------------- ImFontAtlasFlags Flags; // Build flags (see ImFontAtlasFlags_) - ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false). bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. @@ -3385,17 +3473,16 @@ struct ImFontAtlas // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. bool TexReady; // Set when texture was built matching current font input - bool TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format. - unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight - unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 - int TexWidth; // Texture width calculated during Build(). - int TexHeight; // Texture height calculated during Build(). + bool TexDirty; // Indicate that content of the atlas texture has changed and need to be uploaded again by backend + ImTextureData TexData; ImVec2 TexUvScale; // = (1.0f/TexWidth, 1.0f/TexHeight) ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector ConfigData; // Configuration data ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines + ImVector TexPages; + ImTextureUpdateData TexUpdateData; // Only access via GetTextureUpdateData() // [Internal] Font builder const ImFontBuilderIO* FontBuilderIO; // Opaque interface to a font builder (default to stb_truetype, can be changed to use FreeType by defining IMGUI_ENABLE_FREETYPE). @@ -3505,6 +3592,39 @@ struct ImGuiViewport ImVec2 GetWorkCenter() const { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); } }; + +//----------------------------------------------------------------------------- +// [SECTION] Inline function implementations +//----------------------------------------------------------------------------- + +// Declared here instead of in ImTexture because we need knowledge of ImFontAtlas +inline ImTexture::ImTexture(ImFontAtlas* font_atlas) +{ + FontAtlas = font_atlas; + Type = ImTextureType_Atlas; + FontAtlasPage = (unsigned short)font_atlas->TexPages.Size; // FIXME-TEXUPDATE: Current? +} + +inline ImTexture::ImTexture(ImTextureID texture_id) +{ + TextureId = texture_id; + Type = ImTextureType_UserID; + FontAtlasPage = 0; +} + +inline ImTextureID ImTexture::GetID() const +{ + if (Type == ImTextureType_Atlas) + { + if (FontAtlasPage < FontAtlas->TexPages.Size) + return FontAtlas->TexPages[FontAtlasPage].GetTexID(); + else + return FontAtlas->TexData.GetTexID(); + } + else + return TextureId; +} + //----------------------------------------------------------------------------- // [SECTION] Platform Dependent Interfaces //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8df1755fb94b..4c638f50c06b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -385,6 +385,8 @@ void ImGui::ShowDemoWindow(bool* p_open) // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. IMGUI_CHECKVERSION(); + ShowFontDemoWindow(); + // Stored data static ImGuiDemoWindowData demo_data; @@ -596,6 +598,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos); ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset); + ImGui::CheckboxFlags("io.BackendFlags: RendererHasTexReload", &io.BackendFlags, ImGuiBackendFlags_RendererHasTexReload); ImGui::EndDisabled(); ImGui::TreePop(); @@ -1307,9 +1310,9 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples - ImTextureID my_tex_id = io.Fonts->TexID; - float my_tex_w = (float)io.Fonts->TexWidth; - float my_tex_h = (float)io.Fonts->TexHeight; + ImTextureID my_tex_id = io.Fonts->GetTexID(); + float my_tex_w = (float)io.Fonts->TexData.TexWidth; + float my_tex_h = (float)io.Fonts->TexData.TexHeight; { static bool use_text_color_for_tint = false; ImGui::Checkbox("Use Text Color for Tint", &use_text_color_for_tint); @@ -7769,7 +7772,7 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset"); ImGui::Separator(); - ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); + ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData.TexWidth, io.Fonts->TexData.TexHeight); ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); ImGui::Separator(); @@ -10362,6 +10365,142 @@ void ShowExampleAppAssetsBrowser(bool* p_open) assets_browser.Draw("Example: Assets Browser", p_open); } +#include "imgui_internal.h" + +static const char* font_path_prefix = "../../misc/fonts/"; +static const char* fonts[] = +{ + "Default (built-in)", + "Roboto-Medium.ttf", + "Cousine-Regular.ttf", + "DroidSans.ttf", + "ProggyTiny.ttf", +}; + +struct FontDemoState +{ + bool want_rebuild; + int font_index; + float font_size; + + FontDemoState() + { + want_rebuild = true; + font_index = 0; + font_size = 13.0f; + } +}; + +static FontDemoState font_demo_state1; +static FontDemoState font_demo_state2; + +static void RebuildAtlas(FontDemoState& state) +{ + ImGuiIO& io = ImGui::GetIO(); + if (!state.want_rebuild) + return; + state.want_rebuild = false; + + io.Fonts->Locked = false; // FIXME-TEXUPDATE: #thedmd: remove this + io.Fonts->PushTexPage(); + io.Fonts->Clear(); + + ImFontConfig cfg; + cfg.SizePixels = state.font_size; + cfg.OversampleH = cfg.OversampleV = 1; + + if (state.font_index == 0) + { + io.Fonts->AddFontDefault(&cfg); + } + else + { + char path[256]; + sprintf(path, "%s%s", font_path_prefix, fonts[state.font_index]); + io.Fonts->AddFontFromFileTTF(path, 0.0f, &cfg); + } + io.Fonts->Build(); + io.Fonts->Locked = true; // FIXME-TEXUPDATE: #thedmd: remove this +} + +static void ShowFontDemo(FontDemoState& state) +{ + ImGuiIO& io = ImGui::GetIO(); + + RebuildAtlas(state); + + // FIXME-TEXUPDATE: What is this doing? + ImGui::SetCurrentFont(ImGui::GetDefaultFont()); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->_CmdHeader.Texture = ImTexture(io.Fonts); + draw_list->_OnChangedTexture(); + + draw_list = ImGui::GetForegroundDrawList(); + draw_list->_CmdHeader.Texture = ImTexture(io.Fonts); + draw_list->_OnChangedTexture(); + + draw_list = ImGui::GetBackgroundDrawList(); + draw_list->_CmdHeader.Texture = ImTexture(io.Fonts); + draw_list->_OnChangedTexture(); + + if (ImGui::BeginCombo("Font", fonts[state.font_index])) + { + for (int i = 0; i < IM_ARRAYSIZE(fonts); ++i) + { + if (ImGui::Selectable(fonts[i])) + { + state.font_index = i; + state.want_rebuild = true; + } + } + + ImGui::EndCombo(); + } + + if (ImGui::SliderFloat("Size", &state.font_size, 6.0f, 48.0f, "%.0f")) + state.want_rebuild = true; + + ImFontAtlas* atlas = io.Fonts; + //if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + ImGui::Text("Atlas texture (%dx%d pixels)", atlas->TexData.TexWidth, atlas->TexData.TexHeight); + { + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); + ImGui::Image(ImTexture(atlas), ImVec2((float)atlas->TexData.TexWidth, (float)atlas->TexData.TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); + //ImGui::TreePop(); + } +} + +void ImGui::ShowFontDemoWindow() +{ + if (ImGui::Begin("Font Test")) + { + ImGuiIO& io = ImGui::GetIO(); + + ImGuiBackendFlags backend_flags = io.BackendFlags; + ImGui::Text("Backends: '%s' + '%s'", io.BackendPlatformName ? io.BackendPlatformName : "", io.BackendRendererName ? io.BackendRendererName : ""); + ImGui::CheckboxFlags("io.BackendFlags: RendererHasTexReload", &backend_flags, ImGuiBackendFlags_RendererHasTexReload); + static bool always_rebuild = true; + + ImGui::Checkbox("Rebuild every frame", &always_rebuild); + if (always_rebuild) + font_demo_state1.want_rebuild = font_demo_state2.want_rebuild = true; + ImGui::SameLine(); HelpMarker("Otherwise last rebuilt will be applied to current atlas."); + + ImGui::Separator(); + + ImGui::PushID("state1"); + ShowFontDemo(font_demo_state1); + ImGui::PopID(); + + ImGui::PushID("state2"); + ShowFontDemo(font_demo_state2); + ImGui::PopID(); + + ImGui::End(); + } +} + // End of Demo code #else diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 8084e53c3006..6c2ff9f45c61 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -399,8 +399,8 @@ void ImDrawList::_ResetForNewFrame() { // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0); - IM_STATIC_ASSERT(offsetof(ImDrawCmd, TextureId) == sizeof(ImVec4)); - IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, Texture) == sizeof(ImVec4)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTexture)); if (_Splitter._Count > 1) _Splitter.Merge(this); @@ -413,7 +413,7 @@ void ImDrawList::_ResetForNewFrame() _VtxWritePtr = NULL; _IdxWritePtr = NULL; _ClipRectStack.resize(0); - _TextureIdStack.resize(0); + _TextureStack.resize(0); _Path.resize(0); _Splitter.Clear(); CmdBuffer.push_back(ImDrawCmd()); @@ -430,7 +430,7 @@ void ImDrawList::_ClearFreeMemory() _VtxWritePtr = NULL; _IdxWritePtr = NULL; _ClipRectStack.clear(); - _TextureIdStack.clear(); + _TextureStack.clear(); _Path.clear(); _Splitter.ClearFreeMemory(); } @@ -449,7 +449,7 @@ void ImDrawList::AddDrawCmd() { ImDrawCmd draw_cmd; draw_cmd.ClipRect = _CmdHeader.ClipRect; // Same as calling ImDrawCmd_HeaderCopy() - draw_cmd.TextureId = _CmdHeader.TextureId; + draw_cmd.Texture = _CmdHeader.Texture; draw_cmd.VtxOffset = _CmdHeader.VtxOffset; draw_cmd.IdxOffset = IdxBuffer.Size; @@ -529,12 +529,12 @@ void ImDrawList::_OnChangedClipRect() curr_cmd->ClipRect = _CmdHeader.ClipRect; } -void ImDrawList::_OnChangedTextureID() +void ImDrawList::_OnChangedTexture() { // If current command is used with different settings we need to add a new command IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) + if (curr_cmd->ElemCount != 0 && curr_cmd->Texture != _CmdHeader.Texture) { AddDrawCmd(); return; @@ -548,7 +548,7 @@ void ImDrawList::_OnChangedTextureID() CmdBuffer.pop_back(); return; } - curr_cmd->TextureId = _CmdHeader.TextureId; + curr_cmd->Texture = _CmdHeader.Texture; } void ImDrawList::_OnChangedVtxOffset() @@ -609,27 +609,40 @@ void ImDrawList::PopClipRect() _OnChangedClipRect(); } +void ImDrawList::PushTexture(ImTexture texture) +{ + _CmdHeader.Texture = texture; + _TextureStack.push_back(_CmdHeader.Texture); + _OnChangedTexture(); +} + +void ImDrawList::PopTexture() +{ + _TextureStack.pop_back(); + if (_TextureStack.Size == 0) + memset(&_CmdHeader.Texture, 0, sizeof(ImTexture)); + else + _CmdHeader.Texture = _TextureStack.Data[_TextureStack.Size - 1]; + _OnChangedTexture(); +} + void ImDrawList::PushTextureID(ImTextureID texture_id) { - _TextureIdStack.push_back(texture_id); - _CmdHeader.TextureId = texture_id; - _OnChangedTextureID(); + PushTexture(ImTexture(texture_id)); } void ImDrawList::PopTextureID() { - _TextureIdStack.pop_back(); - _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? (ImTextureID)NULL : _TextureIdStack.Data[_TextureIdStack.Size - 1]; - _OnChangedTextureID(); + PopTexture(); } // This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTextureID()/PopTextureID(). -void ImDrawList::_SetTextureID(ImTextureID texture_id) +void ImDrawList::_SetTexture(ImTexture texture) { - if (_CmdHeader.TextureId == texture_id) + if (_CmdHeader.Texture == texture) return; - _CmdHeader.TextureId = texture_id; - _OnChangedTextureID(); + _CmdHeader.Texture = texture; + _OnChangedTexture(); } // Reserve space for a number of vertices and indices. @@ -1643,7 +1656,7 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, if (font_size == 0.0f) font_size = _Data->FontSize; - IM_ASSERT(font->ContainerAtlas->TexID == _CmdHeader.TextureId); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + IM_ASSERT(ImTexture(font->ContainerAtlas) == _CmdHeader.Texture); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. ImVec4 clip_rect = _CmdHeader.ClipRect; if (cpu_fine_clip_rect) @@ -1661,39 +1674,49 @@ void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, c AddText(NULL, 0.0f, pos, col, text_begin, text_end); } -void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) +void ImDrawList::AddImage(ImTexture texture, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) return; - const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; - if (push_texture_id) - PushTextureID(user_texture_id); + const bool push_texture = texture != _CmdHeader.Texture; + if (push_texture) + PushTexture(texture); PrimReserve(6, 4); PrimRectUV(p_min, p_max, uv_min, uv_max, col); - if (push_texture_id) + if (push_texture) PopTextureID(); } -void ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col) +void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) +{ + AddImage(ImTexture(user_texture_id), p_min, p_max, uv_min, uv_max, col); +} + +void ImDrawList::AddImageQuad(ImTexture texture, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) return; - const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; - if (push_texture_id) - PushTextureID(user_texture_id); + const bool push_texture = texture != _CmdHeader.Texture; + if (push_texture) + PushTexture(texture); PrimReserve(6, 4); PrimQuadUV(p1, p2, p3, p4, uv1, uv2, uv3, uv4, col); - if (push_texture_id) - PopTextureID(); + if (push_texture) + PopTexture(); } -void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags) +void ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col) +{ + AddImageQuad(ImTexture(user_texture_id), p1, p2, p3, p4, uv1, uv2, uv3, uv4, col); +} + +void ImDrawList::AddImageRounded(ImTexture texture, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0) return; @@ -1701,13 +1724,13 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi flags = FixRectCornerFlags(flags); if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { - AddImage(user_texture_id, p_min, p_max, uv_min, uv_max, col); + AddImage(texture, p_min, p_max, uv_min, uv_max, col); return; } - const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; - if (push_texture_id) - PushTextureID(user_texture_id); + const bool push_texture = texture != _CmdHeader.Texture; + if (push_texture) + PushTexture(texture); int vert_start_idx = VtxBuffer.Size; PathRect(p_min, p_max, rounding, flags); @@ -1715,8 +1738,13 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi int vert_end_idx = VtxBuffer.Size; ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true); - if (push_texture_id) - PopTextureID(); + if (push_texture) + PopTexture(); +} + +void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags) +{ + AddImageRounded(ImTexture(user_texture_id), p_min, p_max, uv_min, uv_max, col, rounding, flags); } //----------------------------------------------------------------------------- @@ -2342,6 +2370,110 @@ ImFontConfig::ImFontConfig() EllipsisChar = (ImWchar)-1; } +//----------------------------------------------------------------------------- +// [SECTION] ImTextureData +//----------------------------------------------------------------------------- + +ImTextureData::ImTextureData() +{ + memset(this, 0, sizeof(*this)); +} + +ImTextureData::~ImTextureData() +{ + DiscardPixels(); +} + +void ImTextureData::Reset() +{ + memset(this, 0, sizeof(*this)); +} + +void ImTextureData::AllocatePixels(int width, int height, ImTextureFormat format, bool clear) +{ + DiscardPixels(); + + int bytes_per_pixel = format == ImTextureFormat_Alpha8 ? 1 : 4; + + TexFormat = format; + TexPixels = IM_ALLOC(width * height * bytes_per_pixel); + TexWidth = width; + TexHeight = height; + + if (clear) + memset(TexPixels, 0, width * height * bytes_per_pixel); +} + +void ImTextureData::DiscardPixels() +{ + if (TexPixels) + { + IM_FREE(TexPixels); + TexPixels = NULL; + } +} + +void ImTextureData::EnsureFormat(ImTextureFormat new_format) +{ + // Skip conversion to same format. + const ImTextureFormat old_format = TexFormat; + if (old_format == new_format) + return; + + TexFormat = new_format; + if (TexPixels == NULL) + return; + + if (old_format == ImTextureFormat_Alpha8 && new_format == ImTextureFormat_RGBA32) + { + // Convert Alpha8 -> RGBA32 format on demand + const unsigned char* pixels = (unsigned char*)TexPixels; + unsigned int* pixels_rgba32 = (unsigned int*)IM_ALLOC((size_t)TexWidth * TexHeight * 4); + const unsigned char* src = pixels; + unsigned int* dst = pixels_rgba32; + for (int n = TexWidth * TexHeight; n > 0; n--) + *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++)); + IM_FREE(TexPixels); + TexPixels = pixels_rgba32; + } + else if (old_format == ImTextureFormat_RGBA32 && new_format == ImTextureFormat_Alpha8) + { + // Convert RGBA8 -> Alpha8 format on demand + // Use Rec. 709 luma coefficients: + // R = 0.2126 G = 0.7152 B = 0.0722 + unsigned int r_coeff_fix24 = 3566836; // R * (1 << 24) + unsigned int g_coeff_fix24 = 11999065; // G * (1 << 24) + unsigned int b_coeff_fix24 = 1211315; // B * (1 << 24) + unsigned int a_coeff_fix16 = 65793; // A * (256 / 255) * (1 << 16) + unsigned int* pixels = (unsigned int*)TexPixels; + unsigned char* pixels_alpha8 = (unsigned char*)IM_ALLOC((size_t)TexWidth * TexHeight); + const unsigned int* src = pixels; + unsigned char* dst = pixels_alpha8; + for (int n = TexWidth * TexHeight; n > 0; n--) + { + // Calculate luma from RGB image, value is in range [0..255] in fixed format + unsigned int color = *src++; + unsigned int luma_fix24 = + ((color >> IM_COL32_R_SHIFT) & 0xFF) * r_coeff_fix24 + + ((color >> IM_COL32_G_SHIFT) & 0xFF) * g_coeff_fix24 + + ((color >> IM_COL32_B_SHIFT) & 0xFF) * b_coeff_fix24; + + // Luma will be used as alpha. If original texture have transparency data, we're pre-multiplying it into luma. + // Multiplying by a_coeff_fix24 move alpha to range [0..256]. + // Luma max value is 255, product brings value back to [0..255] range and can be used directly. + luma_fix24 = a_coeff_fix16 * (luma_fix24 >> 24) * ((color >> IM_COL32_A_SHIFT) & 0xFF); + + *dst++ = luma_fix24 >> 24; + } + IM_FREE(TexPixels); + TexPixels = pixels_alpha8; + } + else + { + IM_ASSERT(0 && "Unsupported texture format convertion."); + } +} + //----------------------------------------------------------------------------- // [SECTION] ImFontAtlas //----------------------------------------------------------------------------- @@ -2407,6 +2539,7 @@ ImFontAtlas::~ImFontAtlas() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); Clear(); + ClearTransientTextures(); } void ImFontAtlas::ClearInputData() @@ -2427,22 +2560,16 @@ void ImFontAtlas::ClearInputData() font->ConfigDataCount = 0; } ConfigData.clear(); - CustomRects.clear(); - PackIdMouseCursors = PackIdLines = -1; + MarkDirty(); // Important: we leave TexReady untouched } void ImFontAtlas::ClearTexData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - if (TexPixelsAlpha8) - IM_FREE(TexPixelsAlpha8); - if (TexPixelsRGBA32) - IM_FREE(TexPixelsRGBA32); - TexPixelsAlpha8 = NULL; - TexPixelsRGBA32 = NULL; - TexPixelsUseColors = false; // Important: we leave TexReady untouched + TexData.DiscardPixels(); + MarkDirty(); } void ImFontAtlas::ClearFonts() @@ -2450,6 +2577,7 @@ void ImFontAtlas::ClearFonts() IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); Fonts.clear_delete(); TexReady = false; + MarkDirty(); } void ImFontAtlas::Clear() @@ -2459,15 +2587,49 @@ void ImFontAtlas::Clear() ClearFonts(); } +void ImFontAtlas::MarkDirty() +{ + TexDirty = true; +} + +void ImFontAtlas::MarkClean() +{ + TexDirty = false; +} + +bool ImFontAtlas::IsDirty() +{ + return TexDirty; +} + +void ImFontAtlas::PushTexPage() +{ + if (TexData.TexPixels == NULL) + return; + + TexPages.push_back(TexData); // ImVector<> use memcpy + TexData.Reset(); // Reset (memset to zero) TexData, memory is now owned by copy in TexPages +} + +void ImFontAtlas::ClearTransientTextures() +{ + for (int i = 0; i < TexPages.Size; ++i) + TexPages[i].DiscardPixels(); + + TexPages.clear(); +} + void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) { // Build atlas on demand - if (TexPixelsAlpha8 == NULL) + if (TexData.TexPixels == NULL || IsDirty()) Build(); - *out_pixels = TexPixelsAlpha8; - if (out_width) *out_width = TexWidth; - if (out_height) *out_height = TexHeight; + TexData.EnsureFormat(ImTextureFormat_Alpha8); + + *out_pixels = (unsigned char*)TexData.TexPixels; + if (out_width) *out_width = TexData.TexWidth; + if (out_height) *out_height = TexData.TexHeight; if (out_bytes_per_pixel) *out_bytes_per_pixel = 1; } @@ -2475,26 +2637,28 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid { // Convert to RGBA32 format on demand // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp - if (!TexPixelsRGBA32) - { - unsigned char* pixels = NULL; - GetTexDataAsAlpha8(&pixels, NULL, NULL); - if (pixels) - { - TexPixelsRGBA32 = (unsigned int*)IM_ALLOC((size_t)TexWidth * (size_t)TexHeight * 4); - const unsigned char* src = pixels; - unsigned int* dst = TexPixelsRGBA32; - for (int n = TexWidth * TexHeight; n > 0; n--) - *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++)); - } - } + if (TexData.TexPixels == NULL || IsDirty()) + Build(); - *out_pixels = (unsigned char*)TexPixelsRGBA32; - if (out_width) *out_width = TexWidth; - if (out_height) *out_height = TexHeight; + TexData.EnsureFormat(ImTextureFormat_RGBA32); + + *out_pixels = (unsigned char*)TexData.TexPixels; + if (out_width) *out_width = TexData.TexWidth; + if (out_height) *out_height = TexData.TexHeight; if (out_bytes_per_pixel) *out_bytes_per_pixel = 4; } +ImTextureUpdateData* ImFontAtlas::GetTextureUpdateData() +{ + ImTextureUpdateData* tex_update_data = &TexUpdateData; + tex_update_data->Textures.resize(0); + tex_update_data->Textures.reserve(TexPages.Size + 1); + tex_update_data->Textures.push_back(&TexData); + for (int i = 0; i < TexPages.Size; ++i) + tex_update_data->Textures.push_back(&TexPages[i]); + return tex_update_data; +} + ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); @@ -2527,6 +2691,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) // Invalidate texture TexReady = false; ClearTexData(); + MarkDirty(); return new_font_cfg.DstFont; } @@ -2658,8 +2823,8 @@ int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int void ImFontAtlas::CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const { - IM_ASSERT(TexWidth > 0 && TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates - IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed + IM_ASSERT(TexData.TexWidth > 0 && TexData.TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates + IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed *out_uv_min = ImVec2((float)rect->X * TexUvScale.x, (float)rect->Y * TexUvScale.y); *out_uv_max = ImVec2((float)(rect->X + rect->Width) * TexUvScale.x, (float)(rect->Y + rect->Height) * TexUvScale.y); } @@ -2777,8 +2942,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) ImFontAtlasBuildInit(atlas); // Clear atlas - atlas->TexID = (ImTextureID)NULL; - atlas->TexWidth = atlas->TexHeight = 0; + atlas->TexData = ImTextureData(); atlas->TexUvScale = ImVec2(0.0f, 0.0f); atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); atlas->ClearTexData(); @@ -2925,17 +3089,17 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface. const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1; - atlas->TexHeight = 0; + atlas->TexData.TexHeight = 0; if (atlas->TexDesiredWidth > 0) - atlas->TexWidth = atlas->TexDesiredWidth; + atlas->TexData.TexWidth = atlas->TexDesiredWidth; else - atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512; + atlas->TexData.TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512; // 5. Start packing // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). const int TEX_HEIGHT_MAX = 1024 * 32; stbtt_pack_context spc = {}; - stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL); + stbtt_PackBegin(&spc, NULL, atlas->TexData.TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL); ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info); // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point. @@ -2951,16 +3115,16 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) // FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?) for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) if (src_tmp.Rects[glyph_i].was_packed) - atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h); + atlas->TexData.TexHeight = ImMax(atlas->TexData.TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h); } // 7. Allocate texture - atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); - atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); - atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight); - memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); - spc.pixels = atlas->TexPixelsAlpha8; - spc.height = atlas->TexHeight; + atlas->TexData.TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexData.TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexData.TexHeight); + atlas->TexUvScale = ImVec2(1.0f / atlas->TexData.TexWidth, 1.0f / atlas->TexData.TexHeight); + atlas->TexData.TexPixels = (unsigned char*)IM_ALLOC(atlas->TexData.TexWidth * atlas->TexData.TexHeight); + memset(atlas->TexData.TexPixels, 0, atlas->TexData.TexWidth * atlas->TexData.TexHeight); + spc.pixels = (unsigned char*)atlas->TexData.TexPixels; + spc.height = atlas->TexData.TexHeight; // 8. Render/rasterize font characters into the texture for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) @@ -2980,7 +3144,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) stbrp_rect* r = &src_tmp.Rects[0]; for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++, r++) if (r->was_packed) - ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, atlas->TexPixelsAlpha8, r->x, r->y, r->w, r->h, atlas->TexWidth * 1); + ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, (unsigned char*)atlas->TexData.TexPixels, r->x, r->y, r->w, r->h, atlas->TexData.TexWidth * 1); } src_tmp.Rects = NULL; } @@ -3018,7 +3182,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) const stbtt_packedchar& pc = src_tmp.PackedChars[glyph_i]; stbtt_aligned_quad q; float unused_x = 0.0f, unused_y = 0.0f; - stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0); + stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexData.TexWidth, atlas->TexData.TexHeight, glyph_i, &unused_x, &unused_y, &q, 0); float x0 = q.x0 * inv_rasterization_scale + font_off_x; float y0 = q.y0 * inv_rasterization_scale + font_off_y; float x1 = q.x1 * inv_rasterization_scale + font_off_x; @@ -3096,26 +3260,28 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa user_rects[i].X = (unsigned short)pack_rects[i].x; user_rects[i].Y = (unsigned short)pack_rects[i].y; IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height); - atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h); + atlas->TexData.TexHeight = ImMax(atlas->TexData.TexHeight, pack_rects[i].y + pack_rects[i].h); } } void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value) { - IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth); - IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight); - unsigned char* out_pixel = atlas->TexPixelsAlpha8 + x + (y * atlas->TexWidth); - for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w) + IM_ASSERT(x >= 0 && x + w <= atlas->TexData.TexWidth); + IM_ASSERT(y >= 0 && y + h <= atlas->TexData.TexHeight); + IM_ASSERT(ImTextureFormat_Alpha8 == atlas->TexData.TexFormat); + unsigned char* out_pixel = (unsigned char*)atlas->TexData.TexPixels + x + (y * atlas->TexData.TexWidth); + for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexData.TexWidth, in_str += w) for (int off_x = 0; off_x < w; off_x++) out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : 0x00; } void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value) { - IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth); - IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight); - unsigned int* out_pixel = atlas->TexPixelsRGBA32 + x + (y * atlas->TexWidth); - for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w) + IM_ASSERT(x >= 0 && x + w <= atlas->TexData.TexWidth); + IM_ASSERT(y >= 0 && y + h <= atlas->TexData.TexHeight); + IM_ASSERT(ImTextureFormat_RGBA32 == atlas->TexData.TexFormat); + unsigned int* out_pixel = (unsigned int*)atlas->TexData.TexPixels + x + (y * atlas->TexData.TexWidth); + for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexData.TexWidth, in_str += w) for (int off_x = 0; off_x < w; off_x++) out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : IM_COL32_BLACK_TRANS; } @@ -3125,14 +3291,14 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors); IM_ASSERT(r->IsPacked()); - const int w = atlas->TexWidth; + const int w = atlas->TexData.TexWidth; if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) { // Render/copy pixels IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); const int x_for_white = r->X; const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1; - if (atlas->TexPixelsAlpha8 != NULL) + if (atlas->TexData.TexFormat == ImTextureFormat_Alpha8) { ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', 0xFF); ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', 0xFF); @@ -3148,13 +3314,15 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) // Render 4 white pixels IM_ASSERT(r->Width == 2 && r->Height == 2); const int offset = (int)r->X + (int)r->Y * w; - if (atlas->TexPixelsAlpha8 != NULL) + if (atlas->TexData.TexFormat == ImTextureFormat_Alpha8) { - atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF; + unsigned char* pixels_alpha8 = (unsigned char*)atlas->TexData.TexPixels; + pixels_alpha8[offset] = pixels_alpha8[offset + 1] = pixels_alpha8[offset + w] = pixels_alpha8[offset + w + 1] = 0xFF; } else { - atlas->TexPixelsRGBA32[offset] = atlas->TexPixelsRGBA32[offset + 1] = atlas->TexPixelsRGBA32[offset + w] = atlas->TexPixelsRGBA32[offset + w + 1] = IM_COL32_WHITE; + unsigned int* pixels_rgba32 = (unsigned int*)atlas->TexData.TexPixels; + pixels_rgba32[offset] = pixels_rgba32[offset + 1] = pixels_rgba32[offset + w] = pixels_rgba32[offset + w + 1] = IM_COL32_WHITE; } } atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); @@ -3178,9 +3346,10 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas) // Write each slice IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels - if (atlas->TexPixelsAlpha8 != NULL) + if (atlas->TexData.TexFormat == ImTextureFormat_Alpha8) { - unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)]; + unsigned char* pixels_alpha8 = (unsigned char*)atlas->TexData.TexPixels; + unsigned char* write_ptr = &pixels_alpha8[r->X + ((r->Y + y) * atlas->TexData.TexWidth)]; for (unsigned int i = 0; i < pad_left; i++) *(write_ptr + i) = 0x00; @@ -3192,7 +3361,8 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas) } else { - unsigned int* write_ptr = &atlas->TexPixelsRGBA32[r->X + ((r->Y + y) * atlas->TexWidth)]; + unsigned int* pixels_rgba32 = (unsigned int*)atlas->TexData.TexPixels; + unsigned int* write_ptr = &pixels_rgba32[r->X + ((r->Y + y) * atlas->TexData.TexWidth)]; for (unsigned int i = 0; i < pad_left; i++) *(write_ptr + i) = IM_COL32(255, 255, 255, 0); @@ -3221,6 +3391,8 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) for (ImFontConfig& cfg : atlas->ConfigData) cfg.SizePixels = ImTrunc(cfg.SizePixels); + atlas->PushTexPage(); + // Register texture region for mouse cursors or standard white pixels if (atlas->PackIdMouseCursors < 0) { @@ -3243,7 +3415,7 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data blocks - IM_ASSERT(atlas->TexPixelsAlpha8 != NULL || atlas->TexPixelsRGBA32 != NULL); + IM_ASSERT(atlas->TexData.TexPixels != NULL); ImFontAtlasBuildRenderDefaultTexData(atlas); ImFontAtlasBuildRenderLinesTexData(atlas); @@ -3267,6 +3439,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) font->BuildLookupTable(); atlas->TexReady = true; + atlas->TexData.MarkDirty(); } // Retrieve list of range (2 int per range, values are inclusive) @@ -3778,7 +3951,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa // We use (U1-U0)*TexWidth instead of X1-X0 to account for oversampling. float pad = ContainerAtlas->TexGlyphPadding + 0.99f; DirtyLookupTables = true; - MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + pad) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + pad); + MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexData.TexWidth + pad) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexData.TexHeight + pad); } void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) diff --git a/imgui_internal.h b/imgui_internal.h index 9f47ecd21a0e..172e6088aa59 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3356,7 +3356,7 @@ namespace ImGui IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); - IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); + IMGUI_API bool ImageButtonEx(ImGuiID id, ImTexture texture, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f); IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width); IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 322c0846c9b0..c96f4f4362ff 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1053,7 +1053,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. -void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +void ImGui::Image(ImTexture texture, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -1069,12 +1069,17 @@ void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const I // Render if (border_size > 0.0f) window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f, ImDrawFlags_None, border_size); - window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + window->DrawList->AddImage(texture, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); +} + +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +{ + Image(ImTexture(user_texture_id), image_size, uv0, uv1, tint_col, border_col); } // ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390) // We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API. -bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) +bool ImGui::ImageButtonEx(ImGuiID id, ImTexture texture, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -1096,7 +1101,7 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& imag RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding)); if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); - window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + window->DrawList->AddImage(texture, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); return pressed; } @@ -1109,7 +1114,7 @@ bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const I if (window->SkipItems) return false; - return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col); + return ImageButtonEx(window->GetID(str_id), ImTexture(user_texture_id), image_size, uv0, uv1, bg_col, tint_col); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1131,6 +1136,10 @@ bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const I PopID(); return ret; } +bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) +{ + return ImageButton(ImTexture(user_texture_id), size, uv0, uv1, frame_padding, bg_col, tint_col); +} */ #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index d97605b25868..7ea0d0ecc17f 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -437,8 +437,7 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u ImFontAtlasBuildInit(atlas); // Clear atlas - atlas->TexID = 0; - atlas->TexWidth = atlas->TexHeight = 0; + atlas->TexData = ImTextureData(); atlas->TexUvScale = ImVec2(0.0f, 0.0f); atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); atlas->ClearTexData(); @@ -617,20 +616,20 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface. const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1; - atlas->TexHeight = 0; + atlas->TexData.TexHeight = 0; if (atlas->TexDesiredWidth > 0) - atlas->TexWidth = atlas->TexDesiredWidth; + atlas->TexData.TexWidth = atlas->TexDesiredWidth; else - atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512; + atlas->TexData.TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512; // 5. Start packing // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). const int TEX_HEIGHT_MAX = 1024 * 32; - const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding; + const int num_nodes_for_packing_algorithm = atlas->TexData.TexWidth - atlas->TexGlyphPadding; ImVector pack_nodes; pack_nodes.resize(num_nodes_for_packing_algorithm); stbrp_context pack_context; - stbrp_init_target(&pack_context, atlas->TexWidth - atlas->TexGlyphPadding, TEX_HEIGHT_MAX - atlas->TexGlyphPadding, pack_nodes.Data, pack_nodes.Size); + stbrp_init_target(&pack_context, atlas->TexData.TexWidth - atlas->TexGlyphPadding, TEX_HEIGHT_MAX - atlas->TexGlyphPadding, pack_nodes.Data, pack_nodes.Size); ImFontAtlasBuildPackCustomRects(atlas, &pack_context); // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point. @@ -646,28 +645,17 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u // FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?) for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) if (src_tmp.Rects[glyph_i].was_packed) - atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h); + atlas->TexData.TexHeight = ImMax(atlas->TexData.TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h); } // 7. Allocate texture - atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); - atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); - if (src_load_color) - { - size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 4; - atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(tex_size); - memset(atlas->TexPixelsRGBA32, 0, tex_size); - } - else - { - size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 1; - atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(tex_size); - memset(atlas->TexPixelsAlpha8, 0, tex_size); - } + atlas->TexData.TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexData.TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexData.TexHeight); + atlas->TexUvScale = ImVec2(1.0f / atlas->TexData.TexWidth, 1.0f / atlas->TexData.TexHeight); + atlas->TexData.AllocatePixels(atlas->TexData.TexWidth, atlas->TexData.TexHeight, + src_load_color ? ImTextureFormat_RGBA32 : ImTextureFormat_Alpha8); // 8. Copy rasterized font characters back into the main texture // 9. Setup ImFont and glyphs for runtime - bool tex_use_colors = false; for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; @@ -706,31 +694,31 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u float y0 = info.OffsetY * src_tmp.Font.InvRasterizationDensity + font_off_y; float x1 = x0 + info.Width * src_tmp.Font.InvRasterizationDensity; float y1 = y0 + info.Height * src_tmp.Font.InvRasterizationDensity; - float u0 = (tx) / (float)atlas->TexWidth; - float v0 = (ty) / (float)atlas->TexHeight; - float u1 = (tx + info.Width) / (float)atlas->TexWidth; - float v1 = (ty + info.Height) / (float)atlas->TexHeight; + float u0 = (tx) / (float)atlas->TexData.TexWidth; + float v0 = (ty) / (float)atlas->TexData.TexHeight; + float u1 = (tx + info.Width) / (float)atlas->TexData.TexWidth; + float v1 = (ty + info.Height) / (float)atlas->TexData.TexHeight; dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX * src_tmp.Font.InvRasterizationDensity); ImFontGlyph* dst_glyph = &dst_font->Glyphs.back(); IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint); if (src_glyph.Info.IsColored) - dst_glyph->Colored = tex_use_colors = true; + atlas->TexData.EnsureFormat(ImTextureFormat_RGBA32); // Blit from temporary buffer to final texture size_t blit_src_stride = (size_t)src_glyph.Info.Width; - size_t blit_dst_stride = (size_t)atlas->TexWidth; + size_t blit_dst_stride = (size_t)atlas->TexData.TexWidth; unsigned int* blit_src = src_glyph.BitmapData; - if (atlas->TexPixelsAlpha8 != nullptr) + if (atlas->TexData.TexFormat == ImTextureFormat_Alpha8) { - unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx; + unsigned char* blit_dst = (unsigned char*)atlas->TexData.TexPixels + (ty * blit_dst_stride) + tx; for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride) for (int x = 0; x < info.Width; x++) blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF); } else { - unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx; + unsigned int* blit_dst = (unsigned int*)atlas->TexData.TexPixels + (ty * blit_dst_stride) + tx; for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride) for (int x = 0; x < info.Width; x++) blit_dst[x] = blit_src[x]; @@ -739,7 +727,6 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u src_tmp.Rects = nullptr; } - atlas->TexPixelsUseColors = tex_use_colors; // Cleanup for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)