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

Improved presentation scheduling

Fixes cursor flickering issues in Siege of Avalon (issue #34)
and multimon flip issues on Windows 8.1 (issue #28).
This commit is contained in:
narzoul 2018-10-03 20:49:50 +02:00
parent e69b5e312c
commit 537ef9c595
16 changed files with 708 additions and 463 deletions

View File

@ -10,14 +10,14 @@ namespace Time
void init(); void init();
inline long long msToQpc(int ms) inline long long msToQpc(long long ms)
{ {
return static_cast<long long>(ms) * g_qpcFrequency / 1000; return static_cast<long long>(ms) * g_qpcFrequency / 1000;
} }
inline int qpcToMs(long long qpc) inline long long qpcToMs(long long qpc)
{ {
return static_cast<int>(qpc * 1000 / g_qpcFrequency); return qpc * 1000 / g_qpcFrequency;
} }
inline long long queryPerformanceCounter() inline long long queryPerformanceCounter()

View File

@ -4,6 +4,7 @@ typedef unsigned long DWORD;
namespace Config namespace Config
{ {
const int delayedFlipModeTimeout = 200;
const int maxPaletteUpdatesPerMs = 5; const int maxPaletteUpdatesPerMs = 5;
const int minExpectedFlipsPerSec = 5; const int minExpectedFlipsPerSec = 5;
const DWORD primarySurfaceExtraRows = 2; const DWORD primarySurfaceExtraRows = 2;

View File

@ -1,3 +1,6 @@
#include <d3d.h>
#include <../km/d3dkmthk.h>
#include "D3dDdi/AdapterFuncs.h" #include "D3dDdi/AdapterFuncs.h"
#include "D3dDdi/Device.h" #include "D3dDdi/Device.h"
#include "D3dDdi/DeviceFuncs.h" #include "D3dDdi/DeviceFuncs.h"
@ -208,7 +211,7 @@ namespace D3dDdi
{ {
if (resource == m_sharedPrimary) if (resource == m_sharedPrimary)
{ {
KernelModeThunks::releaseVidPnSources(); D3DKMTReleaseProcessVidPnSourceOwners(GetCurrentProcess());
} }
HRESULT result = m_origVtable->pfnDestroyResource(m_device, resource); HRESULT result = m_origVtable->pfnDestroyResource(m_device, resource);

View File

@ -1,3 +1,4 @@
#include <atomic>
#include <map> #include <map>
#include <d3d.h> #include <d3d.h>
@ -6,12 +7,23 @@
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/Hook.h" #include "Common/Hook.h"
#include "Common/ScopedCriticalSection.h"
#include "Common/Time.h"
#include "D3dDdi/Hooks.h" #include "D3dDdi/Hooks.h"
#include "D3dDdi/KernelModeThunks.h" #include "D3dDdi/KernelModeThunks.h"
#include "D3dDdi/Log/KernelModeThunksLog.h"
#include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h"
#include "Win32/DisplayMode.h"
namespace namespace
{ {
struct AdapterInfo
{
UINT adapter;
UINT vidPnSourceId;
RECT monitorRect;
};
struct ContextInfo struct ContextInfo
{ {
D3DKMT_HANDLE device; D3DKMT_HANDLE device;
@ -19,40 +31,26 @@ namespace
ContextInfo() : device(0) {} ContextInfo() : device(0) {}
}; };
struct DeviceInfo
{
D3DKMT_HANDLE adapter;
D3DDDI_VIDEO_PRESENT_SOURCE_ID vidPnSourceId;
DeviceInfo() : adapter(0), vidPnSourceId(D3DDDI_ID_UNINITIALIZED) {}
};
std::map<D3DKMT_HANDLE, ContextInfo> g_contexts; std::map<D3DKMT_HANDLE, ContextInfo> g_contexts;
std::map<D3DKMT_HANDLE, DeviceInfo> g_devices; AdapterInfo g_gdiAdapterInfo = {};
HMONITOR g_lastOpenAdapterMonitor = nullptr; AdapterInfo g_lastOpenAdapterInfo = {};
UINT g_lastFlipInterval = 0;
UINT g_flipIntervalOverride = 0;
D3DKMT_HANDLE g_lastPresentContext = 0;
UINT g_presentCount = 0;
std::atomic<long long> g_qpcLastVerticalBlank = 0;
CRITICAL_SECTION g_vblankCs = {};
decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr; decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr;
decltype(D3DKMTSetVidPnSourceOwner1)* g_origD3dKmtSetVidPnSourceOwner1 = nullptr;
D3DDDI_FLIPINTERVAL_TYPE g_overrideFlipInterval = D3DDDI_FLIPINTERVAL_NOOVERRIDE; NTSTATUS APIENTRY closeAdapter(const D3DKMT_CLOSEADAPTER* pData)
UINT g_presentCount = 0;
NTSTATUS APIENTRY createDevice(D3DKMT_CREATEDEVICE* pData)
{ {
Compat::LogEnter("D3DKMTCreateDevice", pData); Compat::ScopedCriticalSection lock(g_vblankCs);
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateDevice)(pData); if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter)
if (SUCCEEDED(result))
{ {
g_devices[pData->hDevice].adapter = pData->hAdapter; g_lastOpenAdapterInfo = {};
D3DKMT_SETQUEUEDLIMIT limit = {};
limit.hDevice = pData->hDevice;
limit.Type = D3DKMT_SET_QUEUEDLIMIT_PRESENT;
limit.QueuedPresentLimit = 1;
CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(&limit);
} }
Compat::LogLeave("D3DKMTCreateDevice", pData) << result; return CALL_ORIG_FUNC(D3DKMTCloseAdapter)(pData);
return result;
} }
NTSTATUS APIENTRY createContext(D3DKMT_CREATECONTEXT* pData) NTSTATUS APIENTRY createContext(D3DKMT_CREATECONTEXT* pData)
@ -98,6 +96,22 @@ namespace
return result; return result;
} }
NTSTATUS APIENTRY createDevice(D3DKMT_CREATEDEVICE* pData)
{
Compat::LogEnter("D3DKMTCreateDevice", pData);
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateDevice)(pData);
if (SUCCEEDED(result))
{
D3DKMT_SETQUEUEDLIMIT limit = {};
limit.hDevice = pData->hDevice;
limit.Type = D3DKMT_SET_QUEUEDLIMIT_PRESENT;
limit.QueuedPresentLimit = 2;
CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(&limit);
}
Compat::LogLeave("D3DKMTCreateDevice", pData) << result;
return result;
}
NTSTATUS APIENTRY destroyContext(const D3DKMT_DESTROYCONTEXT* pData) NTSTATUS APIENTRY destroyContext(const D3DKMT_DESTROYCONTEXT* pData)
{ {
Compat::LogEnter("D3DKMTDestroyContext", pData); Compat::LogEnter("D3DKMTDestroyContext", pData);
@ -105,89 +119,71 @@ namespace
if (SUCCEEDED(result)) if (SUCCEEDED(result))
{ {
g_contexts.erase(pData->hContext); g_contexts.erase(pData->hContext);
if (g_lastPresentContext == pData->hContext)
{
g_lastPresentContext = 0;
}
} }
Compat::LogLeave("D3DKMTDestroyContext", pData) << result; Compat::LogLeave("D3DKMTDestroyContext", pData) << result;
return result; return result;
} }
NTSTATUS APIENTRY destroyDevice(const D3DKMT_DESTROYDEVICE* pData) AdapterInfo getAdapterInfo(const D3DKMT_OPENADAPTERFROMHDC& data)
{ {
Compat::LogEnter("D3DKMTDestroyDevice", pData); AdapterInfo adapterInfo = {};
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTDestroyDevice)(pData); adapterInfo.adapter = data.hAdapter;
if (SUCCEEDED(result)) adapterInfo.vidPnSourceId = data.VidPnSourceId;
{
g_devices.erase(pData->hDevice); POINT p = {};
} GetDCOrgEx(data.hDc, &p);
Compat::LogLeave("D3DKMTDestroyDevice", pData) << result; MONITORINFO mi = {};
return result; mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY), &mi);
adapterInfo.monitorRect = mi.rcMonitor;
return adapterInfo;
} }
NTSTATUS APIENTRY openAdapterFromHdc(D3DKMT_OPENADAPTERFROMHDC* pData) NTSTATUS APIENTRY openAdapterFromHdc(D3DKMT_OPENADAPTERFROMHDC* pData)
{ {
Compat::LogEnter("D3DKMTOpenAdapterFromHdc", pData); Compat::LogEnter("D3DKMTOpenAdapterFromHdc", pData);
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(pData); NTSTATUS result = CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(pData);
if (pData) if (SUCCEEDED(result))
{ {
POINT p = {}; Compat::ScopedCriticalSection lock(g_vblankCs);
GetDCOrgEx(pData->hDc, &p); g_lastOpenAdapterInfo = getAdapterInfo(*pData);
g_lastOpenAdapterMonitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY);
} }
Compat::LogLeave("D3DKMTOpenAdapterFromHdc", pData) << result; Compat::LogLeave("D3DKMTOpenAdapterFromHdc", pData) << result;
return result; return result;
} }
bool isPresentReady(D3DKMT_HANDLE device, D3DDDI_VIDEO_PRESENT_SOURCE_ID vidPnSourceId)
{
D3DKMT_GETDEVICESTATE deviceState = {};
deviceState.hDevice = device;
deviceState.StateType = D3DKMT_DEVICESTATE_PRESENT;
deviceState.PresentState.VidPnSourceId = vidPnSourceId;
NTSTATUS stateResult = D3DKMTGetDeviceState(&deviceState);
return FAILED(stateResult) ||
g_presentCount == deviceState.PresentState.PresentStats.PresentCount ||
0 == deviceState.PresentState.PresentStats.PresentCount;
}
NTSTATUS APIENTRY present(D3DKMT_PRESENT* pData) NTSTATUS APIENTRY present(D3DKMT_PRESENT* pData)
{ {
Compat::LogEnter("D3DKMTPresent", pData); Compat::LogEnter("D3DKMTPresent", pData);
if (pData->Flags.Flip && D3DDDI_FLIPINTERVAL_NOOVERRIDE != g_overrideFlipInterval) if (pData->Flags.Flip)
{ {
pData->FlipInterval = g_overrideFlipInterval; g_lastFlipInterval = pData->FlipInterval;
} g_lastPresentContext = pData->hContext;
++g_presentCount; if (UINT_MAX == g_flipIntervalOverride)
pData->Flags.PresentCountValid = 1; {
pData->PresentCount = g_presentCount; Compat::LogLeave("D3DKMTPresent", pData) << S_OK;
return 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);
}
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTPresent)(pData); NTSTATUS result = CALL_ORIG_FUNC(D3DKMTPresent)(pData);
if (SUCCEEDED(result) &&
1 == DDraw::PrimarySurface::getDesc().dwBackBufferCount &&
pData->Flags.Flip &&
D3DDDI_FLIPINTERVAL_IMMEDIATE != pData->FlipInterval &&
D3DDDI_FLIPINTERVAL_NOOVERRIDE == g_overrideFlipInterval)
{
auto contextIt = g_contexts.find(pData->hContext);
auto deviceIt = (contextIt != g_contexts.end())
? g_devices.find(contextIt->second.device)
: g_devices.find(pData->hDevice);
if (deviceIt != g_devices.end())
{
D3DKMT_WAITFORVERTICALBLANKEVENT vbEvent = {};
vbEvent.hAdapter = deviceIt->second.adapter;
vbEvent.hDevice = deviceIt->first;
vbEvent.VidPnSourceId = deviceIt->second.vidPnSourceId;
while (!isPresentReady(deviceIt->first, deviceIt->second.vidPnSourceId))
{
if (FAILED(D3DKMTWaitForVerticalBlankEvent(&vbEvent)))
{
Sleep(1);
}
}
}
}
Compat::LogLeave("D3DKMTPresent", pData) << result; Compat::LogLeave("D3DKMTPresent", pData) << result;
return result; return result;
@ -212,7 +208,7 @@ namespace
if (D3DKMT_SET_QUEUEDLIMIT_PRESENT == pData->Type) if (D3DKMT_SET_QUEUEDLIMIT_PRESENT == pData->Type)
{ {
const UINT origLimit = pData->QueuedPresentLimit; const UINT origLimit = pData->QueuedPresentLimit;
const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = 1; const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = 2;
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(pData); NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(pData);
const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = origLimit; const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = origLimit;
Compat::LogLeave("D3DKMTSetQueuedLimit", pData) << result; Compat::LogLeave("D3DKMTSetQueuedLimit", pData) << result;
@ -223,42 +219,30 @@ namespace
return result; return result;
} }
void processSetVidPnSourceOwner(const D3DKMT_SETVIDPNSOURCEOWNER* pData) void updateGdiAdapterInfo()
{ {
auto& vidPnSourceId = g_devices[pData->hDevice].vidPnSourceId; static auto lastDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
for (UINT i = 0; i < pData->VidPnSourceCount; ++i) const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
if (currentDisplaySettingsUniqueness != lastDisplaySettingsUniqueness)
{ {
if (D3DKMT_VIDPNSOURCEOWNER_UNOWNED != pData->pType[i]) if (g_gdiAdapterInfo.adapter)
{ {
vidPnSourceId = pData->pVidPnSourceId[i]; D3DKMT_CLOSEADAPTER data = {};
return; data.hAdapter = g_gdiAdapterInfo.adapter;
CALL_ORIG_FUNC(D3DKMTCloseAdapter)(&data);
g_gdiAdapterInfo = {};
} }
}
vidPnSourceId = D3DDDI_ID_UNINITIALIZED;
}
NTSTATUS APIENTRY setVidPnSourceOwner(const D3DKMT_SETVIDPNSOURCEOWNER* pData) D3DKMT_OPENADAPTERFROMHDC data = {};
{ data.hDc = GetDC(nullptr);
Compat::LogEnter("D3DKMTSetVidPnSourceOwner", pData); if (SUCCEEDED(CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(&data)))
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetVidPnSourceOwner)(pData); {
if (SUCCEEDED(result)) g_gdiAdapterInfo = getAdapterInfo(data);
{ }
processSetVidPnSourceOwner(pData); ReleaseDC(nullptr, data.hDc);
}
Compat::LogLeave("D3DKMTSetVidPnSourceOwner", pData) << result;
return result;
}
NTSTATUS APIENTRY setVidPnSourceOwner1(const D3DKMT_SETVIDPNSOURCEOWNER1* pData) lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness;
{
Compat::LogEnter("D3DKMTSetVidPnSourceOwner1", pData);
NTSTATUS result = g_origD3dKmtSetVidPnSourceOwner1(pData);
if (SUCCEEDED(result))
{
processSetVidPnSourceOwner(&pData->Version0);
} }
Compat::LogLeave("D3DKMTSetVidPnSourceOwner1", pData) << result;
return result;
} }
} }
@ -266,58 +250,113 @@ namespace D3dDdi
{ {
namespace KernelModeThunks namespace KernelModeThunks
{ {
HMONITOR getLastOpenAdapterMonitor() UINT getLastFlipInterval()
{ {
return g_lastOpenAdapterMonitor; return g_lastFlipInterval;
} }
bool isPresentReady() UINT getLastDisplayedFrameCount()
{ {
for (auto it : g_devices) auto contextIter = g_contexts.find(g_lastPresentContext);
if (contextIter == g_contexts.end())
{ {
if (D3DDDI_ID_UNINITIALIZED != it.second.vidPnSourceId) return g_presentCount;
{
return ::isPresentReady(it.first, it.second.vidPnSourceId);
}
} }
return true;
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());
if (!primary)
{
return {};
}
static auto lastDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
if (currentDisplaySettingsUniqueness != lastDisplaySettingsUniqueness)
{
lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness;
CompatPtr<IUnknown> ddUnk;
primary->GetDDInterface(primary, reinterpret_cast<void**>(&ddUnk.getRef()));
CompatPtr<IDirectDraw7> dd7(ddUnk);
DDDEVICEIDENTIFIER2 di = {};
dd7->GetDeviceIdentifier(dd7, &di, 0);
}
return g_lastOpenAdapterInfo.monitorRect;
}
long long getQpcLastVerticalBlank()
{
return g_qpcLastVerticalBlank;
} }
void installHooks() void installHooks()
{ {
InitializeCriticalSection(&g_vblankCs);
HOOK_FUNCTION(gdi32, D3DKMTCloseAdapter, closeAdapter);
HOOK_FUNCTION(gdi32, D3DKMTCreateContext, createContext); HOOK_FUNCTION(gdi32, D3DKMTCreateContext, createContext);
HOOK_FUNCTION(gdi32, D3DKMTCreateDevice, createDevice); HOOK_FUNCTION(gdi32, D3DKMTCreateDevice, createDevice);
HOOK_FUNCTION(gdi32, D3DKMTCreateDCFromMemory, createDcFromMemory); HOOK_FUNCTION(gdi32, D3DKMTCreateDCFromMemory, createDcFromMemory);
HOOK_FUNCTION(gdi32, D3DKMTDestroyContext, destroyContext); HOOK_FUNCTION(gdi32, D3DKMTDestroyContext, destroyContext);
HOOK_FUNCTION(gdi32, D3DKMTDestroyDevice, destroyDevice);
HOOK_FUNCTION(gdi32, D3DKMTOpenAdapterFromHdc, openAdapterFromHdc); HOOK_FUNCTION(gdi32, D3DKMTOpenAdapterFromHdc, openAdapterFromHdc);
HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, queryAdapterInfo); HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, queryAdapterInfo);
HOOK_FUNCTION(gdi32, D3DKMTPresent, present); HOOK_FUNCTION(gdi32, D3DKMTPresent, present);
HOOK_FUNCTION(gdi32, D3DKMTSetQueuedLimit, setQueuedLimit); HOOK_FUNCTION(gdi32, D3DKMTSetQueuedLimit, setQueuedLimit);
HOOK_FUNCTION(gdi32, D3DKMTSetVidPnSourceOwner, setVidPnSourceOwner);
// Functions not available in Windows Vista // Functions not available in Windows Vista
Compat::hookFunction("gdi32", "D3DKMTCreateContextVirtual", Compat::hookFunction("gdi32", "D3DKMTCreateContextVirtual",
reinterpret_cast<void*&>(g_origD3dKmtCreateContextVirtual), createContextVirtual); reinterpret_cast<void*&>(g_origD3dKmtCreateContextVirtual), createContextVirtual);
Compat::hookFunction("gdi32", "D3DKMTSetVidPnSourceOwner1",
reinterpret_cast<void*&>(g_origD3dKmtSetVidPnSourceOwner1), setVidPnSourceOwner1);
} }
void overrideFlipInterval(D3DDDI_FLIPINTERVAL_TYPE flipInterval) void setFlipIntervalOverride(UINT flipInterval)
{ {
g_overrideFlipInterval = flipInterval; g_flipIntervalOverride = flipInterval;
} }
void releaseVidPnSources() void waitForVerticalBlank()
{ {
for (auto it : g_devices) D3DKMT_WAITFORVERTICALBLANKEVENT data = {};
{ {
if (D3DDDI_ID_UNINITIALIZED != it.second.vidPnSourceId) Compat::ScopedCriticalSection lock(g_vblankCs);
if (g_lastOpenAdapterInfo.adapter)
{ {
D3DKMT_SETVIDPNSOURCEOWNER vidPnSourceOwner = {}; data.hAdapter = g_lastOpenAdapterInfo.adapter;
vidPnSourceOwner.hDevice = it.first; data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
D3DKMTSetVidPnSourceOwner(&vidPnSourceOwner);
} }
else
{
updateGdiAdapterInfo();
data.hAdapter = g_gdiAdapterInfo.adapter;
data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId;
}
}
if (data.hAdapter)
{
D3DKMTWaitForVerticalBlankEvent(&data);
g_qpcLastVerticalBlank = Time::queryPerformanceCounter();
} }
} }
} }

