Explorar o código

Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_dx12.cpp
ocornut hai 1 semana
pai
achega
1aa91ac426

+ 34 - 25
backends/imgui_impl_dx12.cpp

@@ -23,6 +23,8 @@
 // CHANGELOG
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 // (minor and older changes stripped away, please see git history for details)
 //  2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
 //  2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2025-09-29: DirectX12: Rework synchronization logic. (#8961)
+//  2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time.
 //  2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
 //  2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
 //  2025-06-19: Fixed build on MinGW. (#8702, #4594)
 //  2025-06-19: Fixed build on MinGW. (#8702, #4594)
 //  2025-06-11: DirectX12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
 //  2025-06-11: DirectX12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
@@ -98,8 +100,14 @@ struct ImGui_ImplDX12_Data
     DXGI_FORMAT                 RTVFormat;
     DXGI_FORMAT                 RTVFormat;
     DXGI_FORMAT                 DSVFormat;
     DXGI_FORMAT                 DSVFormat;
     ID3D12DescriptorHeap*       pd3dSrvDescHeap;
     ID3D12DescriptorHeap*       pd3dSrvDescHeap;
+    ID3D12Fence*                Fence;
+    UINT64                      FenceLastSignaledValue;
+    HANDLE                      FenceEvent;
     UINT                        numFramesInFlight;
     UINT                        numFramesInFlight;
 
 
+    ID3D12CommandAllocator*     pTexCmdAllocator;
+    ID3D12GraphicsCommandList*  pTexCmdList;
+
     ImGui_ImplDX12_RenderBuffers* pFrameResources;
     ImGui_ImplDX12_RenderBuffers* pFrameResources;
     UINT                        frameIndex;
     UINT                        frameIndex;
 
 
@@ -529,29 +537,15 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
         props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
         props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
         props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
         props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
 
 
-        // FIXME-OPT: Can upload buffer be reused?
+        // FIXME-OPT: Could upload buffer be kept around, reused, and grown only when needed? Would that be worth it?
         ID3D12Resource* uploadBuffer = nullptr;
         ID3D12Resource* uploadBuffer = nullptr;
         HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
         HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
             D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer));
             D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer));
         IM_ASSERT(SUCCEEDED(hr));
         IM_ASSERT(SUCCEEDED(hr));
 
 
-        // Create temporary command list and execute immediately
-        ID3D12Fence* fence = nullptr;
-        hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
-        IM_ASSERT(SUCCEEDED(hr));
-
-        HANDLE event = ::CreateEvent(0, 0, 0, 0);
-        IM_ASSERT(event != nullptr);
-
-        // FIXME-OPT: Create once and reuse?
-        ID3D12CommandAllocator* cmdAlloc = nullptr;
-        hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
-        IM_ASSERT(SUCCEEDED(hr));
-
-        // FIXME-OPT: Can be use the one from user? (pass ID3D12GraphicsCommandList* to ImGui_ImplDX12_UpdateTextures)
-        ID3D12GraphicsCommandList* cmdList = nullptr;
-        hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList));
-        IM_ASSERT(SUCCEEDED(hr));
+        bd->pTexCmdAllocator->Reset();
+        bd->pTexCmdList->Reset(bd->pTexCmdAllocator, nullptr);
+        ID3D12GraphicsCommandList* cmdList = bd->pTexCmdList;
 
 
         // Copy to upload buffer
         // Copy to upload buffer
         void* mapped = nullptr;
         void* mapped = nullptr;
@@ -606,20 +600,16 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
 
 
         ID3D12CommandQueue* cmdQueue = bd->pCommandQueue;
         ID3D12CommandQueue* cmdQueue = bd->pCommandQueue;
         cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList);
         cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList);
-        hr = cmdQueue->Signal(fence, 1);
+        hr = cmdQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue);
         IM_ASSERT(SUCCEEDED(hr));
         IM_ASSERT(SUCCEEDED(hr));
 
 
         // FIXME-OPT: Suboptimal?
         // FIXME-OPT: Suboptimal?
         // - To remove this may need to create NumFramesInFlight x ImGui_ImplDX12_FrameContext in backend data (mimick docking version)
         // - To remove this may need to create NumFramesInFlight x ImGui_ImplDX12_FrameContext in backend data (mimick docking version)
         // - Store per-frame in flight: upload buffer?
         // - Store per-frame in flight: upload buffer?
         // - Where do cmdList and cmdAlloc fit?
         // - Where do cmdList and cmdAlloc fit?
