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

Removed dependency on D3DKMTPresent

Fixes presentation issues when Hardware Accelerated GPU Scheduling is enabled
(issue #73).
This commit is contained in:
narzoul 2020-07-13 23:44:05 +02:00
parent b8bff744e5
commit 799e9a95ae
15 changed files with 244 additions and 332 deletions

View File

@ -0,0 +1,48 @@
#pragma once
#include <Windows.h>
namespace Compat
{
class SrwLock : public SRWLOCK
{
public:
SrwLock() : SRWLOCK(SRWLOCK_INIT)
{
}
};
class ScopedSrwLockExclusive
{
public:
ScopedSrwLockExclusive(SRWLOCK& lock) : m_lock(lock)
{
AcquireSRWLockExclusive(&m_lock);
}
~ScopedSrwLockExclusive()
{
ReleaseSRWLockExclusive(&m_lock);
}
private:
SRWLOCK& m_lock;
};
class ScopedSrwLockShared
{
public:
ScopedSrwLockShared(SRWLOCK& lock) : m_lock(lock)
{
AcquireSRWLockShared(&m_lock);
}
~ScopedSrwLockShared()
{
ReleaseSRWLockShared(&m_lock);
}
private:
SRWLOCK& m_lock;
};
}

View File

@ -100,5 +100,6 @@ namespace D3dDdi
void uninstallHooks()
{
unhookOpenAdapter();
KernelModeThunks::stopVsyncThread();
}
}

View File