View File

@ -1,22 +1,20 @@
#pragma once #pragma once
#include <d3d.h> #define WIN32_LEAN_AND_MEAN
#include <d3dumddi.h>
#include <../km/d3dkmthk.h>
#include <Windows.h> #include <Windows.h>
#include "D3dDdi/Log/KernelModeThunksLog.h"
static const auto D3DDDI_FLIPINTERVAL_NOOVERRIDE = static_cast<D3DDDI_FLIPINTERVAL_TYPE>(5);
namespace D3dDdi namespace D3dDdi
{ {
namespace KernelModeThunks namespace KernelModeThunks
{ {
HMONITOR getLastOpenAdapterMonitor(); UINT getLastFlipInterval();
UINT getLastDisplayedFrameCount();
UINT getLastSubmittedFrameCount();
RECT getMonitorRect();
long long getQpcLastVerticalBlank();
void installHooks(); void installHooks();
bool isPresentReady(); void setFlipIntervalOverride(UINT flipInterval);
void overrideFlipInterval(D3DDDI_FLIPINTERVAL_TYPE flipInterval); void waitForVerticalBlank();
void releaseVidPnSources();
} }
} }

View File

@ -1,4 +1,5 @@
#include "Common/CompatPtr.h" #include "Common/CompatPtr.h"
#include "D3dDdi/KernelModeThunks.h"
#include "DDraw/ActivateAppHandler.h" #include "DDraw/ActivateAppHandler.h"
#include "DDraw/DirectDraw.h" #include "DDraw/DirectDraw.h"
#include "DDraw/Repository.h" #include "DDraw/Repository.h"
@ -117,6 +118,7 @@ namespace DDraw
vtable.GetGDISurface = &GetGDISurface; vtable.GetGDISurface = &GetGDISurface;
vtable.SetCooperativeLevel = &SetCooperativeLevel; vtable.SetCooperativeLevel = &SetCooperativeLevel;
vtable.SetDisplayMode = &SetDisplayMode; vtable.SetDisplayMode = &SetDisplayMode;
vtable.WaitForVerticalBlank = &WaitForVerticalBlank;
} }
template <typename TDirectDraw> template <typename TDirectDraw>
@ -206,6 +208,30 @@ namespace DDraw
return setDisplayMode(This, dwWidth, dwHeight, dwBPP, params...); return setDisplayMode(This, dwWidth, dwHeight, dwBPP, params...);
} }
template <typename TDirectDraw>
HRESULT STDMETHODCALLTYPE DirectDraw<TDirectDraw>::WaitForVerticalBlank(
TDirectDraw* This, DWORD dwFlags, HANDLE hEvent)
{
if (!This || (DDWAITVB_BLOCKBEGIN != dwFlags && DDWAITVB_BLOCKEND != dwFlags))
{
return s_origVtable.WaitForVerticalBlank(This, dwFlags, hEvent);
}
DWORD scanLine = 0;
if (DDERR_VERTICALBLANKINPROGRESS != s_origVtable.GetScanLine(This, &scanLine))
{
D3dDdi::KernelModeThunks::waitForVerticalBlank();
}
if (DDWAITVB_BLOCKEND == dwFlags)
{
while (DDERR_VERTICALBLANKINPROGRESS == s_origVtable.GetScanLine(This, &scanLine));
}
return DD_OK;
}
template DirectDraw<IDirectDraw>; template DirectDraw<IDirectDraw>;
template DirectDraw<IDirectDraw2>; template DirectDraw<IDirectDraw2>;
template DirectDraw<IDirectDraw4>; template DirectDraw<IDirectDraw4>;

