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:
parent
b8bff744e5
commit
799e9a95ae
48
DDrawCompat/Common/ScopedSrwLock.h
Normal file
48
DDrawCompat/Common/ScopedSrwLock.h
Normal 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;
|
||||
};
|
||||
}
|
@ -100,5 +100,6 @@ namespace D3dDdi
|
||||
void uninstallHooks()
|
||||
{
|
||||
unhookOpenAdapter();
|
||||
KernelModeThunks::stopVsyncThread();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -34,7 +34,7 @@ namespace Gdi
|
||||
if (ACCESS_WRITE == m_access &&
|
||||
(!gdiResource || DDraw::PrimarySurface::getFrontResource() == *gdiResource))
|
||||
{
|
||||
DDraw::RealPrimarySurface::gdiUpdate();
|
||||
DDraw::RealPrimarySurface::scheduleUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ namespace Gdi
|
||||
}
|
||||
}
|
||||
|
||||
DDraw::RealPrimarySurface::gdiUpdate();
|
||||
DDraw::RealPrimarySurface::scheduleUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user