@ -1,15 +1,8 @@
#include <atomic>
#include <map>
#include <string>
#include <d3d.h>
#include <d3dumddi.h>
#include <winternl.h>
#include <../km/d3dkmthk.h>
#include <Common/Log.h>
#include <Common/Hook.h>
#include <Common/ScopedCriticalSection.h>
#include <Common/ScopedSrwLock.h>
#include <Common/Time.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/Hooks.h>
@ -32,33 +25,24 @@ namespace
RECT monitorRect;
};
struct ContextInfo
{
D3DKMT_HANDLE device;
ContextInfo() : device(0) {}
};
std::map<D3DKMT_HANDLE, ContextInfo> g_contexts;
D3DDDIFORMAT g_dcFormatOverride = D3DDDIFMT_UNKNOWN;
bool g_dcPaletteOverride = false;
AdapterInfo g_gdiAdapterInfo = {};
AdapterInfo g_lastOpenAdapterInfo = {};
Compat::SrwLock g_lastOpenAdapterInfoSrwLock;
std::string g_lastDDrawCreateDcDevice;
UINT g_lastFlipInterval = 0;
UINT g_flipIntervalOverride = 0;
D3DKMT_HANDLE g_lastPresentContext = 0;
UINT g_presentCount = 0;
std::atomic<long long> g_qpcLastVerticalBlank = 0;
Compat::CriticalSection g_vblankCs;
decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr;
HANDLE g_vsyncThread = nullptr;
bool g_stopVsyncThread = false;
UINT g_vsyncCounter = 0;
CONDITION_VARIABLE g_vsyncCounterCv = CONDITION_VARIABLE_INIT;
Compat::SrwLock g_vsyncCounterSrwLock;
DWORD WINAPI waitForVsyncThreadProc(LPVOID lpParameter);
void waitForVerticalBlank();
NTSTATUS APIENTRY closeAdapter(const D3DKMT_CLOSEADAPTER* pData)
{
Compat::ScopedCriticalSection lock(g_vblankCs);
Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock);
if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter)
{
g_lastOpenAdapterInfo = {};
@ -66,30 +50,6 @@ namespace
return D3DKMTCloseAdapter(pData);
}
NTSTATUS APIENTRY createContext(D3DKMT_CREATECONTEXT* pData)
{
LOG_FUNC("D3DKMTCreateContext", pData);
NTSTATUS result = D3DKMTCreateContext(pData);
if (SUCCEEDED(result))
{
g_contexts[pData->hContext].device = pData->hDevice;
}
return LOG_RESULT(result);
}
NTSTATUS APIENTRY createContextVirtual(D3DKMT_CREATECONTEXTVIRTUAL* pData)
{
LOG_FUNC("D3DKMTCreateContextVirtual", pData);
static auto d3dKmtCreateContextVirtual = reinterpret_cast<decltype(D3DKMTCreateContextVirtual)*>(
GetProcAddress(GetModuleHandle("gdi32"), "D3DKMTCreateContextVirtual"));
NTSTATUS result = d3dKmtCreateContextVirtual(pData);
if (SUCCEEDED(result))
{
g_contexts[pData->hContext].device = pData->hDevice;
}
return LOG_RESULT(result);
}
NTSTATUS APIENTRY createDcFromMemory(D3DKMT_CREATEDCFROMMEMORY* pData)
{
LOG_FUNC("D3DKMTCreateDCFromMemory", pData);
@ -154,21 +114,6 @@ namespace
return LOG_RESULT(CreateDCA(pwszDriver, pwszDevice, pszPort, pdm));
}
NTSTATUS APIENTRY destroyContext(const D3DKMT_DESTROYCONTEXT* pData)
{
LOG_FUNC("D3DKMTDestroyContext", pData);
NTSTATUS result = D3DKMTDestroyContext(pData);
if (SUCCEEDED(result))
{
g_contexts.erase(pData->hContext);
if (g_lastPresentContext == pData->hContext)
{
g_lastPresentContext = 0;
}
}
return LOG_RESULT(result);
}
BOOL CALLBACK findDDrawMonitorRect(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
{
MONITORINFOEX mi = {};
@ -208,40 +153,12 @@ namespace
NTSTATUS result = D3DKMTOpenAdapterFromHdc(pData);
if (SUCCEEDED(result))
{
Compat::ScopedCriticalSection lock(g_vblankCs);
Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock);
g_lastOpenAdapterInfo = getAdapterInfo(*pData);
}
return LOG_RESULT(result);
}
NTSTATUS APIENTRY present(D3DKMT_PRESENT* pData)
{
LOG_FUNC("D3DKMTPresent", pData);
if (pData->Flags.Flip)
{
g_lastFlipInterval = pData->FlipInterval;
g_lastPresentContext = pData->hContext;
if (UINT_MAX == g_flipIntervalOverride)
{
return LOG_RESULT(S_OK);
}
++g_presentCount;
if (0 == g_presentCount)
{
g_presentCount = 1;
}
pData->PresentCount = g_presentCount;
pData->Flags.PresentCountValid = 1;
pData->FlipInterval = static_cast<D3DDDI_FLIPINTERVAL_TYPE>(g_flipIntervalOverride);
}
return LOG_RESULT(D3DKMTPresent(pData));
}
NTSTATUS APIENTRY queryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* pData)
{
LOG_FUNC("D3DKMTQueryAdapterInfo", pData);
@ -283,12 +200,13 @@ namespace
NTSTATUS APIENTRY setGammaRamp(const D3DKMT_SETGAMMARAMP* pData)
{
LOG_FUNC("D3DKMTSetGammaRamp", pData);
HANDLE vsyncThread = CreateThread(nullptr, 0, &waitForVsyncThreadProc, nullptr, 0, nullptr);
SetThreadPriority(vsyncThread, THREAD_PRIORITY_TIME_CRITICAL);
UINT vsyncCounter = D3dDdi::KernelModeThunks::getVsyncCounter();
DDraw::RealPrimarySurface::flush();
HRESULT result = D3DKMTSetGammaRamp(pData);
WaitForSingleObject(vsyncThread, INFINITE);
CloseHandle(vsyncThread);
if (SUCCEEDED(result))
{
D3dDdi::KernelModeThunks::waitForVsyncCounter(vsyncCounter + 1);
}
return LOG_RESULT(result);
}
@ -322,49 +240,50 @@ namespace
}
}
DWORD WINAPI waitForVsyncThreadProc(LPVOID /*lpParameter*/)
DWORD WINAPI vsyncThreadProc(LPVOID /*lpParameter*/)
{
D3dDdi::KernelModeThunks::waitForVerticalBlank();
while (!g_stopVsyncThread)
{
waitForVerticalBlank();
{
Compat::ScopedSrwLockExclusive lock(g_vsyncCounterSrwLock);
++g_vsyncCounter;
}
WakeAllConditionVariable(&g_vsyncCounterCv);
}
return 0;
}
void waitForVerticalBlank()
{
D3DKMT_WAITFORVERTICALBLANKEVENT data = {};
{
Compat::ScopedSrwLockShared lock(g_lastOpenAdapterInfoSrwLock);
data.hAdapter = g_lastOpenAdapterInfo.adapter;
data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
}
if (!data.hAdapter)
{
updateGdiAdapterInfo();
data.hAdapter = g_gdiAdapterInfo.adapter;
data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId;
}
if (!data.hAdapter || FAILED(D3DKMTWaitForVerticalBlankEvent(&data)))
{
Sleep(16);
}
}
}
namespace D3dDdi
{
namespace KernelModeThunks
{
UINT getLastFlipInterval()
{
return g_lastFlipInterval;
}
UINT getLastDisplayedFrameCount()
{
auto contextIter = g_contexts.find(g_lastPresentContext);
if (contextIter == g_contexts.end())
{
return g_presentCount;
}
D3DKMT_GETDEVICESTATE data = {};
data.hDevice = contextIter->second.device;
data.StateType = D3DKMT_DEVICESTATE_PRESENT;
data.PresentState.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
D3DKMTGetDeviceState(&data);
if (0 == data.PresentState.PresentStats.PresentCount)
{
return g_presentCount;
}
return data.PresentState.PresentStats.PresentCount;
}
UINT getLastSubmittedFrameCount()
{
return g_presentCount;
}
RECT getMonitorRect()
{
auto primary(DDraw::PrimarySurface::getPrimary());
@ -389,28 +308,23 @@ namespace D3dDdi
return g_lastOpenAdapterInfo.monitorRect;
}
long long getQpcLastVerticalBlank()
UINT getVsyncCounter()
{
return g_qpcLastVerticalBlank;
Compat::ScopedSrwLockShared lock(g_vsyncCounterSrwLock);
return g_vsyncCounter;
}
void installHooks(HMODULE origDDrawModule)
{
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "CreateDCA", ddrawCreateDcA);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCloseAdapter", closeAdapter);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCreateContext", createContext);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCreateContextVirtual", createContextVirtual);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCreateDCFromMemory", createDcFromMemory);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTDestroyContext", destroyContext);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTOpenAdapterFromHdc", openAdapterFromHdc);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTQueryAdapterInfo", queryAdapterInfo);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTPresent", present);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTSetGammaRamp", setGammaRamp);
}
void setFlipIntervalOverride(UINT flipInterval)
{
g_flipIntervalOverride = flipInterval;
g_vsyncThread = CreateThread(nullptr, 0, &vsyncThreadProc, nullptr, 0, nullptr);
SetThreadPriority(g_vsyncThread, THREAD_PRIORITY_TIME_CRITICAL);
}
void setDcFormatOverride(UINT format)
@ -423,30 +337,33 @@ namespace D3dDdi
g_dcPaletteOverride = enable;
}
void waitForVerticalBlank()
void stopVsyncThread()
{
D3DKMT_WAITFORVERTICALBLANKEVENT data = {};
g_stopVsyncThread = true;
if (WAIT_OBJECT_0 != WaitForSingleObject(g_vsyncThread, 100))
{
TerminateThread(g_vsyncThread, 0);
Compat::Log() << "The vsync thread was terminated forcefully";
}
g_vsyncThread = nullptr;
}
void waitForVsync()
{
waitForVsyncCounter(getVsyncCounter() + 1);
}
bool waitForVsyncCounter(UINT counter)
{
bool waited = false;
Compat::ScopedSrwLockShared lock(g_vsyncCounterSrwLock);
while (static_cast<INT>(g_vsyncCounter - counter) < 0)
{
Compat::ScopedCriticalSection lock(g_vblankCs);
if (g_lastOpenAdapterInfo.adapter)
{
data.hAdapter = g_lastOpenAdapterInfo.adapter;
data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
}
else
{
updateGdiAdapterInfo();
data.hAdapter = g_gdiAdapterInfo.adapter;
data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId;
}
}
if (data.hAdapter)
{
D3DKMTWaitForVerticalBlankEvent(&data);
g_qpcLastVerticalBlank = Time::queryPerformanceCounter();
SleepConditionVariableSRW(&g_vsyncCounterCv, &g_vsyncCounterSrwLock, INFINITE,
CONDITION_VARIABLE_LOCKMODE_SHARED);
waited = true;
}
return waited;
}
}
}

