From c50c3275bec09bdcc6a7fd6c8d3310dc870ecb15 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 11 Sep 2025 07:51:30 +0200 Subject: [PATCH] Vulkan: Acquire/release swapchain images `GHOST_SwapWindowBuffers` doesn't fit well when using swapchains. In that case an approach where swap chain images are acquired and released would map better. This PR introduces `GHOST_SwapWindowBufferAcquire` and `GHOST_SwapWindowBufferRelease` to be more in line with vulkan swap chains. Previous implementation would first record all GPU commands based on the last used swap chain. In case a swapchain needed to be recreated (window resize, move to other monitor) the recorded commands would not match the swap chain and could lead to artifacts. OpenGL only implements the release functions as they don't have a mechanism to acquire a swap chain image. (Need to validate with the Metal API how this is working and adapt is needed). Currently when starting blender on a HDR capable display the first frame would be based on an sRGB surface and presented on an extended RGB (or other) surface. As these don't match the first frame could be incorrect and also lead to UBs as another surface is expected. Pull Request: https://projects.blender.org/blender/blender/pulls/145728 --- intern/ghost/GHOST_C-api.h | 27 ++++-- intern/ghost/GHOST_IContext.hh | 27 +++--- intern/ghost/GHOST_IWindow.hh | 8 +- intern/ghost/intern/GHOST_C-api.cc | 19 ++-- intern/ghost/intern/GHOST_Context.hh | 9 +- intern/ghost/intern/GHOST_ContextD3D.cc | 2 +- intern/ghost/intern/GHOST_ContextD3D.hh | 8 +- intern/ghost/intern/GHOST_ContextEGL.cc | 2 +- intern/ghost/intern/GHOST_ContextEGL.hh | 8 +- intern/ghost/intern/GHOST_ContextGLX.cc | 2 +- intern/ghost/intern/GHOST_ContextGLX.hh | 8 +- intern/ghost/intern/GHOST_ContextMTL.hh | 8 +- intern/ghost/intern/GHOST_ContextMTL.mm | 2 +- intern/ghost/intern/GHOST_ContextNone.cc | 2 +- intern/ghost/intern/GHOST_ContextNone.hh | 8 +- intern/ghost/intern/GHOST_ContextSDL.cc | 2 +- intern/ghost/intern/GHOST_ContextSDL.hh | 8 +- intern/ghost/intern/GHOST_ContextVK.cc | 91 ++++++++++++------- intern/ghost/intern/GHOST_ContextVK.hh | 15 ++- intern/ghost/intern/GHOST_ContextWGL.cc | 2 +- intern/ghost/intern/GHOST_ContextWGL.hh | 8 +- intern/ghost/intern/GHOST_Window.cc | 8 +- intern/ghost/intern/GHOST_Window.hh | 7 +- intern/ghost/intern/GHOST_WindowNULL.hh | 2 +- intern/ghost/intern/GHOST_WindowWayland.cc | 4 +- intern/ghost/intern/GHOST_WindowWayland.hh | 2 +- intern/ghost/test/gears/GHOST_C-Test.c | 3 +- intern/ghost/test/gears/GHOST_Test.cpp | 2 +- intern/ghost/test/multitest/MultiTest.c | 9 +- source/blender/gpu/vulkan/vk_backend.cc | 4 +- source/blender/gpu/vulkan/vk_context.cc | 20 ++-- source/blender/gpu/vulkan/vk_context.hh | 8 +- .../blender/windowmanager/intern/wm_draw.cc | 3 +- .../windowmanager/intern/wm_playanim.cc | 7 +- .../blender/windowmanager/intern/wm_window.cc | 14 ++- source/blender/windowmanager/wm_window.hh | 3 +- 36 files changed, 244 insertions(+), 118 deletions(-) diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 1b78196fd9f..2dea63ef0db 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -721,12 +721,19 @@ extern GHOST_TSuccess GHOST_SetWindowModifiedState(GHOST_WindowHandle windowhand extern GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle, GHOST_TWindowOrder order); +/** + * Acquire a swap chain buffer. + * \param windowhandle: The handle to the window. + * \return A success indicator. + */ +extern GHOST_TSuccess GHOST_SwapWindowBufferAcquire(GHOST_WindowHandle windowhandle); + /** * Swaps front and back buffers of a window. * \param windowhandle: The handle to the window. * \return A success indicator. */ -extern GHOST_TSuccess GHOST_SwapWindowBuffers(GHOST_WindowHandle windowhandle); +extern GHOST_TSuccess GHOST_SwapWindowBufferRelease(GHOST_WindowHandle windowhandle); /** * Sets the swap interval for #swapBuffers. @@ -1299,20 +1306,20 @@ void GHOST_GetVulkanHandles(GHOST_ContextHandle context, GHOST_VulkanHandles *r_ * * \param context: GHOST context handle of a vulkan context to * get the Vulkan handles from. - * \param swap_buffers_pre_callback: Function pointer to be called at the beginning of swapBuffers. - * Inside this callback the next swap-chain image needs to be acquired and filled. - * \param swap_buffers_post_callback: Function to be called at th end of swapBuffers. swapBuffers - * can recreate the swap-chain. When this is done the application should be informed by those - * changes. + * \param swap_buffer_draw_callback: Function pointer to be called when acquired swap buffer is + * released, allowing Vulkan backend to update the swap chain. + * \param swap_buffer_acquired_callback: Function to be called at when swap buffer is acquired. + * Allowing Vulkan backend to update the framebuffer. It is also called when no swap chain + * exists indicating that the window was minimuzed. * \param openxr_acquire_image_callback: Function to be called when an image needs to be acquired * to be drawn to an OpenXR swap-chain. - * \param openxr_release_image_callback: Function to be called after an image has been drawn to the - * OpenXR swap-chain. + * \param openxr_release_image_callback: Function to be called after an image has been drawn to + * the OpenXR swap-chain. */ void GHOST_SetVulkanSwapBuffersCallbacks( GHOST_ContextHandle context, - void (*swap_buffers_pre_callback)(const GHOST_VulkanSwapChainData *), - void (*swap_buffers_post_callback)(void), + void (*swap_buffer_draw_callback)(const GHOST_VulkanSwapChainData *), + void (*swap_buffer_acquired_callback)(void), void (*openxr_acquire_image_callback)(GHOST_VulkanOpenXRData *), void (*openxr_release_image_callback)(GHOST_VulkanOpenXRData *)); diff --git a/intern/ghost/GHOST_IContext.hh b/intern/ghost/GHOST_IContext.hh index a6331a1487e..0b95762eb99 100644 --- a/intern/ghost/GHOST_IContext.hh +++ b/intern/ghost/GHOST_IContext.hh @@ -51,11 +51,17 @@ class GHOST_IContext { * \return The ID of an OpenGL frame-buffer object. */ virtual unsigned int getDefaultFramebuffer() = 0; + /** + * Acquire next buffer for drawing. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess swapBufferAcquire() = 0; + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - virtual GHOST_TSuccess swapBuffers() = 0; + virtual GHOST_TSuccess swapBufferRelease() = 0; #ifdef WITH_VULKAN_BACKEND /** @@ -91,20 +97,19 @@ class GHOST_IContext { * * \param context: GHOST context handle of a vulkan context to * get the Vulkan handles from. - * \param swap_buffers_pre_callback: Function pointer to be called at the beginning of - * swapBuffers. Inside this callback the next swap-chain image needs to be acquired and - * filled. - * \param swap_buffers_post_callback: Function to be called at th end of swapBuffers. - * swapBuffers can recreate the swap-chain. When this is done the application should be - * informed by those changes. - * \param openxr_acquire_image_callback: Function to be called when an - * image needs to be acquired to be drawn to an OpenXR swap-chain. + * \param swap_buffer_draw_callback: Function pointer to be called when acquired swap buffer is + * released, allowing Vulkan backend to update the swap chain. + * \param swap_buffer_acquired_callback: Function to be called at when swap buffer is acquired. + * Allowing Vulkan backend to update the framebuffer. It is also called when no swap chain + * exists indicating that the window was minimuzed. + * \param openxr_acquire_image_callback: Function to be called when an image needs to be acquired + * to be drawn to an OpenXR swap-chain. * \param openxr_release_image_callback: Function to be called after an image has been drawn to * the OpenXR swap-chain. */ virtual GHOST_TSuccess setVulkanSwapBuffersCallbacks( - std::function swap_buffers_pre_callback, - std::function swap_buffers_post_callback, + std::function swap_buffer_draw_callback, + std::function swap_buffer_acquired_callback, std::function openxr_acquire_framebuffer_image_callback, std::function openxr_release_framebuffer_image_callback) = 0; #endif diff --git a/intern/ghost/GHOST_IWindow.hh b/intern/ghost/GHOST_IWindow.hh index b7a95bed10b..0725dff60e5 100644 --- a/intern/ghost/GHOST_IWindow.hh +++ b/intern/ghost/GHOST_IWindow.hh @@ -205,11 +205,17 @@ class GHOST_IWindow { */ virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 0; + /** + * Acquire the next buffer of the swap chain. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess swapBufferAcquire() = 0; + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - virtual GHOST_TSuccess swapBuffers() = 0; + virtual GHOST_TSuccess swapBufferRelease() = 0; /** * Sets the swap interval for #swapBuffers. diff --git a/intern/ghost/intern/GHOST_C-api.cc b/intern/ghost/intern/GHOST_C-api.cc index c10e0b20f24..913e0f069d4 100644 --- a/intern/ghost/intern/GHOST_C-api.cc +++ b/intern/ghost/intern/GHOST_C-api.cc @@ -711,11 +711,18 @@ GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle, GHOST_TWind return window->setOrder(order); } -GHOST_TSuccess GHOST_SwapWindowBuffers(GHOST_WindowHandle windowhandle) +GHOST_TSuccess GHOST_SwapWindowBufferAcquire(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - return window->swapBuffers(); + return window->swapBufferAcquire(); +} + +GHOST_TSuccess GHOST_SwapWindowBufferRelease(GHOST_WindowHandle windowhandle) +{ + GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; + + return window->swapBufferRelease(); } GHOST_TSuccess GHOST_SetSwapInterval(GHOST_WindowHandle windowhandle, int interval) @@ -1273,14 +1280,14 @@ void GHOST_GetVulkanHandles(GHOST_ContextHandle contexthandle, GHOST_VulkanHandl void GHOST_SetVulkanSwapBuffersCallbacks( GHOST_ContextHandle contexthandle, - void (*swap_buffers_pre_callback)(const GHOST_VulkanSwapChainData *), - void (*swap_buffers_post_callback)(void), + void (*swap_buffer_draw_callback)(const GHOST_VulkanSwapChainData *), + void (*swap_buffer_acquired_callback)(void), void (*openxr_acquire_image_callback)(GHOST_VulkanOpenXRData *), void (*openxr_release_image_callback)(GHOST_VulkanOpenXRData *)) { GHOST_IContext *context = (GHOST_IContext *)contexthandle; - context->setVulkanSwapBuffersCallbacks(swap_buffers_pre_callback, - swap_buffers_post_callback, + context->setVulkanSwapBuffersCallbacks(swap_buffer_draw_callback, + swap_buffer_acquired_callback, openxr_acquire_image_callback, openxr_release_image_callback); } diff --git a/intern/ghost/intern/GHOST_Context.hh b/intern/ghost/intern/GHOST_Context.hh index 2214d86fa6d..cd3bfbb0022 100644 --- a/intern/ghost/intern/GHOST_Context.hh +++ b/intern/ghost/intern/GHOST_Context.hh @@ -43,8 +43,11 @@ class GHOST_Context : public GHOST_IContext { return active_context_; } + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override = 0; + /** \copydoc #GHOST_IContext::swapBuffers */ - GHOST_TSuccess swapBuffers() override = 0; + GHOST_TSuccess swapBufferRelease() override = 0; /** \copydoc #GHOST_IContext::activateDrawingContext */ GHOST_TSuccess activateDrawingContext() override = 0; @@ -155,8 +158,8 @@ class GHOST_Context : public GHOST_IContext { /** \copydoc #GHOST_IContext::setVulkanSwapBuffersCallbacks */ virtual GHOST_TSuccess setVulkanSwapBuffersCallbacks( - std::function /*swap_buffers_pre_callback*/, - std::function /*swap_buffers_post_callback*/, + std::function /*swap_buffer_draw_callback*/, + std::function /*swap_buffer_acquired_callback*/, std::function /*openxr_acquire_framebuffer_image_callback*/, std::function /*openxr_release_framebuffer_image_callback*/) override diff --git a/intern/ghost/intern/GHOST_ContextD3D.cc b/intern/ghost/intern/GHOST_ContextD3D.cc index e50d555d5b0..b5baec045c1 100644 --- a/intern/ghost/intern/GHOST_ContextD3D.cc +++ b/intern/ghost/intern/GHOST_ContextD3D.cc @@ -32,7 +32,7 @@ GHOST_ContextD3D::~GHOST_ContextD3D() device_ctx_->Release(); } -GHOST_TSuccess GHOST_ContextD3D::swapBuffers() +GHOST_TSuccess GHOST_ContextD3D::swapBufferRelease() { return GHOST_kSuccess; } diff --git a/intern/ghost/intern/GHOST_ContextD3D.hh b/intern/ghost/intern/GHOST_ContextD3D.hh index feb811a9f7e..48de1ee7c22 100644 --- a/intern/ghost/intern/GHOST_ContextD3D.hh +++ b/intern/ghost/intern/GHOST_ContextD3D.hh @@ -26,11 +26,17 @@ class GHOST_ContextD3D : public GHOST_Context { GHOST_ContextD3D(const GHOST_ContextParams &context_params, HWND hWnd); ~GHOST_ContextD3D() override; + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override + { + return GHOST_kSuccess; + } + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** * Activates the drawing context of this window. diff --git a/intern/ghost/intern/GHOST_ContextEGL.cc b/intern/ghost/intern/GHOST_ContextEGL.cc index 4c623c64bc3..adb88fe0904 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.cc +++ b/intern/ghost/intern/GHOST_ContextEGL.cc @@ -246,7 +246,7 @@ GHOST_ContextEGL::~GHOST_ContextEGL() } } -GHOST_TSuccess GHOST_ContextEGL::swapBuffers() +GHOST_TSuccess GHOST_ContextEGL::swapBufferRelease() { return EGL_CHK(::eglSwapBuffers(display_, surface_)) ? GHOST_kSuccess : GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_ContextEGL.hh b/intern/ghost/intern/GHOST_ContextEGL.hh index 5f5f620ec7b..a26cd4926d0 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.hh +++ b/intern/ghost/intern/GHOST_ContextEGL.hh @@ -50,11 +50,17 @@ class GHOST_ContextEGL : public GHOST_Context { */ ~GHOST_ContextEGL() override; + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override + { + return GHOST_kSuccess; + } + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** * Activates the drawing context of this window. diff --git a/intern/ghost/intern/GHOST_ContextGLX.cc b/intern/ghost/intern/GHOST_ContextGLX.cc index bd5ed6c16a2..35584790b87 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.cc +++ b/intern/ghost/intern/GHOST_ContextGLX.cc @@ -73,7 +73,7 @@ GHOST_ContextGLX::~GHOST_ContextGLX() } } -GHOST_TSuccess GHOST_ContextGLX::swapBuffers() +GHOST_TSuccess GHOST_ContextGLX::swapBufferRelease() { ::glXSwapBuffers(display_, window_); diff --git a/intern/ghost/intern/GHOST_ContextGLX.hh b/intern/ghost/intern/GHOST_ContextGLX.hh index 2707dc2e24e..6ed91793e34 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.hh +++ b/intern/ghost/intern/GHOST_ContextGLX.hh @@ -44,11 +44,17 @@ class GHOST_ContextGLX : public GHOST_Context { */ ~GHOST_ContextGLX() override; + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override + { + return GHOST_kSuccess; + } + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** * Activates the drawing context of this window. diff --git a/intern/ghost/intern/GHOST_ContextMTL.hh b/intern/ghost/intern/GHOST_ContextMTL.hh index 28cb6431412..ec8f25bc735 100644 --- a/intern/ghost/intern/GHOST_ContextMTL.hh +++ b/intern/ghost/intern/GHOST_ContextMTL.hh @@ -50,11 +50,17 @@ class GHOST_ContextMTL : public GHOST_Context { */ ~GHOST_ContextMTL() override; + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override + { + return GHOST_kSuccess; + } + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** * Activates the drawing context of this window. diff --git a/intern/ghost/intern/GHOST_ContextMTL.mm b/intern/ghost/intern/GHOST_ContextMTL.mm index 051add2bf4a..66da5873514 100644 --- a/intern/ghost/intern/GHOST_ContextMTL.mm +++ b/intern/ghost/intern/GHOST_ContextMTL.mm @@ -138,7 +138,7 @@ GHOST_ContextMTL::~GHOST_ContextMTL() } } -GHOST_TSuccess GHOST_ContextMTL::swapBuffers() +GHOST_TSuccess GHOST_ContextMTL::swapBufferRelease() { if (metal_view_) { metalSwapBuffers(); diff --git a/intern/ghost/intern/GHOST_ContextNone.cc b/intern/ghost/intern/GHOST_ContextNone.cc index 25614d4ac4e..c7b7f731608 100644 --- a/intern/ghost/intern/GHOST_ContextNone.cc +++ b/intern/ghost/intern/GHOST_ContextNone.cc @@ -10,7 +10,7 @@ #include "GHOST_ContextNone.hh" -GHOST_TSuccess GHOST_ContextNone::swapBuffers() +GHOST_TSuccess GHOST_ContextNone::swapBufferRelease() { return GHOST_kSuccess; } diff --git a/intern/ghost/intern/GHOST_ContextNone.hh b/intern/ghost/intern/GHOST_ContextNone.hh index 55ca1807e5c..0b776d6a832 100644 --- a/intern/ghost/intern/GHOST_ContextNone.hh +++ b/intern/ghost/intern/GHOST_ContextNone.hh @@ -16,11 +16,17 @@ class GHOST_ContextNone : public GHOST_Context { public: GHOST_ContextNone(const GHOST_ContextParams &context_params) : GHOST_Context(context_params) {} + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override + { + return GHOST_kSuccess; + } + /** * Dummy function * \return Always succeeds */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** * Dummy function diff --git a/intern/ghost/intern/GHOST_ContextSDL.cc b/intern/ghost/intern/GHOST_ContextSDL.cc index 5b7a51cc5df..f62976eecb4 100644 --- a/intern/ghost/intern/GHOST_ContextSDL.cc +++ b/intern/ghost/intern/GHOST_ContextSDL.cc @@ -61,7 +61,7 @@ GHOST_ContextSDL::~GHOST_ContextSDL() } } -GHOST_TSuccess GHOST_ContextSDL::swapBuffers() +GHOST_TSuccess GHOST_ContextSDL::swapBufferRelease() { SDL_GL_SwapWindow(window_); diff --git a/intern/ghost/intern/GHOST_ContextSDL.hh b/intern/ghost/intern/GHOST_ContextSDL.hh index e504235c038..4d0e918afc7 100644 --- a/intern/ghost/intern/GHOST_ContextSDL.hh +++ b/intern/ghost/intern/GHOST_ContextSDL.hh @@ -44,11 +44,17 @@ class GHOST_ContextSDL : public GHOST_Context { */ ~GHOST_ContextSDL() override; + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override + { + return GHOST_kSuccess; + } + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** * Activates the drawing context of this window. diff --git a/intern/ghost/intern/GHOST_ContextVK.cc b/intern/ghost/intern/GHOST_ContextVK.cc index 8928814bc77..66aeb68211b 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cc +++ b/intern/ghost/intern/GHOST_ContextVK.cc @@ -696,8 +696,13 @@ GHOST_ContextVK::~GHOST_ContextVK() } } -GHOST_TSuccess GHOST_ContextVK::swapBuffers() +GHOST_TSuccess GHOST_ContextVK::swapBufferAcquire() { + if (acquired_swapchain_image_index_.has_value()) { + assert(false); + return GHOST_kFailure; + } + GHOST_DeviceVK &device_vk = vulkan_instance->device.value(); VkDevice vk_device = device_vk.vk_device; @@ -708,7 +713,7 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers() * submission fence to be signaled, to ensure the invariant holds for the next call to * `swapBuffers`. * - * We will pass the current GHOST_Frame to the swap_buffers_pre_callback_ for command buffer + * We will pass the current GHOST_Frame to the swap_buffer_draw_callback_ for command buffer * submission, and it is the responsibility of that callback to use the current GHOST_Frame's * fence for it's submission fence. Since the callback is called after we wait for the next frame * to be complete, it is also safe in the callback to clean up resources associated with the next @@ -749,8 +754,8 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers() } #endif } - /* There is no valid swapchain as the previous window was minimized. User can have maximized the - * window so we need to check if the swapchain can be created. */ + /* there is no valid swapchain when the previous window was minimized. User can have maximized + * the window so we need to check if the swapchain has to be created. */ if (swapchain_ == VK_NULL_HANDLE) { recreateSwapchain(use_hdr_swapchain); } @@ -776,21 +781,22 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers() } } - /* Fast path for invalid swapchains. When not valid we don't acquire/present, but we do render to - * make sure the render graphs don't keep memory allocated that isn't used. */ - if (swapchain_ == VK_NULL_HANDLE) { - CLOG_TRACE( - &LOG, - "Swap-chain invalid (due to minimized window), perform rendering to reduce render graph " - "resources."); - GHOST_VulkanSwapChainData swap_chain_data = {}; - if (swap_buffers_pre_callback_) { - swap_buffers_pre_callback_(&swap_chain_data); - } - if (swap_buffers_post_callback_) { - swap_buffers_post_callback_(); - } + /* Acquired callback is also called when there is no swapchain. + * + * When acquiring swap chain (image) and the swap chain is discarded (window has been minimized). + * We have trigger a last acquired callback to reduce the attachments of the GPUFramebuffer. + * Vulkan backend will retrieve the data (getVulkanSwapChainFormat) containing a render extent of + * 0,0. + * + * The next frame window manager will detect that the window is minimized and doesn't draw the + * window at all. + */ + if (swap_buffer_acquired_callback_) { + swap_buffer_acquired_callback_(); + } + if (swapchain_ == VK_NULL_HANDLE) { + CLOG_TRACE(&LOG, "Swap-chain unavailable (minimized window)."); return GHOST_kSuccess; } @@ -798,7 +804,35 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers() "Acquired swap-chain image (render_frame=%lu, image_index=%u)", render_frame_, image_index); + acquired_swapchain_image_index_ = image_index; + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::swapBufferRelease() +{ + /* Minimized windows don't have a swapchain and swapchain image. In this case we perform the draw + * to release render graph and discarded resources. */ + if (swapchain_ == VK_NULL_HANDLE) { + GHOST_VulkanSwapChainData swap_chain_data = {}; + if (swap_buffer_draw_callback_) { + swap_buffer_draw_callback_(&swap_chain_data); + } + return GHOST_kSuccess; + } + + if (!acquired_swapchain_image_index_.has_value()) { + assert(false); + return GHOST_kFailure; + } + GHOST_DeviceVK &device_vk = vulkan_instance->device.value(); + VkDevice vk_device = device_vk.vk_device; + + uint32_t image_index = acquired_swapchain_image_index_.value(); GHOST_SwapchainImage &swapchain_image = swapchain_images_[image_index]; + GHOST_Frame &submission_frame_data = frame_data_[render_frame_]; + const bool use_hdr_swapchain = hdr_info_ && hdr_info_->hdr_enabled && + device_vk.use_vk_ext_swapchain_colorspace; GHOST_VulkanSwapChainData swap_chain_data; swap_chain_data.image = swapchain_image.vk_image; @@ -810,8 +844,8 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers() swap_chain_data.sdr_scale = (hdr_info_) ? hdr_info_->sdr_white_level : 1.0f; vkResetFences(vk_device, 1, &submission_frame_data.submission_fence); - if (swap_buffers_pre_callback_) { - swap_buffers_pre_callback_(&swap_chain_data); + if (swap_buffer_draw_callback_) { + swap_buffer_draw_callback_(&swap_chain_data); } VkPresentInfoKHR present_info = {}; @@ -828,22 +862,17 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers() std::scoped_lock lock(device_vk.queue_mutex); present_result = vkQueuePresentKHR(device_vk.generic_queue, &present_info); } + acquired_swapchain_image_index_.reset(); if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR) { recreateSwapchain(use_hdr_swapchain); - if (swap_buffers_post_callback_) { - swap_buffers_post_callback_(); - } return GHOST_kSuccess; } if (present_result != VK_SUCCESS) { CLOG_ERROR(&LOG, "Vulkan: failed to present swap-chain image : %s", vulkan_error_as_string(present_result)); - } - - if (swap_buffers_post_callback_) { - swap_buffers_post_callback_(); + return GHOST_kFailure; } return GHOST_kSuccess; @@ -888,13 +917,13 @@ GHOST_TSuccess GHOST_ContextVK::getVulkanHandles(GHOST_VulkanHandles &r_handles) } GHOST_TSuccess GHOST_ContextVK::setVulkanSwapBuffersCallbacks( - std::function swap_buffers_pre_callback, - std::function swap_buffers_post_callback, + std::function swap_buffer_draw_callback, + std::function swap_buffer_acquired_callback, std::function openxr_acquire_framebuffer_image_callback, std::function openxr_release_framebuffer_image_callback) { - swap_buffers_pre_callback_ = swap_buffers_pre_callback; - swap_buffers_post_callback_ = swap_buffers_post_callback; + swap_buffer_draw_callback_ = swap_buffer_draw_callback; + swap_buffer_acquired_callback_ = swap_buffer_acquired_callback; openxr_acquire_framebuffer_image_callback_ = openxr_acquire_framebuffer_image_callback; openxr_release_framebuffer_image_callback_ = openxr_release_framebuffer_image_callback; return GHOST_kSuccess; diff --git a/intern/ghost/intern/GHOST_ContextVK.hh b/intern/ghost/intern/GHOST_ContextVK.hh index f5fcc30a540..9ebf9bf0996 100644 --- a/intern/ghost/intern/GHOST_ContextVK.hh +++ b/intern/ghost/intern/GHOST_ContextVK.hh @@ -33,6 +33,7 @@ # endif #endif +#include #include #ifndef GHOST_OPENGL_VK_CONTEXT_FLAGS @@ -125,11 +126,13 @@ class GHOST_ContextVK : public GHOST_Context { */ ~GHOST_ContextVK() override; + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override; /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** * Activates the drawing context of this window. @@ -165,8 +168,8 @@ class GHOST_ContextVK : public GHOST_Context { GHOST_TSuccess getVulkanSwapChainFormat(GHOST_VulkanSwapChainData *r_swap_chain_data) override; GHOST_TSuccess setVulkanSwapBuffersCallbacks( - std::function swap_buffers_pre_callback, - std::function swap_buffers_post_callback, + std::function swap_buffer_draw_callback, + std::function swap_buffer_acquired_callback, std::function openxr_acquire_framebuffer_image_callback, std::function openxr_release_framebuffer_image_callback) override; @@ -237,8 +240,10 @@ class GHOST_ContextVK : public GHOST_Context { VkSurfaceFormatKHR surface_format_; bool use_hdr_swapchain_; - std::function swap_buffers_pre_callback_; - std::function swap_buffers_post_callback_; + std::optional acquired_swapchain_image_index_; + + std::function swap_buffer_draw_callback_; + std::function swap_buffer_acquired_callback_; std::function openxr_acquire_framebuffer_image_callback_; std::function openxr_release_framebuffer_image_callback_; diff --git a/intern/ghost/intern/GHOST_ContextWGL.cc b/intern/ghost/intern/GHOST_ContextWGL.cc index 0762b577145..b2adf52b6ee 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.cc +++ b/intern/ghost/intern/GHOST_ContextWGL.cc @@ -83,7 +83,7 @@ GHOST_ContextWGL::~GHOST_ContextWGL() #endif } -GHOST_TSuccess GHOST_ContextWGL::swapBuffers() +GHOST_TSuccess GHOST_ContextWGL::swapBufferRelease() { return WIN32_CHK(::SwapBuffers(h_DC_)) ? GHOST_kSuccess : GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_ContextWGL.hh b/intern/ghost/intern/GHOST_ContextWGL.hh index a97ad414e97..26d9b1ddd39 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.hh +++ b/intern/ghost/intern/GHOST_ContextWGL.hh @@ -39,11 +39,17 @@ class GHOST_ContextWGL : public GHOST_Context { */ ~GHOST_ContextWGL() override; + /** \copydoc #GHOST_IContext::swapBuffersAcquire */ + GHOST_TSuccess swapBufferAcquire() override + { + return GHOST_kSuccess; + } + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** * Activates the drawing context of this window. diff --git a/intern/ghost/intern/GHOST_Window.cc b/intern/ghost/intern/GHOST_Window.cc index 143bac01a8e..c392da7cdf3 100644 --- a/intern/ghost/intern/GHOST_Window.cc +++ b/intern/ghost/intern/GHOST_Window.cc @@ -102,9 +102,13 @@ GHOST_IContext *GHOST_Window::getDrawingContext() return context_; } -GHOST_TSuccess GHOST_Window::swapBuffers() +GHOST_TSuccess GHOST_Window::swapBufferAcquire() { - return context_->swapBuffers(); + return context_->swapBufferAcquire(); +} +GHOST_TSuccess GHOST_Window::swapBufferRelease() +{ + return context_->swapBufferRelease(); } GHOST_TSuccess GHOST_Window::setSwapInterval(int interval) diff --git a/intern/ghost/intern/GHOST_Window.hh b/intern/ghost/intern/GHOST_Window.hh index aaa8c4fef65..f7536de6925 100644 --- a/intern/ghost/intern/GHOST_Window.hh +++ b/intern/ghost/intern/GHOST_Window.hh @@ -56,7 +56,8 @@ class GHOST_Window : public GHOST_IWindow { * virtual GHOST_TWindowState getState() const = 0; * virtual GHOST_TSuccess setState(GHOST_TWindowState state) = 0; * virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 0; - * virtual GHOST_TSuccess swapBuffers() = 0; + * virtual GHOST_TSuccess swapBufferAcquire() = 0; + * virtual GHOST_TSuccess swapBufferRelease() = 0; * virtual GHOST_TSuccess setSwapInterval() = 0; * virtual GHOST_TSuccess getSwapInterval(int& interval_out) = 0; * virtual GHOST_TSuccess activateDrawingContext() = 0; @@ -195,8 +196,10 @@ class GHOST_Window : public GHOST_IWindow { /** \copydoc #GHOST_IWindow::getDrawingContext */ GHOST_IContext *getDrawingContext() override; + /** \copydoc #GHOST_IWindow::swapBufferAcquire */ + GHOST_TSuccess swapBufferAcquire() override; /** \copydoc #GHOST_IWindow::swapBuffers */ - GHOST_TSuccess swapBuffers() override; + GHOST_TSuccess swapBufferRelease() override; /** \copydoc #GHOST_IWindow::activateDrawingContext */ GHOST_TSuccess activateDrawingContext() override; diff --git a/intern/ghost/intern/GHOST_WindowNULL.hh b/intern/ghost/intern/GHOST_WindowNULL.hh index b81cf6dd8b1..06c6c898d4d 100644 --- a/intern/ghost/intern/GHOST_WindowNULL.hh +++ b/intern/ghost/intern/GHOST_WindowNULL.hh @@ -105,7 +105,7 @@ class GHOST_WindowNULL : public GHOST_Window { outX = inX; outY = inY; } - GHOST_TSuccess swapBuffers() override + GHOST_TSuccess swapBufferRelease() override { return GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_WindowWayland.cc b/intern/ghost/intern/GHOST_WindowWayland.cc index 954e812be26..db92925e72b 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cc +++ b/intern/ghost/intern/GHOST_WindowWayland.cc @@ -2170,10 +2170,10 @@ GHOST_WindowWayland::~GHOST_WindowWayland() } #ifdef USE_EVENT_BACKGROUND_THREAD -GHOST_TSuccess GHOST_WindowWayland::swapBuffers() +GHOST_TSuccess GHOST_WindowWayland::swapBufferRelease() { GHOST_ASSERT(system_->main_thread_id == std::this_thread::get_id(), "Only from main thread!"); - return GHOST_Window::swapBuffers(); + return GHOST_Window::swapBufferRelease(); } #endif /* USE_EVENT_BACKGROUND_THREAD */ diff --git a/intern/ghost/intern/GHOST_WindowWayland.hh b/intern/ghost/intern/GHOST_WindowWayland.hh index 4aeab9e0acf..6e78fb3faae 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.hh +++ b/intern/ghost/intern/GHOST_WindowWayland.hh @@ -85,7 +85,7 @@ class GHOST_WindowWayland : public GHOST_Window { /* Ghost API */ #ifdef USE_EVENT_BACKGROUND_THREAD - GHOST_TSuccess swapBuffers() override; /* Only for assertion. */ + GHOST_TSuccess swapBufferRelease() override; /* Only for assertion. */ #endif uint16_t getDPIHint() override; diff --git a/intern/ghost/test/gears/GHOST_C-Test.c b/intern/ghost/test/gears/GHOST_C-Test.c index 703165ed6a4..b725f8b7e4f 100644 --- a/intern/ghost/test/gears/GHOST_C-Test.c +++ b/intern/ghost/test/gears/GHOST_C-Test.c @@ -376,8 +376,9 @@ bool processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr user_data) break; } setViewPortGL(window2); + GHOST_SwapWindowBufferAcquire(window2); drawGL(); - GHOST_SwapWindowBuffers(window2); + GHOST_SwapWindowBufferRelease(window2); break; } default: diff --git a/intern/ghost/test/gears/GHOST_Test.cpp b/intern/ghost/test/gears/GHOST_Test.cpp index 7d8bfec7531..15f051e1c56 100644 --- a/intern/ghost/test/gears/GHOST_Test.cpp +++ b/intern/ghost/test/gears/GHOST_Test.cpp @@ -609,7 +609,7 @@ bool Application::processEvent(const GHOST_IEvent *event) RenderScene(); glPopMatrix(); } - window2->swapBuffers(); + window2->swapBufferRelease(); break; } diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c index 50a3cb8602f..16083408ae3 100644 --- a/intern/ghost/test/multitest/MultiTest.c +++ b/intern/ghost/test/multitest/MultiTest.c @@ -141,6 +141,7 @@ static void mainwindow_log(MainWindow *mw, char *str) static void mainwindow_do_draw(MainWindow *mw) { GHOST_ActivateWindowDrawingContext(mw->win); + GHOST_SwapWindowBufferAcquire(mw->win); GPU_context_active_set(mw->gpu_context); if (mw->lmbut[0]) { @@ -154,7 +155,7 @@ static void mainwindow_do_draw(MainWindow *mw) glColor3f(0.5, 0.6, 0.8); glRecti(mw->tmouse[0] - 5, mw->tmouse[1] - 5, mw->tmouse[0] + 5, mw->tmouse[1] + 5); - GHOST_SwapWindowBuffers(mw->win); + GHOST_SwapWindowBufferRelease(mw->win); } static void mainwindow_do_reshape(MainWindow *mw) @@ -432,6 +433,7 @@ static void loggerwindow_do_draw(LoggerWindow *lw) int sb_rect[2][2], sb_thumb[2][2]; GHOST_ActivateWindowDrawingContext(lw->win); + GHOST_SwapWindowBufferAcquire(lw->win); GPU_context_active_set(lw->gpu_context); glClearColor(1, 1, 1, 1); @@ -468,7 +470,7 @@ static void loggerwindow_do_draw(LoggerWindow *lw) BLF_draw(lw->font, line, 256); // XXX } - GHOST_SwapWindowBuffers(lw->win); + GHOST_SwapWindowBufferRelease(lw->win); immDeactivate(); } @@ -657,6 +659,7 @@ typedef struct { static void extrawindow_do_draw(ExtraWindow *ew) { GHOST_ActivateWindowDrawingContext(ew->win); + GHOST_SwapWindowBufferAcquire(eq->win); GPU_context_active_set(ew->gpu_context); glClearColor(1, 1, 1, 1); @@ -665,7 +668,7 @@ static void extrawindow_do_draw(ExtraWindow *ew) glColor3f(0.8, 0.8, 0.8); glRecti(10, 10, ew->size[0] - 10, ew->size[1] - 10); - GHOST_SwapWindowBuffers(ew->win); + GHOST_SwapWindowBufferRelease(ew->win); } static void extrawindow_do_reshape(ExtraWindow *ew) diff --git a/source/blender/gpu/vulkan/vk_backend.cc b/source/blender/gpu/vulkan/vk_backend.cc index 9389190dc66..c82931e27a2 100644 --- a/source/blender/gpu/vulkan/vk_backend.cc +++ b/source/blender/gpu/vulkan/vk_backend.cc @@ -589,8 +589,8 @@ Context *VKBackend::context_alloc(void *ghost_window, void *ghost_context) VKContext *context = new VKContext(ghost_window, ghost_context); device.context_register(*context); GHOST_SetVulkanSwapBuffersCallbacks((GHOST_ContextHandle)ghost_context, - VKContext::swap_buffers_pre_callback, - VKContext::swap_buffers_post_callback, + VKContext::swap_buffer_draw_callback, + VKContext::swap_buffer_acquired_callback, VKContext::openxr_acquire_framebuffer_image_callback, VKContext::openxr_release_framebuffer_image_callback); diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index 8947f41ef38..3a72e700882 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -348,21 +348,26 @@ render_graph::VKResourceAccessInfo &VKContext::reset_and_get_access_info() /** \name Graphics pipeline * \{ */ -void VKContext::swap_buffers_pre_callback(const GHOST_VulkanSwapChainData *swap_chain_data) +void VKContext::swap_buffer_acquired_callback() { VKContext *context = VKContext::get(); BLI_assert(context); - context->swap_buffers_pre_handler(*swap_chain_data); + context->swap_buffer_acquired_handler(); } -void VKContext::swap_buffers_post_callback() +void VKContext::swap_buffer_draw_callback(const GHOST_VulkanSwapChainData *swap_chain_data) { VKContext *context = VKContext::get(); BLI_assert(context); - context->swap_buffers_post_handler(); + context->swap_buffer_draw_handler(*swap_chain_data); } -void VKContext::swap_buffers_pre_handler(const GHOST_VulkanSwapChainData &swap_chain_data) +void VKContext::swap_buffer_acquired_handler() +{ + sync_backbuffer(); +} + +void VKContext::swap_buffer_draw_handler(const GHOST_VulkanSwapChainData &swap_chain_data) { const bool do_blit_to_swapchain = swap_chain_data.image != VK_NULL_HANDLE; const bool use_shader = swap_chain_data.surface_format.colorSpace == @@ -445,11 +450,6 @@ void VKContext::swap_buffers_pre_handler(const GHOST_VulkanSwapChainData &swap_c #endif } -void VKContext::swap_buffers_post_handler() -{ - sync_backbuffer(); -} - void VKContext::specialization_constants_set( const shader::SpecializationConstants *constants_state) { diff --git a/source/blender/gpu/vulkan/vk_context.hh b/source/blender/gpu/vulkan/vk_context.hh index bce610762ce..5de362dbf90 100644 --- a/source/blender/gpu/vulkan/vk_context.hh +++ b/source/blender/gpu/vulkan/vk_context.hh @@ -148,16 +148,16 @@ class VKContext : public Context, NonCopyable { VKDescriptorSetTracker &descriptor_set_get(); VKStateManager &state_manager_get() const; - static void swap_buffers_pre_callback(const GHOST_VulkanSwapChainData *data); - static void swap_buffers_post_callback(); + static void swap_buffer_draw_callback(const GHOST_VulkanSwapChainData *data); + static void swap_buffer_acquired_callback(); static void openxr_acquire_framebuffer_image_callback(GHOST_VulkanOpenXRData *data); static void openxr_release_framebuffer_image_callback(GHOST_VulkanOpenXRData *data); void specialization_constants_set(const shader::SpecializationConstants *constants_state); private: - void swap_buffers_pre_handler(const GHOST_VulkanSwapChainData &data); - void swap_buffers_post_handler(); + void swap_buffer_draw_handler(const GHOST_VulkanSwapChainData &data); + void swap_buffer_acquired_handler(); void openxr_acquire_framebuffer_image_handler(GHOST_VulkanOpenXRData &data); void openxr_release_framebuffer_image_handler(GHOST_VulkanOpenXRData &data); diff --git a/source/blender/windowmanager/intern/wm_draw.cc b/source/blender/windowmanager/intern/wm_draw.cc index 3bcd4821493..dba7aa1603d 100644 --- a/source/blender/windowmanager/intern/wm_draw.cc +++ b/source/blender/windowmanager/intern/wm_draw.cc @@ -1645,6 +1645,7 @@ void wm_draw_update(bContext *C) if (wm_draw_update_test_window(bmain, C, win)) { /* Sets context window+screen. */ wm_window_make_drawable(wm, win); + wm_window_swap_buffer_acquire(win); /* Notifiers for screen redraw. */ ED_screen_ensure_updated(C, wm, win); @@ -1652,7 +1653,7 @@ void wm_draw_update(bContext *C) wm_draw_window(C, win); wm_draw_update_clear_window(C, win); - wm_window_swap_buffers(win); + wm_window_swap_buffer_release(win); } } diff --git a/source/blender/windowmanager/intern/wm_playanim.cc b/source/blender/windowmanager/intern/wm_playanim.cc index a6b7b68fafd..d588fa503c1 100644 --- a/source/blender/windowmanager/intern/wm_playanim.cc +++ b/source/blender/windowmanager/intern/wm_playanim.cc @@ -676,7 +676,9 @@ static void playanim_toscreen_ex(GhostData &ghost_data, GHOST_ActivateWindowDrawingContext(ghost_data.window); GPU_render_begin(); + GHOST_SwapWindowBufferAcquire(ghost_data.window); GPUContext *restore_context = GPU_context_active_get(); + GPU_context_active_set(ghost_data.gpu_context); GPU_context_begin_frame(ghost_data.gpu_context); @@ -787,7 +789,7 @@ static void playanim_toscreen_ex(GhostData &ghost_data, } GPU_context_end_frame(ghost_data.gpu_context); - GHOST_SwapWindowBuffers(ghost_data.window); + GHOST_SwapWindowBufferRelease(ghost_data.window); GPU_context_active_set(restore_context); GPU_render_end(); } @@ -1921,6 +1923,7 @@ static std::optional wm_main_playanim_intern(int argc, const char **argv, P ps.display_ctx.size = ps.ibuf_size; + GHOST_SwapWindowBufferAcquire(ps.ghost_data.window); GPU_render_begin(); GPU_render_step(); GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f); @@ -1932,7 +1935,7 @@ static std::optional wm_main_playanim_intern(int argc, const char **argv, P playanim_gpu_matrix(); } - GHOST_SwapWindowBuffers(ps.ghost_data.window); + GHOST_SwapWindowBufferRelease(ps.ghost_data.window); GPU_render_end(); /* One of the frames was invalid or not passed in. */ diff --git a/source/blender/windowmanager/intern/wm_window.cc b/source/blender/windowmanager/intern/wm_window.cc index 0de9e585a37..976b37d7fc8 100644 --- a/source/blender/windowmanager/intern/wm_window.cc +++ b/source/blender/windowmanager/intern/wm_window.cc @@ -1005,12 +1005,13 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, UI_GetThemeColor3fv(TH_BACK, window_bg_color); /* Until screens get drawn, draw a default background using the window theme color. */ + wm_window_swap_buffer_acquire(win); GPU_clear_color(window_bg_color[0], window_bg_color[1], window_bg_color[2], 1.0f); /* Needed here, because it's used before it reads #UserDef. */ WM_window_dpi_set_userdef(win); - wm_window_swap_buffers(win); + wm_window_swap_buffer_release(win); /* Clear double buffer to avoids flickering of new windows on certain drivers, see #97600. */ GPU_clear_color(window_bg_color[0], window_bg_color[1], window_bg_color[2], 1.0f); @@ -1768,7 +1769,7 @@ static bool ghost_event_proc(GHOST_EventHandle ghost_event, GHOST_TUserDataPtr C #if 0 /* NOTE(@ideasman42): Ideally we could swap-buffers to avoid a full redraw. * however this causes window flickering on resize with LIBDECOR under WAYLAND. */ - wm_window_swap_buffers(win); + wm_window_swap_buffer_release(win); #else WM_event_add_notifier_ex(wm, win, NC_WINDOW, nullptr); #endif @@ -2725,9 +2726,14 @@ void wm_window_raise(wmWindow *win) /** \name Window Buffers * \{ */ -void wm_window_swap_buffers(wmWindow *win) +void wm_window_swap_buffer_acquire(wmWindow *win) { - GHOST_SwapWindowBuffers(static_cast(win->ghostwin)); + GHOST_SwapWindowBufferAcquire(static_cast(win->ghostwin)); +} + +void wm_window_swap_buffer_release(wmWindow *win) +{ + GHOST_SwapWindowBufferRelease(static_cast(win->ghostwin)); } void wm_window_set_swap_interval(wmWindow *win, int interval) diff --git a/source/blender/windowmanager/wm_window.hh b/source/blender/windowmanager/wm_window.hh index b86f4c13c5f..7fde735addd 100644 --- a/source/blender/windowmanager/wm_window.hh +++ b/source/blender/windowmanager/wm_window.hh @@ -100,7 +100,8 @@ void wm_window_set_size(wmWindow *win, int width, int height); /** * \brief Push rendered buffer to the screen. */ -void wm_window_swap_buffers(wmWindow *win); +void wm_window_swap_buffer_acquire(wmWindow *win); +void wm_window_swap_buffer_release(wmWindow *win); void wm_window_set_swap_interval(wmWindow *win, int interval); bool wm_window_get_swap_interval(wmWindow *win, int *r_interval);