From e774564467bb681520ae256c05c43060f631eb81 Mon Sep 17 00:00:00 2001 From: narzoul Date: Fri, 15 Apr 2016 00:08:22 +0200 Subject: [PATCH] Improved scheduling of real primary surface updates --- DDrawCompat/Config.h | 4 +- DDrawCompat/DDrawCompat.vcxproj | 4 +- DDrawCompat/RealPrimarySurface.cpp | 119 +++++++++++++++++++---------- 3 files changed, 83 insertions(+), 44 deletions(-) diff --git a/DDrawCompat/Config.h b/DDrawCompat/Config.h index 08d07cb..9882c47 100644 --- a/DDrawCompat/Config.h +++ b/DDrawCompat/Config.h @@ -4,7 +4,7 @@ typedef unsigned long DWORD; namespace Config { - const DWORD minRefreshInterval = 1000 / 60; - const DWORD minRefreshIntervalAfterFlip = 1000 / 10; + const int maxPrimaryUpdateRate = 60; + const int primaryUpdateDelayAfterFlip = 100; const DWORD preallocatedGdiDcCount = 4; } diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index cbb1100..d236c99 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -94,7 +94,7 @@ DDrawCompat.def - dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;%(AdditionalDependencies) + dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) @@ -121,7 +121,7 @@ DDrawCompat.def - dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;%(AdditionalDependencies) + dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) No diff --git a/DDrawCompat/RealPrimarySurface.cpp b/DDrawCompat/RealPrimarySurface.cpp index 21049ed..a6b791a 100644 --- a/DDrawCompat/RealPrimarySurface.cpp +++ b/DDrawCompat/RealPrimarySurface.cpp @@ -7,6 +7,7 @@ #include "CompatPaletteConverter.h" #include "CompatPrimarySurface.h" #include "Config.h" +#include "DDrawScopedThreadLock.h" #include "DDrawProcs.h" #include "DDrawTypes.h" #include "Hook.h" @@ -15,8 +16,10 @@ namespace { + long long msToQpc(int ms); void onRelease(); - void updateNow(); + int qpcToMs(long long qpc); + void updateNow(long long qpcNow); IDirectDrawSurface7* g_frontBuffer = nullptr; IDirectDrawSurface7* g_backBuffer = nullptr; @@ -25,9 +28,9 @@ namespace HANDLE g_updateThread = nullptr; HANDLE g_updateEvent = nullptr; RECT g_updateRect = {}; - bool g_isFlipEvent = false; - LARGE_INTEGER g_lastUpdateTime = {}; - LARGE_INTEGER g_qpcFrequency = {}; + long long g_qpcFrequency = 0; + long long g_qpcMinUpdateInterval = 0; + std::atomic g_qpcNextUpdate = 0; std::atomic g_isFullScreen(false); @@ -88,18 +91,31 @@ namespace Compat::LogLeave("RealPrimarySurface::compatBlt", dest); return result; } - - DWORD getTimeElapsedInMs(const LARGE_INTEGER& time) + + long long getNextUpdateQpc(long long qpcNow) { - LARGE_INTEGER currentTime = {}; - QueryPerformanceCounter(¤tTime); - return static_cast((currentTime.QuadPart - time.QuadPart) * 1000 / g_qpcFrequency.QuadPart); + long long qpcNextUpdate = g_qpcNextUpdate; + const long long missedIntervals = (qpcNow - qpcNextUpdate) / g_qpcMinUpdateInterval; + return qpcNextUpdate + g_qpcMinUpdateInterval * (missedIntervals + 1); + } + + bool isNextUpdateSignaledAndReady(long long qpcNow) + { + return qpcToMs(qpcNow - g_qpcNextUpdate) >= 0 && + WAIT_OBJECT_0 == WaitForSingleObject(g_updateEvent, 0); + } + + long long msToQpc(int ms) + { + return static_cast(ms) * g_qpcFrequency / 1000; } void onRelease() { Compat::LogEnter("RealPrimarySurface::onRelease"); + ResetEvent(g_updateEvent); + timeEndPeriod(1); g_frontBuffer = nullptr; g_backBuffer = nullptr; g_isFullScreen = false; @@ -110,10 +126,31 @@ namespace Compat::LogLeave("RealPrimarySurface::onRelease"); } - void updateNow() + int qpcToMs(long long qpc) { - QueryPerformanceCounter(&g_lastUpdateTime); - compatBlt(g_frontBuffer); + return static_cast(qpc * 1000 / g_qpcFrequency); + } + + long long queryPerformanceCounter() + { + LARGE_INTEGER qpc = {}; + QueryPerformanceCounter(&qpc); + return qpc.QuadPart; + } + + void updateNow(long long qpcNow) + { + ResetEvent(g_updateEvent); + + if (compatBlt(g_frontBuffer)) + { + long long qpcNextUpdate = getNextUpdateQpc(qpcNow); + if (qpcToMs(qpcNow - qpcNextUpdate) >= 0) + { + qpcNextUpdate += g_qpcMinUpdateInterval; + } + g_qpcNextUpdate = qpcNextUpdate; + } } DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/) @@ -122,31 +159,19 @@ namespace { WaitForSingleObject(g_updateEvent, INFINITE); - Compat::origProcs.AcquireDDThreadLock(); - ResetEvent(g_updateEvent); - if (!g_frontBuffer) + const long long qpcTargetNextUpdate = g_qpcNextUpdate; + const int msUntilNextUpdate = qpcToMs(qpcTargetNextUpdate - queryPerformanceCounter()); + if (msUntilNextUpdate > 0) { - Compat::origProcs.ReleaseDDThreadLock(); - continue; + Sleep(msUntilNextUpdate); } - if (!g_isFlipEvent) + Compat::DDrawScopedThreadLock lock; + const long long qpcNow = queryPerformanceCounter(); + const bool isTargetUpdateStillNeeded = qpcTargetNextUpdate == g_qpcNextUpdate; + if (g_frontBuffer && (isTargetUpdateStillNeeded || isNextUpdateSignaledAndReady(qpcNow))) { - updateNow(); - } - - DWORD timeSinceLastUpdate = getTimeElapsedInMs(g_lastUpdateTime); - DWORD minRefreshInterval = g_isFlipEvent ? - Config::minRefreshIntervalAfterFlip : Config::minRefreshInterval; - DWORD minRefreshIntervalTimeout = timeSinceLastUpdate < minRefreshInterval ? - minRefreshInterval - timeSinceLastUpdate : 0; - - g_isFlipEvent = false; - Compat::origProcs.ReleaseDDThreadLock(); - - if (minRefreshIntervalTimeout) - { - Sleep(minRefreshIntervalTimeout); + updateNow(qpcNow); } } } @@ -205,7 +230,11 @@ HRESULT RealPrimarySurface::create(DirectDraw& dd) g_frontBuffer->lpVtbl->GetAttachedSurface(g_frontBuffer, &backBufferCaps, &g_backBuffer); } - QueryPerformanceFrequency(&g_qpcFrequency); + LARGE_INTEGER qpc; + QueryPerformanceFrequency(&qpc); + g_qpcFrequency = qpc.QuadPart; + g_qpcMinUpdateInterval = g_qpcFrequency / Config::maxPrimaryUpdateRate; + g_qpcNextUpdate = queryPerformanceCounter(); if (!g_updateEvent) { @@ -222,6 +251,7 @@ HRESULT RealPrimarySurface::create(DirectDraw& dd) IID_IReleaseNotifier, &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER); g_isFullScreen = isFlippable; + timeBeginPeriod(1); return DD_OK; } @@ -238,6 +268,8 @@ HRESULT RealPrimarySurface::flip(DWORD flags) return DDERR_NOTFLIPPABLE; } + ResetEvent(g_updateEvent); + invalidate(nullptr); compatBlt(g_backBuffer); if (flags & DDFLIP_DONOTWAIT) @@ -248,9 +280,9 @@ HRESULT RealPrimarySurface::flip(DWORD flags) HRESULT result = g_frontBuffer->lpVtbl->Flip(g_frontBuffer, nullptr, flags | DDFLIP_WAIT); if (SUCCEEDED(result)) { - QueryPerformanceCounter(&g_lastUpdateTime); - g_isFlipEvent = true; - SetEvent(g_updateEvent); + g_qpcNextUpdate = getNextUpdateQpc( + queryPerformanceCounter() + msToQpc(Config::primaryUpdateDelayAfterFlip)); + SetRectEmpty(&g_updateRect); } return result; } @@ -328,8 +360,15 @@ void RealPrimarySurface::update() { if (!IsRectEmpty(&g_updateRect)) { - g_isFlipEvent = false; - SetEvent(g_updateEvent); + const long long qpcNow = queryPerformanceCounter(); + if (qpcToMs(qpcNow - g_qpcNextUpdate) >= 0) + { + updateNow(qpcNow); + } + else + { + SetEvent(g_updateEvent); + } } } @@ -338,5 +377,5 @@ void RealPrimarySurface::updatePalette(DWORD startingEntry, DWORD count) CompatPaletteConverter::setPrimaryPalette(startingEntry, count); CompatGdi::updatePalette(startingEntry, count); invalidate(nullptr); - updateNow(); + update(); }