View File

@ -6,15 +6,13 @@ namespace D3dDdi
{
namespace KernelModeThunks
{
UINT getLastFlipInterval();
UINT getLastDisplayedFrameCount();
UINT getLastSubmittedFrameCount();
RECT getMonitorRect();
long long getQpcLastVerticalBlank();
UINT getVsyncCounter();
void installHooks(HMODULE origDDrawModule);
void setFlipIntervalOverride(UINT flipInterval);
void setDcFormatOverride(UINT format);
void setDcPaletteOverride(bool enable);
void waitForVerticalBlank();
void stopVsyncThread();
void waitForVsync();
bool waitForVsyncCounter(UINT counter);
}
}

View File

@ -141,7 +141,7 @@ namespace DDraw
DWORD scanLine = 0;
if (DDERR_VERTICALBLANKINPROGRESS != s_origVtable.GetScanLine(This, &scanLine))
{
D3dDdi::KernelModeThunks::waitForVerticalBlank();
D3dDdi::KernelModeThunks::waitForVsync();
}
if (DDWAITVB_BLOCKEND == dwFlags)

View File

@ -1,30 +1,30 @@
#include <atomic>
#include <memory>
#include <vector>
#include "Common/CompatPtr.h"
#include "Common/Hook.h"
#include "Common/Time.h"
#include "Config/Config.h"
#include "D3dDdi/Device.h"
#include "D3dDdi/KernelModeThunks.h"
#include "DDraw/DirectDraw.h"
#include "DDraw/DirectDrawSurface.h"
#include "DDraw/IReleaseNotifier.h"
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/ScopedThreadLock.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "DDraw/Types.h"
#include "Gdi/AccessGuard.h"
#include "Gdi/Caret.h"
#include "Gdi/Gdi.h"
#include "Gdi/VirtualScreen.h"
#include "Gdi/Window.h"
#include "Win32/DisplayMode.h"
#include <Common/CompatPtr.h>
#include <Common/Hook.h>
#include <Common/Time.h>
#include <Config/Config.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/KernelModeThunks.h>
#include <DDraw/DirectDraw.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/IReleaseNotifier.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/ScopedThreadLock.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <DDraw/Types.h>
#include <Gdi/AccessGuard.h>
#include <Gdi/Caret.h>
#include <Gdi/Gdi.h>
#include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h>
#include <Win32/DisplayMode.h>
namespace
{
void onRelease();
DWORD WINAPI updateThreadProc(LPVOID lpParameter);
CompatWeakPtr<IDirectDrawSurface7> g_frontBuffer;
CompatWeakPtr<IDirectDrawSurface7> g_paletteConverter;
@ -34,16 +34,15 @@ namespace
bool g_stopUpdateThread = false;
HANDLE g_updateThread = nullptr;
bool g_isGdiUpdatePending = false;
bool g_isFlipPending = false;
bool g_isPresentPending = false;
bool g_isUpdatePending = false;
bool g_isFullScreen = false;
bool g_waitingForPrimaryUnlock = false;
UINT g_flipIntervalDriverOverride = UINT_MAX;
UINT g_lastFlipFrameCount = 0;
DDraw::Surface* g_lastFlipSurface = nullptr;
long long g_qpcLastUpdate = 0;
bool g_isUpdatePending = false;
bool g_waitingForPrimaryUnlock = false;
std::atomic<long long> g_qpcLastUpdate = 0;
long long g_qpcFlipEnd = 0;
UINT g_flipEndVsyncCount = 0;
UINT g_presentEndVsyncCount = 0;
CompatPtr<IDirectDrawSurface7> getBackBuffer();
CompatPtr<IDirectDrawSurface7> getLastSurface();
@ -246,13 +245,13 @@ namespace
return lastSurface;
}
UINT getFlipIntervalFromFlags(DWORD flags)
UINT getFlipInterval(DWORD flags)
{
if (flags & DDFLIP_NOVSYNC)
{
return 0;
}
if (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4))
{
UINT flipInterval = (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) >> 24;
@ -266,39 +265,14 @@ namespace
return 1;
}
UINT getFlipInterval(DWORD flags)
{
if (0 == g_flipIntervalDriverOverride)
{
return 0;
}
UINT flipInterval = getFlipIntervalFromFlags(flags);
if (UINT_MAX != g_flipIntervalDriverOverride)
{
return max(flipInterval, g_flipIntervalDriverOverride);
}
return flipInterval;
}
bool isFlipPending()
{
if (g_isFlipPending)
{
g_isFlipPending = static_cast<int>(D3dDdi::KernelModeThunks::getLastDisplayedFrameCount() -
g_lastFlipFrameCount) < 0;
}
return g_isFlipPending;
return static_cast<INT>(D3dDdi::KernelModeThunks::getVsyncCounter() - g_flipEndVsyncCount) < 0;
}
bool isPresentPending()
{
if (g_isPresentPending)
{
g_isPresentPending = static_cast<int>(D3dDdi::KernelModeThunks::getLastDisplayedFrameCount() -
D3dDdi::KernelModeThunks::getLastSubmittedFrameCount()) < 0;
}
return g_isPresentPending;
return static_cast<INT>(D3dDdi::KernelModeThunks::getVsyncCounter() - g_presentEndVsyncCount) < 0;
}
void onRelease()
@ -308,8 +282,6 @@ namespace
g_frontBuffer = nullptr;
g_clipper.release();
g_isFullScreen = false;
g_isFlipPending = false;
g_isPresentPending = false;
g_waitingForPrimaryUnlock = false;
g_paletteConverter.release();
g_surfaceDesc = {};
@ -332,32 +304,16 @@ namespace
g_surfaceDesc = desc;
g_isFullScreen = isFlippable;
g_flipIntervalDriverOverride = UINT_MAX;
g_isFlipPending = false;
g_isPresentPending = false;
g_isUpdatePending = false;
g_qpcLastUpdate = Time::queryPerformanceCounter() - Time::msToQpc(Config::delayedFlipModeTimeout);
if (isFlippable)
{
D3dDdi::KernelModeThunks::setFlipIntervalOverride(UINT_MAX);
g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT | DDFLIP_NOVSYNC);
g_flipIntervalDriverOverride = D3dDdi::KernelModeThunks::getLastFlipInterval();
g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT);
if (0 == g_flipIntervalDriverOverride)
{
g_flipIntervalDriverOverride = D3dDdi::KernelModeThunks::getLastFlipInterval();
if (1 == g_flipIntervalDriverOverride)
{
g_flipIntervalDriverOverride = UINT_MAX;
}
}
g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT);
D3dDdi::KernelModeThunks::setFlipIntervalOverride(0);
g_lastFlipFrameCount = D3dDdi::KernelModeThunks::getLastSubmittedFrameCount();
g_frontBuffer->Flip(g_frontBuffer, getLastSurface(), DDFLIP_WAIT);
g_flipEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + 1;
g_presentEndVsyncCount = g_flipEndVsyncCount;
D3dDdi::KernelModeThunks::waitForVsyncCounter(g_flipEndVsyncCount);
}
D3dDdi::KernelModeThunks::waitForVerticalBlank();
}
void presentToPrimaryChain(CompatWeakPtr<IDirectDrawSurface7> src)
@ -410,47 +366,19 @@ namespace
void updateNow(CompatWeakPtr<IDirectDrawSurface7> src, UINT flipInterval)
{
DDraw::ScopedThreadLock lock;
if (flipInterval <= 1 && isPresentPending())
{
g_isUpdatePending = true;
return;
}
presentToPrimaryChain(src);
g_isUpdatePending = false;
g_waitingForPrimaryUnlock = false;
if (!g_isFullScreen)
if (g_isFullScreen)
{
g_isPresentPending = true;
return;
g_frontBuffer->Flip(g_frontBuffer, getBackBuffer(), DDFLIP_WAIT);
}
if (flipInterval > 1 && isPresentPending())
{
--flipInterval;
}
D3dDdi::KernelModeThunks::setFlipIntervalOverride(flipInterval);
g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT);
// Workaround for Windows 8 multimon display glitches when presenting from GDI shared primary surface
D3dDdi::KernelModeThunks::setFlipIntervalOverride(UINT_MAX);
g_frontBuffer->Flip(g_frontBuffer, getLastSurface(), DDFLIP_WAIT);
D3dDdi::KernelModeThunks::setFlipIntervalOverride(0);
g_isPresentPending = 0 != flipInterval;
g_presentEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + max(flipInterval, 1);
}
void updateNowIfNotBusy()
{
if (g_isGdiUpdatePending)
{
g_qpcLastUpdate = Time::queryPerformanceCounter();
g_isUpdatePending = true;
g_isGdiUpdatePending = false;
}
auto primary(DDraw::PrimarySurface::getPrimary());
RECT emptyRect = {};
HRESULT result = primary ? primary->BltFast(primary, 0, 0, primary, &emptyRect, DDBLTFAST_WAIT) : DD_OK;
@ -465,16 +393,30 @@ namespace
DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/)
{
bool skipWaitForVsync = false;
while (!g_stopUpdateThread)
{
D3dDdi::KernelModeThunks::waitForVerticalBlank();
if (!g_isFullScreen)
if (!skipWaitForVsync)
{
g_isPresentPending = false;
D3dDdi::KernelModeThunks::waitForVsync();
}
skipWaitForVsync = false;
Gdi::Caret::blink();
Sleep(1);
DDraw::RealPrimarySurface::flush();
DDraw::ScopedThreadLock lock;
if (g_isUpdatePending && !isPresentPending())
{
if (Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcFlipEnd) < 1)
{
skipWaitForVsync = true;
}
else
{
updateNowIfNotBusy();
}
}
}
return 0;
@ -533,62 +475,53 @@ namespace DDraw
HRESULT RealPrimarySurface::flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags)
{
DDraw::ScopedThreadLock lock;
const DWORD flipInterval = getFlipInterval(flags);
if (0 == flipInterval)
{
g_isUpdatePending = true;
return DD_OK;
}
DWORD flipInterval = getFlipInterval(flags);
const auto msSinceLastUpdate = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate);
const bool isFlipDelayed = msSinceLastUpdate >= 0 && msSinceLastUpdate <= Config::delayedFlipModeTimeout;
if (isFlipDelayed)
{
CompatPtr<IDirectDrawSurface7> prevPrimarySurface(
surfaceTargetOverride ? surfaceTargetOverride : PrimarySurface::getLastSurface());
updateNow(prevPrimarySurface, flipInterval);
if (!isPresentPending())
{
CompatPtr<IDirectDrawSurface7> prevPrimarySurface(
surfaceTargetOverride ? surfaceTargetOverride : PrimarySurface::getLastSurface());
updateNow(prevPrimarySurface, 0);
}
g_isUpdatePending = true;
}
const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY);
if (isFlipEmulated)
{
surfaceTargetOverride.get()->lpVtbl->Blt(
surfaceTargetOverride, nullptr, PrimarySurface::getPrimary(), nullptr, DDBLT_WAIT, nullptr);
}
if (!isFlipDelayed)
else
{
updateNow(PrimarySurface::getPrimary(), flipInterval);
}
g_flipEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + flipInterval;
g_presentEndVsyncCount = g_flipEndVsyncCount;
if (0 != flipInterval)
{
g_isFlipPending = true;
g_lastFlipFrameCount = D3dDdi::KernelModeThunks::getLastSubmittedFrameCount();
}
g_lastFlipSurface = nullptr;
if (g_isFlipPending && !isFlipEmulated)
{
g_lastFlipSurface = Surface::getSurface(
surfaceTargetOverride ? *surfaceTargetOverride : *PrimarySurface::getLastSurface());
}
else
{
g_lastFlipSurface = nullptr;
}
return DD_OK;
}
void RealPrimarySurface::flush()
{
DDraw::ScopedThreadLock lock;
Gdi::Caret::blink();
if ((g_isUpdatePending || g_isGdiUpdatePending) && !isPresentPending())
if (g_isUpdatePending && !isPresentPending())
{
updateNowIfNotBusy();
}
}
void RealPrimarySurface::gdiUpdate()
{
g_isGdiUpdatePending = true;
}
HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData)
{
DDraw::ScopedThreadLock lock;
@ -656,6 +589,12 @@ namespace DDraw
return result;
}
void RealPrimarySurface::scheduleUpdate()
{
g_qpcLastUpdate = Time::queryPerformanceCounter();
g_isUpdatePending = true;
}
HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData)
{
DDraw::ScopedThreadLock lock;
@ -694,11 +633,10 @@ namespace DDraw
return !isFlipPending();
}
while (isFlipPending())
if (D3dDdi::KernelModeThunks::waitForVsyncCounter(g_flipEndVsyncCount))
{
D3dDdi::KernelModeThunks::waitForVerticalBlank();
g_qpcFlipEnd = Time::queryPerformanceCounter();
}
return true;
}
}

