diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 6ee88a18..79b169da 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -9,6 +9,7 @@ // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // Missing features or Issues: // [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. +// [ ] Renderer: Missing support for DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest callbacks: Allegro5 cannot enable/disable LINEAR bitmap flags after creation. // [ ] Platform: Missing gamepad support. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. @@ -21,6 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState (others cannot be supported by Allegro5). (#9378) // 2025-09-18: Call platform_io.ClearRendererHandlers() and platform_io.ClearPlatformHandlers() on shutdown. // 2025-08-12: Inputs: fixed missing support for ImGuiKey_PrintScreen under Windows, as raw Allegro 5 does not receive it. // 2025-08-12: Added ImGui_ImplAllegro5_SetDisplay() function to change current ALLEGRO_DISPLAY, as Allegro applications often need to do that. @@ -136,7 +138,10 @@ static void ImGui_ImplAllegro5_SetupRenderState(ImDrawData* draw_data) } } -// Render function. +// Draw callbacks +static void ImGui_ImplAllegro5_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. + +// Render function void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized @@ -152,6 +157,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) // Backup Allegro state that will be modified ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ALLEGRO_TRANSFORM last_transform = *al_get_current_transform(); ALLEGRO_TRANSFORM last_projection_transform = *al_get_current_projection_transform(); int last_clip_x, last_clip_y, last_clip_w, last_clip_h; @@ -208,8 +214,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) if (pcmd->UserCallback) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplAllegro5_DrawCallback_ResetRenderState) ImGui_ImplAllegro5_SetupRenderState(draw_data); else pcmd->UserCallback(draw_list, pcmd); @@ -486,11 +491,12 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) ImGui_ImplAllegro5_SetDisplay(display); -#if ALLEGRO_HAS_CLIPBOARD ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); +#if ALLEGRO_HAS_CLIPBOARD platform_io.Platform_SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText; platform_io.Platform_GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText; #endif + platform_io.DrawCallback_ResetRenderState = ImGui_ImplAllegro5_DrawCallback_ResetRenderState; return true; } diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h index 135c4ff8..84ebf433 100644 --- a/backends/imgui_impl_allegro5.h +++ b/backends/imgui_impl_allegro5.h @@ -9,6 +9,7 @@ // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // Missing features or Issues: // [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. +// [ ] Renderer: Missing support for DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest callbacks: Allegro5 cannot enable/disable LINEAR bitmap flags after creation. // [ ] Platform: Missing gamepad support. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 11879832..9858cbe9 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -16,7 +16,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2026-01-19: DirectX11: Added 'SamplerNearest' in ImGui_ImplDX11_RenderState. Renamed 'SamplerDefault' to 'SamplerLinear'. +// 2026-04-23: DirectX10: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. Obsoleting samplers from ImGui_ImplDX10_RenderState. (#9378) +// 2026-01-19: DirectX10: Added 'SamplerNearest' in ImGui_ImplDX10_RenderState. Renamed 'SamplerDefault' to 'SamplerLinear'. // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: DirectX10: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. // 2025-05-07: DirectX10: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows). @@ -153,6 +154,11 @@ static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* device->RSSetState(bd->pRasterizerState); } +// Draw callbacks +static void ImGui_ImplDX10_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplDX10_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); bd->pd3dDevice->PSSetSamplers(0, 1, &bd->pTexSamplerLinear); } +static void ImGui_ImplDX10_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); bd->pd3dDevice->PSSetSamplers(0, 1, &bd->pTexSamplerNearest); } + // Render function void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) { @@ -177,7 +183,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D10_BUFFER_DESC desc = {}; desc.Usage = D3D10_USAGE_DYNAMIC; - desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); + desc.ByteWidth = (UINT)bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; desc.MiscFlags = 0; @@ -191,7 +197,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D10_BUFFER_DESC desc = {}; desc.Usage = D3D10_USAGE_DYNAMIC; - desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); + desc.ByteWidth = (UINT)bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D10_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; if (device->CreateBuffer(&desc, nullptr, &bd->pIB) < 0) @@ -260,8 +266,6 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplDX10_RenderState render_state; render_state.Device = bd->pd3dDevice; - render_state.SamplerLinear = bd->pTexSamplerLinear; - render_state.SamplerNearest = bd->pTexSamplerNearest; render_state.VertexConstantBuffer = bd->pVertexConstantBuffer; platform_io.Renderer_RenderState = &render_state; @@ -279,8 +283,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplDX10_DrawCallback_ResetRenderState) ImGui_ImplDX10_SetupRenderState(draw_data, device); else pcmd->UserCallback(draw_list, pcmd); @@ -613,6 +616,9 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; + platform_io.DrawCallback_ResetRenderState = ImGui_ImplDX10_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplDX10_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplDX10_DrawCallback_SetSamplerNearest; // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h index 87945d64..179756fb 100644 --- a/backends/imgui_impl_dx10.h +++ b/backends/imgui_impl_dx10.h @@ -41,9 +41,9 @@ IMGUI_IMPL_API void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex); struct ImGui_ImplDX10_RenderState { ID3D10Device* Device; - ID3D10SamplerState* SamplerLinear; - ID3D10SamplerState* SamplerNearest; ID3D10Buffer* VertexConstantBuffer; + //ID3D10SamplerState* SamplerLinear; // Use ImDrawList::AddCallback(ImGui::GetPlatform().DrawCallback_SetSamplerLinear) + //ID3D10SamplerState* SamplerNearest; // Use ImDrawList::AddCallback(ImGui::GetPlatform().DrawCallback_SetSamplerNearest) }; #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index 193127b4..6573d274 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -17,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: DirectX11: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. Obsoleting samplers from ImGui_ImplDX11_RenderState. (#9378) // 2026-01-19: DirectX11: Added 'SamplerNearest' in ImGui_ImplDX11_RenderState. Renamed 'SamplerDefault' to 'SamplerLinear'. // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: DirectX11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. @@ -85,6 +86,7 @@ struct ImGui_ImplDX11_Data ID3D11DepthStencilState* pDepthStencilState; int VertexBufferSize; int IndexBufferSize; + ImGui_ImplDX11_RenderState* RenderState; // == (ImGui_ImplDX11_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState during rendering. ImGui_ImplDX11_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } }; @@ -159,6 +161,11 @@ static void ImGui_ImplDX11_SetupRenderState(const ImDrawData* draw_data, ID3D11D device_ctx->RSSetState(bd->pRasterizerState); } +// Draw callbacks +static void ImGui_ImplDX11_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplDX11_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); bd->RenderState->DeviceContext->PSSetSamplers(0, 1, &bd->pTexSamplerLinear); } +static void ImGui_ImplDX11_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); bd->RenderState->DeviceContext->PSSetSamplers(0, 1, &bd->pTexSamplerNearest); } + // Render function void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) { @@ -183,7 +190,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D11_BUFFER_DESC desc = {}; desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); + desc.ByteWidth = (UINT)bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.MiscFlags = 0; @@ -196,7 +203,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D11_BUFFER_DESC desc = {}; desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); + desc.ByteWidth = (UINT)bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; if (bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pIB) < 0) @@ -274,10 +281,8 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ImGui_ImplDX11_RenderState render_state; render_state.Device = bd->pd3dDevice; render_state.DeviceContext = bd->pd3dDeviceContext; - render_state.SamplerLinear = bd->pTexSamplerLinear; - render_state.SamplerNearest = bd->pTexSamplerNearest; render_state.VertexConstantBuffer = bd->pVertexConstantBuffer; - platform_io.Renderer_RenderState = &render_state; + platform_io.Renderer_RenderState = bd->RenderState = &render_state; // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) @@ -293,8 +298,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplDX11_DrawCallback_ResetRenderState) ImGui_ImplDX11_SetupRenderState(draw_data, device); else pcmd->UserCallback(draw_list, pcmd); @@ -320,7 +324,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) global_idx_offset += draw_list->IdxBuffer.Size; global_vtx_offset += draw_list->VtxBuffer.Size; } - platform_io.Renderer_RenderState = nullptr; + platform_io.Renderer_RenderState = bd->RenderState = nullptr; // Restore modified DX state device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); @@ -629,6 +633,9 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + platform_io.DrawCallback_ResetRenderState = ImGui_ImplDX11_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplDX11_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplDX11_DrawCallback_SetSamplerNearest; // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h index 338e0093..56166b92 100644 --- a/backends/imgui_impl_dx11.h +++ b/backends/imgui_impl_dx11.h @@ -44,9 +44,9 @@ struct ImGui_ImplDX11_RenderState { ID3D11Device* Device; ID3D11DeviceContext* DeviceContext; - ID3D11SamplerState* SamplerLinear; - ID3D11SamplerState* SamplerNearest; ID3D11Buffer* VertexConstantBuffer; + //ID3D11SamplerState* SamplerLinear; // Use ImDrawList::AddCallback(ImGui::GetPlatform().DrawCallback_SetSamplerLinear) + //ID3D11SamplerState* SamplerNearest; // Use ImDrawList::AddCallback(ImGui::GetPlatform().DrawCallback_SetSamplerNearest) }; #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index a5420782..157384ab 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002) // 2025-09-29: DirectX12: Rework synchronization logic. (#8961) // 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965) @@ -91,6 +92,8 @@ struct ImGui_ImplDX12_Texture struct ImGui_ImplDX12_Data { ImGui_ImplDX12_InitInfo InitInfo; + ImGui_ImplDX12_RenderState* RenderState; + IDXGIFactory5* pdxgiFactory; ID3D12Device* pd3dDevice; ID3D12RootSignature* pRootSignatureLinear; @@ -142,19 +145,15 @@ struct VERTEX_CONSTANT_BUFFER_DX12 float mvp[4][4]; }; -// FIXME-WIP: Allow user to forward declare those two, for until we come up with a backend agnostic API to do this. (#9173) -void ImGui_ImplDX12_SetupSamplerLinear(ID3D12GraphicsCommandList* command_list); -void ImGui_ImplDX12_SetupSamplerNearest(ID3D12GraphicsCommandList* command_list); - // Functions -void ImGui_ImplDX12_SetupSamplerLinear(ID3D12GraphicsCommandList* command_list) +static void ImGui_ImplDX12_SetupSamplerLinear(ID3D12GraphicsCommandList* command_list) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); command_list->SetPipelineState(bd->pPipelineStateLinear); command_list->SetGraphicsRootSignature(bd->pRootSignatureLinear); } -void ImGui_ImplDX12_SetupSamplerNearest(ID3D12GraphicsCommandList* command_list) +static void ImGui_ImplDX12_SetupSamplerNearest(ID3D12GraphicsCommandList* command_list) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); command_list->SetPipelineState(bd->pPipelineStateNearest); @@ -200,7 +199,7 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic command_list->IASetVertexBuffers(0, 1, &vbv); D3D12_INDEX_BUFFER_VIEW ibv = {}; ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress(); - ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx); + ibv.SizeInBytes = (UINT)fr->IndexBufferSize * sizeof(ImDrawIdx); ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; command_list->IASetIndexBuffer(&ibv); command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); @@ -220,6 +219,11 @@ static inline void SafeRelease(T*& res) res = nullptr; } +// Draw callbacks +static void ImGui_ImplDX12_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplDX12_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); ImGui_ImplDX12_SetupSamplerLinear(bd->RenderState->CommandList); } +static void ImGui_ImplDX12_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); ImGui_ImplDX12_SetupSamplerNearest(bd->RenderState->CommandList); } + // Render function void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list) { @@ -317,7 +321,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL ImGui_ImplDX12_RenderState render_state; render_state.Device = bd->pd3dDevice; render_state.CommandList = command_list; - platform_io.Renderer_RenderState = &render_state; + platform_io.Renderer_RenderState = bd->RenderState = &render_state; // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) @@ -334,7 +338,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplDX12_DrawCallback_ResetRenderState) ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr); else pcmd->UserCallback(draw_list, pcmd); @@ -361,7 +365,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL global_idx_offset += draw_list->IdxBuffer.Size; global_vtx_offset += draw_list->VtxBuffer.Size; } - platform_io.Renderer_RenderState = nullptr; + platform_io.Renderer_RenderState = bd->RenderState = nullptr; } static void ImGui_ImplDX12_DestroyTexture(ImTextureData* tex) @@ -664,7 +668,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignatureLinear)); blob->Release(); - // Root Signature for ImDrawCallback_SetSamplerNearest + // Nearest sampler staticSampler[0].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK) return false; @@ -813,7 +817,6 @@ bool ImGui_ImplDX12_CreateDeviceObjects() return false; } - // Pipeline State for ImDrawCallback_SetSamplerNearest psoDesc.pRootSignature = bd->pRootSignatureNearest; result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineStateNearest)); @@ -927,6 +930,11 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.DrawCallback_ResetRenderState = ImGui_ImplDX12_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplDX12_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplDX12_DrawCallback_SetSamplerNearest; + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS if (init_info->SrvDescriptorAllocFn == nullptr) ImGui_ImplDX12_InitLegacySingleDescriptorMode(init_info); @@ -971,6 +979,7 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO bool ret = ImGui_ImplDX12_Init(&init_info); ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); bd->commandQueueOwned = true; + ImGuiIO& io = ImGui::GetIO(); io.BackendFlags &= ~ImGuiBackendFlags_RendererHasTextures; // Using legacy ImGui_ImplDX12_Init() call with 1 SRV descriptor we cannot support multiple textures. diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 24911fad..360398f0 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -17,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2026-03-19: Fixed issue in ImGui_ImplDX9_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: DirectX9: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. @@ -162,6 +163,11 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) } } +// Draw callbacks +static void ImGui_ImplDX9_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplDX9_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); } +static void ImGui_ImplDX9_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); } + // Render function. void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) { @@ -184,14 +190,14 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) { if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; - if (device->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0) + if (device->CreateVertexBuffer((UINT)bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0) return; } if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) { if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; - if (device->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0) + if (device->CreateIndexBuffer((UINT)bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0) return; } @@ -269,8 +275,7 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplDX9_DrawCallback_ResetRenderState) ImGui_ImplDX9_SetupRenderState(draw_data); else pcmd->UserCallback(draw_list, pcmd); @@ -325,46 +330,6 @@ static bool ImGui_ImplDX9_CheckFormatSupport(LPDIRECT3DDEVICE9 pDevice, D3DFORMA return support; } -bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) -{ - ImGuiIO& io = ImGui::GetIO(); - IMGUI_CHECKVERSION(); - IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); - - // Setup backend capabilities flags - ImGui_ImplDX9_Data* bd = IM_NEW(ImGui_ImplDX9_Data)(); - 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_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. - - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = 4096; - - bd->pd3dDevice = device; - bd->pd3dDevice->AddRef(); - bd->HasRgbaSupport = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8); - - return true; -} - -void ImGui_ImplDX9_Shutdown() -{ - ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); - IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - - ImGui_ImplDX9_InvalidateDeviceObjects(); - if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } - - io.BackendRendererName = nullptr; - io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); - platform_io.ClearRendererHandlers(); - IM_DELETE(bd); -} - // Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices) static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, const ImU32* src, int src_pitch, ImU32* dst, int dst_pitch, int w, int h) { @@ -477,6 +442,50 @@ void ImGui_ImplDX9_NewFrame() IM_UNUSED(bd); } + +bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplDX9_Data* bd = IM_NEW(ImGui_ImplDX9_Data)(); + 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_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = 4096; + platform_io.DrawCallback_ResetRenderState = ImGui_ImplDX9_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplDX9_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplDX9_DrawCallback_SetSamplerNearest; + + bd->pd3dDevice = device; + bd->pd3dDevice->AddRef(); + bd->HasRgbaSupport = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8); + + return true; +} + +void ImGui_ImplDX9_Shutdown() +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + ImGui_ImplDX9_InvalidateDeviceObjects(); + if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); + IM_DELETE(bd); +} + //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 27f5fe90..6af9f5d1 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -29,6 +29,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-21: Added a Win32-specific implementation of ImGui_ImplGlfw_GetContentScaleXXXX functions for legacy GLFW 3.2. // 2026-03-25: Mouse cursor is properly restored if changed by user app/code while using glfwSetInputMode(..., GLFW_CURSOR_DISABLED) or ImGuiConfigFlags_NoMouseCursorChange. Amend change from 2025-12-10. // 2026-02-10: Try to set IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND automatically if corresponding headers are not accessible. (#9225) // 2025-12-12: Added IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND to forcefully disable either. @@ -948,6 +949,16 @@ static void ImGui_ImplGlfw_UpdateGamepads() #undef MAP_ANALOG } +// For GFLW 3.2 + Windows: include a simplified non-monitor aware version of ImGui_ImplWin32_GetDpiScaleForMonitor(). +// This is merely a band-aid to make using GLFW 3.2 a little bit nicer, but prefer to use GLFW 3.3+ or the full correct functions from the Win32 backend. +#if !GLFW_HAS_PER_MONITOR_DPI && defined(_WIN32) && !defined(NOGDI) +static float ImGui_ImplWin32_GetLegacyDpiScale() { const HDC dc = ::GetDC(nullptr); UINT xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); ::ReleaseDC(nullptr, dc); return (float)xdpi / 96.0f; } +static void glfwGetWindowContentScale(GLFWwindow*, float* x_scale, float* y_scale) { *x_scale = *y_scale = ImGui_ImplWin32_GetLegacyDpiScale(); } +static void glfwGetMonitorContentScale(GLFWmonitor*, float* x_scale, float* y_scale) { *x_scale = *y_scale = ImGui_ImplWin32_GetLegacyDpiScale(); } +#undef GLFW_HAS_PER_MONITOR_DPI +#define GLFW_HAS_PER_MONITOR_DPI 1 +#endif + // - On Windows the process needs to be marked DPI-aware!! SDL2 doesn't do it by default. You can call ::SetProcessDPIAware() or call ImGui_ImplWin32_EnableDpiAwareness() from Win32 backend. // - Apple platforms use FramebufferScale so we always return 1.0f. // - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle. @@ -1016,7 +1027,7 @@ void ImGui_ImplGlfw_NewFrame() // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) double current_time = glfwGetTime(); if (current_time <= bd->Time) - current_time = bd->Time + 0.00001f; + current_time = bd->Time + 0.00001; io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; diff --git a/backends/imgui_impl_metal.h b/backends/imgui_impl_metal.h index 8af2c8b6..879fadcf 100644 --- a/backends/imgui_impl_metal.h +++ b/backends/imgui_impl_metal.h @@ -5,6 +5,8 @@ // [X] Renderer: User texture binding. Use 'MTLTexture' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// Missing features or Issues: +// [ ] Renderer: Missing support for DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest callbacks. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 037782bf..db2fcd3c 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -5,6 +5,8 @@ // [X] Renderer: User texture binding. Use 'MTLTexture' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// Missing features or Issues: +// [ ] Renderer: Missing support for DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest callbacks. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -16,6 +18,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState (others are not yet supported). (#9378) +// 2026-04-14: Metal: use a dedicated bufferCacheLock to avoid crashing when bufferCache is replaced by a new object while being used for @synchronize(). (#9367) // 2026-04-03: Metal: avoid redundant vertex buffer bind in SetupRenderState. (#9343) // 2026-03-19: Fixed issue in ImGui_ImplMetal_RenderDrawData() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. @@ -78,6 +82,7 @@ @property (nonatomic, strong) FramebufferDescriptor* framebufferDescriptor; // framebuffer descriptor for current frame; transient @property (nonatomic, strong) NSMutableDictionary* renderPipelineStateCache; // pipeline cache; keyed on framebuffer descriptors @property (nonatomic, strong) NSMutableArray* bufferCache; +@property (nonatomic, strong) NSObject* bufferCacheLock; @property (nonatomic, assign) double lastBufferCachePurge; - (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id)device; - (id)renderPipelineStateForFramebufferDescriptor:(FramebufferDescriptor*)descriptor device:(id)device; @@ -128,41 +133,6 @@ bool ImGui_ImplMetal_CreateDeviceObjects(MTL::Device* device) #pragma mark - Dear ImGui Metal Backend API -bool ImGui_ImplMetal_Init(id device) -{ - ImGuiIO& io = ImGui::GetIO(); - IMGUI_CHECKVERSION(); - IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); - - ImGui_ImplMetal_Data* bd = IM_NEW(ImGui_ImplMetal_Data)(); - 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_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. - - bd->SharedMetalContext = [[MetalContext alloc] init]; - bd->SharedMetalContext.device = device; - - return true; -} - -void ImGui_ImplMetal_Shutdown() -{ - ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); - IM_UNUSED(bd); - IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - - ImGui_ImplMetal_DestroyDeviceObjects(); - ImGui_ImplMetal_DestroyBackendData(); - - io.BackendRendererName = nullptr; - io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); - platform_io.ClearRendererHandlers(); -} - void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor) { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); @@ -219,6 +189,9 @@ static void ImGui_ImplMetal_SetupRenderState(ImDrawData* draw_data, id commandBuffer, id commandEncoder) { @@ -275,8 +248,7 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, id if (pcmd->UserCallback) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplMetal_DrawCallback_ResetRenderState) ImGui_ImplMetal_SetupRenderState(draw_data, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, vertexBufferOffset); else pcmd->UserCallback(draw_list, pcmd); @@ -290,8 +262,8 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, id // Clamp to viewport as setScissorRect() won't accept values that are off bounds if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } - if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } - if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; } if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; if (pcmd->ElemCount == 0) // drawIndexedPrimitives() validation doesn't accept this @@ -328,13 +300,11 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, id MetalContext* sharedMetalContext = bd->SharedMetalContext; [commandBuffer addCompletedHandler:^(id) { - dispatch_async(dispatch_get_main_queue(), ^{ - @synchronized(sharedMetalContext.bufferCache) - { - [sharedMetalContext.bufferCache addObject:vertexBuffer]; - [sharedMetalContext.bufferCache addObject:indexBuffer]; - } - }); + @synchronized(sharedMetalContext.bufferCacheLock) + { + [sharedMetalContext.bufferCache addObject:vertexBuffer]; + [sharedMetalContext.bufferCache addObject:indexBuffer]; + } }]; } @@ -431,6 +401,44 @@ void ImGui_ImplMetal_DestroyDeviceObjects() [bd->SharedMetalContext.renderPipelineStateCache removeAllObjects]; } +bool ImGui_ImplMetal_Init(id device) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + + ImGui_ImplMetal_Data* bd = IM_NEW(ImGui_ImplMetal_Data)(); + 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_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.DrawCallback_ResetRenderState = ImGui_ImplMetal_DrawCallback_ResetRenderState; + + bd->SharedMetalContext = [[MetalContext alloc] init]; + bd->SharedMetalContext.device = device; + + return true; +} + +void ImGui_ImplMetal_Shutdown() +{ + ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); + IM_UNUSED(bd); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + ImGui_ImplMetal_DestroyDeviceObjects(); + ImGui_ImplMetal_DestroyBackendData(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); +} + #pragma mark - MetalBuffer implementation @implementation MetalBuffer @@ -514,6 +522,7 @@ void ImGui_ImplMetal_DestroyDeviceObjects() { self.renderPipelineStateCache = [NSMutableDictionary dictionary]; self.bufferCache = [NSMutableArray array]; + self.bufferCacheLock = [[NSObject alloc] init]; _lastBufferCachePurge = GetMachAbsoluteTimeInSeconds(); } return self; @@ -521,9 +530,9 @@ void ImGui_ImplMetal_DestroyDeviceObjects() - (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id)device { - uint64_t now = GetMachAbsoluteTimeInSeconds(); + double now = GetMachAbsoluteTimeInSeconds(); - @synchronized(self.bufferCache) + @synchronized(self.bufferCacheLock) { // Purge old buffers that haven't been useful for a while if (now - self.lastBufferCachePurge > 1.0) diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 4fffc577..6cbaaac7 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -6,6 +6,7 @@ // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // Missing features or Issues: // [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [ ] Renderer: Use of DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest is emulated by poking to glTexParameter(), as legacy OpenGL doesn't have glBindSampler(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -25,6 +26,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: OpenGL: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-07-15: OpenGL: Set GL_UNPACK_ALIGNMENT to 1 before updating textures. (#8802) @@ -86,6 +88,9 @@ // OpenGL data struct ImGui_ImplOpenGL2_Data { + bool UseTexParameterToSetSampler; + GLuint NextSampler; + ImGui_ImplOpenGL2_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -97,37 +102,6 @@ static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData() } // Functions -bool ImGui_ImplOpenGL2_Init() -{ - ImGuiIO& io = ImGui::GetIO(); - IMGUI_CHECKVERSION(); - IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); - - // Setup backend capabilities flags - ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)(); - io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_opengl2"; - io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. - - return true; -} - -void ImGui_ImplOpenGL2_Shutdown() -{ - ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); - IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - - ImGui_ImplOpenGL2_DestroyDeviceObjects(); - - io.BackendRendererName = nullptr; - io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasTextures); - platform_io.ClearRendererHandlers(); - IM_DELETE(bd); -} - void ImGui_ImplOpenGL2_NewFrame() { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); @@ -138,6 +112,7 @@ void ImGui_ImplOpenGL2_NewFrame() static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height) { // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. + ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // In order to composite our output buffer we need to preserve alpha @@ -155,6 +130,7 @@ static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_wid glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glShadeModel(GL_SMOOTH); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + bd->NextSampler = GL_LINEAR; // If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!), // you may need to backup/reset/restore other state, e.g. for current shader using the commented lines below. @@ -179,12 +155,18 @@ static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_wid glLoadIdentity(); } +// Draw callbacks +static void ImGui_ImplOpenGL2_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } +static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } + // OpenGL2 Render function. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. // This is in order to be able to run within an OpenGL engine that doesn't do so. void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); if (fb_width == 0 || fb_height == 0) @@ -228,8 +210,7 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) if (pcmd->UserCallback) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplOpenGL2_DrawCallback_ResetRenderState) ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height); else pcmd->UserCallback(draw_list, pcmd); @@ -246,7 +227,17 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); + GLuint pcmd_texture = (GLuint)(intptr_t)pcmd->GetTexID(); + glBindTexture(GL_TEXTURE_2D, pcmd_texture); + + // Emulate sampler change (even though it is technically part of texture data) + // As a sort of hack/workaround, we only start writing using glTextParameter() if sampler is ever changed explicitly. + if (bd->UseTexParameterToSetSampler) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bd->NextSampler); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, bd->NextSampler); + } + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset); } } @@ -344,6 +335,42 @@ void ImGui_ImplOpenGL2_DestroyDeviceObjects() } } +bool ImGui_ImplOpenGL2_Init() +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_opengl2"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.DrawCallback_ResetRenderState = ImGui_ImplOpenGL2_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplOpenGL2_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplOpenGL2_DrawCallback_SetSamplerNearest; + + return true; +} + +void ImGui_ImplOpenGL2_Shutdown() +{ + ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + ImGui_ImplOpenGL2_DestroyDeviceObjects(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); + IM_DELETE(bd); +} + //----------------------------------------------------------------------------- #if defined(__clang__) diff --git a/backends/imgui_impl_opengl2.h b/backends/imgui_impl_opengl2.h index 65b0fe0b..b9f7453f 100644 --- a/backends/imgui_impl_opengl2.h +++ b/backends/imgui_impl_opengl2.h @@ -6,6 +6,7 @@ // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // Missing features or Issues: // [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [ ] Renderer: Use of DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest is emulated by poking to glTexParameter(), as legacy OpenGL doesn't have glBindSampler(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index d1894a13..c950c7a0 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: OpenGL: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. @@ -252,6 +253,10 @@ struct ImGui_ImplOpenGL3_Data bool HasBindSampler; bool HasClipOrigin; bool UseBufferSubData; + bool UseTexParameterToSetSampler; + GLuint NextSampler; // Used if !HasBindSampler && UseTexParameterToSetSampler. + GLuint TexSamplers[2]; // Used if IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER && HasBindSimpler (0=linear, 1=nearest). + ImVector TempBuffer; ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); } @@ -312,139 +317,6 @@ static void ImGui_ImplOpenGL3_ShutdownLoader() } // Functions -bool ImGui_ImplOpenGL3_Init(const char* glsl_version) -{ - ImGuiIO& io = ImGui::GetIO(); - IMGUI_CHECKVERSION(); - IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); - - // Initialize loader - if (!ImGui_ImplOpenGL3_InitLoader()) - return false; - - // Setup backend capabilities flags - ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)(); - io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_opengl3"; - - // Query for GL version (e.g. 320 for GL 3.2) - const char* gl_version_str = (const char*)glGetString(GL_VERSION); -#if defined(IMGUI_IMPL_OPENGL_ES2) - // GLES 2 - bd->GlVersion = 200; - bd->GlProfileIsES2 = true; - IM_UNUSED(gl_version_str); -#else - // Desktop or GLES 3 - GLint major = 0; - GLint minor = 0; - glGetIntegerv(GL_MAJOR_VERSION, &major); - glGetIntegerv(GL_MINOR_VERSION, &minor); - if (major == 0 && minor == 0) - sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "." - bd->GlVersion = (GLuint)(major * 100 + minor * 10); - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &bd->MaxTextureSize); - -#if defined(IMGUI_IMPL_OPENGL_ES3) - bd->GlProfileIsES3 = true; -#else - if (strncmp(gl_version_str, "OpenGL ES 3", 11) == 0) - bd->GlProfileIsES3 = true; -#endif - -#if defined(GL_CONTEXT_PROFILE_MASK) - if (!bd->GlProfileIsES3 && bd->GlVersion >= 320) - glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask); - bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; -#endif - - bd->UseBufferSubData = false; - /* - // Query vendor to enable glBufferSubData kludge -#ifdef _WIN32 - if (const char* vendor = (const char*)glGetString(GL_VENDOR)) - if (strncmp(vendor, "Intel", 5) == 0) - bd->UseBufferSubData = true; -#endif - */ -#endif - -#ifdef IMGUI_IMPL_OPENGL_DEBUG - printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2/IsEs3 = %d/%d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] -#endif - -#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET - if (bd->GlVersion >= 320) - io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. -#endif - io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. - - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = (int)bd->MaxTextureSize; - - // 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. - if (glsl_version == nullptr) - { -#if defined(IMGUI_IMPL_OPENGL_ES2) - glsl_version = "#version 100"; -#elif defined(IMGUI_IMPL_OPENGL_ES3) - glsl_version = "#version 300 es"; -#elif defined(__APPLE__) - glsl_version = "#version 150"; -#else - glsl_version = "#version 130"; -#endif - } - IM_ASSERT((int)strlen(glsl_version) + 2 < IM_COUNTOF(bd->GlslVersionString)); - strcpy(bd->GlslVersionString, glsl_version); - strcat(bd->GlslVersionString, "\n"); - - // Make an arbitrary GL call (we don't actually need the result) - // IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know! - GLint current_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); - - // Detect extensions we support -#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE - bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3); -#endif -#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - bd->HasBindSampler = (bd->GlVersion >= 330 || bd->GlProfileIsES3); -#endif - bd->HasClipOrigin = (bd->GlVersion >= 450); -#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS - GLint num_extensions = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); - for (GLint i = 0; i < num_extensions; i++) - { - const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); - if (extension != nullptr && strcmp(extension, "GL_ARB_clip_control") == 0) - bd->HasClipOrigin = true; - } -#endif - - return true; -} - -void ImGui_ImplOpenGL3_Shutdown() -{ - ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); - IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - - ImGui_ImplOpenGL3_DestroyDeviceObjects(); - - io.BackendRendererName = nullptr; - io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); - platform_io.ClearRendererHandlers(); - IM_DELETE(bd); - - ImGui_ImplOpenGL3_ShutdownLoader(); -} - void ImGui_ImplOpenGL3_NewFrame() { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); @@ -511,7 +383,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER if (bd->HasBindSampler) - glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise. + glBindSampler(0, bd->TexSamplers[0]); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise. #endif (void)vertex_array_object; @@ -530,6 +402,11 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col))); } +// Draw callbacks +static void ImGui_ImplOpenGL3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->HasBindSampler) { glBindSampler(0, bd->TexSamplers[0]); } else { bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } } +static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->HasBindSampler) { glBindSampler(0, bd->TexSamplers[1]); } else { bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } } + // OpenGL3 Render function. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. // This is in order to be able to run within an OpenGL engine that doesn't do so. @@ -644,8 +521,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplOpenGL3_DrawCallback_ResetRenderState) ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); else pcmd->UserCallback(draw_list, pcmd); @@ -663,6 +539,15 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // Bind texture, Draw GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID())); + + // Emulate sampler change (even though it is technically part of texture data) + // As a sort of hack/workaround, we only start writing using glTextParameter() if sampler is ever changed explicitly. + if (!bd->HasBindSampler && bd->UseTexParameterToSetSampler) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bd->NextSampler); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, bd->NextSampler); + } + #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET if (bd->GlVersion >= 320) GL_CALL(glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset)); @@ -1028,6 +913,20 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() glGenBuffers(1, &bd->VboHandle); glGenBuffers(1, &bd->ElementsHandle); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + if (bd->HasBindSampler) + { + glGenSamplers(2, &bd->TexSamplers[0]); + for (int sampler_n = 0; sampler_n < 2; sampler_n++) + { + GL_CALL(glSamplerParameteri(bd->TexSamplers[sampler_n], GL_TEXTURE_MIN_FILTER, (sampler_n == 0) ? GL_LINEAR : GL_NEAREST)); + GL_CALL(glSamplerParameteri(bd->TexSamplers[sampler_n], GL_TEXTURE_MAG_FILTER, (sampler_n == 0) ? GL_LINEAR : GL_NEAREST)); + GL_CALL(glSamplerParameteri(bd->TexSamplers[sampler_n], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CALL(glSamplerParameteri(bd->TexSamplers[sampler_n], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + } + } +#endif + // Restore modified GL state glBindTexture(GL_TEXTURE_2D, last_texture); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); @@ -1045,6 +944,7 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() { ImGui_ImplOpenGL3_InitLoader(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (bd->TexSamplers[0]) { glDeleteSamplers(2, &bd->TexSamplers[0]); bd->TexSamplers[0] = bd->TexSamplers[1] = 0; } if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; } if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; } if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; } @@ -1055,6 +955,142 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() ImGui_ImplOpenGL3_DestroyTexture(tex); } +bool ImGui_ImplOpenGL3_Init(const char* glsl_version) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + + // Initialize loader + if (!ImGui_ImplOpenGL3_InitLoader()) + return false; + + // Setup backend capabilities flags + ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_opengl3"; + + // Query for GL version (e.g. 320 for GL 3.2) + const char* gl_version_str = (const char*)glGetString(GL_VERSION); +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GLES 2 + bd->GlVersion = 200; + bd->GlProfileIsES2 = true; + IM_UNUSED(gl_version_str); +#else + // Desktop or GLES 3 + GLint major = 0; + GLint minor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + if (major == 0 && minor == 0) + sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "." + bd->GlVersion = (GLuint)(major * 100 + minor * 10); + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &bd->MaxTextureSize); + +#if defined(IMGUI_IMPL_OPENGL_ES3) + bd->GlProfileIsES3 = true; +#else + if (strncmp(gl_version_str, "OpenGL ES 3", 11) == 0) + bd->GlProfileIsES3 = true; +#endif + +#if defined(GL_CONTEXT_PROFILE_MASK) + if (!bd->GlProfileIsES3 && bd->GlVersion >= 320) + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask); + bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; +#endif + + bd->UseBufferSubData = false; + /* + // Query vendor to enable glBufferSubData kludge +#ifdef _WIN32 + if (const char* vendor = (const char*)glGetString(GL_VENDOR)) + if (strncmp(vendor, "Intel", 5) == 0) + bd->UseBufferSubData = true; +#endif + */ +#endif + +#ifdef IMGUI_IMPL_OPENGL_DEBUG + printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2/IsEs3 = %d/%d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] +#endif + +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET + if (bd->GlVersion >= 320) + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. +#endif + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = (int)bd->MaxTextureSize; + platform_io.DrawCallback_ResetRenderState = ImGui_ImplOpenGL3_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest; + + // 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. + if (glsl_version == nullptr) + { +#if defined(IMGUI_IMPL_OPENGL_ES2) + glsl_version = "#version 100"; +#elif defined(IMGUI_IMPL_OPENGL_ES3) + glsl_version = "#version 300 es"; +#elif defined(__APPLE__) + glsl_version = "#version 150"; +#else + glsl_version = "#version 130"; +#endif + } + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_COUNTOF(bd->GlslVersionString)); + strcpy(bd->GlslVersionString, glsl_version); + strcat(bd->GlslVersionString, "\n"); + + // Make an arbitrary GL call (we don't actually need the result) + // IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know! + GLint current_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); + + // Detect extensions we support +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE + bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3); +#endif +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + bd->HasBindSampler = (bd->GlVersion >= 330 || bd->GlProfileIsES3); +#endif + bd->HasClipOrigin = (bd->GlVersion >= 450); +#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS + GLint num_extensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + for (GLint i = 0; i < num_extensions; i++) + { + const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); + if (extension != nullptr && strcmp(extension, "GL_ARB_clip_control") == 0) + bd->HasClipOrigin = true; + } +#endif + + return true; +} + +void ImGui_ImplOpenGL3_Shutdown() +{ + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + ImGui_ImplOpenGL3_DestroyDeviceObjects(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); + IM_DELETE(bd); + + ImGui_ImplOpenGL3_ShutdownLoader(); +} + //----------------------------------------------------------------------------- #if defined(__GNUC__) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index f578977c..3f4a6ace 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-16: Made ImGui_ImplSDL2_GetContentScaleForWindow(), ImGui_ImplSDL2_GetContentScaleForDisplay() helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value and this breaks scaling border size. (#9369) // 2026-02-13: Inputs: systems other than X11 are back to starting mouse capture on mouse down (reverts 2025-02-26 change). Only X11 requires waiting for a drag by default (not ideal, but a better default for X11 users). Added ImGui_ImplSDL2_SetMouseCaptureMode() for X11 debugger users. (#3650, #6410, #9235) // 2026-01-15: Changed GetClipboardText() handler to return nullptr on error aka clipboard contents is not text. Consistent with other backends. (#9168) // 2025-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_MOUSEMOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786) @@ -748,6 +749,7 @@ float ImGui_ImplSDL2_GetContentScaleForWindow(SDL_Window* window) return ImGui_ImplSDL2_GetContentScaleForDisplay(SDL_GetWindowDisplayIndex(window)); } +// SDL_GetDisplayDPI() seems rather unreliable on Linux. float ImGui_ImplSDL2_GetContentScaleForDisplay(int display_index) { const char* sdl_driver = SDL_GetCurrentVideoDriver(); @@ -757,7 +759,11 @@ float ImGui_ImplSDL2_GetContentScaleForDisplay(int display_index) #if !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) float dpi = 0.0f; if (SDL_GetDisplayDPI(display_index, &dpi, nullptr, nullptr) == 0) + { + if (dpi < 96.0f) + dpi = 96.0f; return dpi / 96.0f; + } #endif #endif IM_UNUSED(display_index); @@ -884,7 +890,7 @@ static void ImGui_ImplSDL2_GetWindowSizeAndFramebufferScale(SDL_Window* window, if (out_size != nullptr) *out_size = ImVec2((float)w, (float)h); if (out_framebuffer_scale != nullptr) - *out_framebuffer_scale = (w > 0 && h > 0) ? ImVec2((float)display_w / w, (float)display_h / h) : ImVec2(1.0f, 1.0f); + *out_framebuffer_scale = (w > 0 && h > 0) ? ImVec2((float)display_w / (float)w, (float)display_h / (float)h) : ImVec2(1.0f, 1.0f); } void ImGui_ImplSDL2_NewFrame() @@ -902,7 +908,7 @@ void ImGui_ImplSDL2_NewFrame() Uint64 current_time = SDL_GetPerformanceCounter(); if (current_time <= bd->Time) current_time = bd->Time + 1; - io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); + io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / (double)frequency) : (float)(1.0f / 60.0f); bd->Time = current_time; if (bd->MouseLastLeaveFrame && bd->MouseLastLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index a2bd606a..bab2d231 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -686,8 +686,8 @@ static void ImGui_ImplSDL3_UpdateMouseData() int window_x, window_y; SDL_GetGlobalMouseState(&mouse_x, &mouse_y); SDL_GetWindowPosition(focused_window, &window_x, &window_y); - mouse_x -= window_x; - mouse_y -= window_y; + mouse_x -= (float)window_x; + mouse_y -= (float)window_y; io.AddMousePosEvent(mouse_x, mouse_y); } } @@ -860,7 +860,7 @@ void ImGui_ImplSDL3_NewFrame() Uint64 current_time = SDL_GetPerformanceCounter(); if (current_time <= bd->Time) current_time = bd->Time + 1; - io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); + io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / (double)frequency) : (float)(1.0f / 60.0f); bd->Time = current_time; if (bd->MousePendingLeaveFrame && bd->MousePendingLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0) diff --git a/backends/imgui_impl_sdlgpu3.cpp b/backends/imgui_impl_sdlgpu3.cpp index 54a527a3..8f0408c8 100644 --- a/backends/imgui_impl_sdlgpu3.cpp +++ b/backends/imgui_impl_sdlgpu3.cpp @@ -22,6 +22,7 @@ // Calling the function is MANDATORY, otherwise the ImGui will not upload neither the vertex nor the index buffer for the GPU. See imgui_impl_sdlgpu3.cpp for more info. // CHANGELOG +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. Obsoleting samplers from ImGui_ImplSDLGPU3_RenderState. (#9378) // 2026-03-19: Fixed issue in ImGui_ImplSDLGPU3_DestroyTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) // 2026-02-25: Removed unnecessary call to SDL_WaitForGPUIdle when releasing vertex/index buffers. (#9262) // 2025-11-26: macOS version can use MSL shaders in order to support macOS 10.14+ (vs Metallib shaders requiring macOS 14+). Requires calling SDL_CreateGPUDevice() with SDL_GPU_SHADERFORMAT_MSL. @@ -59,6 +60,10 @@ struct ImGui_ImplSDLGPU3_Data { ImGui_ImplSDLGPU3_InitInfo InitInfo; + // Render state + ImGui_ImplSDLGPU3_RenderState* RenderState = nullptr; // == ImGui::GetPlatformIO().Renderer_RenderState during rendering. + SDL_GPUSampler* CurrentSampler = nullptr; + // Graphics pipeline & shaders SDL_GPUShader* VertexShader = nullptr; SDL_GPUShader* FragmentShader = nullptr; @@ -87,10 +92,10 @@ static ImGui_ImplSDLGPU3_Data* ImGui_ImplSDLGPU3_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplSDLGPU3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } -static void ImGui_ImplSDLGPU3_SetupRenderState(ImDrawData* draw_data, ImGui_ImplSDLGPU3_RenderState* render_state, SDL_GPUGraphicsPipeline* pipeline, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, ImGui_ImplSDLGPU3_FrameData* fd, uint32_t fb_width, uint32_t fb_height) +static void ImGui_ImplSDLGPU3_SetupRenderState(ImDrawData* draw_data, SDL_GPUGraphicsPipeline* pipeline, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, ImGui_ImplSDLGPU3_FrameData* fd, uint32_t fb_width, uint32_t fb_height) { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - render_state->SamplerCurrent = bd->TexSamplerLinear; + bd->CurrentSampler = bd->TexSamplerLinear; // Bind graphics pipeline SDL_BindGPUGraphicsPipeline(render_pass, pipeline); @@ -174,8 +179,8 @@ void ImGui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuff ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; ImGui_ImplSDLGPU3_FrameData* fd = &bd->MainWindowFrameData; - uint32_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); - uint32_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); + uint32_t vertex_size = (uint32_t)draw_data->TotalVtxCount * sizeof(ImDrawVert); + uint32_t index_size = (uint32_t)draw_data->TotalIdxCount * sizeof(ImDrawIdx); if (fd->VertexBuffer == nullptr || fd->VertexBufferSize < vertex_size) CreateOrResizeBuffers(&fd->VertexBuffer, &fd->VertexTransferBuffer, &fd->VertexBufferSize, vertex_size, SDL_GPU_BUFFERUSAGE_VERTEX); if (fd->IndexBuffer == nullptr || fd->IndexBufferSize < index_size) @@ -216,6 +221,11 @@ void ImGui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuff SDL_EndGPUCopyPass(copy_pass); } +// Draw callbacks +static void ImGui_ImplSDLGPU3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplSDLGPU3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); bd->CurrentSampler = bd->TexSamplerLinear; } +static void ImGui_ImplSDLGPU3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); bd->CurrentSampler = bd->TexSamplerNearest; } + void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, SDL_GPUGraphicsPipeline* pipeline) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) @@ -238,11 +248,9 @@ void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffe ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplSDLGPU3_RenderState render_state; render_state.Device = bd->InitInfo.Device; - render_state.SamplerLinear = render_state.SamplerCurrent = bd->TexSamplerLinear; - render_state.SamplerNearest = bd->TexSamplerNearest; - platform_io.Renderer_RenderState = &render_state; + platform_io.Renderer_RenderState = bd->RenderState = &render_state; - ImGui_ImplSDLGPU3_SetupRenderState(draw_data, &render_state, pipeline, command_buffer, render_pass, fd, fb_width, fb_height); + ImGui_ImplSDLGPU3_SetupRenderState(draw_data, pipeline, command_buffer, render_pass, fd, fb_width, fb_height); // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) @@ -256,9 +264,8 @@ void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffe if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplSDLGPU3_SetupRenderState(draw_data, &render_state, pipeline, command_buffer, render_pass, fd, fb_width, fb_height); + if (pcmd->UserCallback == ImGui_ImplSDLGPU3_DrawCallback_ResetRenderState) + ImGui_ImplSDLGPU3_SetupRenderState(draw_data, pipeline, command_buffer, render_pass, fd, fb_width, fb_height); else pcmd->UserCallback(draw_list, pcmd); } @@ -271,8 +278,8 @@ void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffe // Clamp to viewport as SDL_SetGPUScissor() won't accept values that are off bounds if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } - if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } - if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; } if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; @@ -287,7 +294,7 @@ void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffe // Bind DescriptorSet with font or user texture SDL_GPUTextureSamplerBinding texture_sampler_binding; texture_sampler_binding.texture = (SDL_GPUTexture*)(intptr_t)pcmd->GetTexID(); - texture_sampler_binding.sampler = render_state.SamplerCurrent; + texture_sampler_binding.sampler = bd->CurrentSampler; SDL_BindGPUFragmentSamplers(render_pass, 0, &texture_sampler_binding, 1); // Draw @@ -306,6 +313,8 @@ void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffe // We perform a call to SDL_SetGPUScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644) SDL_Rect scissor_rect { 0, 0, fb_width, fb_height }; SDL_SetGPUScissor(render_pass, &scissor_rect); + + platform_io.Renderer_RenderState = bd->RenderState = nullptr; } static void ImGui_ImplSDLGPU3_DestroyTexture(ImTextureData* tex) @@ -656,6 +665,11 @@ bool ImGui_ImplSDLGPU3_Init(ImGui_ImplSDLGPU3_InitInfo* info) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.DrawCallback_ResetRenderState = ImGui_ImplSDLGPU3_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplSDLGPU3_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplSDLGPU3_DrawCallback_SetSamplerNearest; + IM_ASSERT(info->Device != nullptr); IM_ASSERT(info->ColorTargetFormat != SDL_GPU_TEXTUREFORMAT_INVALID); diff --git a/backends/imgui_impl_sdlgpu3.h b/backends/imgui_impl_sdlgpu3.h index 1c73dfd7..bc21d1c7 100644 --- a/backends/imgui_impl_sdlgpu3.h +++ b/backends/imgui_impl_sdlgpu3.h @@ -57,9 +57,6 @@ IMGUI_IMPL_API void ImGui_ImplSDLGPU3_UpdateTexture(ImTextureData* tex); struct ImGui_ImplSDLGPU3_RenderState { SDL_GPUDevice* Device; - SDL_GPUSampler* SamplerLinear; // Bilinear filtering sampler - SDL_GPUSampler* SamplerNearest; // Nearest/point filtering sampler - SDL_GPUSampler* SamplerCurrent; // Current sampler (may be changed by callback) }; #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp index dc86f6c2..d9f329bd 100644 --- a/backends/imgui_impl_sdlrenderer2.cpp +++ b/backends/imgui_impl_sdlrenderer2.cpp @@ -14,8 +14,10 @@ // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// Missing features or Issues: +// [ ] Renderer: Missing support for DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest callbacks: SDLRenderer2 does not support changing SDL_SCALE_MODE while rendering. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq @@ -24,6 +26,8 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState (others cannot be supported). (#9378) // 2026-03-12: Fixed invalid assert in ImGui_ImplSDLRenderer2_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer2_CreateFontsTexture() and ImGui_ImplSDLRenderer2_DestroyFontsTexture(). @@ -72,41 +76,6 @@ static ImGui_ImplSDLRenderer2_Data* ImGui_ImplSDLRenderer2_GetBackendData() } // Functions -bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer) -{ - ImGuiIO& io = ImGui::GetIO(); - IMGUI_CHECKVERSION(); - IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); - IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!"); - - // Setup backend capabilities flags - ImGui_ImplSDLRenderer2_Data* bd = IM_NEW(ImGui_ImplSDLRenderer2_Data)(); - io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_sdlrenderer2"; - io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. - - bd->Renderer = renderer; - - return true; -} - -void ImGui_ImplSDLRenderer2_Shutdown() -{ - ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData(); - IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - - ImGui_ImplSDLRenderer2_DestroyDeviceObjects(); - - io.BackendRendererName = nullptr; - io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); - platform_io.ClearRendererHandlers(); - IM_DELETE(bd); -} - static void ImGui_ImplSDLRenderer2_SetupRenderState(SDL_Renderer* renderer) { // Clear out any viewports and cliprect set by the user @@ -122,6 +91,9 @@ void ImGui_ImplSDLRenderer2_NewFrame() IM_UNUSED(bd); } +// Draw callbacks +static void ImGui_ImplSDLRenderer2_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. + void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) { // If there's a scale factor set by the user, use that instead @@ -184,8 +156,7 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* if (pcmd->UserCallback) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplSDLRenderer2_DrawCallback_ResetRenderState) ImGui_ImplSDLRenderer2_SetupRenderState(renderer); else pcmd->UserCallback(draw_list, pcmd); @@ -293,6 +264,44 @@ void ImGui_ImplSDLRenderer2_DestroyDeviceObjects() } } +bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!"); + + // Setup backend capabilities flags + ImGui_ImplSDLRenderer2_Data* bd = IM_NEW(ImGui_ImplSDLRenderer2_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_sdlrenderer2"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.DrawCallback_ResetRenderState = ImGui_ImplSDLRenderer2_DrawCallback_ResetRenderState; + + bd->Renderer = renderer; + + return true; +} + +void ImGui_ImplSDLRenderer2_Shutdown() +{ + ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + ImGui_ImplSDLRenderer2_DestroyDeviceObjects(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); + IM_DELETE(bd); +} + //----------------------------------------------------------------------------- #if defined(__clang__) diff --git a/backends/imgui_impl_sdlrenderer2.h b/backends/imgui_impl_sdlrenderer2.h index 860e9550..f33bc160 100644 --- a/backends/imgui_impl_sdlrenderer2.h +++ b/backends/imgui_impl_sdlrenderer2.h @@ -14,6 +14,8 @@ // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// Missing features or Issues: +// [ ] Renderer: Missing support for DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest callbacks: SDLRenderer2 does not support changing SDL_SCALE_MODE while rendering. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index be6c97d8..bf025040 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -15,7 +15,7 @@ // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq @@ -24,6 +24,8 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2026-03-12: Fixed invalid assert in ImGui_ImplSDLRenderer3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer3_CreateFontsTexture() and ImGui_ImplSDLRenderer3_DestroyFontsTexture(). @@ -59,6 +61,9 @@ struct ImGui_ImplSDLRenderer3_Data SDL_Renderer* Renderer; // Main viewport's renderer ImVector ColorBuffer; + // Render State + SDL_ScaleMode CurrentScaleMode; + ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -70,41 +75,6 @@ static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData() } // Functions -bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer) -{ - ImGuiIO& io = ImGui::GetIO(); - IMGUI_CHECKVERSION(); - IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); - IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!"); - - // Setup backend capabilities flags - ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)(); - io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_sdlrenderer3"; - io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. - - bd->Renderer = renderer; - - return true; -} - -void ImGui_ImplSDLRenderer3_Shutdown() -{ - ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); - IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - - ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); - - io.BackendRendererName = nullptr; - io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); - platform_io.ClearRendererHandlers(); - IM_DELETE(bd); -} - static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer) { // Clear out any viewports and cliprect set by the user @@ -138,6 +108,11 @@ static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVectorCurrentScaleMode = SDL_SCALEMODE_LINEAR; } +static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); bd->CurrentScaleMode = SDL_SCALEMODE_NEAREST; } + void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); @@ -204,8 +179,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* if (pcmd->UserCallback) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplSDLRenderer3_DrawCallback_ResetRenderState) ImGui_ImplSDLRenderer3_SetupRenderState(renderer); else pcmd->UserCallback(draw_list, pcmd); @@ -231,6 +205,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* // Bind texture, Draw SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + SDL_SetTextureScaleMode(tex, bd->CurrentScaleMode); SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex, xy, (int)sizeof(ImDrawVert), color, (int)sizeof(ImDrawVert), @@ -309,6 +284,46 @@ void ImGui_ImplSDLRenderer3_DestroyDeviceObjects() } } +bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!"); + + // Setup backend capabilities flags + ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_sdlrenderer3"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.DrawCallback_ResetRenderState = ImGui_ImplSDLRenderer3_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest; + + bd->Renderer = renderer; + + return true; +} + +void ImGui_ImplSDLRenderer3_Shutdown() +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); + IM_DELETE(bd); +} + //----------------------------------------------------------------------------- #if defined(__clang__) diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index f5f53b9d..c01a16d4 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -15,7 +15,7 @@ // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index dc49ac1f..3baf3b75 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -2,7 +2,7 @@ // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. +// [!] Renderer: User texture binding. Use a VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. @@ -27,6 +27,16 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) +// 2026-04-22: *BREAKING CHANGE* redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. This change allows us to facilitate changing samplers, in line with other backends. +// - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. +// - Before: ImGui_ImplVulkan_AddTexture(VkSampler, VkImageView, VkImageLayout) +// - After: ImGui_ImplVulkan_AddTexture(VkImageView, VkImageLayout) +// - Kept inline redirection function that ignores the sampler. +// - When creating your own descriptor pool (instead of letting backend creates its own): +// - Before: need at least IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER. +// - After: need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE. +// + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. // 2026-03-11: Vulkan: Added ImGui_ImplVulkan_PipelineInfo::ExtraDynamicStates[] to allow specifying extra dynamic states to add when creating the VkPipeline. (#9211) // 2025-09-26: [Helpers] *BREAKING CHANGE*: Vulkan: Helper ImGui_ImplVulkanH_DestroyWindow() does not call vkDestroySurfaceKHR(): as surface is created by caller of ImGui_ImplVulkanH_CreateOrResizeWindow(), it is more consistent that we don't destroy it. (#9163) // 2026-01-05: [Helpers] *BREAKING CHANGE*: Vulkan: Helper for creating render pass uses ImGui_ImplVulkanH_Window::AttachmentDesc to create render pass. Removed ClearEnabled. (#9152) @@ -266,10 +276,12 @@ struct ImGui_ImplVulkan_Texture struct ImGui_ImplVulkan_Data { ImGui_ImplVulkan_InitInfo VulkanInitInfo; + ImGui_ImplVulkan_RenderState* RenderState; // == ImGui::GetPlatformIO().Renderer_RenderState during rendering. VkDeviceSize BufferMemoryAlignment; VkDeviceSize NonCoherentAtomSize; VkPipelineCreateFlags PipelineCreateFlags; - VkDescriptorSetLayout DescriptorSetLayout; + VkDescriptorSetLayout DescriptorSetLayoutTexture; + VkDescriptorSetLayout DescriptorSetLayoutSampler; VkPipelineLayout PipelineLayout; VkPipeline Pipeline; // pipeline for main render pass (created by app) VkShaderModule ShaderModuleVert; @@ -278,7 +290,10 @@ struct ImGui_ImplVulkan_Data ImVector PipelineRenderingCreateInfoColorAttachmentFormats; // Deep copy of format array // Texture management - VkSampler TexSamplerLinear; + VkSampler SamplerLinear; + VkSampler SamplerNearest; + VkDescriptorSet SamplerLinearDS; + VkDescriptorSet SamplerNearestDS; VkCommandPool TexCommandPool; VkCommandBuffer TexCommandBuffer; @@ -318,7 +333,7 @@ void main() */ static uint32_t __glsl_shader_vert_spv[] = { - 0x07230203,0x00010000,0x00080001,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x07230203,0x00010000,0x0008000b,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, 0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015, 0x0000001b,0x0000001c,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, @@ -331,10 +346,10 @@ static uint32_t __glsl_shader_vert_spv[] = 0x00050006,0x0000001e,0x00000000,0x61635375,0x0000656c,0x00060006,0x0000001e,0x00000001, 0x61725475,0x616c736e,0x00006574,0x00030005,0x00000020,0x00006370,0x00040047,0x0000000b, 0x0000001e,0x00000000,0x00040047,0x0000000f,0x0000001e,0x00000002,0x00040047,0x00000015, - 0x0000001e,0x00000001,0x00050048,0x00000019,0x00000000,0x0000000b,0x00000000,0x00030047, - 0x00000019,0x00000002,0x00040047,0x0000001c,0x0000001e,0x00000000,0x00050048,0x0000001e, - 0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,0x00000001,0x00000023,0x00000008, - 0x00030047,0x0000001e,0x00000002,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002, + 0x0000001e,0x00000001,0x00030047,0x00000019,0x00000002,0x00050048,0x00000019,0x00000000, + 0x0000000b,0x00000000,0x00040047,0x0000001c,0x0000001e,0x00000000,0x00030047,0x0000001e, + 0x00000002,0x00050048,0x0000001e,0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e, + 0x00000001,0x00000023,0x00000008,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002, 0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017, 0x00000008,0x00000006,0x00000002,0x0004001e,0x00000009,0x00000007,0x00000008,0x00040020, 0x0000000a,0x00000003,0x00000009,0x0004003b,0x0000000a,0x0000000b,0x00000003,0x00040015, @@ -366,39 +381,44 @@ static uint32_t __glsl_shader_vert_spv[] = /* #version 450 core layout(location = 0) out vec4 fColor; -layout(set=0, binding=0) uniform sampler2D sTexture; +layout(set=0, binding=0) uniform texture2D _Texture; +layout(set=1, binding=0) uniform sampler _Sampler; layout(location = 0) in struct { vec4 Color; vec2 UV; } In; void main() { - fColor = In.Color * texture(sTexture, In.UV.st); + fColor = In.Color * texture(sampler2D(_Texture, _Sampler), In.UV.st); } */ static uint32_t __glsl_shader_frag_spv[] = { - 0x07230203,0x00010000,0x00080001,0x0000001e,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x07230203,0x00010000,0x0008000b,0x00000023,0x00000000,0x00020011,0x00000001,0x0006000b, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010, 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, 0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000, 0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001, - 0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000016,0x78655473,0x65727574, - 0x00000000,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d,0x0000001e, - 0x00000000,0x00040047,0x00000016,0x00000022,0x00000000,0x00040047,0x00000016,0x00000021, - 0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006, - 0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000003, - 0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040017,0x0000000a,0x00000006, - 0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,0x0000000c,0x00000001, - 0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,0x0000000e,0x00000020, - 0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,0x00000010,0x00000001, - 0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000, - 0x00000001,0x00000000,0x0003001b,0x00000014,0x00000013,0x00040020,0x00000015,0x00000000, - 0x00000014,0x0004003b,0x00000015,0x00000016,0x00000000,0x0004002b,0x0000000e,0x00000018, - 0x00000001,0x00040020,0x00000019,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004, - 0x00000000,0x00000003,0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d, - 0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000014,0x00000017, - 0x00000016,0x00050041,0x00000019,0x0000001a,0x0000000d,0x00000018,0x0004003d,0x0000000a, - 0x0000001b,0x0000001a,0x00050057,0x00000007,0x0000001c,0x00000017,0x0000001b,0x00050085, - 0x00000007,0x0000001d,0x00000012,0x0000001c,0x0003003e,0x00000009,0x0000001d,0x000100fd, + 0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000015,0x7865545f,0x65727574, + 0x00000000,0x00050005,0x00000019,0x6d61535f,0x72656c70,0x00000000,0x00040047,0x00000009, + 0x0000001e,0x00000000,0x00040047,0x0000000d,0x0000001e,0x00000000,0x00040047,0x00000015, + 0x00000021,0x00000000,0x00040047,0x00000015,0x00000022,0x00000000,0x00040047,0x00000019, + 0x00000021,0x00000000,0x00040047,0x00000019,0x00000022,0x00000001,0x00020013,0x00000002, + 0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007, + 0x00000006,0x00000004,0x00040020,0x00000008,0x00000003,0x00000007,0x0004003b,0x00000008, + 0x00000009,0x00000003,0x00040017,0x0000000a,0x00000006,0x00000002,0x0004001e,0x0000000b, + 0x00000007,0x0000000a,0x00040020,0x0000000c,0x00000001,0x0000000b,0x0004003b,0x0000000c, + 0x0000000d,0x00000001,0x00040015,0x0000000e,0x00000020,0x00000001,0x0004002b,0x0000000e, + 0x0000000f,0x00000000,0x00040020,0x00000010,0x00000001,0x00000007,0x00090019,0x00000013, + 0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x00040020, + 0x00000014,0x00000000,0x00000013,0x0004003b,0x00000014,0x00000015,0x00000000,0x0002001a, + 0x00000017,0x00040020,0x00000018,0x00000000,0x00000017,0x0004003b,0x00000018,0x00000019, + 0x00000000,0x0003001b,0x0000001b,0x00000013,0x0004002b,0x0000000e,0x0000001d,0x00000001, + 0x00040020,0x0000001e,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004,0x00000000, + 0x00000003,0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d,0x0000000f, + 0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000013,0x00000016,0x00000015, + 0x0004003d,0x00000017,0x0000001a,0x00000019,0x00050056,0x0000001b,0x0000001c,0x00000016, + 0x0000001a,0x00050041,0x0000001e,0x0000001f,0x0000000d,0x0000001d,0x0004003d,0x0000000a, + 0x00000020,0x0000001f,0x00050057,0x00000007,0x00000021,0x0000001c,0x00000020,0x00050085, + 0x00000007,0x00000022,0x00000012,0x00000021,0x0003003e,0x00000009,0x00000022,0x000100fd, 0x00010038 }; @@ -481,9 +501,7 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); // Bind pipeline: - { - vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - } + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); // Bind Vertex And Index Buffer: if (draw_data->TotalVtxCount > 0) @@ -495,29 +513,33 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline } // Setup viewport: - { - VkViewport viewport; - viewport.x = 0; - viewport.y = 0; - viewport.width = (float)fb_width; - viewport.height = (float)fb_height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(command_buffer, 0, 1, &viewport); - } + VkViewport viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = (float)fb_width; + viewport.height = (float)fb_height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(command_buffer, 0, 1, &viewport); // Setup scale and translation: // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - { - float constants[4]; - constants[0] = 2.0f / draw_data->DisplaySize.x; // Scale - constants[1] = 2.0f / draw_data->DisplaySize.y; - constants[2] = -1.0f - draw_data->DisplayPos.x * constants[0]; // Translate - constants[3] = -1.0f - draw_data->DisplayPos.y * constants[1]; - vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 4, constants); - } + float constants[4]; + constants[0] = 2.0f / draw_data->DisplaySize.x; // Scale + constants[1] = 2.0f / draw_data->DisplaySize.y; + constants[2] = -1.0f - draw_data->DisplayPos.x * constants[0]; // Translate + constants[3] = -1.0f - draw_data->DisplayPos.y * constants[1]; + vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 4, constants); + + // Setup sampler + vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr); } +// Draw callbacks +static void ImGui_ImplVulkan_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr); } +static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr); } + // Render function void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline) { @@ -589,16 +611,16 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm vkUnmapMemory(v->Device, rb->IndexBufferMemory); } - // Setup desired Vulkan state - ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); - // Setup render state structure (for callbacks and custom texture bindings) ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplVulkan_RenderState render_state; render_state.CommandBuffer = command_buffer; render_state.Pipeline = pipeline; render_state.PipelineLayout = bd->PipelineLayout; - platform_io.Renderer_RenderState = &render_state; + platform_io.Renderer_RenderState = bd->RenderState = &render_state; + + // Setup desired Vulkan state + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports @@ -606,7 +628,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) - VkDescriptorSet last_desc_set = VK_NULL_HANDLE; + VkDescriptorSet last_image_view = VK_NULL_HANDLE; int global_vtx_offset = 0; int global_idx_offset = 0; for (const ImDrawList* draw_list : draw_data->CmdLists) @@ -617,12 +639,13 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplVulkan_DrawCallback_ResetRenderState) + { ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + last_image_view = VK_NULL_HANDLE; + } else pcmd->UserCallback(draw_list, pcmd); - last_desc_set = VK_NULL_HANDLE; } else { @@ -633,8 +656,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } - if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } - if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; } if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; @@ -646,11 +669,11 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y); vkCmdSetScissor(command_buffer, 0, 1, &scissor); - // Bind DescriptorSet with font or user texture - VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID(); - if (desc_set != last_desc_set) - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr); - last_desc_set = desc_set; + // Bind DescriptorSets for image view (font or user texture) and samplers + VkDescriptorSet image_view = (VkDescriptorSet)pcmd->GetTexID(); + if (image_view != last_image_view) + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &image_view, 0, nullptr); + last_image_view = image_view; // Draw vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); @@ -659,7 +682,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm global_idx_offset += draw_list->IdxBuffer.Size; global_vtx_offset += draw_list->VtxBuffer.Size; } - platform_io.Renderer_RenderState = nullptr; + platform_io.Renderer_RenderState = bd->RenderState = nullptr; // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called. // Our last values will leak into user/application rendering IF: @@ -753,7 +776,7 @@ void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex) } // Create the Descriptor Set - backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(bd->TexSamplerLinear, backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // Store identifiers tex->SetTexID((ImTextureID)backend_tex->DescriptorSet); @@ -1048,15 +1071,83 @@ static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAlloc return pipeline; } +static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDescriptorSetLayout* p_layout, VkDescriptorType descriptor_type) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + VkDescriptorSetLayoutBinding binding[1] = {}; + binding[0].descriptorType = descriptor_type; + binding[0].descriptorCount = 1; + binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + VkDescriptorSetLayoutCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + info.bindingCount = 1; + info.pBindings = binding; + VkResult err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, p_layout); + check_vk_result(err); +} + +static VkDescriptorSet ImGui_ImplVulkan_CreateSamplerDS(VkSampler sampler) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + VkDescriptorSet descriptor_set; + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &bd->DescriptorSetLayoutSampler; + VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); + check_vk_result(err); + + VkDescriptorImageInfo desc_image = {}; + desc_image.sampler = sampler; + VkWriteDescriptorSet sampler_write_desc = {}; + sampler_write_desc.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + sampler_write_desc.dstSet = descriptor_set; + sampler_write_desc.descriptorCount = 1; + sampler_write_desc.dstBinding = 0; + sampler_write_desc.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + sampler_write_desc.pImageInfo = &desc_image; + vkUpdateDescriptorSets(v->Device, 1, &sampler_write_desc, 0, nullptr); + return descriptor_set; +} + bool ImGui_ImplVulkan_CreateDeviceObjects() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err; - if (!bd->TexSamplerLinear) + if (!bd->DescriptorSetLayoutTexture) + ImGui_ImplVulkan_CreateDescriptorSetLayout(&bd->DescriptorSetLayoutTexture, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + if (!bd->DescriptorSetLayoutSampler) + ImGui_ImplVulkan_CreateDescriptorSetLayout(&bd->DescriptorSetLayoutSampler, VK_DESCRIPTOR_TYPE_SAMPLER); + + if (v->DescriptorPoolSize != 0) + { + IM_ASSERT(v->DescriptorPoolSize >= IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE); + VkDescriptorPoolSize pool_sizes[] = + { + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, v->DescriptorPoolSize }, + { VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE }, + }; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = v->DescriptorPoolSize + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE; + pool_info.poolSizeCount = (uint32_t)IM_COUNTOF(pool_sizes); + pool_info.pPoolSizes = pool_sizes; + err = vkCreateDescriptorPool(v->Device, &pool_info, v->Allocator, &bd->DescriptorPool); + check_vk_result(err); + } + + // Create samplers + // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. + if (!bd->SamplerLinear || !bd->SamplerNearest) { - // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. VkSamplerCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; info.magFilter = VK_FILTER_LINEAR; @@ -1068,37 +1159,16 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() info.minLod = -1000; info.maxLod = 1000; info.maxAnisotropy = 1.0f; - err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->TexSamplerLinear); + err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->SamplerLinear); check_vk_result(err); - } + bd->SamplerLinearDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerLinear); - if (!bd->DescriptorSetLayout) - { - VkDescriptorSetLayoutBinding binding[1] = {}; - binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - binding[0].descriptorCount = 1; - binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - VkDescriptorSetLayoutCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - info.bindingCount = 1; - info.pBindings = binding; - err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, &bd->DescriptorSetLayout); - check_vk_result(err); - } - - if (v->DescriptorPoolSize != 0) - { - IM_ASSERT(v->DescriptorPoolSize >= IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE); - VkDescriptorPoolSize pool_size = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, v->DescriptorPoolSize }; - VkDescriptorPoolCreateInfo pool_info = {}; - pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - pool_info.maxSets = v->DescriptorPoolSize; - pool_info.poolSizeCount = 1; - pool_info.pPoolSizes = &pool_size; - - err = vkCreateDescriptorPool(v->Device, &pool_info, v->Allocator, &bd->DescriptorPool); + info.magFilter = VK_FILTER_NEAREST; + info.minFilter = VK_FILTER_NEAREST; + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->SamplerNearest); check_vk_result(err); + bd->SamplerNearestDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerNearest); } if (!bd->PipelineLayout) @@ -1108,10 +1178,10 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; push_constants[0].offset = sizeof(float) * 0; push_constants[0].size = sizeof(float) * 4; - VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout }; + VkDescriptorSetLayout set_layout[2] = { bd->DescriptorSetLayoutTexture, bd->DescriptorSetLayoutSampler }; VkPipelineLayoutCreateInfo layout_info = {}; layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layout_info.setLayoutCount = 1; + layout_info.setLayoutCount = 2; layout_info.pSetLayouts = set_layout; layout_info.pushConstantRangeCount = 1; layout_info.pPushConstantRanges = push_constants; @@ -1191,10 +1261,12 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() if (bd->TexCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->TexCommandPool, 1, &bd->TexCommandBuffer); bd->TexCommandBuffer = VK_NULL_HANDLE; } if (bd->TexCommandPool) { vkDestroyCommandPool(v->Device, bd->TexCommandPool, v->Allocator); bd->TexCommandPool = VK_NULL_HANDLE; } - if (bd->TexSamplerLinear) { vkDestroySampler(v->Device, bd->TexSamplerLinear, v->Allocator); bd->TexSamplerLinear = VK_NULL_HANDLE; } + if (bd->SamplerLinear) { vkDestroySampler(v->Device, bd->SamplerLinear, v->Allocator); bd->SamplerLinear = VK_NULL_HANDLE; } + if (bd->SamplerNearest) { vkDestroySampler(v->Device, bd->SamplerNearest, v->Allocator); bd->SamplerNearest = VK_NULL_HANDLE; } if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } - if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } + if (bd->DescriptorSetLayoutTexture) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayoutTexture, v->Allocator); bd->DescriptorSetLayoutTexture = VK_NULL_HANDLE; } + if (bd->DescriptorSetLayoutSampler) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayoutSampler, v->Allocator); bd->DescriptorSetLayoutSampler = VK_NULL_HANDLE; } if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } if (bd->DescriptorPool) { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; } @@ -1290,6 +1362,11 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.DrawCallback_ResetRenderState = ImGui_ImplVulkan_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplVulkan_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplVulkan_DrawCallback_SetSamplerNearest; + // Sanity checks IM_ASSERT(info->Instance != VK_NULL_HANDLE); IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); @@ -1355,7 +1432,7 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) // Register a texture by creating a descriptor // FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. -VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) +VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkImageView image_view, VkImageLayout image_layout) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; @@ -1368,7 +1445,7 @@ VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = pool; alloc_info.descriptorSetCount = 1; - alloc_info.pSetLayouts = &bd->DescriptorSetLayout; + alloc_info.pSetLayouts = &bd->DescriptorSetLayoutTexture; VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); check_vk_result(err); } @@ -1377,20 +1454,27 @@ VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image if (descriptor_set != VK_NULL_HANDLE) { VkDescriptorImageInfo desc_image[1] = {}; - desc_image[0].sampler = sampler; desc_image[0].imageView = image_view; desc_image[0].imageLayout = image_layout; VkWriteDescriptorSet write_desc[1] = {}; write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_desc[0].dstSet = descriptor_set; write_desc[0].descriptorCount = 1; - write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; write_desc[0].pImageInfo = desc_image; vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, nullptr); } return descriptor_set; } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) +{ + IM_UNUSED(sampler); + return ImGui_ImplVulkan_AddTexture(image_view, image_layout); +} +#endif + void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index c3fef510..603f3947 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -2,7 +2,7 @@ // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. +// [!] Renderer: User texture binding. Use a VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. @@ -78,7 +78,8 @@ #endif // Backend uses a small number of descriptors per font atlas + as many as additional calls done to ImGui_ImplVulkan_AddTexture(). -#define IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE (8) // Minimum per atlas +#define IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE (8) // Minimum per atlas +#define IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE (2) // Minimum for linear + nearest // Specify settings to create pipeline and swapchain struct ImGui_ImplVulkan_PipelineInfo @@ -109,7 +110,7 @@ struct ImGui_ImplVulkan_InitInfo uint32_t QueueFamily; VkQueue Queue; VkDescriptorPool DescriptorPool; // See requirements in note above; ignored if using DescriptorPoolSize > 0 - uint32_t DescriptorPoolSize; // Optional: set to create internal descriptor pool automatically instead of using DescriptorPool. + uint32_t DescriptorPoolSize; // Optional: set to create internal ImageView descriptor pool automatically instead of using DescriptorPool. uint32_t MinImageCount; // >= 2 uint32_t ImageCount; // >= MinImageCount VkPipelineCache PipelineCache; // Optional @@ -152,12 +153,14 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ // (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex); -// Register a texture (VkDescriptorSet == ImTextureID) -// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem -// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. -IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); +// Register a texture (VkDescriptorSet for a VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE == ImTextureID) +IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkImageView image_view, VkImageLayout image_layout); IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set); +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); // Ignore VkSampler +#endif + // Optional: load Vulkan functions with a custom function loader // This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr); @@ -254,7 +257,7 @@ struct ImGui_ImplVulkanH_Window memset((void*)this, 0, sizeof(*this)); // Parameters to create SwapChain - PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this. + PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; // Ensure we get an error if user doesn't set this. // Parameters to create RenderPass AttachmentDesc.format = VK_FORMAT_UNDEFINED; // Will automatically use wd->SurfaceFormat.format. diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 86bb36cc..5636178b 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2026-03-25: Added support for WGVK native backend via IMGUI_IMPL_WEBGPU_BACKEND_WGVK define, with SPIRV shaders if WGSL is not available. (#9316, #9246, #9257) // 2026-03-09: Removed support for Emscripten < 4.0.10. (#9281) // 2025-10-16: Update to compile with Dawn and Emscripten's 4.0.10+ '--use-port=emdawnwebgpu' ports. (#8381, #8898) @@ -83,11 +84,13 @@ struct ImGui_ImplWGPU_Texture struct RenderResources { - WGPUSampler Sampler = nullptr; // Sampler for textures - WGPUBuffer Uniforms = nullptr; // Shader uniforms - WGPUBindGroup CommonBindGroup = nullptr; // Resources bind-group to bind the common resources to pipeline - ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map) - WGPUBindGroupLayout ImageBindGroupLayout = nullptr; // Cache layout used for the image bind group. Avoids allocating unnecessary JS objects when working with WebASM + WGPUSampler SamplerLinear = nullptr; // Bilinear sampler + WGPUSampler SamplerNearest = nullptr; // Nearest/point sampler + WGPUBuffer Uniforms = nullptr; // Shader uniforms + WGPUBindGroup CommonBindGroupLinear = nullptr; // Common bind-group bound to group 0 (uniforms + SamplerLinear) + WGPUBindGroup CommonBindGroupNearest = nullptr; // Common bind-group bound to group 0 (uniforms + SamplerNearest) + ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map) + WGPUBindGroupLayout ImageBindGroupLayout = nullptr; // Cache layout used for the image bind group. Avoids allocating unnecessary JS objects when working with WebASM }; struct FrameResources @@ -114,6 +117,7 @@ struct ImGui_ImplWGPU_Data WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined; WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined; WGPURenderPipeline pipelineState = nullptr; + ImGui_ImplWGPU_RenderState* RenderState = nullptr; // == ImGui::GetPlatformIO().Renderer_RenderState during rendering. RenderResources renderResources; FrameResources* pFrameResources = nullptr; @@ -296,9 +300,11 @@ static void SafeRelease(WGPUShaderModule& res) } static void SafeRelease(RenderResources& res) { - SafeRelease(res.Sampler); + SafeRelease(res.SamplerLinear); + SafeRelease(res.SamplerNearest); SafeRelease(res.Uniforms); - SafeRelease(res.CommonBindGroup); + SafeRelease(res.CommonBindGroupLinear); + SafeRelease(res.CommonBindGroupNearest); SafeRelease(res.ImageBindGroupLayout); }; @@ -313,6 +319,7 @@ static void SafeRelease(FrameResources& res) static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModuleWGSL(const char* wgsl_source) { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); + IM_UNUSED(bd); WGPUShaderSourceWGSL wgsl_desc = {}; wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL; @@ -322,9 +329,10 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModuleWGSL(con desc.nextInChain = (WGPUChainedStruct*)&wgsl_desc; WGPUProgrammableStageDescriptor stage_desc = {}; +#if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); - stage_desc.entryPoint = { "main", WGPU_STRLEN }; +#endif return stage_desc; } @@ -418,15 +426,19 @@ static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPas wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize * sizeof(ImDrawVert)); wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, fr->IndexBufferSize * sizeof(ImDrawIdx)); wgpuRenderPassEncoderSetPipeline(ctx, bd->pipelineState); - wgpuRenderPassEncoderSetBindGroup(ctx, 0, bd->renderResources.CommonBindGroup, 0, nullptr); + wgpuRenderPassEncoderSetBindGroup(ctx, 0, bd->renderResources.CommonBindGroupLinear, 0, nullptr); // Setup blend factor WGPUColor blend_color = { 0.f, 0.f, 0.f, 0.f }; wgpuRenderPassEncoderSetBlendConstant(ctx, &blend_color); } +// Draw callbacks +static void ImGui_ImplWGPU_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplWGPU_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); wgpuRenderPassEncoderSetBindGroup(bd->RenderState->RenderPassEncoder, 0, bd->renderResources.CommonBindGroupLinear, 0, nullptr); } +static void ImGui_ImplWGPU_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); wgpuRenderPassEncoderSetBindGroup(bd->RenderState->RenderPassEncoder, 0, bd->renderResources.CommonBindGroupNearest, 0, nullptr); } + // Render function -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder) { // Avoid rendering when minimized @@ -521,7 +533,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder ImGui_ImplWGPU_RenderState render_state; render_state.Device = bd->wgpuDevice; render_state.RenderPassEncoder = pass_encoder; - platform_io.Renderer_RenderState = &render_state; + platform_io.Renderer_RenderState = bd->RenderState = &render_state; // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) @@ -537,8 +549,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + if (pcmd->UserCallback == ImGui_ImplWGPU_DrawCallback_ResetRenderState) ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); else pcmd->UserCallback(draw_list, pcmd); @@ -549,12 +560,11 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder ImTextureID tex_id = pcmd->GetTexID(); ImGuiID tex_id_hash = ImHashData(&tex_id, sizeof(tex_id), 0); WGPUBindGroup bind_group = (WGPUBindGroup)bd->renderResources.ImageBindGroups.GetVoidPtr(tex_id_hash); - if (!bind_group) - { - bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bd->renderResources.ImageBindGroupLayout, (WGPUTextureView)tex_id); - bd->renderResources.ImageBindGroups.SetVoidPtr(tex_id_hash, bind_group); - } - wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, nullptr); + if (!bind_group && tex_id != 0) + if ((bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bd->renderResources.ImageBindGroupLayout, (WGPUTextureView)tex_id)) != 0) + bd->renderResources.ImageBindGroups.SetVoidPtr(tex_id_hash, bind_group); + if (bind_group) + wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, nullptr); // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); @@ -563,8 +573,8 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder // Clamp to viewport as wgpuRenderPassEncoderSetScissorRect() won't accept values that are off bounds if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } - if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } - if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; } if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; @@ -586,7 +596,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder } image_bind_groups.Data.resize(0); - platform_io.Renderer_RenderState = nullptr; + platform_io.Renderer_RenderState = bd->RenderState = nullptr; } static void ImGui_ImplWGPU_DestroyTexture(ImTextureData* tex) @@ -814,29 +824,35 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() ImGui_ImplWGPU_CreateUniformBuffer(); - // Create sampler + // Create samplers (Linear/Nearest) // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; sampler_desc.maxAnisotropy = 1; - bd->renderResources.Sampler = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc); + sampler_desc.minFilter = WGPUFilterMode_Linear; + sampler_desc.magFilter = WGPUFilterMode_Linear; + sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; + bd->renderResources.SamplerLinear = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc); + sampler_desc.minFilter = WGPUFilterMode_Nearest; + sampler_desc.magFilter = WGPUFilterMode_Nearest; + sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Nearest; + bd->renderResources.SamplerNearest = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc); - // Create resource bind group + // Create resource bind groups (one per sampler, otherwise identical) WGPUBindGroupEntry common_bg_entries[] = { { nullptr, 0, bd->renderResources.Uniforms, 0, MEMALIGN(sizeof(Uniforms), 16), 0, 0 }, - { nullptr, 1, 0, 0, 0, bd->renderResources.Sampler, 0 }, + { nullptr, 1, 0, 0, 0, bd->renderResources.SamplerLinear, 0 }, }; WGPUBindGroupDescriptor common_bg_descriptor = {}; common_bg_descriptor.layout = bg_layouts[0]; common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry); common_bg_descriptor.entries = common_bg_entries; - bd->renderResources.CommonBindGroup = wgpuDeviceCreateBindGroup(bd->wgpuDevice, &common_bg_descriptor); + bd->renderResources.CommonBindGroupLinear = wgpuDeviceCreateBindGroup(bd->wgpuDevice, &common_bg_descriptor); + common_bg_entries[1].sampler = bd->renderResources.SamplerNearest; + bd->renderResources.CommonBindGroupNearest = wgpuDeviceCreateBindGroup(bd->wgpuDevice, &common_bg_descriptor); bd->renderResources.ImageBindGroupLayout = bg_layouts[1]; SafeRelease(vertex_shader_desc.module); @@ -888,6 +904,11 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.DrawCallback_ResetRenderState = ImGui_ImplWGPU_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplWGPU_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplWGPU_DrawCallback_SetSamplerNearest; + bd->initInfo = *init_info; bd->wgpuDevice = init_info->Device; bd->defaultQueue = wgpuDeviceGetQueue(bd->wgpuDevice); @@ -896,11 +917,7 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) bd->numFramesInFlight = init_info->NumFramesInFlight; bd->frameIndex = UINT_MAX; - bd->renderResources.Sampler = nullptr; - bd->renderResources.Uniforms = nullptr; - bd->renderResources.CommonBindGroup = nullptr; bd->renderResources.ImageBindGroups.Data.reserve(100); - bd->renderResources.ImageBindGroupLayout = nullptr; // Create buffers with a default size (they will later be grown as needed) bd->pFrameResources = new FrameResources[bd->numFramesInFlight]; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index de92040a..d789f25f 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -412,7 +412,7 @@ void ImGui_ImplWin32_NewFrame() // Setup time step INT64 current_time = 0; ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); - io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; + io.DeltaTime = (float)((double)(current_time - bd->Time) / (double)bd->TicksPerSecond); bd->Time = current_time; // Update OS mouse position @@ -930,7 +930,7 @@ float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) { GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! - return xdpi / 96.0f; + return (float)xdpi / 96.0f; } } #ifndef NOGDI @@ -940,7 +940,7 @@ float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! ::ReleaseDC(nullptr, dc); #endif - return xdpi / 96.0f; + return (float)xdpi / 96.0f; } float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) diff --git a/backends/vulkan/glsl_shader.frag b/backends/vulkan/glsl_shader.frag index ce7e6f72..00fa28c6 100644 --- a/backends/vulkan/glsl_shader.frag +++ b/backends/vulkan/glsl_shader.frag @@ -1,7 +1,8 @@ #version 450 core layout(location = 0) out vec4 fColor; -layout(set=0, binding=0) uniform sampler2D sTexture; +layout(set=0, binding=0) uniform texture2D _Texture; +layout(set=1, binding=0) uniform sampler _Sampler; layout(location = 0) in struct { vec4 Color; @@ -10,5 +11,5 @@ layout(location = 0) in struct { void main() { - fColor = In.Color * texture(sTexture, In.UV.st); + fColor = In.Color * texture(sampler2D(_Texture, _Sampler), In.UV.st); } diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 13d49f2d..08ae9f11 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -258,8 +258,8 @@ void MyImGuiBackend_RenderDrawData(ImDrawData* draw_data) const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) { - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - MyEngineSetupenderState(); + if (pcmd->UserCallback == platform_io.DrawCallback_ResetRenderState) + MyEngineSetupSenderState(); else pcmd->UserCallback(cmd_list, pcmd); } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e18f5a99..c1d9f2b7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,8 +41,39 @@ HOW TO UPDATE? Breaking Changes: +- DrawList: + - Obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, + which is part of our new standard draw callbacks. (#9378) + Redirecting the earlier value into the later one when set, so both old and new code should work. +- Backends: + - Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. (#914) + This change allows us to facilitate changing samplers, in line with other backends. [@yaz0r, @ocornut] + - When creating your own descriptor pool (instead of letting backend creates its own): + - Before: need at least IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER. + - After: need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE. + + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. + - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. + - Before: ImGui_ImplVulkan_AddTexture(VkSampler, VkImageView, VkImageLayout) + - After: ImGui_ImplVulkan_AddTexture(VkImageView, VkImageLayout) + - Kept inline redirection function that ignores the sampler (will obsolete). + - DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from ImGui_ImplXXXX_RenderState. + Prefer to use backend-agnostic DrawCallback_SetSamplerLinear which works everywhere! (#9378) + If there is a legit need/request for them or any render state we can always add them back. + Other Changes: +- DrawList: + - Added room in ImGuiPlatformIO for standard backend-agnostic draw callbacks. Those callbacks + are setup/provided by the backend and available in most of our standard backends. + They allow backend-agnostic code from e.g. switching to a Nearest/Point sampler without + messing with custom Renderer-specific callbacks. + platform_io.DrawCallback_ResetRenderState; // Request to reset the graphics/render state. + platform_io.DrawCallback_SetSamplerLinear; // Request to set current texture sampling to Linear + platform_io.DrawCallback_SetSamplerNearest; // Request to set current texture sampling to Nearest/Point + Note that some backends might not support all callbacks. + (#9378, #9371, #3590, #8926, #2973, #7485, #7468, #6969, #5118, #7616, #9173, #8322, #7230, + #5999, #6452, #5156, #7342, #7592, #7511) + - Made AddCallback() user data default to Null for convenience. - InputText: - InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. @@ -59,6 +90,14 @@ Other Changes: - Child windows with only ImGuiChildFlags_AutoResizeY flag keep using the proportional default ItemWidth. (#9355) - Multi-Select: + - Fixed an issue using Multi-Select within a Table causing column width measurement to + be invalid when trailing column contents is not submitted in the last row. (#9341, #8250) + - Fixed an issue using Multi-Select within a Table with the right-most column visible, + which could lead to an extra vertical offset in the Header row. (#8250) + - Box-Select: fixed an issue using ImGuiMultiSelectFlags_BoxSelect1d mode while scrolling. + Notably, using mouse wheel while holding a box-selection could lead items close to windows + edges from not being correctly unselected. (#7994, #8250, #7821, #7850, #7970) + - Box-Select: improved dirty/unclip rectangle logic for ImGuiMultiSelectFlags_BoxSelect2d. - Box-Select: fixed an issue using ImGuiMultiSelectFlags_BoxSelect2d mode, where items out of view wouldn't be properly selected while scrolling while mouse cursor is hovering outside of selection scope. (#7994, #1861, #6518) @@ -72,8 +111,20 @@ Other Changes: and ImGuiSelectionUserData is technically opaque storage. (#7994, #1861) (we will probably bring this back as a minor optimization if we have a way to for user to tell us ImGuiSelectionUserData are indices) + - Box-Select: fixes for using accross nested child windows. (#8364) + - Box-Select + Clipper: fixed an issue selecting items while scrolling while a clipper + active. (#7994, #8250, #7821, #7850, #7970) + - Box-Select + Tables: fixed an issue using box-selection in a tables with + items straying out of columns boundaries. (#7994, #2221) + - Box-Select + Tables: fixed an issue when calling `BeginMultiSelect()` in a table + before layout has been locked (first row or headers row submitted). (#8250) +- Style: + - Fixed vertical scrollbar top coordinates when using thick borders on windows + with no title bar and no menu bar. (#9366) - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. + - Assert when using MergeMode with an explicit size over a target font with an implicit + size, as scale factor are likely erroneous. (#9361) - Clipper: - Improved error reporting when misusing the clipper inside a table (prioritize reporting the common clipper error over a table sanity check assert). (#9350) @@ -81,9 +132,40 @@ Other Changes: a better recoverable error. (#9350) - Misc: - Minor optimization: reduce redudant label scanning in common widgets. + - Added missing Test Engine hooks for PlotXXX(), VSliderXXX(), TableHeader(). - Backends: - - Metal: avoid redundant vertex buffer bind in SetupRenderState, which leads + - Added support for new standardized draw callbacks in most backends: (#9378) + - Allegro5: Reset n/a n/a + - DX9: Reset SetSamplerLinear SetSamplerNearest + - DX10: Reset SetSamplerLinear SetSamplerNearest + - DX11: Reset SetSamplerLinear SetSamplerNearest + - DX12: Reset SetSamplerLinear SetSamplerNearest + - Metal: Reset *missing* *missing* + - OpenGL2: Reset SetSamplerLinear SetSamplerNearest + - OpenGL3+: Reset SetSamplerLinear SetSamplerNearest + - SDLGPU3: Reset SetSamplerLinear SetSamplerNearest + - SDLRenderer2: Reset n/a n/a + - SDLRenderer3: Reset SetSamplerLinear SetSamplerNearest + - Vulkan: Reset SetSamplerLinear SetSamplerNearest + - WebGPU: Reset SetSamplerLinear SetSamplerNearest + (Vulkan backend by @yaz0r, others by @ocornut) + - GLFW: added a Win32-specific implementation of `ImGui_ImplGlfw_GetContentScaleXXXX` + functions for legacy GLFW 3.2. (#9003) + - Metal: avoid redundant vertex buffer bind in `SetupRenderState()`, which leads to validation issue. (#9343) [@Hunam6] + - Metal: use a dedicated `bufferCacheLock` to avoid crashing when `bufferCache` is + replaced by a new object while being used for `@synchronize()`. (#9367) [@andygrundman] + - SDL2: made `ImGui_ImplSDL2_GetContentScaleForWindow()`/`ImGui_ImplSDL2_GetContentScaleForDisplay()` + helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value + and this breaks scaling border size. (#9369) + - WebGPU: always use SPIR-V shader on WGVK, as it cannot be detected at runtime. (#9316, #9246, #9257) +- Examples: + - Update VS toolset in all .vcxproj from VS2015 (v140) to VS2017 (v141). The later is the + first that supports vcpkg. Onward we will likely stop testing building the library with VS2015. + - GLFW+Vulkan, SDL2+Vulkan, SDL3+Vulkan, Win32+Vulkan: reworked to create a descriptor pool with: + - IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE. + - IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. + ----------------------------------------------------------------------- diff --git a/examples/example_allegro5/example_allegro5.vcxproj b/examples/example_allegro5/example_allegro5.vcxproj index 02f6a474..aa8a752d 100644 --- a/examples/example_allegro5/example_allegro5.vcxproj +++ b/examples/example_allegro5/example_allegro5.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj b/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj index 2aa25506..bce5781f 100644 --- a/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj +++ b/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj b/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj index 4bd503af..cd59daed 100644 --- a/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj +++ b/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj b/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj index a81d328d..6f44abfe 100644 --- a/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj +++ b/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index d70c7ce1..f2139cef 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -193,7 +193,8 @@ static void SetupVulkan(ImVector instance_extensions) { VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE }, }; VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; diff --git a/examples/example_glut_opengl2/example_glut_opengl2.vcxproj b/examples/example_glut_opengl2/example_glut_opengl2.vcxproj index c56452b2..2e12665d 100644 --- a/examples/example_glut_opengl2/example_glut_opengl2.vcxproj +++ b/examples/example_glut_opengl2/example_glut_opengl2.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_null/example_null.vcxproj b/examples/example_null/example_null.vcxproj index 8ef991f3..75fd43b4 100644 --- a/examples/example_null/example_null.vcxproj +++ b/examples/example_null/example_null.vcxproj @@ -21,33 +21,34 @@ {1A0BF63C-18EF-4BAE-A8DA-055481B11F5D} example_win32_directx11 + 8.1 Application true Unicode - v140 + v141 Application true Unicode - v140 + v141 Application false true Unicode - v140 + v141 Application false true Unicode - v140 + v141 diff --git a/examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj b/examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj index c23800c9..2bcc7097 100644 --- a/examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj +++ b/examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj @@ -29,27 +29,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_sdl2_opengl2/example_sdl2_opengl2.vcxproj b/examples/example_sdl2_opengl2/example_sdl2_opengl2.vcxproj index 036463f9..aca6edc5 100644 --- a/examples/example_sdl2_opengl2/example_sdl2_opengl2.vcxproj +++ b/examples/example_sdl2_opengl2/example_sdl2_opengl2.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_sdl2_opengl3/example_sdl2_opengl3.vcxproj b/examples/example_sdl2_opengl3/example_sdl2_opengl3.vcxproj index 6a81c677..d0cb96ba 100644 --- a/examples/example_sdl2_opengl3/example_sdl2_opengl3.vcxproj +++ b/examples/example_sdl2_opengl3/example_sdl2_opengl3.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj b/examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj index cf2c890b..86b883a6 100644 --- a/examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj +++ b/examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj @@ -29,27 +29,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 @@ -184,4 +184,4 @@ - + \ No newline at end of file diff --git a/examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj b/examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj index bcf99a46..4bedc45d 100644 --- a/examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj +++ b/examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 @@ -187,4 +187,4 @@ - + \ No newline at end of file diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index f3953439..13171292 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -184,7 +184,8 @@ static void SetupVulkan(ImVector instance_extensions) { VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE }, }; VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; diff --git a/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj index 340901c3..f38d290c 100644 --- a/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj +++ b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj @@ -29,27 +29,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj b/examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj index 051f87d7..c470f88b 100644 --- a/examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj +++ b/examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj b/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj index 3d034f52..02b0d35f 100644 --- a/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj +++ b/examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 @@ -186,4 +186,4 @@ - + \ No newline at end of file diff --git a/examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3.vcxproj b/examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3.vcxproj index 8b71324c..f4201454 100644 --- a/examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3.vcxproj +++ b/examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj b/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj index d48e4aff..af114478 100644 --- a/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj +++ b/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj @@ -28,27 +28,27 @@ Application true MultiByte - v140 + v141 Application true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 Application false true MultiByte - v140 + v141 diff --git a/examples/example_sdl3_vulkan/main.cpp b/examples/example_sdl3_vulkan/main.cpp index f461aab4..04d17ba7 100644 --- a/examples/example_sdl3_vulkan/main.cpp +++ b/examples/example_sdl3_vulkan/main.cpp @@ -186,7 +186,8 @@ static void SetupVulkan(ImVector instance_extensions) { VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE }, }; VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; diff --git a/examples/example_win32_directx10/example_win32_directx10.vcxproj b/examples/example_win32_directx10/example_win32_directx10.vcxproj index d11aed88..7fb76770 100644 --- a/examples/example_win32_directx10/example_win32_directx10.vcxproj +++ b/examples/example_win32_directx10/example_win32_directx10.vcxproj @@ -28,27 +28,27 @@ Application true Unicode - v140 + v141 Application true Unicode - v140 + v141 Application false true Unicode - v140 + v141 Application false true Unicode - v140 + v141 diff --git a/examples/example_win32_directx11/example_win32_directx11.vcxproj b/examples/example_win32_directx11/example_win32_directx11.vcxproj index bace6a2c..9e89353d 100644 --- a/examples/example_win32_directx11/example_win32_directx11.vcxproj +++ b/examples/example_win32_directx11/example_win32_directx11.vcxproj @@ -21,33 +21,34 @@ {9F316E83-5AE5-4939-A723-305A94F48005} example_win32_directx11 + 8.1 Application true Unicode - v140 + v141 Application true Unicode - v140 + v141 Application false true Unicode - v140 + v141 Application false true Unicode - v140 + v141 diff --git a/examples/example_win32_directx9/example_win32_directx9.vcxproj b/examples/example_win32_directx9/example_win32_directx9.vcxproj index 8c3f9958..c5efda38 100644 --- a/examples/example_win32_directx9/example_win32_directx9.vcxproj +++ b/examples/example_win32_directx9/example_win32_directx9.vcxproj @@ -28,27 +28,27 @@ Application true Unicode - v140 + v141 Application true Unicode - v140 + v141 Application false true Unicode - v140 + v141 Application false true Unicode - v140 + v141 diff --git a/examples/example_win32_opengl3/example_win32_opengl3.vcxproj b/examples/example_win32_opengl3/example_win32_opengl3.vcxproj index 98fc38fd..b9b42677 100644 --- a/examples/example_win32_opengl3/example_win32_opengl3.vcxproj +++ b/examples/example_win32_opengl3/example_win32_opengl3.vcxproj @@ -28,27 +28,27 @@ Application true Unicode - v140 + v141 Application true Unicode - v140 + v141 Application false true Unicode - v140 + v141 Application false true Unicode - v140 + v141 diff --git a/examples/example_win32_vulkan/example_win32_vulkan.vcxproj b/examples/example_win32_vulkan/example_win32_vulkan.vcxproj index dab8afd4..f3ba4aa6 100644 --- a/examples/example_win32_vulkan/example_win32_vulkan.vcxproj +++ b/examples/example_win32_vulkan/example_win32_vulkan.vcxproj @@ -21,33 +21,34 @@ {0A1E32DF-E0F4-4CCE-B3DC-9644C503BD88} example_win32_directx11 + 8.1 Application true Unicode - v140 + v141 Application true Unicode - v140 + v141 Application false true Unicode - v140 + v141 Application false true Unicode - v140 + v141 diff --git a/examples/example_win32_vulkan/main.cpp b/examples/example_win32_vulkan/main.cpp index 7f1ccd0c..cbdbdb83 100644 --- a/examples/example_win32_vulkan/main.cpp +++ b/examples/example_win32_vulkan/main.cpp @@ -182,7 +182,8 @@ static void SetupVulkan(ImVector instance_extensions) { VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE }, }; VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; diff --git a/imgui.cpp b/imgui.cpp index 0dbd6321..1635e18a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -395,6 +395,10 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/04/23 (1.92.8) - Obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) + - 2026/04/22 (1.92.8) - Backends: Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. + - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. + - When creating your own descriptor pool (instead of letting backend creates its own): need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. - 2026/03/19 (1.92.7) - MultiSelect: renamed ImGuiMultiSelectFlags_SelectOnClick to ImGuiMultiSelectFlags_SelectOnAuto. - 2026/02/26 (1.92.7) - Separator: fixed a legacy quirk where Separator() was submitting a zero-height item for layout purpose, even though it draws a 1-pixel separator. The fix could affect code e.g. computing height from multiple widgets in order to allocate vertical space for a footer or multi-line status bar. (#2657, #9263) @@ -1495,7 +1499,7 @@ ImGuiStyle::ImGuiStyle() TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. TabMinWidthBase = 1.0f; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected. - TabMinWidthShrink = 80.0f; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy. + TabMinWidthShrink = 80.0f; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy. FLT_MAX: never shrink, will behave like ImGuiTabBarFlags_FittingPolicyScroll. TabCloseButtonMinWidthSelected = -1.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. TabCloseButtonMinWidthUnselected = 0.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected. TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. @@ -1542,6 +1546,7 @@ ImGuiStyle::ImGuiStyle() // Scale all spacing/padding/thickness values. Do not scale fonts. +// Consider not calling this if your initial scale factor if <1.0. // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times. void ImGuiStyle::ScaleAllSizes(float scale_factor) { @@ -3453,12 +3458,14 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos. // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that. // As a workaround we currently half ItemSpacing worth on each side. - min_y -= g.Style.ItemSpacing.y; - max_y += g.Style.ItemSpacing.y; + float pad_y = g.Style.ItemSpacing.y; + min_y -= pad_y; + max_y += pad_y; // Box-select on 2D area requires different clipping. + // (best adding pad_y here than in BeginBoxSelect() as we are closer to current state) if (bs->UnclipMode) - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0)); + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y - pad_y, bs->UnclipRect.Max.y + pad_y, 0, 0)); } // Add main visible range @@ -6585,6 +6592,14 @@ void ImGui::EndChild() g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } +ImGuiWindow* ImGui::FindFrontMostVisibleChildWindow(ImGuiWindow* window) +{ + for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--) + if (IsWindowActiveAndVisible(window->DC.ChildWindows[n])) + return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]); + return window; +} + static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) { window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); @@ -8751,6 +8766,17 @@ void ImGui::PopFocusScope() g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0; } +bool ImGui::IsInNavFocusRoute(ImGuiID focus_scope_id) +{ + ImGuiContext& g = *GImGui; + if (g.NavFocusScopeId == focus_scope_id) + return true; + for (const ImGuiFocusScopeData& focus_scope : g.NavFocusRoute) + if (focus_scope.ID == focus_scope_id) + return true; + return false; +} + void ImGui::SetNavFocusScope(ImGuiID focus_scope_id) { ImGuiContext& g = *GImGui; @@ -9107,7 +9133,7 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) } g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size) : NULL; - g.FontBakedScale = (g.Font != NULL && window != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f; + g.FontBakedScale = (g.FontBaked != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f; g.DrawListSharedData.FontScale = g.FontBakedScale; } @@ -10817,7 +10843,8 @@ bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, si // to extend contents size of our parent container (e.g. window contents size, which is used for auto-resizing // windows, table column contents size used for auto-resizing columns, group size). // This was causing issues and ambiguities and we needed to retire that. -// From 1.89, extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED. +// 2022/08/05 (1.89): extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED. However we gated the new logic behind a '#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS' block. +// 2025/06/25 (1.92): removed the legacy path and turned into an assert. It was a mistake that there was a #ifndef before: our obsolescence schedule gets pushed back a bit more :( // // Previously this would make the window content size ~200x200: // Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK ANYMORE @@ -13326,7 +13353,7 @@ static void ImGui::NavProcessItem() const ImGuiID id = g.LastItemData.ID; const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags; - // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816) + // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816, #7994) ImRect nav_bb = g.LastItemData.NavRect; if (window->DC.NavIsScrollPushableX == false) { @@ -15790,6 +15817,7 @@ void ImGuiPlatformIO::ClearRendererHandlers() { Renderer_TextureMaxWidth = Renderer_TextureMaxHeight = 0; Renderer_RenderState = NULL; + DrawCallback_ResetRenderState = DrawCallback_SetSamplerLinear = DrawCallback_SetSamplerNearest = NULL; } ImGuiViewport* ImGui::GetMainViewport() diff --git a/imgui.h b/imgui.h index e73b5b0d..f867f34b 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.8 WIP" -#define IMGUI_VERSION_NUM 19272 +#define IMGUI_VERSION_NUM 19274 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -1401,7 +1401,7 @@ enum ImGuiTabBarFlags_ ImGuiTabBarFlags_DrawSelectedOverline = 1 << 6, // Draw selected overline markers over selected tab // Fitting/Resize policy - ImGuiTabBarFlags_FittingPolicyMixed = 1 << 7, // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons. + ImGuiTabBarFlags_FittingPolicyMixed = 1 << 7, // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling. Setting TabMinWidthShrink to FLT_MAX makes this behave like ImGuiTabBarFlags_FittingPolicyScroll. ImGuiTabBarFlags_FittingPolicyShrink = 1 << 8, // Shrink down tabs when they don't fit ImGuiTabBarFlags_FittingPolicyScroll = 1 << 9, // Enable scrolling buttons when tabs don't fit ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyMixed | ImGuiTabBarFlags_FittingPolicyShrink | ImGuiTabBarFlags_FittingPolicyScroll, @@ -2319,7 +2319,7 @@ struct ImGuiStyle float TabBorderSize; // Thickness of border around tabs. float TabMinWidthBase; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected. float TabMinWidthShrink; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy. - float TabCloseButtonMinWidthSelected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. + float TabCloseButtonMinWidthSelected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never shrink, will behave like ImGuiTabBarFlags_FittingPolicyScroll. float TabCloseButtonMinWidthUnselected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected. float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. float TabBarOverlineSize; // Thickness of tab-bar overline, which highlights the selected tab-bar. @@ -2335,7 +2335,7 @@ struct ImGuiStyle ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. - float SeparatorSize; // Thickness of border in Separator() + float SeparatorSize; // Thickness of border in Separator(). Must be >= 1.0f. float SeparatorTextBorderSize; // Thickness of border in SeparatorText() ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). ImVec2 SeparatorTextPadding; // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. @@ -2365,7 +2365,7 @@ struct ImGuiStyle // Functions IMGUI_API ImGuiStyle(); - IMGUI_API void ScaleAllSizes(float scale_factor); // Scale all spacing/padding/thickness values. Do not scale fonts. + IMGUI_API void ScaleAllSizes(float scale_factor); // Scale all spacing/padding/thickness values. Do not scale fonts. See comments in definition. Consider not calling this if your initial scale factor if <1.0. // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -3166,12 +3166,6 @@ typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibilit typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); #endif -// Special Draw callback value to request renderer backend to reset the graphics/render state. -// The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address. -// This is useful, for example, if you submitted callbacks which you know have altered the render state and you want it to be restored. -// Render state is not reset by default because they are many perfectly useful way of altering render state (e.g. changing shader/blending settings before an Image call). -#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8) - // Typically, 1 command = 1 GPU draw call (unless command is a callback) // - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled, // this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices. @@ -3378,14 +3372,15 @@ struct ImDrawList // Advanced: Draw Callbacks // - May be used to alter render state (change sampler, blending, current shader). May be used to emit custom rendering commands (difficult to do correctly, but possible). - // - Use special ImDrawCallback_ResetRenderState callback to instruct backend to reset its render state to the default. + // - Use special GetPlatformIO().DrawCallback_ResetRenderState callback to instruct backend to reset its render state to the default. + // - See other standard callbacks in GetPlatformIO(), which may or not be supported by your backend. // - Your rendering loop must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. All standard backends are honoring this. // - For some backends, the callback may access selected render-states exposed by the backend in a ImGui_ImplXXXX_RenderState structure pointed to by platform_io.Renderer_RenderState. // - IMPORTANT: please be mindful of the different level of indirection between using size==0 (copying argument) and using size>0 (copying pointed data into a buffer). // - If userdata_size == 0: we copy/store the 'userdata' argument as-is. It will be available unmodified in ImDrawCmd::UserCallbackData during render. // - If userdata_size > 0, we copy/store 'userdata_size' bytes pointed to by 'userdata'. We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData will point inside that buffer so you have to retrieve data from there. Your callback may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data. // - Support for userdata_size > 0 was added in v1.91.4, October 2024. So earlier code always only allowed to copy/store a simple void*. - IMGUI_API void AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size = 0); + IMGUI_API void AddCallback(ImDrawCallback callback, void* userdata = NULL, size_t userdata_size = 0); // Advanced: Miscellaneous IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible @@ -3861,6 +3856,7 @@ enum ImFontFlags_ ImFontFlags_NoLoadError = 1 << 1, // Disable throwing an error/assert when calling AddFontXXX() with missing file/data. Calling code is expected to check AddFontXXX() return value. ImFontFlags_NoLoadGlyphs = 1 << 2, // [Internal] Disable loading new glyphs. ImFontFlags_LockBakedSizes = 1 << 3, // [Internal] Disable loading new baked sizes, disable garbage collecting current ones. e.g. if you want to lock a font to a single size. Important: if you use this to preload given sizes, consider the possibility of multiple font density used on Retina display. + ImFontFlags_ImplicitRefSize = 1 << 4, // [Internal] Reference size was not set explicitly. }; // Font runtime data and rendering @@ -4021,6 +4017,12 @@ struct ImGuiPlatformIO // Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure. void* Renderer_RenderState; + // Standard draw callbacks + ImDrawCallback DrawCallback_ResetRenderState; // Request to reset the graphics/render state. + ImDrawCallback DrawCallback_SetSamplerLinear; // Request to set current texture sampling to Linear + ImDrawCallback DrawCallback_SetSamplerNearest; // Request to set current texture sampling to Nearest/Point + //ImDrawCallback DrawCallback_SetSamplerCustom; // Request to set current texture sampling using Backend Specific data. + //------------------------------------------------------------------ // Output //------------------------------------------------------------------ @@ -4149,6 +4151,8 @@ namespace ImGui //static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETED in 1.42 } +#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8) // OBSOLETED in 1.92.8: Use ImGui::GetPlatformIO().DrawCallback_ResetRenderState + //-- OBSOLETED in 1.92.0: ImFontAtlasCustomRect becomes ImTextureRect // - ImFontAtlasCustomRect::X,Y --> ImTextureRect::x,y // - ImFontAtlasCustomRect::Width,Height --> ImTextureRect::w,h diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 461b2cd8..bbdfa1c3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -10724,10 +10724,11 @@ struct ExampleAssetsBrowser bool AllowBoxSelect = true; // Will set ImGuiMultiSelectFlags_BoxSelect2d bool AllowBoxSelectInsideSelection = false; // Will set ImGuiMultiSelectFlags_SelectOnClickAlways bool AllowDragUnselected = false; // Will set ImGuiMultiSelectFlags_SelectOnClickRelease - float IconSize = 32.0f; + float IconSize = 0; int IconSpacing = 10; - int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. + int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. bool StretchSpacing = true; + bool UseScrollX = false; // Debug: submit twice the number of items per line (overflow horizontally to exercise ScrollX + box-select) // State ImVector Items; // Our items @@ -10778,12 +10779,15 @@ struct ExampleAssetsBrowser // Layout: calculate number of icon per line and number of lines LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize)); LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1); - LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount; // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer. if (StretchSpacing && LayoutColumnCount > 1) LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount; + if (UseScrollX) + LayoutColumnCount *= 2; + LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount; + LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing); LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f); LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f); @@ -10791,6 +10795,9 @@ struct ExampleAssetsBrowser void Draw(const char* title, bool* p_open) { + if (IconSize <= 0.0f) + IconSize = ImGui::CalcTextSize("99999").x; + ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver); if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar)) { @@ -10840,6 +10847,7 @@ struct ExampleAssetsBrowser ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32); ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32); ImGui::Checkbox("Stretch Spacing", &StretchSpacing); + ImGui::Checkbox("Use ScrollX", &UseScrollX); ImGui::PopItemWidth(); ImGui::EndMenu(); } @@ -10869,7 +10877,7 @@ struct ExampleAssetsBrowser ImGuiIO& io = ImGui::GetIO(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing))); - if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove)) + if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_HorizontalScrollbar)) { ImDrawList* draw_list = ImGui::GetWindowDrawList(); @@ -11013,6 +11021,8 @@ struct ExampleAssetsBrowser } } clipper.End(); + if (Items.Size == 0) + ImGui::Dummy(ImVec2(0, 0)); ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing // Context menu diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 97c6f355..dc814a4a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -528,9 +528,14 @@ void ImDrawList::_PopUnusedDrawCmd() void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size) { + IM_ASSERT(callback != NULL); +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (callback == ImDrawCallback_ResetRenderState && _Data->Context != NULL && _Data->Context->PlatformIO.DrawCallback_ResetRenderState != NULL) + callback = _Data->Context->PlatformIO.DrawCallback_ResetRenderState; // == ImGui::GetPlatformIO().DrawCallback_ResetRenderState +#endif + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - IM_ASSERT(callback != NULL); IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) { @@ -662,7 +667,7 @@ void ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool i if (intersect_with_current_clip_rect) { ImVec4 current = _CmdHeader.ClipRect; - if (cr.x < current.x) cr.x = current.x; + if (cr.x < current.x) cr.x = current.x; // = ClipWith(). Note that passing inverted range wouldn't be fixed here. if (cr.y < current.y) cr.y = current.y; if (cr.z > current.z) cr.z = current.z; if (cr.w > current.w) cr.w = current.w; @@ -3039,7 +3044,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) } else { - IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. + IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font!"); // When using MergeMode make sure that a font has already been added before. font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back(); ImFontAtlasFontDiscardBakes(this, font, 0); // Need to discard bakes if the font was already used, because baked->FontLoaderDatas[] will change size. (#9162) } @@ -3067,6 +3072,11 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) IM_ASSERT(font_cfg->FontLoader->FontBakedLoadGlyph != NULL); IM_ASSERT(font_cfg->FontLoader->LoaderInit == NULL && font_cfg->FontLoader->LoaderShutdown == NULL); // FIXME-NEWATLAS: Unsupported yet. } + // | Target w/ Implicit RefSize | Target w/ Explicit RefSize | + // Adding w/ Implicit RefSize: | OK (same scale) | OK (same scale) | + // Adding w/ Explicit RefSize: | KO | OK (custom scale) | + if (font_cfg_in->MergeMode && font_cfg_in->SizePixels > 0) + IM_ASSERT((font->Flags & ImFontFlags_ImplicitRefSize) == 0 && "Cannot use MergeMode with an explicit reference size when the destination font used an implicit reference size!"); IM_ASSERT(font_cfg->FontLoaderData == NULL); if (!ImFontAtlasFontSourceInit(this, font_cfg)) @@ -3134,7 +3144,10 @@ ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template) if (!font_cfg_template) font_cfg.PixelSnapH = true; // Prevents sub-integer scaling factors at lower-level layers. if (font_cfg.SizePixels <= 0.0f) + { font_cfg.SizePixels = 13.0f; // This only serves (1) as a reference for GlyphOffset.y setting and (2) as a default for pre-1.92 backend. + font_cfg.Flags |= ImFontFlags_ImplicitRefSize; + } if (font_cfg.Name[0] == '\0') ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyClean.ttf"); font_cfg.EllipsisChar = (ImWchar)0x0085; @@ -3159,7 +3172,10 @@ ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) if (!font_cfg_template) font_cfg.PixelSnapH = true; // Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers. if (font_cfg.SizePixels <= 0.0f) + { font_cfg.SizePixels = 13.0f; + font_cfg.Flags |= ImFontFlags_ImplicitRefSize; + } if (font_cfg.Name[0] == '\0') ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyForever.ttf"); font_cfg.ExtraSizeScale *= 1.015f; // Match ProggyClean diff --git a/imgui_internal.h b/imgui_internal.h index 85c9daab..70589997 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -509,7 +509,8 @@ inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } template T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } template T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } template T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } -template T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } +template T ImLerp(double a, double b, float t) { return (T)(a + (b - a) * (double)t); } +template T ImLerp(T a, T b, float t) { return (T)((float)a + (float)(b - a) * t); } template void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } template T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } template T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } @@ -606,6 +607,8 @@ struct IMGUI_API ImRect bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; } void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; } + void AddX(float x) { if (Min.x > x) Min.x = x; if (Max.x < x) Max.x = x; } + void AddY(float y) { if (Min.y > y) Min.y = y; if (Max.y < y) Max.y = y; } void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; } @@ -1893,6 +1896,7 @@ struct ImGuiBoxSelectState // Temporary/Transient data bool UnclipMode; // (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select. ImRect UnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. + ImRect UnclipRects[2]; // Per-axis versions. ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos) ImRect BoxSelectRectCurr; @@ -3325,6 +3329,7 @@ namespace ImGui // Childs IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags); + IMGUI_API ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window); // Popups, Modals IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags); @@ -3478,6 +3483,7 @@ namespace ImGui // We don't use the ID Stack for this as it is common to want them separate. IMGUI_API void PushFocusScope(ImGuiID id); IMGUI_API void PopFocusScope(); + IMGUI_API bool IsInNavFocusRoute(ImGuiID focus_scope_id); inline ImGuiID GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; } // Focus scope we are outputting into, set by PushFocusScope() // Drag and Drop @@ -3544,6 +3550,7 @@ namespace ImGui IMGUI_API void TableUpdateLayout(ImGuiTable* table); IMGUI_API void TableUpdateBorders(ImGuiTable* table); IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table); + IMGUI_API void TableApplyExternalUnclipRect(ImGuiTable* table, ImRect& rect); IMGUI_API void TableDrawBorders(ImGuiTable* table); IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display); IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index d06fe1e7..cc910b98 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -240,6 +240,7 @@ Index of this file: #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*' #pragma GCC diagnostic ignored "-Wstrict-overflow" #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may change value #pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif @@ -1152,7 +1153,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->ClipRect.Min.y = work_rect.Min.y; column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX; column->ClipRect.Max.y = FLT_MAX; - ImRect clip_rect_unclipped = column->ClipRect; column->ClipRect.ClipWithFull(host_clip_rect); // Mark column as Clipped (not in sight) @@ -1170,9 +1170,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output. column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0; - ImGuiBoxSelectState* bs = &g.BoxSelectState; - if (!column->IsRequestOutput && bs->UnclipMode && bs->UnclipRect.Overlaps(clip_rect_unclipped)) - column->IsRequestOutput = true; // Mark column as SkipItems (ignoring all items/layout) // (table->HostSkipItems is a copy of inner_window->SkipItems before we cleared it above in Part 2) @@ -1319,14 +1316,32 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight; table_instance->LastFrozenHeight = 0.0f; - // Initial state ImGuiWindow* inner_window = table->InnerWindow; + ImGuiBoxSelectState* bs = &g.BoxSelectState; + if (bs->Window == inner_window && bs->UnclipMode) + TableApplyExternalUnclipRect(table, bs->UnclipRect); + + // Initial state if (table->Flags & ImGuiTableFlags_NoClip) table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); else inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect? } +// When starting a BeginMultiSelect() after table has been layout we update IsRequestOutput fields. +void ImGui::TableApplyExternalUnclipRect(ImGuiTable* table, ImRect& rect) +{ + if (rect.IsInverted()) + return; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (!column->IsRequestOutput) + if (rect.Overlaps(ImRect(column->MinX, table->WorkRect.Min.y, column->MaxX, FLT_MAX))) + column->IsRequestOutput = true; + } +} + // Process hit-testing on resizing borders. Actual size change will be applied in EndTable() // - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise. void ImGui::TableUpdateBorders(ImGuiTable* table) @@ -1647,7 +1662,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo ImGuiTable* table = g.CurrentTable; IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, "TableSetupColumn(): called too many times!"); - IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); + IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); // Table layout is locked when submitting a row or when calling BeginMultiSelect() with box-select. IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount]; @@ -3299,6 +3314,8 @@ void ImGui::TableHeader(const char* label) // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden if (IsPopupOpenRequestForItem(ImGuiPopupFlags_None, id)) TableOpenContextMenu(column_n); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); } // Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7d0ac8c3..9f3f871f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -92,6 +92,7 @@ Index of this file: #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers +#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may change value #pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif @@ -977,7 +978,7 @@ ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) IM_ASSERT(scrollbar_size >= 0.0f); const float border_size = IM_ROUND(window->WindowBorderSize * 0.5f); - const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : 0.0f; + const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : (window->Flags & ImGuiWindowFlags_NoTitleBar) ? border_size : 0; if (axis == ImGuiAxis_X) return ImRect(inner_rect.Min.x + border_size, ImMax(outer_rect.Min.y + border_size, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); else @@ -1558,7 +1559,7 @@ bool ImGui::TextLink(const char* label) window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf), 1.0f * (float)(int)g.Style._MainScale); // FIXME-TEXT: Underline mode // FIXME-DPI PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf)); - RenderText(bb.Min, label, label_end); + RenderText(bb.Min, label, label_end, false); PopStyleColor(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); @@ -1728,7 +1729,7 @@ void ImGui::Separator() if (window->DC.CurrentColumns) flags |= ImGuiSeparatorFlags_SpanAllColumns; - SeparatorEx(flags, g.Style.SeparatorSize); + SeparatorEx(flags, ImMax(g.Style.SeparatorSize, 1.0f)); } void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w) @@ -1950,7 +1951,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const char* label_end = FindRenderedTextEnd(label); const ImVec2 label_size = CalcTextSize(label, label_end, false); - const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; + const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, false).x : 0.0f; const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth()); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); @@ -3542,6 +3543,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, label_end, false); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return value_changed; } @@ -7507,8 +7509,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, label_end, &label_size, style.SelectableTextAlign, &bb); #ifdef IMGUI_DEBUG_BOXSELECT - if (g.BoxSelectState.UnclipMode) - GetForegroundDrawList()->AddText(pos, IM_COL32(255,255,0,200), label); + if (g.BoxSelectState.UnclipMode) { GetForegroundDrawList()->AddText(pos, IM_COL32(255,255,0,200), label, label_end); } #endif // Automatically close popups @@ -7826,7 +7827,7 @@ bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiI return false; // Current frame absolute prev/current rectangles are used to toggle selection. - // They are derived from positions relative to scrolling space. + // They are derived from positions relative to scrolling space, so "previous" rectangle is reprojected for current frame coordinates. ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already ImVec2 curr_end_pos_abs = g.IO.MousePos; @@ -7836,24 +7837,68 @@ bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiI bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); + //IMGUI_DEBUG_LOG("StartPosRel (%.2f,%.2f) EndPosRel (%.2f,%.2f) -> (%.2f,%.2f)\n", bs->StartPosRel.x, bs->StartPosRel.y, bs->EndPosRel.x, bs->EndPosRel.y, WindowPosAbsToRel(window, g.IO.MousePos).x, WindowPosAbsToRel(window, g.IO.MousePos).y); // Box-select 2D mode detects change of the rectangle. - // Storing unclip rect used by widgets supporting box-select. - if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) + // Storing unclip rects which will be tested by widgets supporting box-select. Always update rectangles when active (even if we don't use them). + // To facilitate understanding this: enable IMGUI_DEBUG_BOXSELECT and visualize all geometry. + if (ms_flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) { - if (bs->BoxSelectRectPrev.Min != bs->BoxSelectRectCurr.Min || bs->BoxSelectRectPrev.Max != bs->BoxSelectRectCurr.Max) - bs->UnclipMode = true; + // For both sides, compute the area differing between Prev and Curr rectangles. + bs->UnclipRects[0] = bs->UnclipRects[1] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); + for (int side = 0; side < 2; side++) + { + ImVec2 d_min = (side == 0) ? ImMin(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectPrev.Min) : ImMin(bs->BoxSelectRectCurr.Max, bs->BoxSelectRectPrev.Max); + ImVec2 d_max = (side == 0) ? ImMax(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectPrev.Min) : ImMax(bs->BoxSelectRectCurr.Max, bs->BoxSelectRectPrev.Max); + if (d_min.x != d_max.x) + { + bs->UnclipRects[0].AddX(d_min.x); + bs->UnclipRects[0].AddX(d_max.x); + } + if (d_min.y != d_max.y) + { + bs->UnclipRects[1].AddY(d_min.y); + bs->UnclipRects[1].AddY(d_max.y); + } + } - // Always update rect even if we don't use it. - bs->UnclipRect = bs->BoxSelectRectPrev; // FIXME-OPT: UnclipRect X coordinates could be intersection of Prev and Curr rect on X axis. - bs->UnclipRect.Add(bs->BoxSelectRectCurr); + ImRect box_select_intersection = bs->BoxSelectRectPrev; + box_select_intersection.Add(bs->BoxSelectRectCurr); + if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) + if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) + { + bs->UnclipRects[0].AddY(box_select_intersection.Min.y); + bs->UnclipRects[0].AddY(box_select_intersection.Max.y); + } + if (ms_flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) + if (bs->BoxSelectRectPrev.Min.y != bs->BoxSelectRectCurr.Min.y || bs->BoxSelectRectPrev.Max.y != bs->BoxSelectRectCurr.Max.y) + { + bs->UnclipRects[1].AddX(box_select_intersection.Min.x); + bs->UnclipRects[1].AddX(box_select_intersection.Max.x); + } + + // Merge both rectangles into one. + // FIXME-OPT: When UnclipRect.Area() is much larger than the sum of UnclipRects[0]/[1] Areas, widgets should + // ideally first use UnclipRect as a first coarse cull layer + the individual ones as a second validation. + bs->UnclipRect = bs->UnclipRects[0]; + bs->UnclipRect.Add(bs->UnclipRects[1]); + if (!bs->UnclipRect.IsInverted() && (!window->ClipRect.Contains(bs->UnclipRect.Min) || !window->ClipRect.Contains(bs->UnclipRect.Max))) // !! Don't use Contains(ImRect) + bs->UnclipMode = true; + if (bs->UnclipMode && g.CurrentTable != NULL) + TableApplyExternalUnclipRect(g.CurrentTable, bs->UnclipRect); // No need submitting both } #ifdef IMGUI_DEBUG_BOXSELECT - if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) - GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, bs->UnclipMode ? IM_COL32(255,255,0,200) : IM_COL32(255,0,0,200), 0.0f, 0, 4.0f); + //GetForegroundDrawList()->AddRect(scope_rect.Min, scope_rect.Max, IM_COL32(0, 255, 0, 200), 0.0f, 0, 4.0f); //GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); //GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f); + if (ms_flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) + { + for (ImRect& unclip_r : bs->UnclipRects) + if (!unclip_r.IsInverted()) + GetForegroundDrawList()->AddRect(unclip_r.Min, unclip_r.Max, bs->UnclipMode ? IM_COL32(255, 255, 0, 200) : IM_COL32(255, 0, 0, 200), 0.0f, 0, 4.0f); + GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, bs->UnclipMode ? IM_COL32(255, 255, 0, 200) : IM_COL32(255, 0, 0, 200), 0.0f, 0, 2.0f); + } #endif return true; } @@ -7870,8 +7915,9 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view ImRect box_select_r = bs->BoxSelectRectCurr; box_select_r.ClipWith(scope_rect); - window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling - window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling + ImGuiWindow* draw_window = FindFrontMostVisibleChildWindow(window); + draw_window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling + draw_window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling // Scroll const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0; @@ -7911,18 +7957,18 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window) { - ImGuiContext& g = *GImGui; if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) { // Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only + // This probably doesn't work inside a table as there are ample ambiguities related to exact time of calling BeginMultiSelect()/EndMultiSelect(). return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)); } else { - // When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970) + //// When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970) ImRect scope_rect = window->InnerClipRect; - if (g.CurrentTable != NULL) - scope_rect = g.CurrentTable->HostClipRect; + //if (g.CurrentTable != NULL) + // scope_rect = g.CurrentTable->HostClipRect; // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect? scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max); @@ -7958,18 +8004,24 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel // FIXME: Workaround to the fact we override CursorMaxPos, meaning size measurement are lost. (#8250) // They should perhaps be stacked properly? if (ImGuiTable* table = g.CurrentTable) - if (table->CurrentColumn != -1) + { + if (!table->IsLayoutLocked) + TableUpdateLayout(table); + else if (table->CurrentColumn != -1) TableEndCell(table); // This is currently safe to call multiple time. If that properly is lost we can extract the "save measurement" part of it. + } // FIXME: BeginFocusScope() const ImGuiID id = window->IDStack.back(); ms->Clear(); ms->FocusScopeId = id; ms->Flags = flags; - ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); ms->BackupCursorMaxPos = window->DC.CursorMaxPos; - ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; + ms->ScopeRectMin = window->DC.CursorPos; + if (flags & ImGuiMultiSelectFlags_ScopeRect) + window->DC.CursorMaxPos = ms->ScopeRectMin; // CalcScopeRect() for ImGuiMultiSelectFlags_ScopeRect will measure in EndMultiSelect(). PushFocusScope(ms->FocusScopeId); + ms->IsFocused = IsInNavFocusRoute(g.CurrentFocusScopeId); if (flags & ImGuiMultiSelectFlags_ScopeWindow) // Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items. window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; @@ -8097,7 +8149,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! // The InnerRect test is necessary for non-child/decorated windows. - bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(g.IO.MousePos); + bool scope_hovered = window->InnerRect.Contains(g.IO.MousePos) && IsWindowHovered(ImGuiHoveredFlags_ChildWindows); if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)) scope_hovered &= scope_rect.Contains(g.IO.MousePos); if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) @@ -8123,10 +8175,13 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX) { IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow); // Only supported at window scope - ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); + NavMoveRequestTryWrapping(GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); } // Unwind + if (ImGuiTable* table = g.CurrentTable) + if (table->IsInsideRow) + TableEndRow(table); window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); PopFocusScope(); @@ -8304,8 +8359,19 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (ms->BoxSelectId != 0) if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId)) { - const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); - const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); + ImRect item_rect = g.LastItemData.Rect; + if (!window->DC.NavIsScrollPushableX) // FIXME: Rename to be more generic. + if (ImGuiTable* table = g.CurrentTable) + if (table->CurrentColumn != -1) + { + // FIXME: We cannot use current ClipRect as it includes HostClipRect. + // A more generic version would be nice, but window->WorkRect.Min/Max exclude CellPadding. (#7994) + ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; + item_rect.Min.x = ImMax(item_rect.Min.x, column->MinX); + item_rect.Max.x = ImMin(item_rect.Max.x, column->MaxX); + } + const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(item_rect); + const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(item_rect); if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) { if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce) @@ -8804,9 +8870,7 @@ bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)( // - PlotHistogram() //------------------------------------------------------------------------- // Plot/Graph widgets are not very good. -// Consider writing your own, or using a third-party one, see: -// - ImPlot https://github.com/epezent/implot -// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions +// Consider using ImPlot (https://github.com/epezent/implot) which is much better! //------------------------------------------------------------------------- int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg) @@ -8923,6 +8987,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get // Return hovered index or -1 if none are hovered. // This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx(). + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return idx_hovered; } @@ -8952,6 +9017,7 @@ void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); } +// Plot Histogram (the data provided _is_ histogram data. it doesn't compute the histogram of your data) void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) { ImGuiPlotArrayGetterData data(values, stride); @@ -10598,7 +10664,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation) const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX); if (want_clip_rect) - PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true); + PushClipRect(ImVec2(ImClamp(bb.Min.x, tab_bar->ScrollingRectMinX, tab_bar->ScrollingRectMaxX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true); ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos; ItemSize(bb.GetSize(), style.FramePadding.y); diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 76c0ac9b..3c817d20 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -428,8 +428,9 @@ static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* s { IM_UNUSED(atlas); float size = baked->Size; + const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; if (src->MergeMode && src->SizePixels != 0.0f) - size *= (src->SizePixels / baked->OwnerFont->Sources[0]->SizePixels); + size *= (src->SizePixels / ref_size); size *= src->ExtraSizeScale; ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;