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:
parent
e69b5e312c
commit
537ef9c595
@ -10,14 +10,14 @@ namespace Time
|
||||
|
||||
void init();
|
||||
|
||||
inline long long msToQpc(int ms)
|
||||
inline long long msToQpc(long long ms)
|
||||
{
|
||||
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()
|
||||
|
@ -4,6 +4,7 @@ typedef unsigned long DWORD;
|
||||
|
||||
namespace Config
|
||||
{
|
||||
const int delayedFlipModeTimeout = 200;
|
||||
const int maxPaletteUpdatesPerMs = 5;
|
||||
const int minExpectedFlipsPerSec = 5;
|
||||
const DWORD primarySurfaceExtraRows = 2;
|
||||
|
@ -1,3 +1,6 @@
|
||||
#include <d3d.h>
|
||||
#include <../km/d3dkmthk.h>
|
||||
|
||||
#include "D3dDdi/AdapterFuncs.h"
|
||||
#include "D3dDdi/Device.h"
|
||||
#include "D3dDdi/DeviceFuncs.h"
|
||||
@ -208,7 +211,7 @@ namespace D3dDdi
|
||||
{
|
||||
if (resource == m_sharedPrimary)
|
||||
{
|
||||
KernelModeThunks::releaseVidPnSources();
|
||||
D3DKMTReleaseProcessVidPnSourceOwners(GetCurrentProcess());
|
||||
}
|
||||
|
||||
HRESULT result = m_origVtable->pfnDestroyResource(m_device, resource);
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
|
||||
#include <d3d.h>
|
||||
@ -6,12 +7,23 @@
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Hook.h"
|
||||
#include "Common/ScopedCriticalSection.h"
|
||||
#include "Common/Time.h"
|
||||
#include "D3dDdi/Hooks.h"
|
||||
#include "D3dDdi/KernelModeThunks.h"
|
||||
#include "D3dDdi/Log/KernelModeThunksLog.h"
|
||||
#include "DDraw/Surfaces/PrimarySurface.h"
|
||||
#include "Win32/DisplayMode.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct AdapterInfo
|
||||
{
|
||||
UINT adapter;
|
||||
UINT vidPnSourceId;
|
||||
RECT monitorRect;
|
||||
};
|
||||
|
||||
struct ContextInfo
|
||||
{
|
||||
D3DKMT_HANDLE device;
|
||||
@ -19,40 +31,26 @@ namespace
|
||||
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, DeviceInfo> g_devices;
|
||||
HMONITOR g_lastOpenAdapterMonitor = nullptr;
|
||||
AdapterInfo g_gdiAdapterInfo = {};
|
||||
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(D3DKMTSetVidPnSourceOwner1)* g_origD3dKmtSetVidPnSourceOwner1 = nullptr;
|
||||
|
||||
D3DDDI_FLIPINTERVAL_TYPE g_overrideFlipInterval = D3DDDI_FLIPINTERVAL_NOOVERRIDE;
|
||||
UINT g_presentCount = 0;
|
||||
|
||||
NTSTATUS APIENTRY createDevice(D3DKMT_CREATEDEVICE* pData)
|
||||
NTSTATUS APIENTRY closeAdapter(const D3DKMT_CLOSEADAPTER* pData)
|
||||
{
|
||||
Compat::LogEnter("D3DKMTCreateDevice", pData);
|
||||
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateDevice)(pData);
|
||||
if (SUCCEEDED(result))
|
||||
Compat::ScopedCriticalSection lock(g_vblankCs);
|
||||
if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter)
|
||||
{
|
||||
g_devices[pData->hDevice].adapter = pData->hAdapter;
|
||||
|
||||
D3DKMT_SETQUEUEDLIMIT limit = {};
|
||||
limit.hDevice = pData->hDevice;
|
||||
limit.Type = D3DKMT_SET_QUEUEDLIMIT_PRESENT;
|
||||
limit.QueuedPresentLimit = 1;
|
||||
CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(&limit);
|
||||
g_lastOpenAdapterInfo = {};
|
||||
}
|
||||
Compat::LogLeave("D3DKMTCreateDevice", pData) << result;
|
||||
return result;
|
||||
return CALL_ORIG_FUNC(D3DKMTCloseAdapter)(pData);
|
||||
}
|
||||
|
||||
NTSTATUS APIENTRY createContext(D3DKMT_CREATECONTEXT* pData)
|
||||
@ -98,6 +96,22 @@ namespace
|
||||
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)
|
||||
{
|
||||
Compat::LogEnter("D3DKMTDestroyContext", pData);
|
||||
@ -105,89 +119,71 @@ namespace
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
g_contexts.erase(pData->hContext);
|
||||
if (g_lastPresentContext == pData->hContext)
|
||||
{
|
||||
g_lastPresentContext = 0;
|
||||
}
|
||||
}
|
||||
Compat::LogLeave("D3DKMTDestroyContext", pData) << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
NTSTATUS APIENTRY destroyDevice(const D3DKMT_DESTROYDEVICE* pData)
|
||||
AdapterInfo getAdapterInfo(const D3DKMT_OPENADAPTERFROMHDC& data)
|
||||
{
|
||||
Compat::LogEnter("D3DKMTDestroyDevice", pData);
|
||||
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTDestroyDevice)(pData);
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
g_devices.erase(pData->hDevice);
|
||||
}
|
||||
Compat::LogLeave("D3DKMTDestroyDevice", pData) << result;
|
||||
return result;
|
||||
AdapterInfo adapterInfo = {};
|
||||
adapterInfo.adapter = data.hAdapter;
|
||||
adapterInfo.vidPnSourceId = data.VidPnSourceId;
|
||||
|
||||
POINT p = {};
|
||||
GetDCOrgEx(data.hDc, &p);
|
||||
MONITORINFO mi = {};
|
||||
mi.cbSize = sizeof(mi);
|
||||
GetMonitorInfo(MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY), &mi);
|
||||
adapterInfo.monitorRect = mi.rcMonitor;
|
||||
|
||||
return adapterInfo;
|
||||
}
|
||||
|
||||
NTSTATUS APIENTRY openAdapterFromHdc(D3DKMT_OPENADAPTERFROMHDC* pData)
|
||||
{
|
||||
Compat::LogEnter("D3DKMTOpenAdapterFromHdc", pData);
|
||||
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(pData);
|
||||
if (pData)
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
POINT p = {};
|
||||
GetDCOrgEx(pData->hDc, &p);
|
||||
g_lastOpenAdapterMonitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY);
|
||||
Compat::ScopedCriticalSection lock(g_vblankCs);
|
||||
g_lastOpenAdapterInfo = getAdapterInfo(*pData);
|
||||
}
|
||||
Compat::LogLeave("D3DKMTOpenAdapterFromHdc", pData) << 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)
|
||||
{
|
||||
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;
|
||||
pData->Flags.PresentCountValid = 1;
|
||||
pData->PresentCount = g_presentCount;
|
||||
if (UINT_MAX == g_flipIntervalOverride)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
return result;
|
||||
@ -212,7 +208,7 @@ namespace
|
||||
if (D3DKMT_SET_QUEUEDLIMIT_PRESENT == pData->Type)
|
||||
{
|
||||
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);
|
||||
const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = origLimit;
|
||||
Compat::LogLeave("D3DKMTSetQueuedLimit", pData) << result;
|
||||
@ -223,42 +219,30 @@ namespace
|
||||
return result;
|
||||
}
|
||||
|
||||
void processSetVidPnSourceOwner(const D3DKMT_SETVIDPNSOURCEOWNER* pData)
|
||||
void updateGdiAdapterInfo()
|
||||
{
|
||||
auto& vidPnSourceId = g_devices[pData->hDevice].vidPnSourceId;
|
||||
for (UINT i = 0; i < pData->VidPnSourceCount; ++i)
|
||||
static auto lastDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
|
||||
const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
|
||||
if (currentDisplaySettingsUniqueness != lastDisplaySettingsUniqueness)
|
||||
{
|
||||
if (D3DKMT_VIDPNSOURCEOWNER_UNOWNED != pData->pType[i])
|
||||
if (g_gdiAdapterInfo.adapter)
|
||||
{
|
||||
vidPnSourceId = pData->pVidPnSourceId[i];
|
||||
return;
|
||||
D3DKMT_CLOSEADAPTER data = {};
|
||||
data.hAdapter = g_gdiAdapterInfo.adapter;
|
||||
CALL_ORIG_FUNC(D3DKMTCloseAdapter)(&data);
|
||||
g_gdiAdapterInfo = {};
|
||||
}
|
||||
}
|
||||
vidPnSourceId = D3DDDI_ID_UNINITIALIZED;
|
||||
}
|
||||
|
||||
NTSTATUS APIENTRY setVidPnSourceOwner(const D3DKMT_SETVIDPNSOURCEOWNER* pData)
|
||||
{
|
||||
Compat::LogEnter("D3DKMTSetVidPnSourceOwner", pData);
|
||||
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetVidPnSourceOwner)(pData);
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
processSetVidPnSourceOwner(pData);
|
||||
}
|
||||
Compat::LogLeave("D3DKMTSetVidPnSourceOwner", pData) << result;
|
||||
return result;
|
||||
}
|
||||
D3DKMT_OPENADAPTERFROMHDC data = {};
|
||||
data.hDc = GetDC(nullptr);
|
||||
if (SUCCEEDED(CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(&data)))
|
||||
{
|
||||
g_gdiAdapterInfo = getAdapterInfo(data);
|
||||
}
|
||||
ReleaseDC(nullptr, data.hDc);
|
||||
|
||||
NTSTATUS APIENTRY setVidPnSourceOwner1(const D3DKMT_SETVIDPNSOURCEOWNER1* pData)
|
||||
{
|
||||
Compat::LogEnter("D3DKMTSetVidPnSourceOwner1", pData);
|
||||
NTSTATUS result = g_origD3dKmtSetVidPnSourceOwner1(pData);
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
processSetVidPnSourceOwner(&pData->Version0);
|
||||
lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness;
|
||||
}
|
||||
Compat::LogLeave("D3DKMTSetVidPnSourceOwner1", pData) << result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,58 +250,113 @@ namespace D3dDdi
|
||||
{
|
||||
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 ::isPresentReady(it.first, it.second.vidPnSourceId);
|
||||
}
|
||||
return g_presentCount;
|
||||
}
|
||||
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()
|
||||
{
|
||||
InitializeCriticalSection(&g_vblankCs);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTCloseAdapter, closeAdapter);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTCreateContext, createContext);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTCreateDevice, createDevice);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTCreateDCFromMemory, createDcFromMemory);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTDestroyContext, destroyContext);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTDestroyDevice, destroyDevice);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTOpenAdapterFromHdc, openAdapterFromHdc);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, queryAdapterInfo);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTPresent, present);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTSetQueuedLimit, setQueuedLimit);
|
||||
HOOK_FUNCTION(gdi32, D3DKMTSetVidPnSourceOwner, setVidPnSourceOwner);
|
||||
|
||||
// Functions not available in Windows Vista
|
||||
Compat::hookFunction("gdi32", "D3DKMTCreateContextVirtual",
|
||||
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 = {};
|
||||
vidPnSourceOwner.hDevice = it.first;
|
||||
D3DKMTSetVidPnSourceOwner(&vidPnSourceOwner);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d.h>
|
||||
#include <d3dumddi.h>
|
||||
#include <../km/d3dkmthk.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "D3dDdi/Log/KernelModeThunksLog.h"
|
||||
|
||||
static const auto D3DDDI_FLIPINTERVAL_NOOVERRIDE = static_cast<D3DDDI_FLIPINTERVAL_TYPE>(5);
|
||||
|
||||
namespace D3dDdi
|
||||
{
|
||||
namespace KernelModeThunks
|
||||
{
|
||||
HMONITOR getLastOpenAdapterMonitor();
|
||||
UINT getLastFlipInterval();
|
||||
UINT getLastDisplayedFrameCount();
|
||||
UINT getLastSubmittedFrameCount();
|
||||
RECT getMonitorRect();
|
||||
long long getQpcLastVerticalBlank();
|
||||
void installHooks();
|
||||
bool isPresentReady();
|
||||
void overrideFlipInterval(D3DDDI_FLIPINTERVAL_TYPE flipInterval);
|
||||
void releaseVidPnSources();
|
||||
void setFlipIntervalOverride(UINT flipInterval);
|
||||
void waitForVerticalBlank();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "Common/CompatPtr.h"
|
||||
#include "D3dDdi/KernelModeThunks.h"
|
||||
#include "DDraw/ActivateAppHandler.h"
|
||||
#include "DDraw/DirectDraw.h"
|
||||
#include "DDraw/Repository.h"
|
||||
@ -117,6 +118,7 @@ namespace DDraw
|
||||
vtable.GetGDISurface = &GetGDISurface;
|
||||
vtable.SetCooperativeLevel = &SetCooperativeLevel;
|
||||
vtable.SetDisplayMode = &SetDisplayMode;
|
||||
vtable.WaitForVerticalBlank = &WaitForVerticalBlank;
|
||||
}
|
||||
|
||||
template <typename TDirectDraw>
|
||||
@ -206,6 +208,30 @@ namespace DDraw
|
||||
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<IDirectDraw2>;
|
||||
template DirectDraw<IDirectDraw4>;
|
||||
|
@ -46,6 +46,8 @@ namespace DDraw
|
||||
DWORD dwHeight,
|
||||
DWORD dwBPP,
|
||||
Params... params);
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE WaitForVerticalBlank(TDirectDraw* This, DWORD dwFlags, HANDLE hEvent);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,10 @@ namespace DDraw
|
||||
SET_COMPAT_METHOD(Blt);
|
||||
SET_COMPAT_METHOD(BltFast);
|
||||
SET_COMPAT_METHOD(Flip);
|
||||
SET_COMPAT_METHOD(GetBltStatus);
|
||||
SET_COMPAT_METHOD(GetCaps);
|
||||
SET_COMPAT_METHOD(GetDC);
|
||||
SET_COMPAT_METHOD(GetFlipStatus);
|
||||
SET_COMPAT_METHOD(GetSurfaceDesc);
|
||||
SET_COMPAT_METHOD(IsLost);
|
||||
SET_COMPAT_METHOD(Lock);
|
||||
|
@ -1,4 +1,3 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@ -39,7 +38,6 @@ namespace
|
||||
DWORD WINAPI updateThreadProc(LPVOID lpParameter);
|
||||
|
||||
CompatWeakPtr<IDirectDrawSurface7> g_frontBuffer;
|
||||
CompatWeakPtr<IDirectDrawSurface7> g_backBuffer;
|
||||
CompatWeakPtr<IDirectDrawSurface7> g_paletteConverter;
|
||||
CompatWeakPtr<IDirectDrawClipper> g_clipper;
|
||||
DDSURFACEDESC2 g_surfaceDesc = {};
|
||||
@ -47,15 +45,19 @@ namespace
|
||||
|
||||
bool g_stopUpdateThread = false;
|
||||
HANDLE g_updateThread = nullptr;
|
||||
HANDLE g_updateEvent = nullptr;
|
||||
std::atomic<int> g_disableUpdateCount = 0;
|
||||
bool g_isUpdateSuspended = false;
|
||||
long long g_qpcFlipModeTimeout = 0;
|
||||
long long g_qpcLastFlip = 0;
|
||||
long long g_qpcNextUpdate = 0;
|
||||
long long g_qpcUpdateInterval = 0;
|
||||
unsigned int g_disableUpdateCount = 0;
|
||||
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;
|
||||
|
||||
std::atomic<bool> g_isFullScreen(false);
|
||||
CompatPtr<IDirectDrawSurface7> getBackBuffer();
|
||||
CompatPtr<IDirectDrawSurface7> getLastSurface();
|
||||
|
||||
BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
@ -140,8 +142,14 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
auto backBuffer(getBackBuffer());
|
||||
if (!backBuffer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HDC backBufferDc = nullptr;
|
||||
g_backBuffer->GetDC(g_backBuffer, &backBufferDc);
|
||||
backBuffer->GetDC(backBuffer, &backBufferDc);
|
||||
|
||||
for (auto it = visibleLayeredWindows.rbegin(); it != visibleLayeredWindows.rend(); ++it)
|
||||
{
|
||||
@ -159,76 +167,22 @@ namespace
|
||||
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));
|
||||
return DD_OK;
|
||||
}
|
||||
|
||||
bool compatBlt()
|
||||
{
|
||||
Compat::LogEnter("RealPrimarySurface::compatBlt");
|
||||
|
||||
BltToWindowViaGdiArgs bltToWindowViaGdiArgs;
|
||||
if (!g_frontBuffer || (!g_isFullScreen && DDraw::RealPrimarySurface::isLost()))
|
||||
auto backBuffer(getBackBuffer());
|
||||
if (backBuffer)
|
||||
{
|
||||
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
|
||||
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
|
||||
Compat::LogLeave("RealPrimarySurface::compatBlt") << false;
|
||||
return false;
|
||||
backBuffer->Blt(backBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
|
||||
}
|
||||
|
||||
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>
|
||||
@ -263,124 +217,278 @@ namespace
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename DirectDraw>
|
||||
HRESULT init(CompatRef<DirectDraw> dd, CompatPtr<IDirectDrawSurface7> surface)
|
||||
CompatPtr<IDirectDrawSurface7> getBackBuffer()
|
||||
{
|
||||
DDSURFACEDESC2 desc = {};
|
||||
desc.dwSize = sizeof(desc);
|
||||
surface->GetSurfaceDesc(surface, &desc);
|
||||
|
||||
const bool isFlippable = 0 != (desc.ddsCaps.dwCaps & DDSCAPS_FLIP);
|
||||
DDSCAPS2 caps = {};
|
||||
caps.dwCaps = DDSCAPS_BACKBUFFER;
|
||||
CompatPtr<IDirectDrawSurface7> backBuffer;
|
||||
if (isFlippable)
|
||||
if (g_frontBuffer)
|
||||
{
|
||||
DDSCAPS2 backBufferCaps = {};
|
||||
backBufferCaps.dwCaps = DDSCAPS_BACKBUFFER;
|
||||
surface->GetAttachedSurface(surface, &backBufferCaps, &backBuffer.getRef());
|
||||
g_frontBuffer->GetAttachedSurface(g_frontBuffer, &caps, &backBuffer.getRef());
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
return backBuffer;
|
||||
}
|
||||
|
||||
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;
|
||||
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)
|
||||
if (flags & DDFLIP_NOVSYNC)
|
||||
{
|
||||
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()
|
||||
{
|
||||
Compat::LogEnter("RealPrimarySurface::onRelease");
|
||||
|
||||
ResetEvent(g_updateEvent);
|
||||
g_frontBuffer = nullptr;
|
||||
g_backBuffer = nullptr;
|
||||
g_clipper.release();
|
||||
g_isFullScreen = false;
|
||||
g_isFlipPending = false;
|
||||
g_isPresentPending = false;
|
||||
g_waitingForPrimaryUnlock = false;
|
||||
g_paletteConverter.release();
|
||||
g_qpcUpdateInterval = Time::g_qpcFrequency / 60;
|
||||
|
||||
ZeroMemory(&g_surfaceDesc, sizeof(g_surfaceDesc));
|
||||
g_surfaceDesc = {};
|
||||
|
||||
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();
|
||||
if (compatBlt() && g_isFullScreen)
|
||||
|
||||
BltToWindowViaGdiArgs bltToWindowViaGdiArgs;
|
||||
if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost())
|
||||
{
|
||||
D3dDdi::KernelModeThunks::overrideFlipInterval(
|
||||
Time::queryPerformanceCounter() - g_qpcLastFlip >= g_qpcFlipModeTimeout
|
||||
? D3DDDI_FLIPINTERVAL_ONE
|
||||
: D3DDDI_FLIPINTERVAL_IMMEDIATE);
|
||||
g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT);
|
||||
D3dDdi::KernelModeThunks::overrideFlipInterval(D3DDDI_FLIPINTERVAL_NOOVERRIDE);
|
||||
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
|
||||
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
|
||||
Compat::LogLeave("RealPrimarySurface::presentToPrimaryChain", src.get()) << false;
|
||||
return;
|
||||
}
|
||||
|
||||
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*/)
|
||||
{
|
||||
while (true)
|
||||
const int msPresentDelayAfterVBlank = 1;
|
||||
bool waitForVBlank = true;
|
||||
|
||||
while (!g_stopUpdateThread)
|
||||
{
|
||||
WaitForSingleObject(g_updateEvent, INFINITE);
|
||||
|
||||
if (g_stopUpdateThread)
|
||||
if (waitForVBlank)
|
||||
{
|
||||
return 0;
|
||||
D3dDdi::KernelModeThunks::waitForVerticalBlank();
|
||||
if (!g_isFullScreen)
|
||||
{
|
||||
g_isPresentPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
const int waitTime = msUntilNextUpdate();
|
||||
if (waitTime > 0)
|
||||
{
|
||||
Sleep(waitTime);
|
||||
continue;
|
||||
}
|
||||
Sleep(msPresentDelayAfterVBlank);
|
||||
|
||||
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>
|
||||
HRESULT RealPrimarySurface::create(CompatRef<DirectDraw> dd)
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
HRESULT result = createPaletteConverter(dd);
|
||||
if (FAILED(result))
|
||||
{
|
||||
@ -400,7 +509,7 @@ namespace DDraw
|
||||
desc.dwSize = sizeof(desc);
|
||||
desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
|
||||
desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
|
||||
desc.dwBackBufferCount = 1;
|
||||
desc.dwBackBufferCount = 2;
|
||||
|
||||
CompatPtr<typename Types<DirectDraw>::TCreatedSurface> surface;
|
||||
result = dd->CreateSurface(&dd, &desc, &surface.getRef(), nullptr);
|
||||
@ -420,7 +529,12 @@ namespace DDraw
|
||||
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>);
|
||||
@ -430,41 +544,73 @@ namespace DDraw
|
||||
|
||||
void RealPrimarySurface::disableUpdates()
|
||||
{
|
||||
if (0 == g_disableUpdateCount++ && isUpdateScheduled())
|
||||
{
|
||||
ResetEvent(g_updateEvent);
|
||||
g_isUpdateSuspended = true;
|
||||
}
|
||||
DDraw::ScopedThreadLock lock;
|
||||
--g_disableUpdateCount;
|
||||
}
|
||||
|
||||
void RealPrimarySurface::enableUpdates()
|
||||
{
|
||||
if (0 == --g_disableUpdateCount && g_isUpdateSuspended)
|
||||
{
|
||||
SetEvent(g_updateEvent);
|
||||
g_isUpdateSuspended = false;
|
||||
}
|
||||
DDraw::ScopedThreadLock lock;
|
||||
++g_disableUpdateCount;
|
||||
}
|
||||
|
||||
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);
|
||||
g_isUpdateSuspended = false;
|
||||
HRESULT result = primary->Flip(primary, surfaceTargetOverride, DDFLIP_WAIT);
|
||||
if (FAILED(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
g_qpcLastFlip = Time::queryPerformanceCounter();
|
||||
compatBlt();
|
||||
HRESULT result = g_frontBuffer->Flip(g_frontBuffer, nullptr, flags);
|
||||
g_qpcNextUpdate = Time::queryPerformanceCounter();
|
||||
return result;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
auto gammaControl(CompatPtr<IDirectDrawGammaControl>::from(g_frontBuffer.get()));
|
||||
if (!gammaControl)
|
||||
{
|
||||
@ -481,11 +627,6 @@ namespace DDraw
|
||||
|
||||
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);
|
||||
SetThreadPriority(g_updateThread, THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
@ -497,11 +638,13 @@ namespace DDraw
|
||||
|
||||
bool RealPrimarySurface::isLost()
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
return g_frontBuffer && DDERR_SURFACELOST == g_frontBuffer->IsLost(g_frontBuffer);
|
||||
}
|
||||
|
||||
void RealPrimarySurface::release()
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
g_frontBuffer.release();
|
||||
}
|
||||
|
||||
@ -513,24 +656,28 @@ namespace DDraw
|
||||
}
|
||||
|
||||
g_stopUpdateThread = true;
|
||||
SetEvent(g_updateEvent);
|
||||
if (WAIT_OBJECT_0 != WaitForSingleObject(g_updateThread, 1000))
|
||||
{
|
||||
TerminateThread(g_updateThread, 0);
|
||||
Compat::Log() << "The update thread was terminated forcefully";
|
||||
}
|
||||
ResetEvent(g_updateEvent);
|
||||
g_stopUpdateThread = false;
|
||||
g_updateThread = nullptr;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
auto gammaControl(CompatPtr<IDirectDrawGammaControl>::from(g_frontBuffer.get()));
|
||||
if (!gammaControl)
|
||||
{
|
||||
@ -542,6 +689,7 @@ namespace DDraw
|
||||
|
||||
void RealPrimarySurface::setPalette()
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
if (g_surfaceDesc.ddpfPixelFormat.dwRGBBitCount <= 8)
|
||||
{
|
||||
g_frontBuffer->SetPalette(g_frontBuffer, PrimarySurface::s_palette);
|
||||
@ -552,41 +700,44 @@ namespace DDraw
|
||||
|
||||
void RealPrimarySurface::update()
|
||||
{
|
||||
if (g_isUpdateSuspended)
|
||||
DDraw::ScopedThreadLock lock;
|
||||
g_qpcLastUpdate = Time::queryPerformanceCounter();
|
||||
g_isUpdatePending = true;
|
||||
if (g_waitingForPrimaryUnlock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
updateNowIfNotBusy();
|
||||
}
|
||||
}
|
||||
|
||||
void RealPrimarySurface::updatePalette()
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
if (PrimarySurface::s_palette)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
|
||||
#include <ddraw.h>
|
||||
|
||||
#include "Common/CompatWeakPtr.h"
|
||||
#include "Common/CompatPtr.h"
|
||||
#include "Common/CompatRef.h"
|
||||
|
||||
namespace DDraw
|
||||
{
|
||||
class Surface;
|
||||
|
||||
class RealPrimarySurface
|
||||
{
|
||||
public:
|
||||
@ -15,7 +17,7 @@ namespace DDraw
|
||||
|
||||
static void disableUpdates();
|
||||
static void enableUpdates();
|
||||
static HRESULT flip(DWORD flags);
|
||||
static HRESULT flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags);
|
||||
static HRESULT getGammaRamp(DDGAMMARAMP* rampData);
|
||||
static CompatWeakPtr<IDirectDrawSurface7> getSurface();
|
||||
static void init();
|
||||
@ -28,5 +30,6 @@ namespace DDraw
|
||||
static void setPalette();
|
||||
static void update();
|
||||
static void updatePalette();
|
||||
static bool waitForFlip(Surface* surface, bool wait = true);
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
#include <ddraw.h>
|
||||
|
||||
#include "Common/CompatPtr.h"
|
||||
#include "Common/CompatRef.h"
|
||||
#include "Config/Config.h"
|
||||
@ -16,24 +14,6 @@ namespace
|
||||
CompatWeakPtr<IDirectDrawSurface7> g_primarySurface;
|
||||
HANDLE g_gdiResourceHandle = nullptr;
|
||||
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>
|
||||
HANDLE getResourceHandle(TSurface& surface)
|
||||
@ -56,7 +36,6 @@ namespace DDraw
|
||||
g_gdiResourceHandle = nullptr;
|
||||
g_primarySurface = nullptr;
|
||||
g_origCaps = 0;
|
||||
g_monitorRect = {};
|
||||
s_palette = nullptr;
|
||||
s_surfaceBuffers.clear();
|
||||
ZeroMemory(&s_paletteEntries, sizeof(s_paletteEntries));
|
||||
@ -100,19 +79,8 @@ namespace DDraw
|
||||
|
||||
g_primarySurface = surface7;
|
||||
g_origCaps = origCaps;
|
||||
g_monitorRect = getDdMonitorRect(*CompatPtr<IDirectDraw7>::from(&dd));
|
||||
|
||||
ZeroMemory(&g_primarySurfaceDesc, sizeof(g_primarySurfaceDesc));
|
||||
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));
|
||||
onRestore();
|
||||
|
||||
return DD_OK;
|
||||
}
|
||||
@ -178,9 +146,29 @@ namespace DDraw
|
||||
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()
|
||||
@ -207,14 +195,20 @@ namespace DDraw
|
||||
|
||||
void PrimarySurface::onRestore()
|
||||
{
|
||||
CompatPtr<IUnknown> ddUnk;
|
||||
g_primarySurface.get()->lpVtbl->GetDDInterface(
|
||||
g_primarySurface, reinterpret_cast<void**>(&ddUnk.getRef()));
|
||||
CompatPtr<IDirectDraw7> dd7(ddUnk);
|
||||
g_monitorRect = getDdMonitorRect(*dd7);
|
||||
g_primarySurfaceDesc = {};
|
||||
g_primarySurfaceDesc.dwSize = sizeof(g_primarySurfaceDesc);
|
||||
g_primarySurface->GetSurfaceDesc(g_primarySurface, &g_primarySurfaceDesc);
|
||||
|
||||
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 = {};
|
||||
flipCaps.dwCaps = DDSCAPS_FLIP;
|
||||
@ -226,7 +220,7 @@ namespace DDraw
|
||||
const DWORD newBufferSize = g_primarySurfaceDesc.lPitch *
|
||||
(g_primarySurfaceDesc.dwHeight + Config::primarySurfaceExtraRows);
|
||||
|
||||
auto surfacePtr(CompatPtr<IDirectDrawSurface7>::from(&surface));
|
||||
auto surfacePtr(CompatPtr<IDirectDrawSurface7>::from(g_primarySurface.get()));
|
||||
do
|
||||
{
|
||||
s_surfaceBuffers.push_back(std::vector<unsigned char>(newBufferSize));
|
||||
@ -236,7 +230,7 @@ namespace DDraw
|
||||
CompatPtr<IDirectDrawSurface7> nextSurface;
|
||||
surfacePtr->GetAttachedSurface(surfacePtr, &flipCaps, &nextSurface.getRef());
|
||||
surfacePtr.swap(nextSurface);
|
||||
} while (surfacePtr && surfacePtr != &surface);
|
||||
} while (surfacePtr && surfacePtr != g_primarySurface.get());
|
||||
}
|
||||
|
||||
CompatWeakPtr<IDirectDrawPalette> PrimarySurface::s_palette;
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <ddraw.h>
|
||||
|
||||
#include "Common/CompatPtr.h"
|
||||
#include "Common/CompatRef.h"
|
||||
#include "DDraw/Surfaces/Surface.h"
|
||||
@ -19,7 +21,8 @@ namespace DDraw
|
||||
static HRESULT flipToGdiSurface();
|
||||
static const DDSURFACEDESC2& getDesc();
|
||||
static CompatPtr<IDirectDrawSurface7> getGdiSurface();
|
||||
static RECT getMonitorRect();
|
||||
static CompatPtr<IDirectDrawSurface7> getBackBuffer();
|
||||
static CompatPtr<IDirectDrawSurface7> getLastSurface();
|
||||
static CompatWeakPtr<IDirectDrawSurface7> getPrimary();
|
||||
static DWORD getOrigCaps();
|
||||
static void onRestore();
|
||||
@ -35,7 +38,7 @@ namespace DDraw
|
||||
|
||||
virtual void createImpl() override;
|
||||
|
||||
static void resizeBuffers(CompatRef<IDirectDrawSurface7> surface);
|
||||
static void resizeBuffers();
|
||||
|
||||
std::unique_ptr<Surface> m_surface;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "DDraw/DirectDrawPalette.h"
|
||||
#include "DDraw/DirectDrawSurface.h"
|
||||
#include "DDraw/RealPrimarySurface.h"
|
||||
#include "DDraw/Surfaces/PrimarySurface.h"
|
||||
#include "DDraw/Surfaces/PrimarySurfaceImpl.h"
|
||||
@ -65,44 +66,14 @@ namespace DDraw
|
||||
template <typename TSurface>
|
||||
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);
|
||||
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;
|
||||
return RealPrimarySurface::flip(CompatPtr<IDirectDrawSurface7>::from(lpDDSurfaceTargetOverride), dwFlags);
|
||||
}
|
||||
|
||||
template <typename TSurface>
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "Common/CompatRef.h"
|
||||
#include "DDraw/RealPrimarySurface.h"
|
||||
#include "DDraw/Repository.h"
|
||||
#include "DDraw/Surfaces/PrimarySurface.h"
|
||||
#include "DDraw/Surfaces/Surface.h"
|
||||
@ -28,6 +29,20 @@ namespace
|
||||
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
|
||||
@ -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>
|
||||
HRESULT SurfaceImpl<TSurface>::Blt(
|
||||
TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect,
|
||||
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 srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface));
|
||||
HRESULT result = s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx);
|
||||
@ -182,6 +182,11 @@ 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;
|
||||
}
|
||||
|
||||
Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This));
|
||||
Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface));
|
||||
HRESULT result = s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans);
|
||||
@ -221,6 +226,21 @@ namespace DDraw
|
||||
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>
|
||||
HRESULT SurfaceImpl<TSurface>::GetCaps(TSurface* This, TDdsCaps* lpDDSCaps)
|
||||
{
|
||||
@ -230,8 +250,19 @@ namespace DDraw
|
||||
template <typename TSurface>
|
||||
HRESULT SurfaceImpl<TSurface>::GetDC(TSurface* This, HDC* lphDC)
|
||||
{
|
||||
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_WRITE);
|
||||
return s_origVtable.GetDC(This, lphDC);
|
||||
HRESULT result = DD_OK;
|
||||
|
||||
{
|
||||
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>
|
||||
@ -246,6 +277,21 @@ namespace DDraw
|
||||
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>
|
||||
HRESULT SurfaceImpl<TSurface>::GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc)
|
||||
{
|
||||
@ -263,6 +309,11 @@ namespace DDraw
|
||||
TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc,
|
||||
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,
|
||||
PrimarySurface::isGdiSurface(This));
|
||||
HRESULT result = s_origVtable.Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent);
|
||||
|
@ -40,8 +40,10 @@ namespace DDraw
|
||||
virtual HRESULT BltFast(TSurface* This, DWORD dwX, DWORD dwY,
|
||||
TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans);
|
||||
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 GetDC(TSurface* This, HDC* lphDC);
|
||||
virtual HRESULT GetFlipStatus(TSurface* This, DWORD dwFlags);
|
||||
virtual HRESULT GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc);
|
||||
virtual HRESULT IsLost(TSurface* This);
|
||||
virtual HRESULT Lock(TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc,
|
||||
@ -52,8 +54,6 @@ namespace DDraw
|
||||
virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect);
|
||||
|
||||
protected:
|
||||
void undoFlip(TSurface* This, TSurface* targetOverride);
|
||||
|
||||
static const Vtable<TSurface>& s_origVtable;
|
||||
|
||||
private:
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "D3dDdi/KernelModeThunks.h"
|
||||
#include "DDraw/RealPrimarySurface.h"
|
||||
#include "DDraw/ScopedThreadLock.h"
|
||||
#include "DDraw/Surfaces/PrimarySurface.h"
|
||||
@ -83,7 +84,7 @@ namespace
|
||||
return false;
|
||||
}
|
||||
|
||||
auto gdiSurface(Gdi::VirtualScreen::createSurface(DDraw::PrimarySurface::getMonitorRect()));
|
||||
auto gdiSurface(Gdi::VirtualScreen::createSurface(D3dDdi::KernelModeThunks::getMonitorRect()));
|
||||
if (!gdiSurface)
|
||||
{
|
||||
return false;
|
||||
@ -96,8 +97,8 @@ namespace
|
||||
CompatPtr<IDirectDrawClipper> clipper;
|
||||
ddrawSurface->GetClipper(ddrawSurface, &clipper.getRef());
|
||||
ddrawSurface->SetClipper(ddrawSurface, nullptr);
|
||||
result = SUCCEEDED(ddrawSurface->BltFast(
|
||||
ddrawSurface, 0, 0, gdiSurface, nullptr, DDBLTFAST_WAIT));
|
||||
result = SUCCEEDED(ddrawSurface->Blt(
|
||||
ddrawSurface, nullptr, gdiSurface, nullptr, DDBLT_WAIT, nullptr));
|
||||
ddrawSurface->SetClipper(ddrawSurface, clipper);
|
||||
}
|
||||
else
|
||||
|
Loading…
x
Reference in New Issue
Block a user