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