View File

@ -46,6 +46,8 @@ namespace DDraw
DWORD dwHeight, DWORD dwHeight,
DWORD dwBPP, DWORD dwBPP,
Params... params); Params... params);
static HRESULT STDMETHODCALLTYPE WaitForVerticalBlank(TDirectDraw* This, DWORD dwFlags, HANDLE hEvent);
}; };
} }

View File

@ -32,8 +32,10 @@ namespace DDraw
SET_COMPAT_METHOD(Blt); SET_COMPAT_METHOD(Blt);
SET_COMPAT_METHOD(BltFast); SET_COMPAT_METHOD(BltFast);
SET_COMPAT_METHOD(Flip); SET_COMPAT_METHOD(Flip);
SET_COMPAT_METHOD(GetBltStatus);
SET_COMPAT_METHOD(GetCaps); SET_COMPAT_METHOD(GetCaps);
SET_COMPAT_METHOD(GetDC); SET_COMPAT_METHOD(GetDC);
SET_COMPAT_METHOD(GetFlipStatus);
SET_COMPAT_METHOD(GetSurfaceDesc); SET_COMPAT_METHOD(GetSurfaceDesc);
SET_COMPAT_METHOD(IsLost); SET_COMPAT_METHOD(IsLost);
SET_COMPAT_METHOD(Lock); SET_COMPAT_METHOD(Lock);

View File

