1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Improved scheduling of real primary surface updates

This commit is contained in:
narzoul 2016-04-15 00:08:22 +02:00
parent 2937bde6a4
commit e774564467
3 changed files with 83 additions and 44 deletions

View File

@ -4,7 +4,7 @@ typedef unsigned long DWORD;
namespace Config namespace Config
{ {
const DWORD minRefreshInterval = 1000 / 60; const int maxPrimaryUpdateRate = 60;
const DWORD minRefreshIntervalAfterFlip = 1000 / 10; const int primaryUpdateDelayAfterFlip = 100;
const DWORD preallocatedGdiDcCount = 4; const DWORD preallocatedGdiDcCount = 4;
} }

View File

@ -94,7 +94,7 @@
</ClCompile> </ClCompile>
<Link> <Link>
<ModuleDefinitionFile>DDrawCompat.def</ModuleDefinitionFile> <ModuleDefinitionFile>DDrawCompat.def</ModuleDefinitionFile>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -121,7 +121,7 @@
</ClCompile> </ClCompile>
<Link> <Link>
<ModuleDefinitionFile>DDrawCompat.def</ModuleDefinitionFile> <ModuleDefinitionFile>DDrawCompat.def</ModuleDefinitionFile>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>No</GenerateDebugInformation> <GenerateDebugInformation>No</GenerateDebugInformation>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>

View File

