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
This commit is contained in:
Christoph Neuhauser
2025-08-11 12:09:21 +02:00
committed by Clément Foucault
parent 2193096106
commit aafef977fb
9 changed files with 65 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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")
};

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -849,6 +849,23 @@ static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device,
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, nullptr);
vector<VkPresentModeKHR> 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.

View File

@@ -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) {

View File

@@ -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");