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
This commit is contained in:
Jeroen Bakker
2025-09-11 07:51:30 +02:00
parent 9dc6a2d7f3
commit c50c3275be
36 changed files with 244 additions and 118 deletions

View File

@@ -721,12 +721,19 @@ extern GHOST_TSuccess GHOST_SetWindowModifiedState(GHOST_WindowHandle windowhand
extern GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle, extern GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle,
GHOST_TWindowOrder order); 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. * Swaps front and back buffers of a window.
* \param windowhandle: The handle to the window. * \param windowhandle: The handle to the window.
* \return A success indicator. * \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. * 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 * \param context: GHOST context handle of a vulkan context to
* get the Vulkan handles from. * get the Vulkan handles from.
* \param swap_buffers_pre_callback: Function pointer to be called at the beginning of swapBuffers. * \param swap_buffer_draw_callback: Function pointer to be called when acquired swap buffer is
* Inside this callback the next swap-chain image needs to be acquired and filled. * released, allowing Vulkan backend to update the swap chain.
* \param swap_buffers_post_callback: Function to be called at th end of swapBuffers. swapBuffers * \param swap_buffer_acquired_callback: Function to be called at when swap buffer is acquired.
* can recreate the swap-chain. When this is done the application should be informed by those * Allowing Vulkan backend to update the framebuffer. It is also called when no swap chain
* changes. * exists indicating that the window was minimuzed.
* \param openxr_acquire_image_callback: Function to be called when an image needs to be acquired * \param openxr_acquire_image_callback: Function to be called when an image needs to be acquired
* to be drawn to an OpenXR swap-chain. * 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 * \param openxr_release_image_callback: Function to be called after an image has been drawn to
* OpenXR swap-chain. * the OpenXR swap-chain.
*/ */
void GHOST_SetVulkanSwapBuffersCallbacks( void GHOST_SetVulkanSwapBuffersCallbacks(
GHOST_ContextHandle context, GHOST_ContextHandle context,
void (*swap_buffers_pre_callback)(const GHOST_VulkanSwapChainData *), void (*swap_buffer_draw_callback)(const GHOST_VulkanSwapChainData *),
void (*swap_buffers_post_callback)(void), void (*swap_buffer_acquired_callback)(void),
void (*openxr_acquire_image_callback)(GHOST_VulkanOpenXRData *), void (*openxr_acquire_image_callback)(GHOST_VulkanOpenXRData *),
void (*openxr_release_image_callback)(GHOST_VulkanOpenXRData *)); void (*openxr_release_image_callback)(GHOST_VulkanOpenXRData *));

View File

