Browse Source

Windows: fix some cases of stuttering in windowed mode.

Use DwmFlush instead of OpenGL vsync, when specific conditions are met. Fixes #1628.
Alex Szpakowski 3 years ago
parent
commit
5175b0d1b5
2 changed files with 53 additions and 0 deletions
  1. 1 0
      CMakeLists.txt
  2. 52 0
      src/modules/window/sdl/Window.cpp

+ 1 - 0
CMakeLists.txt

@@ -1755,6 +1755,7 @@ if(MSVC)
 	set(LOVE_LINK_LIBRARIES ${LOVE_LINK_LIBRARIES}
 		ws2_32.lib
 		winmm.lib
+		dwmapi.lib
 	)
 
 	set(LOVE_RC

+ 52 - 0
src/modules/window/sdl/Window.cpp

@@ -44,6 +44,8 @@
 
 #if defined(LOVE_WINDOWS)
 #include <windows.h>
+#include <dwmapi.h>
+#include <VersionHelpers.h>
 #elif defined(LOVE_MACOSX)
 #include "common/macosx.h"
 #endif
@@ -1015,7 +1017,57 @@ bool Window::isMinimized() const
 
 void Window::swapBuffers()
 {
+#ifdef LOVE_WINDOWS
+	bool useDwmFlush = false;
+	int swapInterval = getVSync();
+
+	// https://github.com/love2d/love/issues/1628
+	// VSync can interact badly with Windows desktop composition (DWM) in windowed mode. DwmFlush can be used instead
+	// of vsync, but it's much less flexible so we're very conservative here with where it's used:
+	// - It won't work with exclusive or desktop fullscreen.
+	// - DWM refreshes don't always match the refresh rate of the monitor the window is in (or the requested swap
+	//   interval), so we only use it when they do match.
+	// - The user may force GL vsync, and DwmFlush shouldn't be used together with GL vsync.
+	if (context != nullptr && !settings.fullscreen && swapInterval == 1)
+	{
+		// Desktop composition is always enabled in Windows 8+. But DwmIsCompositionEnabled won't always return true...
+		// (see DwmIsCompositionEnabled docs).
+		BOOL compositionEnabled = IsWindows8OrGreater();
+		if (compositionEnabled || (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled)) && compositionEnabled))
+		{
+			DWM_TIMING_INFO info = {};
+			info.cbSize = sizeof(DWM_TIMING_INFO);
+			double dwmRefreshRate = 0;
+			if (SUCCEEDED(DwmGetCompositionTimingInfo(nullptr, &info)))
+				dwmRefreshRate = (double)info.rateRefresh.uiNumerator / (double)info.rateRefresh.uiDenominator;
+
+			SDL_DisplayMode dmode = {};
+			int displayindex = SDL_GetWindowDisplayIndex(window);
+
+			if (displayindex >= 0)
+				SDL_GetCurrentDisplayMode(displayindex, &dmode);
+
+			if (dmode.refresh_rate > 0 && dwmRefreshRate > 0 && (fabs(dmode.refresh_rate - dwmRefreshRate) < 2))
+			{
+				SDL_GL_SetSwapInterval(0);
+				if (SDL_GL_GetSwapInterval() == 0)
+					useDwmFlush = true;
+				else
+					SDL_GL_SetSwapInterval(swapInterval);
+			}
+		}
+	}
+#endif
+
 	SDL_GL_SwapWindow(window);
+
+#ifdef LOVE_WINDOWS
+	if (useDwmFlush)
+	{
+		DwmFlush();
+		SDL_GL_SetSwapInterval(swapInterval);
+	}
+#endif
 }
 
 bool Window::hasFocus() const