@ -1,4 +1,3 @@
#include <atomic>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -39,7 +38,6 @@ namespace
DWORD WINAPI updateThreadProc(LPVOID lpParameter); DWORD WINAPI updateThreadProc(LPVOID lpParameter);
CompatWeakPtr<IDirectDrawSurface7> g_frontBuffer; CompatWeakPtr<IDirectDrawSurface7> g_frontBuffer;
CompatWeakPtr<IDirectDrawSurface7> g_backBuffer;
CompatWeakPtr<IDirectDrawSurface7> g_paletteConverter; CompatWeakPtr<IDirectDrawSurface7> g_paletteConverter;
CompatWeakPtr<IDirectDrawClipper> g_clipper; CompatWeakPtr<IDirectDrawClipper> g_clipper;
DDSURFACEDESC2 g_surfaceDesc = {}; DDSURFACEDESC2 g_surfaceDesc = {};
@ -47,15 +45,19 @@ namespace
bool g_stopUpdateThread = false; bool g_stopUpdateThread = false;
HANDLE g_updateThread = nullptr; HANDLE g_updateThread = nullptr;
HANDLE g_updateEvent = nullptr; unsigned int g_disableUpdateCount = 0;
std::atomic<int> g_disableUpdateCount = 0; bool g_isFlipPending = false;
bool g_isUpdateSuspended = false; bool g_isPresentPending = false;
long long g_qpcFlipModeTimeout = 0; bool g_isUpdatePending = false;
long long g_qpcLastFlip = 0; bool g_isFullScreen = false;
long long g_qpcNextUpdate = 0; bool g_waitingForPrimaryUnlock = false;
long long g_qpcUpdateInterval = 0; UINT g_flipIntervalDriverOverride = UINT_MAX;
UINT g_lastFlipFrameCount = 0;
DDraw::Surface* g_lastFlipSurface = nullptr;
long long g_qpcLastUpdate = 0;
std::atomic<bool> g_isFullScreen(false); CompatPtr<IDirectDrawSurface7> getBackBuffer();
CompatPtr<IDirectDrawSurface7> getLastSurface();
BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam) BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam)
{ {
@ -140,8 +142,14 @@ namespace
return; return;
} }
auto backBuffer(getBackBuffer());
if (!backBuffer)
{
return;
}
HDC backBufferDc = nullptr; HDC backBufferDc = nullptr;
g_backBuffer->GetDC(g_backBuffer, &backBufferDc); backBuffer->GetDC(backBuffer, &backBufferDc);
for (auto it = visibleLayeredWindows.rbegin(); it != visibleLayeredWindows.rend(); ++it) for (auto it = visibleLayeredWindows.rbegin(); it != visibleLayeredWindows.rend(); ++it)
{ {
@ -159,76 +167,22 @@ namespace
ReleaseDC(*it, windowDc); ReleaseDC(*it, windowDc);
} }
g_backBuffer->ReleaseDC(g_backBuffer, backBufferDc); backBuffer->ReleaseDC(backBuffer, backBufferDc);
} }
HRESULT bltToPrimaryChain(CompatRef<IDirectDrawSurface7> src) void bltToPrimaryChain(CompatRef<IDirectDrawSurface7> src)
{ {
if (g_isFullScreen) if (!g_isFullScreen)
{ {
return g_backBuffer->Blt(g_backBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr); EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindow, reinterpret_cast<LPARAM>(&src));
return;
} }
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindow, reinterpret_cast<LPARAM>(&src)); auto backBuffer(getBackBuffer());
return DD_OK; if (backBuffer)
}
bool compatBlt()
{
Compat::LogEnter("RealPrimarySurface::compatBlt");
BltToWindowViaGdiArgs bltToWindowViaGdiArgs;
if (!g_frontBuffer || (!g_isFullScreen && DDraw::RealPrimarySurface::isLost()))
{ {
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi, backBuffer->Blt(backBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
Compat::LogLeave("RealPrimarySurface::compatBlt") << false;
return false;
} }
Gdi::Region primaryRegion(DDraw::PrimarySurface::getMonitorRect());
bltToWindowViaGdiArgs.primaryRegion = &primaryRegion;
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
bool result = false;
auto primary(DDraw::PrimarySurface::getPrimary());
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(primary.get()));
if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8)
{
HDC paletteConverterDc = nullptr;
g_paletteConverter->GetDC(g_paletteConverter, &paletteConverterDc);
HDC primaryDc = nullptr;
D3dDdi::Device::setReadOnlyGdiLock(true);
primary->GetDC(primary, &primaryDc);
D3dDdi::Device::setReadOnlyGdiLock(false);
if (paletteConverterDc && primaryDc)
{
result = TRUE == CALL_ORIG_FUNC(BitBlt)(paletteConverterDc,
0, 0, g_surfaceDesc.dwWidth, g_surfaceDesc.dwHeight, primaryDc, 0, 0, SRCCOPY);
}
primary->ReleaseDC(primary, primaryDc);
g_paletteConverter->ReleaseDC(g_paletteConverter, paletteConverterDc);
if (result)
{
result = SUCCEEDED(bltToPrimaryChain(*g_paletteConverter));
}
}
else
{
result = SUCCEEDED(bltToPrimaryChain(*primary));
}
if (result && g_isFullScreen && primary == DDraw::PrimarySurface::getGdiSurface())
{
bltVisibleLayeredWindowsToBackBuffer();
}
Compat::LogLeave("RealPrimarySurface::compatBlt") << result;
return result;
} }
template <typename TDirectDraw> template <typename TDirectDraw>
@ -263,124 +217,278 @@ namespace
return result; return result;
} }
template <typename DirectDraw> CompatPtr<IDirectDrawSurface7> getBackBuffer()
HRESULT init(CompatRef<DirectDraw> dd, CompatPtr<IDirectDrawSurface7> surface)
{ {
DDSURFACEDESC2 desc = {}; DDSCAPS2 caps = {};
desc.dwSize = sizeof(desc); caps.dwCaps = DDSCAPS_BACKBUFFER;
surface->GetSurfaceDesc(surface, &desc);
const bool isFlippable = 0 != (desc.ddsCaps.dwCaps & DDSCAPS_FLIP);
CompatPtr<IDirectDrawSurface7> backBuffer; CompatPtr<IDirectDrawSurface7> backBuffer;
if (isFlippable) if (g_frontBuffer)
{ {
DDSCAPS2 backBufferCaps = {}; g_frontBuffer->GetAttachedSurface(g_frontBuffer, &caps, &backBuffer.getRef());
backBufferCaps.dwCaps = DDSCAPS_BACKBUFFER;
surface->GetAttachedSurface(surface, &backBufferCaps, &backBuffer.getRef());
} }
else return backBuffer;
{
CALL_ORIG_PROC(DirectDrawCreateClipper, 0, &g_clipper.getRef(), nullptr);
surface->SetClipper(surface, g_clipper);
}
g_qpcLastFlip = Time::queryPerformanceCounter() - g_qpcFlipModeTimeout;
g_qpcNextUpdate = Time::queryPerformanceCounter();
typename DDraw::Types<DirectDraw>::TSurfaceDesc dm = {};
dm.dwSize = sizeof(dm);
dd->GetDisplayMode(&dd, &dm);
if (dm.dwRefreshRate <= 1 || dm.dwRefreshRate >= 1000)
{
dm.dwRefreshRate = 60;
}
g_qpcUpdateInterval = Time::g_qpcFrequency / dm.dwRefreshRate;
surface->SetPrivateData(surface, IID_IReleaseNotifier,
&g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER);
g_frontBuffer = surface.detach();
g_backBuffer = backBuffer;
g_surfaceDesc = desc;
g_isFullScreen = isFlippable;
return DD_OK;
} }
bool isUpdateScheduled() CompatPtr<IDirectDrawSurface7> getLastSurface()
{ {
return WAIT_OBJECT_0 == WaitForSingleObject(g_updateEvent, 0); DDSCAPS2 caps = {};
caps.dwCaps = DDSCAPS_FLIP;;
CompatPtr<IDirectDrawSurface7> backBuffer(getBackBuffer());
CompatPtr<IDirectDrawSurface7> lastSurface;
if (backBuffer)
{
backBuffer->GetAttachedSurface(backBuffer, &caps, &lastSurface.getRef());
}
return lastSurface;
} }
int msUntilNextUpdate() UINT getFlipIntervalFromFlags(DWORD flags)
{ {
DDraw::ScopedThreadLock lock; if (flags & DDFLIP_NOVSYNC)
const auto qpcNow = Time::queryPerformanceCounter();
const int result = max(0, Time::qpcToMs(g_qpcNextUpdate - qpcNow));
if (0 == result && g_isFullScreen && qpcNow - g_qpcLastFlip >= g_qpcFlipModeTimeout)
{ {
return D3dDdi::KernelModeThunks::isPresentReady() ? 0 : 2; return 0;
} }
return result;
if (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4))
{
UINT flipInterval = (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) >> 24;
if (flipInterval < 2 || flipInterval > 4)
{
flipInterval = 1;
}
return flipInterval;
}
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;
}
bool isPresentPending()
{
if (g_isPresentPending)
{
g_isPresentPending = static_cast<int>(D3dDdi::KernelModeThunks::getLastDisplayedFrameCount() -
D3dDdi::KernelModeThunks::getLastSubmittedFrameCount()) < 0;
}
return g_isPresentPending;
} }
void onRelease() void onRelease()
{ {
Compat::LogEnter("RealPrimarySurface::onRelease"); Compat::LogEnter("RealPrimarySurface::onRelease");
ResetEvent(g_updateEvent);
g_frontBuffer = nullptr; g_frontBuffer = nullptr;
g_backBuffer = nullptr;
g_clipper.release(); g_clipper.release();
g_isFullScreen = false; g_isFullScreen = false;
g_isFlipPending = false;
g_isPresentPending = false;
g_waitingForPrimaryUnlock = false;
g_paletteConverter.release(); g_paletteConverter.release();
g_qpcUpdateInterval = Time::g_qpcFrequency / 60; g_surfaceDesc = {};
ZeroMemory(&g_surfaceDesc, sizeof(g_surfaceDesc));
Compat::LogLeave("RealPrimarySurface::onRelease"); Compat::LogLeave("RealPrimarySurface::onRelease");
} }
void updateNow() void onRestore()
{ {
ResetEvent(g_updateEvent); DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
g_frontBuffer->GetSurfaceDesc(g_frontBuffer, &desc);
g_clipper.release();
const bool isFlippable = 0 != (desc.ddsCaps.dwCaps & DDSCAPS_FLIP);
if (!isFlippable)
{
CALL_ORIG_PROC(DirectDrawCreateClipper, 0, &g_clipper.getRef(), nullptr);
g_frontBuffer->SetClipper(g_frontBuffer, g_clipper);
}
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();
}
D3dDdi::KernelModeThunks::waitForVerticalBlank();
}
void presentToPrimaryChain(CompatWeakPtr<IDirectDrawSurface7> src)
{
Compat::LogEnter("RealPrimarySurface::presentToPrimaryChain", src.get());
Gdi::VirtualScreen::update(); Gdi::VirtualScreen::update();
if (compatBlt() && g_isFullScreen)
BltToWindowViaGdiArgs bltToWindowViaGdiArgs;
if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost())
{ {
D3dDdi::KernelModeThunks::overrideFlipInterval( EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
Time::queryPerformanceCounter() - g_qpcLastFlip >= g_qpcFlipModeTimeout reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
? D3DDDI_FLIPINTERVAL_ONE Compat::LogLeave("RealPrimarySurface::presentToPrimaryChain", src.get()) << false;
: D3DDDI_FLIPINTERVAL_IMMEDIATE); return;
g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT); }
D3dDdi::KernelModeThunks::overrideFlipInterval(D3DDDI_FLIPINTERVAL_NOOVERRIDE);
Gdi::Region primaryRegion(D3dDdi::KernelModeThunks::getMonitorRect());
bltToWindowViaGdiArgs.primaryRegion = &primaryRegion;
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(src.get()));
if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8)
{
HDC paletteConverterDc = nullptr;
g_paletteConverter->GetDC(g_paletteConverter, &paletteConverterDc);
HDC srcDc = nullptr;
D3dDdi::Device::setReadOnlyGdiLock(true);
src->GetDC(src, &srcDc);
D3dDdi::Device::setReadOnlyGdiLock(false);
if (paletteConverterDc && srcDc)
{
CALL_ORIG_FUNC(BitBlt)(paletteConverterDc,
0, 0, g_surfaceDesc.dwWidth, g_surfaceDesc.dwHeight, srcDc, 0, 0, SRCCOPY);
}
src->ReleaseDC(src, srcDc);
g_paletteConverter->ReleaseDC(g_paletteConverter, paletteConverterDc);
bltToPrimaryChain(*g_paletteConverter);
}
else
{
bltToPrimaryChain(*src);
}
if (g_isFullScreen && src == DDraw::PrimarySurface::getGdiSurface())
{
bltVisibleLayeredWindowsToBackBuffer();
}
Compat::LogLeave("RealPrimarySurface::presentToPrimaryChain", src.get());
}
void updateNow(CompatWeakPtr<IDirectDrawSurface7> src, UINT flipInterval)
{
DDraw::ScopedThreadLock lock;
if (flipInterval <= 1 && isPresentPending())
{
g_isUpdatePending = true;
return;
}
presentToPrimaryChain(src);
g_isUpdatePending = false;
if (!g_isFullScreen)
{
g_isPresentPending = true;
return;
}
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;
}
void updateNowIfNotBusy()
{
auto primary(DDraw::PrimarySurface::getPrimary());
RECT emptyRect = {};
HRESULT result = primary ? primary->BltFast(primary, 0, 0, primary, &emptyRect, DDBLTFAST_WAIT) : DD_OK;
g_waitingForPrimaryUnlock = DDERR_SURFACEBUSY == result || DDERR_LOCKEDSURFACES == result;
if (!g_waitingForPrimaryUnlock && DDERR_SURFACELOST != result)
{
const auto msSinceLastUpdate = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate);
updateNow(primary, msSinceLastUpdate > Config::delayedFlipModeTimeout ? 0 : 1);
} }
} }
DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/) DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/)
{ {
while (true) const int msPresentDelayAfterVBlank = 1;
bool waitForVBlank = true;
while (!g_stopUpdateThread)
{ {
WaitForSingleObject(g_updateEvent, INFINITE); if (waitForVBlank)
if (g_stopUpdateThread)
{ {
return 0; D3dDdi::KernelModeThunks::waitForVerticalBlank();
if (!g_isFullScreen)
{
g_isPresentPending = false;
}
} }
const int waitTime = msUntilNextUpdate(); Sleep(msPresentDelayAfterVBlank);
if (waitTime > 0)
{
Sleep(waitTime);
continue;
}
DDraw::ScopedThreadLock lock; DDraw::ScopedThreadLock lock;
if (isUpdateScheduled() && msUntilNextUpdate() <= 0) waitForVBlank = Time::qpcToMs(Time::queryPerformanceCounter() -
D3dDdi::KernelModeThunks::getQpcLastVerticalBlank()) >= msPresentDelayAfterVBlank;
if (waitForVBlank && g_isUpdatePending && 0 == g_disableUpdateCount && !isPresentPending())
{ {
updateNow(); updateNowIfNotBusy();
} }
} }
return 0;
} }
} }
@ -389,6 +497,7 @@ namespace DDraw
template <typename DirectDraw> template <typename DirectDraw>
HRESULT RealPrimarySurface::create(CompatRef<DirectDraw> dd) HRESULT RealPrimarySurface::create(CompatRef<DirectDraw> dd)
{ {
DDraw::ScopedThreadLock lock;
HRESULT result = createPaletteConverter(dd); HRESULT result = createPaletteConverter(dd);
if (FAILED(result)) if (FAILED(result))
{ {
@ -400,7 +509,7 @@ namespace DDraw
desc.dwSize = sizeof(desc); desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP; desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
desc.dwBackBufferCount = 1; desc.dwBackBufferCount = 2;
CompatPtr<typename Types<DirectDraw>::TCreatedSurface> surface; CompatPtr<typename Types<DirectDraw>::TCreatedSurface> surface;
result = dd->CreateSurface(&dd, &desc, &surface.getRef(), nullptr); result = dd->CreateSurface(&dd, &desc, &surface.getRef(), nullptr);
@ -420,7 +529,12 @@ namespace DDraw
return result; return result;
} }
return ::init(dd, surface); g_frontBuffer = CompatPtr<IDirectDrawSurface7>::from(surface.get()).detach();
g_frontBuffer->SetPrivateData(g_frontBuffer, IID_IReleaseNotifier,
&g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER);
onRestore();
return DD_OK;
} }
template HRESULT RealPrimarySurface::create(CompatRef<IDirectDraw>); template HRESULT RealPrimarySurface::create(CompatRef<IDirectDraw>);
@ -430,41 +544,73 @@ namespace DDraw
void RealPrimarySurface::disableUpdates() void RealPrimarySurface::disableUpdates()
{ {
if (0 == g_disableUpdateCount++ && isUpdateScheduled()) DDraw::ScopedThreadLock lock;
{ --g_disableUpdateCount;
ResetEvent(g_updateEvent);
g_isUpdateSuspended = true;
}
} }
void RealPrimarySurface::enableUpdates() void RealPrimarySurface::enableUpdates()
{ {
if (0 == --g_disableUpdateCount && g_isUpdateSuspended) DDraw::ScopedThreadLock lock;
{ ++g_disableUpdateCount;
SetEvent(g_updateEvent);
g_isUpdateSuspended = false;
}
} }
HRESULT RealPrimarySurface::flip(DWORD flags) HRESULT RealPrimarySurface::flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags)
{ {
if (!g_isFullScreen) DDraw::ScopedThreadLock lock;
auto primary(PrimarySurface::getPrimary());
const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY);
if (isFlipEmulated && !surfaceTargetOverride)
{ {
return DDERR_NOTFLIPPABLE; surfaceTargetOverride = PrimarySurface::getBackBuffer();
} }
ResetEvent(g_updateEvent); HRESULT result = primary->Flip(primary, surfaceTargetOverride, DDFLIP_WAIT);
g_isUpdateSuspended = false; if (FAILED(result))
{
return result;
}
g_qpcLastFlip = Time::queryPerformanceCounter(); DWORD flipInterval = getFlipInterval(flags);
compatBlt(); const auto msSinceLastUpdate = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate);
HRESULT result = g_frontBuffer->Flip(g_frontBuffer, nullptr, flags); const bool isFlipDelayed = msSinceLastUpdate >= 0 && msSinceLastUpdate <= Config::delayedFlipModeTimeout;
g_qpcNextUpdate = Time::queryPerformanceCounter(); if (isFlipDelayed)
return result; {
CompatPtr<IDirectDrawSurface7> prevPrimarySurface(
surfaceTargetOverride ? surfaceTargetOverride : PrimarySurface::getLastSurface());
updateNow(prevPrimarySurface, flipInterval);
g_isUpdatePending = true;
}
if (isFlipEmulated)
{
surfaceTargetOverride->Blt(surfaceTargetOverride, nullptr, primary, nullptr, DDBLT_WAIT, nullptr);
}
if (!isFlipDelayed)
{
updateNow(primary, flipInterval);
}
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());
}
return DD_OK;
} }
HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData) HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData)
{ {
DDraw::ScopedThreadLock lock;
auto gammaControl(CompatPtr<IDirectDrawGammaControl>::from(g_frontBuffer.get())); auto gammaControl(CompatPtr<IDirectDrawGammaControl>::from(g_frontBuffer.get()));
if (!gammaControl) if (!gammaControl)
{ {
@ -481,11 +627,6 @@ namespace DDraw
void RealPrimarySurface::init() void RealPrimarySurface::init()
{ {
g_qpcNextUpdate = Time::queryPerformanceCounter();
g_qpcUpdateInterval = Time::g_qpcFrequency / 60;
g_qpcFlipModeTimeout = Time::g_qpcFrequency / Config::minExpectedFlipsPerSec;
g_updateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
g_updateThread = CreateThread(nullptr, 0, &updateThreadProc, nullptr, 0, nullptr); g_updateThread = CreateThread(nullptr, 0, &updateThreadProc, nullptr, 0, nullptr);
SetThreadPriority(g_updateThread, THREAD_PRIORITY_TIME_CRITICAL); SetThreadPriority(g_updateThread, THREAD_PRIORITY_TIME_CRITICAL);
} }
@ -497,11 +638,13 @@ namespace DDraw
bool RealPrimarySurface::isLost() bool RealPrimarySurface::isLost()
{ {
DDraw::ScopedThreadLock lock;
return g_frontBuffer && DDERR_SURFACELOST == g_frontBuffer->IsLost(g_frontBuffer); return g_frontBuffer && DDERR_SURFACELOST == g_frontBuffer->IsLost(g_frontBuffer);
} }
void RealPrimarySurface::release() void RealPrimarySurface::release()
{ {
DDraw::ScopedThreadLock lock;
g_frontBuffer.release(); g_frontBuffer.release();
} }
@ -513,24 +656,28 @@ namespace DDraw
} }
g_stopUpdateThread = true; g_stopUpdateThread = true;
SetEvent(g_updateEvent);
if (WAIT_OBJECT_0 != WaitForSingleObject(g_updateThread, 1000)) if (WAIT_OBJECT_0 != WaitForSingleObject(g_updateThread, 1000))
{ {
TerminateThread(g_updateThread, 0); TerminateThread(g_updateThread, 0);
Compat::Log() << "The update thread was terminated forcefully"; Compat::Log() << "The update thread was terminated forcefully";
} }
ResetEvent(g_updateEvent);
g_stopUpdateThread = false;
g_updateThread = nullptr; g_updateThread = nullptr;
} }
HRESULT RealPrimarySurface::restore() HRESULT RealPrimarySurface::restore()
{ {
return g_frontBuffer->Restore(g_frontBuffer); DDraw::ScopedThreadLock lock;
HRESULT result = g_frontBuffer->Restore(g_frontBuffer);
if (SUCCEEDED(result))
{
onRestore();
}
return result;
} }
HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData) HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData)
{ {
DDraw::ScopedThreadLock lock;
auto gammaControl(CompatPtr<IDirectDrawGammaControl>::from(g_frontBuffer.get())); auto gammaControl(CompatPtr<IDirectDrawGammaControl>::from(g_frontBuffer.get()));
if (!gammaControl) if (!gammaControl)
{ {
@ -542,6 +689,7 @@ namespace DDraw
void RealPrimarySurface::setPalette() void RealPrimarySurface::setPalette()
{ {
DDraw::ScopedThreadLock lock;
if (g_surfaceDesc.ddpfPixelFormat.dwRGBBitCount <= 8) if (g_surfaceDesc.ddpfPixelFormat.dwRGBBitCount <= 8)
{ {
g_frontBuffer->SetPalette(g_frontBuffer, PrimarySurface::s_palette); g_frontBuffer->SetPalette(g_frontBuffer, PrimarySurface::s_palette);
@ -552,41 +700,44 @@ namespace DDraw
void RealPrimarySurface::update() void RealPrimarySurface::update()
{ {
if (g_isUpdateSuspended) DDraw::ScopedThreadLock lock;
g_qpcLastUpdate = Time::queryPerformanceCounter();
g_isUpdatePending = true;
if (g_waitingForPrimaryUnlock)
{ {
return; updateNowIfNotBusy();
}
if (!isUpdateScheduled())
{
const auto qpcNow = Time::queryPerformanceCounter();
const long long missedIntervals = (qpcNow - g_qpcNextUpdate) / g_qpcUpdateInterval;
g_qpcNextUpdate += g_qpcUpdateInterval * (missedIntervals + 1);
if (Time::qpcToMs(g_qpcNextUpdate - qpcNow) < 2)
{
g_qpcNextUpdate += g_qpcUpdateInterval;
}
if (g_disableUpdateCount <= 0)
{
SetEvent(g_updateEvent);
}
else
{
g_isUpdateSuspended = true;
}
}
else if (msUntilNextUpdate() <= 0)
{
updateNow();
} }
} }
void RealPrimarySurface::updatePalette() void RealPrimarySurface::updatePalette()
{ {
DDraw::ScopedThreadLock lock;
if (PrimarySurface::s_palette) if (PrimarySurface::s_palette)
{ {
update(); update();
} }
} }
bool RealPrimarySurface::waitForFlip(Surface* surface, bool wait)
{
auto primary(DDraw::PrimarySurface::getPrimary());
if (!surface || !primary ||
surface != g_lastFlipSurface &&
surface != Surface::getSurface(*DDraw::PrimarySurface::getPrimary()))
{
return true;
}
if (!wait)
{
return !isFlipPending();
}
while (isFlipPending())
{
D3dDdi::KernelModeThunks::waitForVerticalBlank();
}
return true;
}
} }

View File

@ -2,11 +2,13 @@
#include <ddraw.h> #include <ddraw.h>
#include "Common/CompatWeakPtr.h" #include "Common/CompatPtr.h"
#include "Common/CompatRef.h" #include "Common/CompatRef.h"
namespace DDraw namespace DDraw
{ {
class Surface;
class RealPrimarySurface class RealPrimarySurface
{ {
public: public:
@ -15,7 +17,7 @@ namespace DDraw
static void disableUpdates(); static void disableUpdates();
static void enableUpdates(); static void enableUpdates();
static HRESULT flip(DWORD flags); static HRESULT flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags);
static HRESULT getGammaRamp(DDGAMMARAMP* rampData); static HRESULT getGammaRamp(DDGAMMARAMP* rampData);
static CompatWeakPtr<IDirectDrawSurface7> getSurface(); static CompatWeakPtr<IDirectDrawSurface7> getSurface();
static void init(); static void init();
@ -28,5 +30,6 @@ namespace DDraw
static void setPalette(); static void setPalette();
static void update(); static void update();
static void updatePalette(); static void updatePalette();
static bool waitForFlip(Surface* surface, bool wait = true);
}; };
} }

View File

@ -1,5 +1,3 @@
#include <ddraw.h>
#include "Common/CompatPtr.h" #include "Common/CompatPtr.h"
#include "Common/CompatRef.h" #include "Common/CompatRef.h"
#include "Config/Config.h" #include "Config/Config.h"
@ -16,24 +14,6 @@ namespace
CompatWeakPtr<IDirectDrawSurface7> g_primarySurface; CompatWeakPtr<IDirectDrawSurface7> g_primarySurface;
HANDLE g_gdiResourceHandle = nullptr; HANDLE g_gdiResourceHandle = nullptr;
DWORD g_origCaps = 0; DWORD g_origCaps = 0;
RECT g_monitorRect = {};
RECT getDdMonitorRect(CompatRef<IDirectDraw7> dd)
{
DDDEVICEIDENTIFIER2 di = {};
dd->GetDeviceIdentifier(&dd, &di, 0); // Calls D3DKMTOpenAdapterFromHdc, which updates last monitor below
HMONITOR monitor = D3dDdi::KernelModeThunks::getLastOpenAdapterMonitor();
if (!monitor)
{
monitor = MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY);
}
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(monitor, &mi);
return mi.rcMonitor;
}
template <typename TSurface> template <typename TSurface>
HANDLE getResourceHandle(TSurface& surface) HANDLE getResourceHandle(TSurface& surface)
@ -56,7 +36,6 @@ namespace DDraw
g_gdiResourceHandle = nullptr; g_gdiResourceHandle = nullptr;
g_primarySurface = nullptr; g_primarySurface = nullptr;
g_origCaps = 0; g_origCaps = 0;
g_monitorRect = {};
s_palette = nullptr; s_palette = nullptr;
s_surfaceBuffers.clear(); s_surfaceBuffers.clear();
ZeroMemory(&s_paletteEntries, sizeof(s_paletteEntries)); ZeroMemory(&s_paletteEntries, sizeof(s_paletteEntries));
@ -100,19 +79,8 @@ namespace DDraw
g_primarySurface = surface7; g_primarySurface = surface7;
g_origCaps = origCaps; g_origCaps = origCaps;
g_monitorRect = getDdMonitorRect(*CompatPtr<IDirectDraw7>::from(&dd));
ZeroMemory(&g_primarySurfaceDesc, sizeof(g_primarySurfaceDesc)); onRestore();
g_primarySurfaceDesc.dwSize = sizeof(g_primarySurfaceDesc);
CompatVtable<IDirectDrawSurface7Vtbl>::s_origVtable.GetSurfaceDesc(surface7, &g_primarySurfaceDesc);
if (g_primarySurfaceDesc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
{
resizeBuffers(*surface7);
}
g_gdiResourceHandle = getResourceHandle(*surface7);
D3dDdi::Device::setGdiResourceHandle(*reinterpret_cast<HANDLE*>(g_gdiResourceHandle));
return DD_OK; return DD_OK;
} }
@ -178,9 +146,29 @@ namespace DDraw
return nullptr; return nullptr;
} }
RECT PrimarySurface::getMonitorRect() CompatPtr<IDirectDrawSurface7> PrimarySurface::getBackBuffer()
{ {
return g_monitorRect; DDSCAPS2 caps = {};
caps.dwCaps = DDSCAPS_BACKBUFFER;
CompatPtr<IDirectDrawSurface7> backBuffer;
g_primarySurface->GetAttachedSurface(g_primarySurface, &caps, &backBuffer.getRef());
return backBuffer;
}
CompatPtr<IDirectDrawSurface7> PrimarySurface::getLastSurface()
{
DDSCAPS2 caps = {};
caps.dwCaps = DDSCAPS_FLIP;
auto surface(CompatPtr<IDirectDrawSurface7>::from(g_primarySurface.get()));
CompatPtr<IDirectDrawSurface7> nextSurface;
while (SUCCEEDED(surface->GetAttachedSurface(surface, &caps, &nextSurface.getRef())) &&
nextSurface != g_primarySurface)
{
surface = nextSurface;
}
return surface;
} }
CompatWeakPtr<IDirectDrawSurface7> PrimarySurface::getPrimary() CompatWeakPtr<IDirectDrawSurface7> PrimarySurface::getPrimary()
@ -207,14 +195,20 @@ namespace DDraw
void PrimarySurface::onRestore() void PrimarySurface::onRestore()
{ {
CompatPtr<IUnknown> ddUnk; g_primarySurfaceDesc = {};
g_primarySurface.get()->lpVtbl->GetDDInterface( g_primarySurfaceDesc.dwSize = sizeof(g_primarySurfaceDesc);
g_primarySurface, reinterpret_cast<void**>(&ddUnk.getRef())); g_primarySurface->GetSurfaceDesc(g_primarySurface, &g_primarySurfaceDesc);
CompatPtr<IDirectDraw7> dd7(ddUnk);
g_monitorRect = getDdMonitorRect(*dd7); if (g_primarySurfaceDesc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
{
resizeBuffers();
}
g_gdiResourceHandle = getResourceHandle(*g_primarySurface);
D3dDdi::Device::setGdiResourceHandle(*reinterpret_cast<HANDLE*>(g_gdiResourceHandle));
} }
void PrimarySurface::resizeBuffers(CompatRef<IDirectDrawSurface7> surface) void PrimarySurface::resizeBuffers()
{ {
DDSCAPS2 flipCaps = {}; DDSCAPS2 flipCaps = {};
flipCaps.dwCaps = DDSCAPS_FLIP; flipCaps.dwCaps = DDSCAPS_FLIP;
@ -226,7 +220,7 @@ namespace DDraw
const DWORD newBufferSize = g_primarySurfaceDesc.lPitch * const DWORD newBufferSize = g_primarySurfaceDesc.lPitch *
(g_primarySurfaceDesc.dwHeight + Config::primarySurfaceExtraRows); (g_primarySurfaceDesc.dwHeight + Config::primarySurfaceExtraRows);
auto surfacePtr(CompatPtr<IDirectDrawSurface7>::from(&surface)); auto surfacePtr(CompatPtr<IDirectDrawSurface7>::from(g_primarySurface.get()));
do do
{ {
s_surfaceBuffers.push_back(std::vector<unsigned char>(newBufferSize)); s_surfaceBuffers.push_back(std::vector<unsigned char>(newBufferSize));
@ -236,7 +230,7 @@ namespace DDraw
CompatPtr<IDirectDrawSurface7> nextSurface; CompatPtr<IDirectDrawSurface7> nextSurface;
surfacePtr->GetAttachedSurface(surfacePtr, &flipCaps, &nextSurface.getRef()); surfacePtr->GetAttachedSurface(surfacePtr, &flipCaps, &nextSurface.getRef());
surfacePtr.swap(nextSurface); surfacePtr.swap(nextSurface);
} while (surfacePtr && surfacePtr != &surface); } while (surfacePtr && surfacePtr != g_primarySurface.get());
} }
CompatWeakPtr<IDirectDrawPalette> PrimarySurface::s_palette; CompatWeakPtr<IDirectDrawPalette> PrimarySurface::s_palette;

View File

@ -2,6 +2,8 @@
#include <vector> #include <vector>
#include <ddraw.h>
#include "Common/CompatPtr.h" #include "Common/CompatPtr.h"
#include "Common/CompatRef.h" #include "Common/CompatRef.h"
#include "DDraw/Surfaces/Surface.h" #include "DDraw/Surfaces/Surface.h"
@ -19,7 +21,8 @@ namespace DDraw
static HRESULT flipToGdiSurface(); static HRESULT flipToGdiSurface();
static const DDSURFACEDESC2& getDesc(); static const DDSURFACEDESC2& getDesc();
static CompatPtr<IDirectDrawSurface7> getGdiSurface(); static CompatPtr<IDirectDrawSurface7> getGdiSurface();
static RECT getMonitorRect(); static CompatPtr<IDirectDrawSurface7> getBackBuffer();
static CompatPtr<IDirectDrawSurface7> getLastSurface();
static CompatWeakPtr<IDirectDrawSurface7> getPrimary(); static CompatWeakPtr<IDirectDrawSurface7> getPrimary();
static DWORD getOrigCaps(); static DWORD getOrigCaps();
static void onRestore(); static void onRestore();
@ -35,7 +38,7 @@ namespace DDraw
virtual void createImpl() override; virtual void createImpl() override;
static void resizeBuffers(CompatRef<IDirectDrawSurface7> surface); static void resizeBuffers();
std::unique_ptr<Surface> m_surface; std::unique_ptr<Surface> m_surface;

View File

@ -1,4 +1,5 @@
#include "DDraw/DirectDrawPalette.h" #include "DDraw/DirectDrawPalette.h"
#include "DDraw/DirectDrawSurface.h"
#include "DDraw/RealPrimarySurface.h" #include "DDraw/RealPrimarySurface.h"
#include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h"
#include "DDraw/Surfaces/PrimarySurfaceImpl.h" #include "DDraw/Surfaces/PrimarySurfaceImpl.h"
@ -65,44 +66,14 @@ namespace DDraw
template <typename TSurface> template <typename TSurface>
HRESULT PrimarySurfaceImpl<TSurface>::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags) HRESULT PrimarySurfaceImpl<TSurface>::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags)
{ {
if (RealPrimarySurface::isLost()) 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))
{ {
return DDERR_SURFACELOST; return DDERR_WASSTILLDRAWING;
} }
HRESULT result = m_impl.Flip(This, lpDDSurfaceTargetOverride, dwFlags); return RealPrimarySurface::flip(CompatPtr<IDirectDrawSurface7>::from(lpDDSurfaceTargetOverride), dwFlags);
if (FAILED(result))
{
return result;
}
const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY);
result = RealPrimarySurface::flip(dwFlags);
if (SUCCEEDED(result) && !isFlipEmulated)
{
return DD_OK;
}
undoFlip(This, lpDDSurfaceTargetOverride);
if (SUCCEEDED(result) && isFlipEmulated)
{
if (lpDDSurfaceTargetOverride)
{
s_origVtable.BltFast(This, 0, 0, lpDDSurfaceTargetOverride, nullptr, DDBLTFAST_WAIT);
}
else
{
TDdsCaps caps = {};
caps.dwCaps = DDSCAPS_BACKBUFFER;
CompatPtr<TSurface> backBuffer;
s_origVtable.GetAttachedSurface(This, &caps, &backBuffer.getRef());
s_origVtable.BltFast(This, 0, 0, backBuffer, nullptr, DDBLTFAST_WAIT);
}
}
return result;
} }
template <typename TSurface> template <typename TSurface>