@@ -51,11 +51,17 @@ class GHOST_IContext {
* \return The ID of an OpenGL frame-buffer object. * \return The ID of an OpenGL frame-buffer object.
*/ */
virtual unsigned int getDefaultFramebuffer() = 0; 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. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
virtual GHOST_TSuccess swapBuffers() = 0; virtual GHOST_TSuccess swapBufferRelease() = 0;
#ifdef WITH_VULKAN_BACKEND #ifdef WITH_VULKAN_BACKEND
/** /**
@@ -91,20 +97,19 @@ class GHOST_IContext {
* *
* \param context: GHOST context handle of a vulkan context to * \param context: GHOST context handle of a vulkan context to
* get the Vulkan handles from. * get the Vulkan handles from.
* \param swap_buffers_pre_callback: Function pointer to be called at the beginning of * \param swap_buffer_draw_callback: Function pointer to be called when acquired swap buffer is
* swapBuffers. Inside this callback the next swap-chain image needs to be acquired and * released, allowing Vulkan backend to update the swap chain.
* filled. * \param swap_buffer_acquired_callback: Function to be called at when swap buffer is acquired.
* \param swap_buffers_post_callback: Function to be called at th end of swapBuffers. * Allowing Vulkan backend to update the framebuffer. It is also called when no swap chain
* swapBuffers can recreate the swap-chain. When this is done the application should be * exists indicating that the window was minimuzed.
* informed by those changes. * \param openxr_acquire_image_callback: Function to be called when an image needs to be acquired
* \param openxr_acquire_image_callback: Function to be called when an * to be drawn to an OpenXR swap-chain.
* 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 * \param openxr_release_image_callback: Function to be called after an image has been drawn to
* the OpenXR swap-chain. * the OpenXR swap-chain.
*/ */
virtual GHOST_TSuccess setVulkanSwapBuffersCallbacks( virtual GHOST_TSuccess setVulkanSwapBuffersCallbacks(
std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffers_pre_callback, std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffer_draw_callback,
std::function<void(void)> swap_buffers_post_callback, std::function<void(void)> swap_buffer_acquired_callback,
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback, std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback,
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback) = 0; std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback) = 0;
#endif #endif

View File

@@ -205,11 +205,17 @@ class GHOST_IWindow {
*/ */
virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 0; 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. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
virtual GHOST_TSuccess swapBuffers() = 0; virtual GHOST_TSuccess swapBufferRelease() = 0;
/** /**
* Sets the swap interval for #swapBuffers. * Sets the swap interval for #swapBuffers.

View File

@@ -711,11 +711,18 @@ GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle, GHOST_TWind
return window->setOrder(order); return window->setOrder(order);
} }
GHOST_TSuccess GHOST_SwapWindowBuffers(GHOST_WindowHandle windowhandle) GHOST_TSuccess GHOST_SwapWindowBufferAcquire(GHOST_WindowHandle windowhandle)
{ {
GHOST_IWindow *window = (GHOST_IWindow *)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) GHOST_TSuccess GHOST_SetSwapInterval(GHOST_WindowHandle windowhandle, int interval)
@@ -1273,14 +1280,14 @@ void GHOST_GetVulkanHandles(GHOST_ContextHandle contexthandle, GHOST_VulkanHandl
void GHOST_SetVulkanSwapBuffersCallbacks( void GHOST_SetVulkanSwapBuffersCallbacks(
GHOST_ContextHandle contexthandle, GHOST_ContextHandle contexthandle,
void (*swap_buffers_pre_callback)(const GHOST_VulkanSwapChainData *), void (*swap_buffer_draw_callback)(const GHOST_VulkanSwapChainData *),
void (*swap_buffers_post_callback)(void), void (*swap_buffer_acquired_callback)(void),
void (*openxr_acquire_image_callback)(GHOST_VulkanOpenXRData *), void (*openxr_acquire_image_callback)(GHOST_VulkanOpenXRData *),
void (*openxr_release_image_callback)(GHOST_VulkanOpenXRData *)) void (*openxr_release_image_callback)(GHOST_VulkanOpenXRData *))
{ {
GHOST_IContext *context = (GHOST_IContext *)contexthandle; GHOST_IContext *context = (GHOST_IContext *)contexthandle;
context->setVulkanSwapBuffersCallbacks(swap_buffers_pre_callback, context->setVulkanSwapBuffersCallbacks(swap_buffer_draw_callback,
swap_buffers_post_callback, swap_buffer_acquired_callback,
openxr_acquire_image_callback, openxr_acquire_image_callback,
openxr_release_image_callback); openxr_release_image_callback);
} }

View File

@@ -43,8 +43,11 @@ class GHOST_Context : public GHOST_IContext {
return active_context_; return active_context_;
} }
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override = 0;
/** \copydoc #GHOST_IContext::swapBuffers */ /** \copydoc #GHOST_IContext::swapBuffers */
GHOST_TSuccess swapBuffers() override = 0; GHOST_TSuccess swapBufferRelease() override = 0;
/** \copydoc #GHOST_IContext::activateDrawingContext */ /** \copydoc #GHOST_IContext::activateDrawingContext */
GHOST_TSuccess activateDrawingContext() override = 0; GHOST_TSuccess activateDrawingContext() override = 0;
@@ -155,8 +158,8 @@ class GHOST_Context : public GHOST_IContext {
/** \copydoc #GHOST_IContext::setVulkanSwapBuffersCallbacks */ /** \copydoc #GHOST_IContext::setVulkanSwapBuffersCallbacks */
virtual GHOST_TSuccess setVulkanSwapBuffersCallbacks( virtual GHOST_TSuccess setVulkanSwapBuffersCallbacks(
std::function<void(const GHOST_VulkanSwapChainData *)> /*swap_buffers_pre_callback*/, std::function<void(const GHOST_VulkanSwapChainData *)> /*swap_buffer_draw_callback*/,
std::function<void(void)> /*swap_buffers_post_callback*/, std::function<void(void)> /*swap_buffer_acquired_callback*/,
std::function<void(GHOST_VulkanOpenXRData *)> /*openxr_acquire_framebuffer_image_callback*/, std::function<void(GHOST_VulkanOpenXRData *)> /*openxr_acquire_framebuffer_image_callback*/,
std::function<void(GHOST_VulkanOpenXRData *)> /*openxr_release_framebuffer_image_callback*/) std::function<void(GHOST_VulkanOpenXRData *)> /*openxr_release_framebuffer_image_callback*/)
override override

View File

@@ -32,7 +32,7 @@ GHOST_ContextD3D::~GHOST_ContextD3D()
device_ctx_->Release(); device_ctx_->Release();
} }
GHOST_TSuccess GHOST_ContextD3D::swapBuffers() GHOST_TSuccess GHOST_ContextD3D::swapBufferRelease()
{ {
return GHOST_kSuccess; return GHOST_kSuccess;
} }

View File

@@ -26,11 +26,17 @@ class GHOST_ContextD3D : public GHOST_Context {
GHOST_ContextD3D(const GHOST_ContextParams &context_params, HWND hWnd); GHOST_ContextD3D(const GHOST_ContextParams &context_params, HWND hWnd);
~GHOST_ContextD3D() override; ~GHOST_ContextD3D() override;
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override
{
return GHOST_kSuccess;
}
/** /**
* Swaps front and back buffers of a window. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** /**
* Activates the drawing context of this window. * Activates the drawing context of this window.

View File

@@ -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; return EGL_CHK(::eglSwapBuffers(display_, surface_)) ? GHOST_kSuccess : GHOST_kFailure;
} }

View File

@@ -50,11 +50,17 @@ class GHOST_ContextEGL : public GHOST_Context {
*/ */
~GHOST_ContextEGL() override; ~GHOST_ContextEGL() override;
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override
{
return GHOST_kSuccess;
}
/** /**
* Swaps front and back buffers of a window. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** /**
* Activates the drawing context of this window. * Activates the drawing context of this window.

View File

@@ -73,7 +73,7 @@ GHOST_ContextGLX::~GHOST_ContextGLX()
} }
} }
GHOST_TSuccess GHOST_ContextGLX::swapBuffers() GHOST_TSuccess GHOST_ContextGLX::swapBufferRelease()
{ {
::glXSwapBuffers(display_, window_); ::glXSwapBuffers(display_, window_);

View File

@@ -44,11 +44,17 @@ class GHOST_ContextGLX : public GHOST_Context {
*/ */
~GHOST_ContextGLX() override; ~GHOST_ContextGLX() override;
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override
{
return GHOST_kSuccess;
}
/** /**
* Swaps front and back buffers of a window. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** /**
* Activates the drawing context of this window. * Activates the drawing context of this window.

View File

@@ -50,11 +50,17 @@ class GHOST_ContextMTL : public GHOST_Context {
*/ */
~GHOST_ContextMTL() override; ~GHOST_ContextMTL() override;
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override
{
return GHOST_kSuccess;
}
/** /**
* Swaps front and back buffers of a window. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** /**
* Activates the drawing context of this window. * Activates the drawing context of this window.

View File

@@ -138,7 +138,7 @@ GHOST_ContextMTL::~GHOST_ContextMTL()
} }
} }
GHOST_TSuccess GHOST_ContextMTL::swapBuffers() GHOST_TSuccess GHOST_ContextMTL::swapBufferRelease()
{ {
if (metal_view_) { if (metal_view_) {
metalSwapBuffers(); metalSwapBuffers();

View File

@@ -10,7 +10,7 @@
#include "GHOST_ContextNone.hh" #include "GHOST_ContextNone.hh"
GHOST_TSuccess GHOST_ContextNone::swapBuffers() GHOST_TSuccess GHOST_ContextNone::swapBufferRelease()
{ {
return GHOST_kSuccess; return GHOST_kSuccess;
} }

View File

@@ -16,11 +16,17 @@ class GHOST_ContextNone : public GHOST_Context {
public: public:
GHOST_ContextNone(const GHOST_ContextParams &context_params) : GHOST_Context(context_params) {} GHOST_ContextNone(const GHOST_ContextParams &context_params) : GHOST_Context(context_params) {}
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override
{
return GHOST_kSuccess;
}
/** /**
* Dummy function * Dummy function
* \return Always succeeds * \return Always succeeds
*/ */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** /**
* Dummy function * Dummy function

View File

@@ -61,7 +61,7 @@ GHOST_ContextSDL::~GHOST_ContextSDL()
} }
} }
GHOST_TSuccess GHOST_ContextSDL::swapBuffers() GHOST_TSuccess GHOST_ContextSDL::swapBufferRelease()
{ {
SDL_GL_SwapWindow(window_); SDL_GL_SwapWindow(window_);

View File

@@ -44,11 +44,17 @@ class GHOST_ContextSDL : public GHOST_Context {
*/ */
~GHOST_ContextSDL() override; ~GHOST_ContextSDL() override;
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override
{
return GHOST_kSuccess;
}
/** /**
* Swaps front and back buffers of a window. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** /**
* Activates the drawing context of this window. * Activates the drawing context of this window.

View File

@@ -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(); GHOST_DeviceVK &device_vk = vulkan_instance->device.value();
VkDevice vk_device = device_vk.vk_device; 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 * submission fence to be signaled, to ensure the invariant holds for the next call to
* `swapBuffers`. * `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 * 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 * 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 * 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 #endif
} }
/* There is no valid swapchain as the previous window was minimized. User can have maximized the /* there is no valid swapchain when the previous window was minimized. User can have maximized
* window so we need to check if the swapchain can be created. */ * the window so we need to check if the swapchain has to be created. */
if (swapchain_ == VK_NULL_HANDLE) { if (swapchain_ == VK_NULL_HANDLE) {
recreateSwapchain(use_hdr_swapchain); 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 /* Acquired callback is also called when there is no swapchain.
* make sure the render graphs don't keep memory allocated that isn't used. */ *
if (swapchain_ == VK_NULL_HANDLE) { * When acquiring swap chain (image) and the swap chain is discarded (window has been minimized).
CLOG_TRACE( * We have trigger a last acquired callback to reduce the attachments of the GPUFramebuffer.
&LOG, * Vulkan backend will retrieve the data (getVulkanSwapChainFormat) containing a render extent of
"Swap-chain invalid (due to minimized window), perform rendering to reduce render graph " * 0,0.
"resources."); *
GHOST_VulkanSwapChainData swap_chain_data = {}; * The next frame window manager will detect that the window is minimized and doesn't draw the
if (swap_buffers_pre_callback_) { * window at all.
swap_buffers_pre_callback_(&swap_chain_data); */
} if (swap_buffer_acquired_callback_) {
if (swap_buffers_post_callback_) { swap_buffer_acquired_callback_();
swap_buffers_post_callback_(); }
}
if (swapchain_ == VK_NULL_HANDLE) {
CLOG_TRACE(&LOG, "Swap-chain unavailable (minimized window).");
return GHOST_kSuccess; return GHOST_kSuccess;
} }
@@ -798,7 +804,35 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers()
"Acquired swap-chain image (render_frame=%lu, image_index=%u)", "Acquired swap-chain image (render_frame=%lu, image_index=%u)",
render_frame_, render_frame_,
image_index); 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_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; GHOST_VulkanSwapChainData swap_chain_data;
swap_chain_data.image = swapchain_image.vk_image; 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; swap_chain_data.sdr_scale = (hdr_info_) ? hdr_info_->sdr_white_level : 1.0f;
vkResetFences(vk_device, 1, &submission_frame_data.submission_fence); vkResetFences(vk_device, 1, &submission_frame_data.submission_fence);
if (swap_buffers_pre_callback_) { if (swap_buffer_draw_callback_) {
swap_buffers_pre_callback_(&swap_chain_data); swap_buffer_draw_callback_(&swap_chain_data);
} }
VkPresentInfoKHR present_info = {}; VkPresentInfoKHR present_info = {};
@@ -828,22 +862,17 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers()
std::scoped_lock lock(device_vk.queue_mutex); std::scoped_lock lock(device_vk.queue_mutex);
present_result = vkQueuePresentKHR(device_vk.generic_queue, &present_info); 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) { if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR) {
recreateSwapchain(use_hdr_swapchain); recreateSwapchain(use_hdr_swapchain);
if (swap_buffers_post_callback_) {
swap_buffers_post_callback_();
}
return GHOST_kSuccess; return GHOST_kSuccess;
} }
if (present_result != VK_SUCCESS) { if (present_result != VK_SUCCESS) {
CLOG_ERROR(&LOG, CLOG_ERROR(&LOG,
"Vulkan: failed to present swap-chain image : %s", "Vulkan: failed to present swap-chain image : %s",
vulkan_error_as_string(present_result)); vulkan_error_as_string(present_result));
} return GHOST_kFailure;
if (swap_buffers_post_callback_) {
swap_buffers_post_callback_();
} }
return GHOST_kSuccess; return GHOST_kSuccess;
@@ -888,13 +917,13 @@ GHOST_TSuccess GHOST_ContextVK::getVulkanHandles(GHOST_VulkanHandles &r_handles)
} }
GHOST_TSuccess GHOST_ContextVK::setVulkanSwapBuffersCallbacks( GHOST_TSuccess GHOST_ContextVK::setVulkanSwapBuffersCallbacks(
std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffers_pre_callback, std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffer_draw_callback,
std::function<void(void)> swap_buffers_post_callback, std::function<void(void)> swap_buffer_acquired_callback,
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback, std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback,
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback) std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback)
{ {
swap_buffers_pre_callback_ = swap_buffers_pre_callback; swap_buffer_draw_callback_ = swap_buffer_draw_callback;
swap_buffers_post_callback_ = swap_buffers_post_callback; swap_buffer_acquired_callback_ = swap_buffer_acquired_callback;
openxr_acquire_framebuffer_image_callback_ = openxr_acquire_framebuffer_image_callback; openxr_acquire_framebuffer_image_callback_ = openxr_acquire_framebuffer_image_callback;
openxr_release_framebuffer_image_callback_ = openxr_release_framebuffer_image_callback; openxr_release_framebuffer_image_callback_ = openxr_release_framebuffer_image_callback;
return GHOST_kSuccess; return GHOST_kSuccess;

View File

@@ -33,6 +33,7 @@
# endif # endif
#endif #endif
#include <optional>
#include <vector> #include <vector>
#ifndef GHOST_OPENGL_VK_CONTEXT_FLAGS #ifndef GHOST_OPENGL_VK_CONTEXT_FLAGS
@@ -125,11 +126,13 @@ class GHOST_ContextVK : public GHOST_Context {
*/ */
~GHOST_ContextVK() override; ~GHOST_ContextVK() override;
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override;
/** /**
* Swaps front and back buffers of a window. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** /**
* Activates the drawing context of this window. * 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 getVulkanSwapChainFormat(GHOST_VulkanSwapChainData *r_swap_chain_data) override;
GHOST_TSuccess setVulkanSwapBuffersCallbacks( GHOST_TSuccess setVulkanSwapBuffersCallbacks(
std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffers_pre_callback, std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffer_draw_callback,
std::function<void(void)> swap_buffers_post_callback, std::function<void(void)> swap_buffer_acquired_callback,
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback, std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback,
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback) std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback)
override; override;
@@ -237,8 +240,10 @@ class GHOST_ContextVK : public GHOST_Context {
VkSurfaceFormatKHR surface_format_; VkSurfaceFormatKHR surface_format_;
bool use_hdr_swapchain_; bool use_hdr_swapchain_;
std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffers_pre_callback_; std::optional<uint32_t> acquired_swapchain_image_index_;
std::function<void(void)> swap_buffers_post_callback_;
std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffer_draw_callback_;
std::function<void(void)> swap_buffer_acquired_callback_;
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback_; std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback_;
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback_; std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback_;

View File

@@ -83,7 +83,7 @@ GHOST_ContextWGL::~GHOST_ContextWGL()
#endif #endif
} }
GHOST_TSuccess GHOST_ContextWGL::swapBuffers() GHOST_TSuccess GHOST_ContextWGL::swapBufferRelease()
{ {
return WIN32_CHK(::SwapBuffers(h_DC_)) ? GHOST_kSuccess : GHOST_kFailure; return WIN32_CHK(::SwapBuffers(h_DC_)) ? GHOST_kSuccess : GHOST_kFailure;
} }

View File

@@ -39,11 +39,17 @@ class GHOST_ContextWGL : public GHOST_Context {
*/ */
~GHOST_ContextWGL() override; ~GHOST_ContextWGL() override;
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
GHOST_TSuccess swapBufferAcquire() override
{
return GHOST_kSuccess;
}
/** /**
* Swaps front and back buffers of a window. * Swaps front and back buffers of a window.
* \return A boolean success indicator. * \return A boolean success indicator.
*/ */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** /**
* Activates the drawing context of this window. * Activates the drawing context of this window.

View File

@@ -102,9 +102,13 @@ GHOST_IContext *GHOST_Window::getDrawingContext()
return context_; 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) GHOST_TSuccess GHOST_Window::setSwapInterval(int interval)

View File

@@ -56,7 +56,8 @@ class GHOST_Window : public GHOST_IWindow {
* virtual GHOST_TWindowState getState() const = 0; * virtual GHOST_TWindowState getState() const = 0;
* virtual GHOST_TSuccess setState(GHOST_TWindowState state) = 0; * virtual GHOST_TSuccess setState(GHOST_TWindowState state) = 0;
* virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 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 setSwapInterval() = 0;
* virtual GHOST_TSuccess getSwapInterval(int& interval_out) = 0; * virtual GHOST_TSuccess getSwapInterval(int& interval_out) = 0;
* virtual GHOST_TSuccess activateDrawingContext() = 0; * virtual GHOST_TSuccess activateDrawingContext() = 0;
@@ -195,8 +196,10 @@ class GHOST_Window : public GHOST_IWindow {
/** \copydoc #GHOST_IWindow::getDrawingContext */ /** \copydoc #GHOST_IWindow::getDrawingContext */
GHOST_IContext *getDrawingContext() override; GHOST_IContext *getDrawingContext() override;
/** \copydoc #GHOST_IWindow::swapBufferAcquire */
GHOST_TSuccess swapBufferAcquire() override;
/** \copydoc #GHOST_IWindow::swapBuffers */ /** \copydoc #GHOST_IWindow::swapBuffers */
GHOST_TSuccess swapBuffers() override; GHOST_TSuccess swapBufferRelease() override;
/** \copydoc #GHOST_IWindow::activateDrawingContext */ /** \copydoc #GHOST_IWindow::activateDrawingContext */
GHOST_TSuccess activateDrawingContext() override; GHOST_TSuccess activateDrawingContext() override;

View File

@@ -105,7 +105,7 @@ class GHOST_WindowNULL : public GHOST_Window {
outX = inX; outX = inX;
outY = inY; outY = inY;
} }
GHOST_TSuccess swapBuffers() override GHOST_TSuccess swapBufferRelease() override
{ {
return GHOST_kFailure; return GHOST_kFailure;
} }

View File

@@ -2170,10 +2170,10 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
} }
#ifdef USE_EVENT_BACKGROUND_THREAD #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!"); 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 */ #endif /* USE_EVENT_BACKGROUND_THREAD */