@ -7,6 +7,7 @@
#include "CompatPaletteConverter.h" #include "CompatPaletteConverter.h"
#include "CompatPrimarySurface.h" #include "CompatPrimarySurface.h"
#include "Config.h" #include "Config.h"
#include "DDrawScopedThreadLock.h"
#include "DDrawProcs.h" #include "DDrawProcs.h"
#include "DDrawTypes.h" #include "DDrawTypes.h"
#include "Hook.h" #include "Hook.h"
@ -15,8 +16,10 @@
namespace namespace
{ {
long long msToQpc(int ms);
void onRelease(); void onRelease();
void updateNow(); int qpcToMs(long long qpc);
void updateNow(long long qpcNow);
IDirectDrawSurface7* g_frontBuffer = nullptr; IDirectDrawSurface7* g_frontBuffer = nullptr;
IDirectDrawSurface7* g_backBuffer = nullptr; IDirectDrawSurface7* g_backBuffer = nullptr;
@ -25,9 +28,9 @@ namespace
HANDLE g_updateThread = nullptr; HANDLE g_updateThread = nullptr;
HANDLE g_updateEvent = nullptr; HANDLE g_updateEvent = nullptr;
RECT g_updateRect = {}; RECT g_updateRect = {};
bool g_isFlipEvent = false; long long g_qpcFrequency = 0;
LARGE_INTEGER g_lastUpdateTime = {}; long long g_qpcMinUpdateInterval = 0;
LARGE_INTEGER g_qpcFrequency = {}; std::atomic<long long> g_qpcNextUpdate = 0;
std::atomic<bool> g_isFullScreen(false); std::atomic<bool> g_isFullScreen(false);
@ -88,18 +91,31 @@ namespace
Compat::LogLeave("RealPrimarySurface::compatBlt", dest); Compat::LogLeave("RealPrimarySurface::compatBlt", dest);
return result; return result;
} }
DWORD getTimeElapsedInMs(const LARGE_INTEGER& time) long long getNextUpdateQpc(long long qpcNow)
{ {
LARGE_INTEGER currentTime = {}; long long qpcNextUpdate = g_qpcNextUpdate;
QueryPerformanceCounter(&currentTime); const long long missedIntervals = (qpcNow - qpcNextUpdate) / g_qpcMinUpdateInterval;
return static_cast<DWORD>((currentTime.QuadPart - time.QuadPart) * 1000 / g_qpcFrequency.QuadPart); 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<long long>(ms) * g_qpcFrequency / 1000;
} }
void onRelease() void onRelease()
{ {
Compat::LogEnter("RealPrimarySurface::onRelease"); Compat::LogEnter("RealPrimarySurface::onRelease");
ResetEvent(g_updateEvent);
timeEndPeriod(1);
g_frontBuffer = nullptr; g_frontBuffer = nullptr;
g_backBuffer = nullptr; g_backBuffer = nullptr;
g_isFullScreen = false; g_isFullScreen = false;
@ -110,10 +126,31 @@ namespace
Compat::LogLeave("RealPrimarySurface::onRelease"); Compat::LogLeave("RealPrimarySurface::onRelease");
} }
void updateNow() int qpcToMs(long long qpc)
{ {
QueryPerformanceCounter(&g_lastUpdateTime); return static_cast<int>(qpc * 1000 / g_qpcFrequency);
compatBlt(g_frontBuffer); }
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*/) DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/)
@ -122,31 +159,19 @@ namespace
{ {
WaitForSingleObject(g_updateEvent, INFINITE); WaitForSingleObject(g_updateEvent, INFINITE);
Compat::origProcs.AcquireDDThreadLock(); const long long qpcTargetNextUpdate = g_qpcNextUpdate;
ResetEvent(g_updateEvent); const int msUntilNextUpdate = qpcToMs(qpcTargetNextUpdate - queryPerformanceCounter());
if (!g_frontBuffer) if (msUntilNextUpdate > 0)
{ {
Compat::origProcs.ReleaseDDThreadLock(); Sleep(msUntilNextUpdate);
continue;
} }
if (!g_isFlipEvent) Compat::DDrawScopedThreadLock lock;
const long long qpcNow = queryPerformanceCounter();
const bool isTargetUpdateStillNeeded = qpcTargetNextUpdate == g_qpcNextUpdate;
if (g_frontBuffer && (isTargetUpdateStillNeeded || isNextUpdateSignaledAndReady(qpcNow)))
{ {
updateNow(); updateNow(qpcNow);
}
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);
} }
} }
} }
@ -205,7 +230,11 @@ HRESULT RealPrimarySurface::create(DirectDraw& dd)
g_frontBuffer->lpVtbl->GetAttachedSurface(g_frontBuffer, &backBufferCaps, &g_backBuffer); 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) if (!g_updateEvent)
{ {
@ -222,6 +251,7 @@ HRESULT RealPrimarySurface::create(DirectDraw& dd)
IID_IReleaseNotifier, &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER); IID_IReleaseNotifier, &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER);
g_isFullScreen = isFlippable; g_isFullScreen = isFlippable;
timeBeginPeriod(1);
return DD_OK; return DD_OK;
} }
@ -238,6 +268,8 @@ HRESULT RealPrimarySurface::flip(DWORD flags)
return DDERR_NOTFLIPPABLE; return DDERR_NOTFLIPPABLE;
} }
ResetEvent(g_updateEvent);
invalidate(nullptr); invalidate(nullptr);
compatBlt(g_backBuffer); compatBlt(g_backBuffer);
if (flags & DDFLIP_DONOTWAIT) 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); HRESULT result = g_frontBuffer->lpVtbl->Flip(g_frontBuffer, nullptr, flags | DDFLIP_WAIT);
if (SUCCEEDED(result)) if (SUCCEEDED(result))
{ {
QueryPerformanceCounter(&g_lastUpdateTime); g_qpcNextUpdate = getNextUpdateQpc(
g_isFlipEvent = true; queryPerformanceCounter() + msToQpc(Config::primaryUpdateDelayAfterFlip));
SetEvent(g_updateEvent); SetRectEmpty(&g_updateRect);
} }
return result; return result;
} }
@ -328,8 +360,15 @@ void RealPrimarySurface::update()
{ {
if (!IsRectEmpty(&g_updateRect)) if (!IsRectEmpty(&g_updateRect))
{ {
g_isFlipEvent = false; const long long qpcNow = queryPerformanceCounter();
SetEvent(g_updateEvent); 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); CompatPaletteConverter::setPrimaryPalette(startingEntry, count);
CompatGdi::updatePalette(startingEntry, count); CompatGdi::updatePalette(startingEntry, count);
invalidate(nullptr); invalidate(nullptr);
updateNow(); update();
} }