View File

@ -1,6 +1,7 @@
#include <set> #include <set>
#include "Common/CompatRef.h" #include "Common/CompatRef.h"
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/Repository.h" #include "DDraw/Repository.h"
#include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h"
#include "DDraw/Surfaces/Surface.h" #include "DDraw/Surfaces/Surface.h"
@ -28,6 +29,20 @@ namespace
dst->SetColorKey(&dst, ckFlag, &ck); dst->SetColorKey(&dst, ckFlag, &ck);
} }
} }
template <typename TSurface>
bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag)
{
if (!This)
{
return true;
}
const bool wait = (flags & waitFlag) || !(flags & doNotWaitFlag) &&
CompatVtable<IDirectDrawSurface7Vtbl>::s_origVtablePtr == static_cast<void*>(This->lpVtbl);
return DDraw::RealPrimarySurface::waitForFlip(DDraw::Surface::getSurface(*This), wait);
}
} }
namespace DDraw namespace DDraw
@ -136,31 +151,16 @@ namespace DDraw
} }
} }
template <typename TSurface>
void SurfaceImpl<TSurface>::undoFlip(TSurface* This, TSurface* targetOverride)
{
if (targetOverride)
{
SurfaceImpl::Flip(This, targetOverride, DDFLIP_WAIT);
}
else
{
TSurfaceDesc desc = {};
desc.dwSize = sizeof(desc);
s_origVtable.GetSurfaceDesc(This, &desc);
for (DWORD i = 0; i < desc.dwBackBufferCount; ++i)
{
SurfaceImpl::Flip(This, nullptr, DDFLIP_WAIT);
}
}
}
template <typename TSurface> template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::Blt( HRESULT SurfaceImpl<TSurface>::Blt(
TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect,
DWORD dwFlags, LPDDBLTFX lpDDBltFx) DWORD dwFlags, LPDDBLTFX lpDDBltFx)
{ {
if (!waitForFlip(This, dwFlags, DDBLT_WAIT, DDBLT_DONOTWAIT))
{
return DDERR_WASSTILLDRAWING;
}
Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This)); Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This));
Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface)); Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface));
HRESULT result = s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); HRESULT result = s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx);
@ -182,6 +182,11 @@ namespace DDraw
HRESULT SurfaceImpl<TSurface>::BltFast( HRESULT SurfaceImpl<TSurface>::BltFast(
TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans)
{ {
if (!waitForFlip(This, dwTrans, DDBLTFAST_WAIT, DDBLTFAST_DONOTWAIT))
{
return DDERR_WASSTILLDRAWING;
}
Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This)); Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This));
Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface)); Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface));
HRESULT result = s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); HRESULT result = s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans);
@ -221,6 +226,21 @@ namespace DDraw
return s_origVtable.Flip(This, lpDDSurfaceTargetOverride, dwFlags); return s_origVtable.Flip(This, lpDDSurfaceTargetOverride, dwFlags);
} }
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetBltStatus(TSurface* This, DWORD dwFlags)
{
HRESULT result = s_origVtable.GetBltStatus(This, dwFlags);
if (SUCCEEDED(result) && (dwFlags & DDGBS_CANBLT))
{
const bool wait = false;
if (!RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait))
{
return DDERR_WASSTILLDRAWING;
}
}
return result;
}
template <typename TSurface> template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetCaps(TSurface* This, TDdsCaps* lpDDSCaps) HRESULT SurfaceImpl<TSurface>::GetCaps(TSurface* This, TDdsCaps* lpDDSCaps)
{ {
@ -230,8 +250,19 @@ namespace DDraw
template <typename TSurface> template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetDC(TSurface* This, HDC* lphDC) HRESULT SurfaceImpl<TSurface>::GetDC(TSurface* This, HDC* lphDC)
{ {
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_WRITE); HRESULT result = DD_OK;
return s_origVtable.GetDC(This, lphDC);
{
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_WRITE);
result = s_origVtable.GetDC(This, lphDC);
}
if (SUCCEEDED(result))
{
RealPrimarySurface::waitForFlip(Surface::getSurface(*This));
}
return result;
} }
template <typename TSurface> template <typename TSurface>
@ -246,6 +277,21 @@ namespace DDraw
reinterpret_cast<IDirectDraw*>(&dd), m_data->m_ddId, lplpDD); reinterpret_cast<IDirectDraw*>(&dd), m_data->m_ddId, lplpDD);
} }
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetFlipStatus(TSurface* This, DWORD dwFlags)
{
HRESULT result = s_origVtable.GetFlipStatus(This, dwFlags);
if (SUCCEEDED(result))
{
const bool wait = false;
if (!RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait))
{
return DDERR_WASSTILLDRAWING;
}
}
return result;
}
template <typename TSurface> template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc) HRESULT SurfaceImpl<TSurface>::GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc)
{ {
@ -263,6 +309,11 @@ namespace DDraw
TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc, TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc,
DWORD dwFlags, HANDLE hEvent) DWORD dwFlags, HANDLE hEvent)
{ {
if (!waitForFlip(This, dwFlags, DDLOCK_WAIT, DDLOCK_DONOTWAIT))
{
return DDERR_WASSTILLDRAWING;
}
Gdi::DDrawAccessGuard accessGuard((dwFlags & DDLOCK_READONLY) ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE, Gdi::DDrawAccessGuard accessGuard((dwFlags & DDLOCK_READONLY) ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE,
PrimarySurface::isGdiSurface(This)); PrimarySurface::isGdiSurface(This));
HRESULT result = s_origVtable.Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); HRESULT result = s_origVtable.Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent);