View File

@@ -85,7 +85,7 @@ class GHOST_WindowWayland : public GHOST_Window {
/* Ghost API */ /* Ghost API */
#ifdef USE_EVENT_BACKGROUND_THREAD #ifdef USE_EVENT_BACKGROUND_THREAD
GHOST_TSuccess swapBuffers() override; /* Only for assertion. */ GHOST_TSuccess swapBufferRelease() override; /* Only for assertion. */
#endif #endif
uint16_t getDPIHint() override; uint16_t getDPIHint() override;

View File

@@ -376,8 +376,9 @@ bool processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr user_data)
break; break;
} }
setViewPortGL(window2); setViewPortGL(window2);
GHOST_SwapWindowBufferAcquire(window2);
drawGL(); drawGL();
GHOST_SwapWindowBuffers(window2); GHOST_SwapWindowBufferRelease(window2);
break; break;
} }
default: default:

View File

@@ -609,7 +609,7 @@ bool Application::processEvent(const GHOST_IEvent *event)
RenderScene(); RenderScene();
glPopMatrix(); glPopMatrix();
} }
window2->swapBuffers(); window2->swapBufferRelease();
break; break;
} }

View File

@@ -141,6 +141,7 @@ static void mainwindow_log(MainWindow *mw, char *str)
static void mainwindow_do_draw(MainWindow *mw) static void mainwindow_do_draw(MainWindow *mw)
{ {
GHOST_ActivateWindowDrawingContext(mw->win); GHOST_ActivateWindowDrawingContext(mw->win);
GHOST_SwapWindowBufferAcquire(mw->win);
GPU_context_active_set(mw->gpu_context); GPU_context_active_set(mw->gpu_context);
if (mw->lmbut[0]) { if (mw->lmbut[0]) {
@@ -154,7 +155,7 @@ static void mainwindow_do_draw(MainWindow *mw)
glColor3f(0.5, 0.6, 0.8); glColor3f(0.5, 0.6, 0.8);
glRecti(mw->tmouse[0] - 5, mw->tmouse[1] - 5, mw->tmouse[0] + 5, mw->tmouse[1] + 5); 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) 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]; int sb_rect[2][2], sb_thumb[2][2];
GHOST_ActivateWindowDrawingContext(lw->win); GHOST_ActivateWindowDrawingContext(lw->win);
GHOST_SwapWindowBufferAcquire(lw->win);
GPU_context_active_set(lw->gpu_context); GPU_context_active_set(lw->gpu_context);
glClearColor(1, 1, 1, 1); glClearColor(1, 1, 1, 1);
@@ -468,7 +470,7 @@ static void loggerwindow_do_draw(LoggerWindow *lw)
BLF_draw(lw->font, line, 256); // XXX BLF_draw(lw->font, line, 256); // XXX
} }
GHOST_SwapWindowBuffers(lw->win); GHOST_SwapWindowBufferRelease(lw->win);
immDeactivate(); immDeactivate();
} }
@@ -657,6 +659,7 @@ typedef struct {
static void extrawindow_do_draw(ExtraWindow *ew) static void extrawindow_do_draw(ExtraWindow *ew)
{ {
GHOST_ActivateWindowDrawingContext(ew->win); GHOST_ActivateWindowDrawingContext(ew->win);
GHOST_SwapWindowBufferAcquire(eq->win);
GPU_context_active_set(ew->gpu_context); GPU_context_active_set(ew->gpu_context);
glClearColor(1, 1, 1, 1); glClearColor(1, 1, 1, 1);
@@ -665,7 +668,7 @@ static void extrawindow_do_draw(ExtraWindow *ew)
glColor3f(0.8, 0.8, 0.8); glColor3f(0.8, 0.8, 0.8);
glRecti(10, 10, ew->size[0] - 10, ew->size[1] - 10); 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) static void extrawindow_do_reshape(ExtraWindow *ew)

View File

@@ -589,8 +589,8 @@ Context *VKBackend::context_alloc(void *ghost_window, void *ghost_context)
VKContext *context = new VKContext(ghost_window, ghost_context); VKContext *context = new VKContext(ghost_window, ghost_context);
device.context_register(*context); device.context_register(*context);
GHOST_SetVulkanSwapBuffersCallbacks((GHOST_ContextHandle)ghost_context, GHOST_SetVulkanSwapBuffersCallbacks((GHOST_ContextHandle)ghost_context,
VKContext::swap_buffers_pre_callback, VKContext::swap_buffer_draw_callback,
VKContext::swap_buffers_post_callback, VKContext::swap_buffer_acquired_callback,
VKContext::openxr_acquire_framebuffer_image_callback, VKContext::openxr_acquire_framebuffer_image_callback,
VKContext::openxr_release_framebuffer_image_callback); VKContext::openxr_release_framebuffer_image_callback);

View File

@@ -348,21 +348,26 @@ render_graph::VKResourceAccessInfo &VKContext::reset_and_get_access_info()
/** \name Graphics pipeline /** \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(); VKContext *context = VKContext::get();
BLI_assert(context); 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(); VKContext *context = VKContext::get();
BLI_assert(context); 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 do_blit_to_swapchain = swap_chain_data.image != VK_NULL_HANDLE;
const bool use_shader = swap_chain_data.surface_format.colorSpace == 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 #endif
} }
void VKContext::swap_buffers_post_handler()
{
sync_backbuffer();
}
void VKContext::specialization_constants_set( void VKContext::specialization_constants_set(
const shader::SpecializationConstants *constants_state) const shader::SpecializationConstants *constants_state)
{ {

View File

@@ -148,16 +148,16 @@ class VKContext : public Context, NonCopyable {
VKDescriptorSetTracker &descriptor_set_get(); VKDescriptorSetTracker &descriptor_set_get();
VKStateManager &state_manager_get() const; VKStateManager &state_manager_get() const;
static void swap_buffers_pre_callback(const GHOST_VulkanSwapChainData *data); static void swap_buffer_draw_callback(const GHOST_VulkanSwapChainData *data);
static void swap_buffers_post_callback(); static void swap_buffer_acquired_callback();
static void openxr_acquire_framebuffer_image_callback(GHOST_VulkanOpenXRData *data); static void openxr_acquire_framebuffer_image_callback(GHOST_VulkanOpenXRData *data);
static void openxr_release_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); void specialization_constants_set(const shader::SpecializationConstants *constants_state);
private: private:
void swap_buffers_pre_handler(const GHOST_VulkanSwapChainData &data); void swap_buffer_draw_handler(const GHOST_VulkanSwapChainData &data);
void swap_buffers_post_handler(); void swap_buffer_acquired_handler();
void openxr_acquire_framebuffer_image_handler(GHOST_VulkanOpenXRData &data); void openxr_acquire_framebuffer_image_handler(GHOST_VulkanOpenXRData &data);
void openxr_release_framebuffer_image_handler(GHOST_VulkanOpenXRData &data); void openxr_release_framebuffer_image_handler(GHOST_VulkanOpenXRData &data);

View File

@@ -1645,6 +1645,7 @@ void wm_draw_update(bContext *C)
if (wm_draw_update_test_window(bmain, C, win)) { if (wm_draw_update_test_window(bmain, C, win)) {
/* Sets context window+screen. */ /* Sets context window+screen. */
wm_window_make_drawable(wm, win); wm_window_make_drawable(wm, win);
wm_window_swap_buffer_acquire(win);
/* Notifiers for screen redraw. */ /* Notifiers for screen redraw. */
ED_screen_ensure_updated(C, wm, win); ED_screen_ensure_updated(C, wm, win);
@@ -1652,7 +1653,7 @@ void wm_draw_update(bContext *C)
wm_draw_window(C, win); wm_draw_window(C, win);
wm_draw_update_clear_window(C, win); wm_draw_update_clear_window(C, win);
wm_window_swap_buffers(win); wm_window_swap_buffer_release(win);
} }
} }