View File

@ -2,8 +2,8 @@
#include <ddraw.h>
#include "Common/CompatPtr.h"
#include "Common/CompatRef.h"
#include <Common/CompatPtr.h>
#include <Common/CompatRef.h>
namespace DDraw
{
@ -17,7 +17,6 @@ namespace DDraw
static HRESULT flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags);
static void flush();
static void gdiUpdate();
static HRESULT getGammaRamp(DDGAMMARAMP* rampData);
static CompatWeakPtr<IDirectDrawSurface7> getSurface();
static void init();
@ -26,6 +25,7 @@ namespace DDraw
static void release();
static void removeUpdateThread();
static HRESULT restore();
static void scheduleUpdate();
static HRESULT setGammaRamp(DDGAMMARAMP* rampData);
static void update();
static bool waitForFlip(Surface* surface, bool wait = true);

View File

@ -120,20 +120,22 @@ namespace DDraw
template <typename TSurface>
HRESULT PrimarySurfaceImpl<TSurface>::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags)
{
const bool wait = (dwFlags & DDFLIP_WAIT) || !(dwFlags & DDFLIP_DONOTWAIT) &&
CompatVtable<IDirectDrawSurface7Vtbl>::s_origVtablePtr == static_cast<void*>(This->lpVtbl);
if (!DDraw::RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait))
if (!waitForFlip(This, dwFlags, DDFLIP_WAIT, DDFLIP_DONOTWAIT))
{
return DDERR_WASSTILLDRAWING;
}
auto surfaceTargetOverride(CompatPtr<TSurface>::from(lpDDSurfaceTargetOverride));
const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY);
if (isFlipEmulated && !surfaceTargetOverride)
if (isFlipEmulated)
{
TDdsCaps caps = {};
caps.dwCaps = DDSCAPS_BACKBUFFER;
s_origVtable.GetAttachedSurface(This, &caps, &surfaceTargetOverride.getRef());
if (!surfaceTargetOverride)
{
TDdsCaps caps = {};
caps.dwCaps = DDSCAPS_BACKBUFFER;
s_origVtable.GetAttachedSurface(This, &caps, &surfaceTargetOverride.getRef());
}
return Blt(This, nullptr, surfaceTargetOverride.get(), nullptr, DDBLT_WAIT, nullptr);
}
HRESULT result = SurfaceImpl::Flip(This, surfaceTargetOverride, DDFLIP_WAIT);