View File

@ -40,8 +40,10 @@ namespace DDraw
virtual HRESULT BltFast(TSurface* This, DWORD dwX, DWORD dwY, virtual HRESULT BltFast(TSurface* This, DWORD dwX, DWORD dwY,
TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans); TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans);
virtual HRESULT Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags); virtual HRESULT Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags);
virtual HRESULT GetBltStatus(TSurface* This, DWORD dwFlags);
virtual HRESULT GetCaps(TSurface* This, TDdsCaps* lpDDSCaps); virtual HRESULT GetCaps(TSurface* This, TDdsCaps* lpDDSCaps);
virtual HRESULT GetDC(TSurface* This, HDC* lphDC); virtual HRESULT GetDC(TSurface* This, HDC* lphDC);
virtual HRESULT GetFlipStatus(TSurface* This, DWORD dwFlags);
virtual HRESULT GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc); virtual HRESULT GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc);
virtual HRESULT IsLost(TSurface* This); virtual HRESULT IsLost(TSurface* This);
virtual HRESULT Lock(TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc, virtual HRESULT Lock(TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc,
@ -52,8 +54,6 @@ namespace DDraw
virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect); virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect);
protected: protected:
void undoFlip(TSurface* This, TSurface* targetOverride);
static const Vtable<TSurface>& s_origVtable; static const Vtable<TSurface>& s_origVtable;
private: private:

View File

@ -2,6 +2,7 @@
#include <Windows.h> #include <Windows.h>
#include "D3dDdi/KernelModeThunks.h"
#include "DDraw/RealPrimarySurface.h" #include "DDraw/RealPrimarySurface.h"
#include "DDraw/ScopedThreadLock.h" #include "DDraw/ScopedThreadLock.h"
#include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h"
@ -83,7 +84,7 @@ namespace
return false; return false;
} }
auto gdiSurface(Gdi::VirtualScreen::createSurface(DDraw::PrimarySurface::getMonitorRect())); auto gdiSurface(Gdi::VirtualScreen::createSurface(D3dDdi::KernelModeThunks::getMonitorRect()));
if (!gdiSurface) if (!gdiSurface)
{ {
return false; return false;
@ -96,8 +97,8 @@ namespace
CompatPtr<IDirectDrawClipper> clipper; CompatPtr<IDirectDrawClipper> clipper;
ddrawSurface->GetClipper(ddrawSurface, &clipper.getRef()); ddrawSurface->GetClipper(ddrawSurface, &clipper.getRef());
ddrawSurface->SetClipper(ddrawSurface, nullptr); ddrawSurface->SetClipper(ddrawSurface, nullptr);
result = SUCCEEDED(ddrawSurface->BltFast( result = SUCCEEDED(ddrawSurface->Blt(
ddrawSurface, 0, 0, gdiSurface, nullptr, DDBLTFAST_WAIT)); ddrawSurface, nullptr, gdiSurface, nullptr, DDBLT_WAIT, nullptr));
ddrawSurface->SetClipper(ddrawSurface, clipper); ddrawSurface->SetClipper(ddrawSurface, clipper);
} }
else else