View File

@@ -676,7 +676,9 @@ static void playanim_toscreen_ex(GhostData &ghost_data,
GHOST_ActivateWindowDrawingContext(ghost_data.window); GHOST_ActivateWindowDrawingContext(ghost_data.window);
GPU_render_begin(); GPU_render_begin();
GHOST_SwapWindowBufferAcquire(ghost_data.window);
GPUContext *restore_context = GPU_context_active_get(); GPUContext *restore_context = GPU_context_active_get();
GPU_context_active_set(ghost_data.gpu_context); GPU_context_active_set(ghost_data.gpu_context);
GPU_context_begin_frame(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); 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_context_active_set(restore_context);
GPU_render_end(); GPU_render_end();
} }
@@ -1921,6 +1923,7 @@ static std::optional<int> wm_main_playanim_intern(int argc, const char **argv, P
ps.display_ctx.size = ps.ibuf_size; ps.display_ctx.size = ps.ibuf_size;
GHOST_SwapWindowBufferAcquire(ps.ghost_data.window);
GPU_render_begin(); GPU_render_begin();
GPU_render_step(); GPU_render_step();
GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f); GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f);
@@ -1932,7 +1935,7 @@ static std::optional<int> wm_main_playanim_intern(int argc, const char **argv, P
playanim_gpu_matrix(); playanim_gpu_matrix();
} }
GHOST_SwapWindowBuffers(ps.ghost_data.window); GHOST_SwapWindowBufferRelease(ps.ghost_data.window);
GPU_render_end(); GPU_render_end();
/* One of the frames was invalid or not passed in. */ /* One of the frames was invalid or not passed in. */