View File

@ -35,6 +35,10 @@ namespace DDraw
HRESULT SurfaceImpl<TSurface>::BltFast(
TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans)
{
if (!waitForFlip(This, dwTrans, DDBLTFAST_WAIT, DDBLTFAST_DONOTWAIT))
{
return DDERR_WASSTILLDRAWING;
}
return s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans);
}

View File

@ -42,11 +42,11 @@ namespace DDraw
virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect);
protected:
bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag);
static const Vtable<TSurface>& s_origVtable;
private:
bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag);
Surface* m_data;
};
}

View File

@ -140,6 +140,7 @@
<ClInclude Include="Common\HResultException.h" />
<ClInclude Include="Common\Log.h" />
<ClInclude Include="Common\LogWrapperVisitor.h" />
<ClInclude Include="Common\ScopedSrwLock.h" />
<ClInclude Include="Common\VtableHookVisitor.h" />
<ClInclude Include="Common\VtableUpdateVisitor.h" />
<ClInclude Include="Common\VtableVisitor.h" />

View File

@ -396,6 +396,9 @@
<ClInclude Include="Win32\MemoryManagement.h">
<Filter>Header Files\Win32</Filter>
</ClInclude>
<ClInclude Include="Common\ScopedSrwLock.h">
<Filter>Header Files\Common</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp">

View File

@ -34,7 +34,7 @@ namespace Gdi
if (ACCESS_WRITE == m_access &&
(!gdiResource || DDraw::PrimarySurface::getFrontResource() == *gdiResource))
{
DDraw::RealPrimarySurface::gdiUpdate();
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
}

View File

@ -266,7 +266,7 @@ namespace Gdi
}
}
DDraw::RealPrimarySurface::gdiUpdate();
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
}

View File

@ -403,7 +403,7 @@ namespace Gdi
{
m_presentationWindow = hwnd;
SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(m_hwnd));
DDraw::RealPrimarySurface::gdiUpdate();
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
@ -477,7 +477,7 @@ namespace Gdi
{
window->m_colorKey = colorKey;
window->m_alpha = alpha;
DDraw::RealPrimarySurface::gdiUpdate();
DDraw::RealPrimarySurface::scheduleUpdate();
}
}