-        fence->SetEventOnCompletion(1, event);
-        ::WaitForSingleObject(event, INFINITE);
+        bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent);
+        ::WaitForSingleObject(bd->FenceEvent, INFINITE);
 
 
-        cmdList->Release();
-        cmdAlloc->Release();
-        ::CloseHandle(event);
-        fence->Release();
         uploadBuffer->Release();
         uploadBuffer->Release();
         tex->SetStatus(ImTextureStatus_OK);
         tex->SetStatus(ImTextureStatus_OK);
     }
     }
@@ -857,6 +847,20 @@ bool    ImGui_ImplDX12_CreateDeviceObjects()
     if (result_pipeline_state != S_OK)
     if (result_pipeline_state != S_OK)
         return false;
         return false;
 
 
+    // Create command allocator and command list for ImGui_ImplDX12_UpdateTexture()
+    HRESULT hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&bd->pTexCmdAllocator));
+    IM_ASSERT(SUCCEEDED(hr));
+    hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, bd->pTexCmdAllocator, nullptr, IID_PPV_ARGS(&bd->pTexCmdList));
+    IM_ASSERT(SUCCEEDED(hr));
+    hr = bd->pTexCmdList->Close();
+    IM_ASSERT(SUCCEEDED(hr));
+
+    // Create fence.
+    hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&bd->Fence));
+    IM_ASSERT(hr == S_OK);
+    bd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    IM_ASSERT(bd->FenceEvent != nullptr);
+
     return true;
     return true;
 }
 }
 
 
@@ -878,6 +882,11 @@ void    ImGui_ImplDX12_InvalidateDeviceObjects()
     bd->commandQueueOwned = false;
     bd->commandQueueOwned = false;
     SafeRelease(bd->pRootSignature);
     SafeRelease(bd->pRootSignature);
     SafeRelease(bd->pPipelineState);
     SafeRelease(bd->pPipelineState);
+    SafeRelease(bd->pTexCmdList);
+    SafeRelease(bd->pTexCmdAllocator);
+    SafeRelease(bd->Fence);
+    CloseHandle(bd->FenceEvent);
+    bd->FenceEvent = nullptr;
 
 
     // Destroy all textures
     // Destroy all textures
     for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
     for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)

+ 7 - 7
backends/imgui_impl_glfw.cpp

@@ -116,6 +116,11 @@
 #endif
 #endif
 
 
 // GLFW
 // GLFW
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
+#define GLFW_HAS_X11_OR_WAYLAND     1
+#else
+#define GLFW_HAS_X11_OR_WAYLAND     0
+#endif
 #include <GLFW/glfw3.h>
 #include <GLFW/glfw3.h>
 #ifdef _WIN32
 #ifdef _WIN32
 #undef APIENTRY
 #undef APIENTRY
@@ -128,8 +133,8 @@
 #define GLFW_EXPOSE_NATIVE_COCOA
 #define GLFW_EXPOSE_NATIVE_COCOA
 #endif
 #endif
 #include <GLFW/glfw3native.h>
 #include <GLFW/glfw3native.h>
-#elif !defined(__EMSCRIPTEN__)
-#ifndef GLFW_EXPOSE_NATIVE_X11      // for glfwGetX11Window() on Freedesktop (Linux, BSD, etc.)
+#elif GLFW_HAS_X11_OR_WAYLAND
+#ifndef GLFW_EXPOSE_NATIVE_X11      // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.)
 #define GLFW_EXPOSE_NATIVE_X11
 #define GLFW_EXPOSE_NATIVE_X11
 #include <X11/Xatom.h>
 #include <X11/Xatom.h>
 #endif
 #endif
@@ -184,11 +189,6 @@
 #define GLFW_HAS_GETKEYNAME             (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName()
 #define GLFW_HAS_GETKEYNAME             (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName()
 #define GLFW_HAS_GETERROR               (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError()
 #define GLFW_HAS_GETERROR               (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError()
 #define GLFW_HAS_GETPLATFORM            (GLFW_VERSION_COMBINED >= 3400) // 3.4+ glfwGetPlatform()
 #define GLFW_HAS_GETPLATFORM            (GLFW_VERSION_COMBINED >= 3400) // 3.4+ glfwGetPlatform()
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
-#define GLFW_HAS_X11_OR_WAYLAND          1
-#else
-#define GLFW_HAS_X11_OR_WAYLAND          0
-#endif
 
 
 // Map GLFWWindow* to ImGuiContext*.
 // Map GLFWWindow* to ImGuiContext*.
 // - Would be simpler if we could use glfwSetWindowUserPointer()/glfwGetWindowUserPointer(), but this is a single and shared resource.
 // - Would be simpler if we could use glfwSetWindowUserPointer()/glfwGetWindowUserPointer(), but this is a single and shared resource.

