From aafef977fb5062759c6ac710f15d41f27511c2c5 Mon Sep 17 00:00:00 2001 From: Christoph Neuhauser Date: Mon, 11 Aug 2025 12:09:21 +0200 Subject: [PATCH] GHOST: Add environment variable for enabling or disabling vsync This PR proposes to add an environment variable for forcing vsync to be on or off. My primary use case was to disable vsync for forcing viewport rendering performance tests not to be capped at the display refresh rate when #142984 is used for removing animation frame rate limits. I initially added the environment variable "GHOST_VSYNC_OFF", but found "GHOST_VSYNC=0/1" to be more easily understandable. Usage: - GHOST_VSYNC=0 => Vsync is forced off - GHOST_VSYNC=1 => Vsync is forced on Pull Request: https://projects.blender.org/blender/blender/pulls/143049 --- intern/ghost/intern/GHOST_Context.cc | 6 ++++++ intern/ghost/intern/GHOST_Context.hh | 3 +++ intern/ghost/intern/GHOST_ContextEGL.cc | 8 ++++++++ intern/ghost/intern/GHOST_ContextGLX.cc | 7 +++++++ intern/ghost/intern/GHOST_ContextMTL.mm | 6 ++++++ intern/ghost/intern/GHOST_ContextSDL.cc | 6 ++++++ intern/ghost/intern/GHOST_ContextVK.cc | 17 +++++++++++++++++ intern/ghost/intern/GHOST_ContextWGL.cc | 8 ++++++++ source/creator/creator_args.cc | 4 ++++ 9 files changed, 65 insertions(+) diff --git a/intern/ghost/intern/GHOST_Context.cc b/intern/ghost/intern/GHOST_Context.cc index d225220fa3e..4f8e9a1521c 100644 --- a/intern/ghost/intern/GHOST_Context.cc +++ b/intern/ghost/intern/GHOST_Context.cc @@ -142,3 +142,9 @@ void GHOST_Context::initClearGL() glClearColor(0.000, 0.000, 0.000, 0.000); } #endif + +const char *GHOST_Context::getEnvVarVsyncString() +{ + const char *ghost_vsync_string = getenv("GHOST_VSYNC"); + return ghost_vsync_string; +} diff --git a/intern/ghost/intern/GHOST_Context.hh b/intern/ghost/intern/GHOST_Context.hh index a4bcfc17906..608fb5f97c3 100644 --- a/intern/ghost/intern/GHOST_Context.hh +++ b/intern/ghost/intern/GHOST_Context.hh @@ -169,6 +169,9 @@ class GHOST_Context : public GHOST_IContext { static void initClearGL(); #endif + /** For performance measurements with vsync disabled. */ + static const char *getEnvVarVsyncString(); + MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_Context") }; diff --git a/intern/ghost/intern/GHOST_ContextEGL.cc b/intern/ghost/intern/GHOST_ContextEGL.cc index 5d354aadfbe..f266cd2d122 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.cc +++ b/intern/ghost/intern/GHOST_ContextEGL.cc @@ -625,6 +625,14 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() goto error; } + { + const char *ghost_vsync_string = getEnvVarVsyncString(); + if (ghost_vsync_string) { + int swapInterval = atoi(ghost_vsync_string); + setSwapInterval(swapInterval); + } + } + if (m_nativeWindow != 0) { initClearGL(); ::eglSwapBuffers(m_display, m_surface); diff --git a/intern/ghost/intern/GHOST_ContextGLX.cc b/intern/ghost/intern/GHOST_ContextGLX.cc index 0ba7b6067a0..0daea2922a3 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.cc +++ b/intern/ghost/intern/GHOST_ContextGLX.cc @@ -280,6 +280,13 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() glXMakeCurrent(m_display, m_window, m_context); + /* For performance measurements with vsync disabled. */ + const char *ghost_vsync_string = getEnvVarVsyncString(); + if (ghost_vsync_string) { + int swapInterval = atoi(ghost_vsync_string); + setSwapInterval(swapInterval); + } + if (m_window) { initClearGL(); ::glXSwapBuffers(m_display, m_window); diff --git a/intern/ghost/intern/GHOST_ContextMTL.mm b/intern/ghost/intern/GHOST_ContextMTL.mm index c9af55c7a64..2c9e03f8273 100644 --- a/intern/ghost/intern/GHOST_ContextMTL.mm +++ b/intern/ghost/intern/GHOST_ContextMTL.mm @@ -88,6 +88,12 @@ GHOST_ContextMTL::GHOST_ContextMTL(bool stereoVisual, m_metalLayer.device = metalDevice; m_metalLayer.allowsNextDrawableTimeout = NO; + const char *ghost_vsync_string = getEnvVarVsyncString(); + if (ghost_vsync_string) { + int swapInterval = atoi(ghost_vsync_string); + m_metalLayer.displaySyncEnabled = swapInterval != 0 ? YES : NO; + } + /* Enable EDR support. This is done by: * 1. Using a floating point render target, so that values outside 0..1 can be used * 2. Informing the OS that we are EDR aware, and intend to use values outside 0..1 diff --git a/intern/ghost/intern/GHOST_ContextSDL.cc b/intern/ghost/intern/GHOST_ContextSDL.cc index 2cb8db00fbd..4bcfadf0313 100644 --- a/intern/ghost/intern/GHOST_ContextSDL.cc +++ b/intern/ghost/intern/GHOST_ContextSDL.cc @@ -134,6 +134,12 @@ GHOST_TSuccess GHOST_ContextSDL::initializeDrawingContext() success = (SDL_GL_MakeCurrent(m_window, m_context) < 0) ? GHOST_kFailure : GHOST_kSuccess; + const char *ghost_vsync_string = getEnvVarVsyncString(); + if (ghost_vsync_string) { + int swapInterval = atoi(ghost_vsync_string); + setSwapInterval(swapInterval); + } + initClearGL(); SDL_GL_SwapWindow(m_window); diff --git a/intern/ghost/intern/GHOST_ContextVK.cc b/intern/ghost/intern/GHOST_ContextVK.cc index 745cffacc4f..30e0e20efb6 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cc +++ b/intern/ghost/intern/GHOST_ContextVK.cc @@ -849,6 +849,23 @@ static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device, vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, nullptr); vector presents(present_count); vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, presents.data()); + + const char *ghost_vsync_string = getEnvVarVsyncString(); + if (ghost_vsync_string) { + bool vsync_off = atoi(ghost_vsync_string) == 0; + if (vsync_off) { + for (auto present_mode : presents) { + if (present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR) { + *r_presentMode = present_mode; + return GHOST_kSuccess; + } + } + fprintf(stderr, + "Vsync off was requested via GHOST_VSYNC, but VK_PRESENT_MODE_IMMEDIATE_KHR is not " + "supported.\n"); + } + } + /* MAILBOX is the lowest latency V-Sync enabled mode. We will use it if available as it fixes * some lag on NVIDIA/Intel GPUs. */ /* TODO: select the correct presentation mode based on the actual being performed by the user. diff --git a/intern/ghost/intern/GHOST_ContextWGL.cc b/intern/ghost/intern/GHOST_ContextWGL.cc index 80072861727..f4c60320cb6 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.cc +++ b/intern/ghost/intern/GHOST_ContextWGL.cc @@ -616,6 +616,14 @@ GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext() } } + { + const char *ghost_vsync_string = getEnvVarVsyncString(); + if (ghost_vsync_string) { + int swapInterval = atoi(ghost_vsync_string); + setSwapInterval(swapInterval); + } + } + s_sharedCount++; if (s_sharedHGLRC == nullptr) { diff --git a/source/creator/creator_args.cc b/source/creator/creator_args.cc index dcf6ceec366..6da467d048a 100644 --- a/source/creator/creator_args.cc +++ b/source/creator/creator_args.cc @@ -866,6 +866,10 @@ static void print_help(bArgs *ba, bool all) PRINT(" $BLENDER_CUSTOM_SPLASH Full path to an image that replaces the splash screen.\n"); PRINT( " $BLENDER_CUSTOM_SPLASH_BANNER Full path to an image to overlay on the splash screen.\n"); + PRINT( + " $GHOST_VSYNC Whether to disable (0) or enable (1) vsync. Otherwise left to the GHOST " + "backend. For the OpenGL backend, the passed value is used as the swap interval " + "(https://www.khronos.org/opengl/wiki/Swap_Interval).\n"); if (defs.with_opencolorio) { PRINT(" $OCIO Path to override the OpenColorIO configuration file.\n");