View File

@@ -1005,12 +1005,13 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
UI_GetThemeColor3fv(TH_BACK, window_bg_color); UI_GetThemeColor3fv(TH_BACK, window_bg_color);
/* Until screens get drawn, draw a default background using the window theme 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); 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. */ /* Needed here, because it's used before it reads #UserDef. */
WM_window_dpi_set_userdef(win); 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. */ /* 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); 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 #if 0
/* NOTE(@ideasman42): Ideally we could swap-buffers to avoid a full redraw. /* NOTE(@ideasman42): Ideally we could swap-buffers to avoid a full redraw.
* however this causes window flickering on resize with LIBDECOR under WAYLAND. */ * however this causes window flickering on resize with LIBDECOR under WAYLAND. */
wm_window_swap_buffers(win); wm_window_swap_buffer_release(win);
#else #else
WM_event_add_notifier_ex(wm, win, NC_WINDOW, nullptr); WM_event_add_notifier_ex(wm, win, NC_WINDOW, nullptr);
#endif #endif
@@ -2725,9 +2726,14 @@ void wm_window_raise(wmWindow *win)
/** \name Window Buffers /** \name Window Buffers
* \{ */ * \{ */
void wm_window_swap_buffers(wmWindow *win) void wm_window_swap_buffer_acquire(wmWindow *win)
{ {
GHOST_SwapWindowBuffers(static_cast<GHOST_WindowHandle>(win->ghostwin)); GHOST_SwapWindowBufferAcquire(static_cast<GHOST_WindowHandle>(win->ghostwin));
}
void wm_window_swap_buffer_release(wmWindow *win)
{
GHOST_SwapWindowBufferRelease(static_cast<GHOST_WindowHandle>(win->ghostwin));
} }
void wm_window_set_swap_interval(wmWindow *win, int interval) void wm_window_set_swap_interval(wmWindow *win, int interval)

View File

@@ -100,7 +100,8 @@ void wm_window_set_size(wmWindow *win, int width, int height);
/** /**
* \brief Push rendered buffer to the screen. * \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); void wm_window_set_swap_interval(wmWindow *win, int interval);
bool wm_window_get_swap_interval(wmWindow *win, int *r_interval); bool wm_window_get_swap_interval(wmWindow *win, int *r_interval);