+ 8 - 0
docs/CHANGELOG.txt

@@ -66,6 +66,8 @@ Other Changes:
 - Windows: added lower-right resize grip on child windows using both
 - Windows: added lower-right resize grip on child windows using both
   ImGuiChildFlags_ResizeX and ImGuiChildFlags_ResizeY flags. (#8501) [@aleksijuvani]
   ImGuiChildFlags_ResizeX and ImGuiChildFlags_ResizeY flags. (#8501) [@aleksijuvani]
   The grip is not visible before hovering to reduce clutter.
   The grip is not visible before hovering to reduce clutter.
+- InputText: fixed single-line InputText() not applying fine character clipping
+  properly (regression in 1.92.3). (#8967) [@Cyphall]
 - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers()
 - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers()
   helpers to null all handlers. (#8945, #2769)
   helpers to null all handlers. (#8945, #2769)
 - Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode,
 - Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode,
@@ -75,8 +77,13 @@ Other Changes:
 - Backends: all backends call ImGuiPlatformIO::ClearPlatformHandlers() and
 - Backends: all backends call ImGuiPlatformIO::ClearPlatformHandlers() and
   ClearRendererHandlers() on shutdown, so as not to leave function pointers
   ClearRendererHandlers() on shutdown, so as not to leave function pointers
   which may be dangling when using backend in e.g. DLL. (#8945, #2769)
   which may be dangling when using backend in e.g. DLL. (#8945, #2769)
+- Backends: DirectX12: reuse a command list and allocator for texture uploads instead
+  of recreating them each time. (#8963, #8465) [@RT2Code]
+- Backends: DirectX12: Rework synchronization logic. (#8961) [@RT2Code]
 - Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support
 - Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support
   `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes]
   `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes]
+- Backends: GLFW: fixed build on platform that are neither Windows, macOS or
+  known Unixes (Regression in 1.92.3). (#8969, #8920, #8921) [@oktonion]
 - Backends: SDL2,SDL3: avoid using the SDL_GetGlobalMouseState() path when one of our
 - Backends: SDL2,SDL3: avoid using the SDL_GetGlobalMouseState() path when one of our
   window is hovered, as the event data is reliable and enough in this case.
   window is hovered, as the event data is reliable and enough in this case.
   - Fix mouse coordinates issue in fullscreen apps with macOS notch. (#7919, #7786)
   - Fix mouse coordinates issue in fullscreen apps with macOS notch. (#7919, #7786)
@@ -89,6 +96,7 @@ Other Changes:
 - Examples: SDL2+DirectX11: Try WARP software driver if hardware driver is
 - Examples: SDL2+DirectX11: Try WARP software driver if hardware driver is
   not available. (#5924, #5562)
   not available. (#5924, #5562)
 - Examples: SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82]
 - Examples: SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82]
+- Examples: Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code]
 - Examples: made examples's main.cpp consistent with returning 1 on error.
 - Examples: made examples's main.cpp consistent with returning 1 on error.
 
 
 Docking+Viewports Branch:
 Docking+Viewports Branch:

+ 21 - 40
examples/example_win32_directx12/main.cpp

@@ -102,8 +102,8 @@ bool CreateDeviceD3D(HWND hWnd);
 void CleanupDeviceD3D();
 void CleanupDeviceD3D();
 void CreateRenderTarget();
 void CreateRenderTarget();
 void CleanupRenderTarget();
 void CleanupRenderTarget();
-void WaitForLastSubmittedFrame();
-FrameContext* WaitForNextFrameResources();
+void WaitForPendingOperations();
+FrameContext* WaitForNextFrameContext();
 LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
 LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
 
 // Main code
 // Main code
@@ -269,7 +269,7 @@ int main(int, char**)
         // Rendering
         // Rendering
         ImGui::Render();
         ImGui::Render();
 
 
-        FrameContext* frameCtx = WaitForNextFrameResources();
+        FrameContext* frameCtx = WaitForNextFrameContext();
         UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
         UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
         frameCtx->CommandAllocator->Reset();
         frameCtx->CommandAllocator->Reset();
 
 
@@ -295,6 +295,8 @@ int main(int, char**)
         g_pd3dCommandList->Close();
         g_pd3dCommandList->Close();
 
 
         g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
         g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
+        g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue);
+        frameCtx->FenceValue = g_fenceLastSignaledValue;
 
 
         // Update and Render additional Platform Windows
         // Update and Render additional Platform Windows
         if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
         if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
@@ -307,14 +309,10 @@ int main(int, char**)
         HRESULT hr = g_pSwapChain->Present(1, 0);   // Present with vsync
         HRESULT hr = g_pSwapChain->Present(1, 0);   // Present with vsync
         //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
         //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
         g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
         g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
-
-        UINT64 fenceValue = g_fenceLastSignaledValue + 1;
-        g_pd3dCommandQueue->Signal(g_fence, fenceValue);
-        g_fenceLastSignaledValue = fenceValue;
-        frameCtx->FenceValue = fenceValue;
+        g_frameIndex++;
     }
     }
 
 
-    WaitForLastSubmittedFrame();
+    WaitForPendingOperations();
 
 
     // Cleanup
     // Cleanup
     ImGui_ImplDX12_Shutdown();
     ImGui_ImplDX12_Shutdown();
@@ -484,49 +482,33 @@ void CreateRenderTarget()
 
 
 void CleanupRenderTarget()
 void CleanupRenderTarget()
 {
 {
-    WaitForLastSubmittedFrame();
+    WaitForPendingOperations();
 
 
     for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
     for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
         if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; }
         if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; }
 }
 }
 
 
-void WaitForLastSubmittedFrame()
+void WaitForPendingOperations()
 {
 {
-    FrameContext* frameCtx = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT];
-
-    UINT64 fenceValue = frameCtx->FenceValue;
-    if (fenceValue == 0)
-        return; // No fence was signaled
-
-    frameCtx->FenceValue = 0;
-    if (g_fence->GetCompletedValue() >= fenceValue)
-        return;
+    g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue);
 
 
-    g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
-    WaitForSingleObject(g_fenceEvent, INFINITE);
+    g_fence->SetEventOnCompletion(g_fenceLastSignaledValue, g_fenceEvent);
+    ::WaitForSingleObject(g_fenceEvent, INFINITE);
 }
 }
 
 
-FrameContext* WaitForNextFrameResources()
+FrameContext* WaitForNextFrameContext()
 {
 {
-    UINT nextFrameIndex = g_frameIndex + 1;
-    g_frameIndex = nextFrameIndex;
-
-    HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, nullptr };
-    DWORD numWaitableObjects = 1;
-
-    FrameContext* frameCtx = &g_frameContext[nextFrameIndex % APP_NUM_FRAMES_IN_FLIGHT];
-    UINT64 fenceValue = frameCtx->FenceValue;
-    if (fenceValue != 0) // means no fence was signaled
+    FrameContext* frame_context = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT];
+    if (g_fence->GetCompletedValue() < frame_context->FenceValue)
     {
     {
-        frameCtx->FenceValue = 0;
-        g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
-        waitableObjects[1] = g_fenceEvent;
-        numWaitableObjects = 2;
+        g_fence->SetEventOnCompletion(frame_context->FenceValue, g_fenceEvent);
+        HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, g_fenceEvent };
+        ::WaitForMultipleObjects(2, waitableObjects, TRUE, INFINITE);
     }
     }
+    else
+        ::WaitForSingleObject(g_hSwapChainWaitableObject, INFINITE);
 
 
-    WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);
-
-    return frameCtx;
+    return frame_context;
 }
 }
 
 
 // Forward declare message handler from imgui_impl_win32.cpp
 // Forward declare message handler from imgui_impl_win32.cpp
@@ -547,7 +529,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_SIZE:
     case WM_SIZE:
         if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED)
         if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED)
         {
         {
-            WaitForLastSubmittedFrame();
             CleanupRenderTarget();
             CleanupRenderTarget();
             HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT);
             HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT);
             assert(SUCCEEDED(result) && "Failed to resize swapchain.");
             assert(SUCCEEDED(result) && "Failed to resize swapchain.");

+ 1 - 1
imgui_widgets.cpp

@@ -5531,7 +5531,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             text_col, clip_rect.AsVec4(),
             text_col, clip_rect.AsVec4(),
             line_index->get_line_begin(buf_display, line_visible_n0),
             line_index->get_line_begin(buf_display, line_visible_n0),
             line_index->get_line_end(buf_display, line_visible_n1 - 1),
             line_index->get_line_end(buf_display, line_visible_n1 - 1),
-            wrap_width, ImDrawTextFlags_WrapKeepBlanks);
+            wrap_width, ImDrawTextFlags_WrapKeepBlanks | ImDrawTextFlags_CpuFineClip);
 
 
     // Render blinking cursor
     // Render blinking cursor
     if (render_cursor)
     if (render_cursor)