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

380 lines
10 KiB
C++

#include <atomic>
#include <map>
#include <string>
#include <d3d.h>
#include <d3dumddi.h>
#include <../km/d3dkmthk.h>
#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;
ContextInfo() : device(0) {}
};
std::map<D3DKMT_HANDLE, ContextInfo> g_contexts;
AdapterInfo g_gdiAdapterInfo = {};
AdapterInfo g_lastOpenAdapterInfo = {};
std::string g_lastDDrawCreateDcDevice;
UINT g_lastFlipInterval = 0;
UINT g_flipIntervalOverride = 0;
D3DKMT_HANDLE g_lastPresentContext = 0;
UINT g_presentCount = 0;
std::atomic<long long> g_qpcLastVerticalBlank = 0;
Compat::CriticalSection g_vblankCs;
decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr;
NTSTATUS APIENTRY closeAdapter(const D3DKMT_CLOSEADAPTER* pData)
{
Compat::ScopedCriticalSection lock(g_vblankCs);
if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter)
{
g_lastOpenAdapterInfo = {};
}
return CALL_ORIG_FUNC(D3DKMTCloseAdapter)(pData);
}
NTSTATUS APIENTRY createContext(D3DKMT_CREATECONTEXT* pData)
{
LOG_FUNC("D3DKMTCreateContext", pData);
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateContext)(pData);
if (SUCCEEDED(result))
{
g_contexts[pData->hContext].device = pData->hDevice;
}
return LOG_RESULT(result);
}
NTSTATUS APIENTRY createContextVirtual(D3DKMT_CREATECONTEXTVIRTUAL* pData)
{
LOG_FUNC("D3DKMTCreateContextVirtual", pData);
NTSTATUS result = g_origD3dKmtCreateContextVirtual(pData);
if (SUCCEEDED(result))
{
g_contexts[pData->hContext].device = pData->hDevice;
}
return LOG_RESULT(result);
}
NTSTATUS APIENTRY createDcFromMemory(D3DKMT_CREATEDCFROMMEMORY* pData)
{
LOG_FUNC("D3DKMTCreateDCFromMemory", pData);
NTSTATUS result = 0;
if (pData && D3DDDIFMT_P8 == pData->Format && !pData->pColorTable &&
DDraw::PrimarySurface::s_palette)
{
pData->pColorTable = DDraw::PrimarySurface::s_paletteEntries;
result = CALL_ORIG_FUNC(D3DKMTCreateDCFromMemory)(pData);
pData->pColorTable = nullptr;
}
else
{
result = CALL_ORIG_FUNC(D3DKMTCreateDCFromMemory)(pData);
}
return LOG_RESULT(result);
}
NTSTATUS APIENTRY createDevice(D3DKMT_CREATEDEVICE* pData)
{
LOG_FUNC("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);
}
return LOG_RESULT(result);
}
HDC WINAPI ddrawCreateDcA(LPCSTR pwszDriver, LPCSTR pwszDevice, LPCSTR pszPort, const DEVMODEA* pdm)
{
LOG_FUNC("ddrawCreateDCA", pwszDriver, pwszDevice, pszPort, pdm);
g_lastDDrawCreateDcDevice = pwszDevice ? pwszDevice : std::string();
return LOG_RESULT(CALL_ORIG_FUNC(CreateDCA)(pwszDriver, pwszDevice, pszPort, pdm));
}
NTSTATUS APIENTRY destroyContext(const D3DKMT_DESTROYCONTEXT* pData)
{
LOG_FUNC("D3DKMTDestroyContext", pData);
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTDestroyContext)(pData);
if (SUCCEEDED(result))
{
g_contexts.erase(pData->hContext);
if (g_lastPresentContext == pData->hContext)
{
g_lastPresentContext = 0;
}
}
return LOG_RESULT(result);
}
BOOL CALLBACK findDDrawMonitorRect(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
{
MONITORINFOEX mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
if (g_lastDDrawCreateDcDevice == mi.szDevice)
{
*reinterpret_cast<RECT*>(dwData) = mi.rcMonitor;
return FALSE;
}
return TRUE;
}
AdapterInfo getAdapterInfo(const D3DKMT_OPENADAPTERFROMHDC& data)
{
AdapterInfo adapterInfo = {};
adapterInfo.adapter = data.hAdapter;
adapterInfo.vidPnSourceId = data.VidPnSourceId;
EnumDisplayMonitors(nullptr, nullptr, findDDrawMonitorRect,
reinterpret_cast<LPARAM>(&adapterInfo.monitorRect));
if (IsRectEmpty(&adapterInfo.monitorRect))
{
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi);
adapterInfo.monitorRect = mi.rcMonitor;
}
return adapterInfo;
}
NTSTATUS APIENTRY openAdapterFromHdc(D3DKMT_OPENADAPTERFROMHDC* pData)
{
LOG_FUNC("D3DKMTOpenAdapterFromHdc", pData);
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(pData);
if (SUCCEEDED(result))
{
Compat::ScopedCriticalSection lock(g_vblankCs);
g_lastOpenAdapterInfo = getAdapterInfo(*pData);
}
return LOG_RESULT(result);
}
NTSTATUS APIENTRY present(D3DKMT_PRESENT* pData)
{
LOG_FUNC("D3DKMTPresent", pData);
if (pData->Flags.Flip)
{
g_lastFlipInterval = pData->FlipInterval;
g_lastPresentContext = pData->hContext;
if (UINT_MAX == g_flipIntervalOverride)
{
return LOG_RESULT(S_OK);
}
++g_presentCount;
if (0 == g_presentCount)
{
g_presentCount = 1;
}
pData->PresentCount = g_presentCount;
pData->Flags.PresentCountValid = 1;
pData->FlipInterval = static_cast<D3DDDI_FLIPINTERVAL_TYPE>(g_flipIntervalOverride);
}
return LOG_RESULT(CALL_ORIG_FUNC(D3DKMTPresent)(pData));
}
NTSTATUS APIENTRY queryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* pData)
{
LOG_FUNC("D3DKMTQueryAdapterInfo", pData);
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTQueryAdapterInfo)(pData);
if (SUCCEEDED(result) && KMTQAITYPE_UMDRIVERNAME == pData->Type)
{
auto info = static_cast<D3DKMT_UMDFILENAMEINFO*>(pData->pPrivateDriverData);
D3dDdi::onUmdFileNameQueried(info->UmdFileName);
}
return LOG_RESULT(result);
}
NTSTATUS APIENTRY setQueuedLimit(const D3DKMT_SETQUEUEDLIMIT* pData)
{
LOG_FUNC("D3DKMTSetQueuedLimit", pData);
if (D3DKMT_SET_QUEUEDLIMIT_PRESENT == pData->Type)
{
const UINT origLimit = pData->QueuedPresentLimit;
const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = 2;
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(pData);
const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = origLimit;
return LOG_RESULT(result);
}
return LOG_RESULT(CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(pData));
}
void updateGdiAdapterInfo()
{
static auto lastDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
if (currentDisplaySettingsUniqueness != lastDisplaySettingsUniqueness)
{
if (g_gdiAdapterInfo.adapter)
{
D3DKMT_CLOSEADAPTER data = {};
data.hAdapter = g_gdiAdapterInfo.adapter;
CALL_ORIG_FUNC(D3DKMTCloseAdapter)(&data);
g_gdiAdapterInfo = {};
}
MONITORINFOEX mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi);
D3DKMT_OPENADAPTERFROMHDC data = {};
data.hDc = CreateDC(mi.szDevice, mi.szDevice, nullptr, nullptr);
if (SUCCEEDED(CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(&data)))
{
g_gdiAdapterInfo = getAdapterInfo(data);
}
DeleteDC(data.hDc);
lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness;
}
}
}
namespace D3dDdi
{
namespace KernelModeThunks
{
UINT getLastFlipInterval()
{
return g_lastFlipInterval;
}
UINT getLastDisplayedFrameCount()
{
auto contextIter = g_contexts.find(g_lastPresentContext);
if (contextIter == g_contexts.end())
{
return g_presentCount;
}
D3DKMT_GETDEVICESTATE data = {};
data.hDevice = contextIter->second.device;
data.StateType = D3DKMT_DEVICESTATE_PRESENT;
data.PresentState.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
D3DKMTGetDeviceState(&data);
if (0 == data.PresentState.PresentStats.PresentCount)
{
return g_presentCount;
}
return data.PresentState.PresentStats.PresentCount;
}
UINT getLastSubmittedFrameCount()
{
return g_presentCount;
}
RECT getMonitorRect()
{
auto primary(DDraw::PrimarySurface::getPrimary());
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.get()->lpVtbl->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(HMODULE origDDrawModule)
{
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, D3DKMTOpenAdapterFromHdc, openAdapterFromHdc);
HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, queryAdapterInfo);
HOOK_FUNCTION(gdi32, D3DKMTPresent, present);
HOOK_FUNCTION(gdi32, D3DKMTSetQueuedLimit, setQueuedLimit);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "CreateDCA", ddrawCreateDcA);
// Functions not available in Windows Vista
Compat::hookFunction("gdi32", "D3DKMTCreateContextVirtual",
reinterpret_cast<void*&>(g_origD3dKmtCreateContextVirtual), createContextVirtual);
}
void setFlipIntervalOverride(UINT flipInterval)
{
g_flipIntervalOverride = flipInterval;
}
void waitForVerticalBlank()
{
D3DKMT_WAITFORVERTICALBLANKEVENT data = {};
{
Compat::ScopedCriticalSection lock(g_vblankCs);
if (g_lastOpenAdapterInfo.adapter)
{
data.hAdapter = g_lastOpenAdapterInfo.adapter;
data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
}
else
{
updateGdiAdapterInfo();
data.hAdapter = g_gdiAdapterInfo.adapter;
data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId;
}
}
if (data.hAdapter)
{
D3DKMTWaitForVerticalBlankEvent(&data);
g_qpcLastVerticalBlank = Time::queryPerformanceCounter();
}
}
}
}