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:
parent
2937bde6a4
commit
e774564467
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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(¤tTime);
|
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();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user