diff --git a/DDrawCompat/Common/Time.cpp b/DDrawCompat/Common/Time.cpp index 2609803..4c8f628 100644 --- a/DDrawCompat/Common/Time.cpp +++ b/DDrawCompat/Common/Time.cpp @@ -5,11 +5,26 @@ namespace Time { long long g_qpcFrequency = 0; + HANDLE g_waitableTimer = nullptr; void init() { LARGE_INTEGER qpc; QueryPerformanceFrequency(&qpc); g_qpcFrequency = qpc.QuadPart; + + g_waitableTimer = CreateWaitableTimer(nullptr, FALSE, nullptr); + } + + void waitForNextTick() + { + LARGE_INTEGER due = {}; + due.QuadPart = -1; + if (!g_waitableTimer || + !SetWaitableTimer(g_waitableTimer, &due, 0, nullptr, nullptr, FALSE) || + WAIT_OBJECT_0 != WaitForSingleObject(g_waitableTimer, INFINITE)) + { + Sleep(1); + } } } diff --git a/DDrawCompat/Common/Time.h b/DDrawCompat/Common/Time.h index 93e11c9..8bbae39 100644 --- a/DDrawCompat/Common/Time.h +++ b/DDrawCompat/Common/Time.h @@ -25,10 +25,5 @@ namespace Time return qpc.QuadPart; } - inline ULONG64 queryThreadCycleTime() - { - ULONG64 cycleTime = 0; - QueryThreadCycleTime(GetCurrentThread(), &cycleTime); - return cycleTime; - } + void waitForNextTick(); } diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index 920a9d8..6a5bdb8 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -21,9 +21,6 @@ namespace Config { - const unsigned delayedFlipModeTimeout = 200; - const unsigned maxPaletteUpdatesPerMs = 5; - extern Settings::AlternatePixelCenter alternatePixelCenter; extern Settings::Antialiasing antialiasing; extern Settings::ConfigHotKey configHotKey; diff --git a/DDrawCompat/D3dDdi/Device.cpp b/DDrawCompat/D3dDdi/Device.cpp index 57b2272..c974941 100644 --- a/DDrawCompat/D3dDdi/Device.cpp +++ b/DDrawCompat/D3dDdi/Device.cpp @@ -27,6 +27,7 @@ namespace D3dDdi : m_origVtable(CompatVtable::s_origVtable) , m_adapter(adapter) , m_device(device) + , m_eventQuery(nullptr) , m_renderTarget(nullptr) , m_renderTargetSubResourceIndex(0) , m_sharedPrimary(nullptr) @@ -34,6 +35,10 @@ namespace D3dDdi , m_state(*this) , m_shaderBlitter(*this) { + D3DDDIARG_CREATEQUERY createQuery = {}; + createQuery.QueryType = D3DDDIQUERYTYPE_EVENT; + m_origVtable.pfnCreateQuery(m_device, &createQuery); + m_eventQuery = createQuery.hQuery; } void Device::add(Adapter& adapter, HANDLE device) @@ -388,6 +393,33 @@ namespace D3dDdi m_state.updateConfig(); } + void Device::waitForIdle() + { + D3dDdi::ScopedCriticalSection lock; + flushPrimitives(); + D3DDDIARG_ISSUEQUERY issueQuery = {}; + issueQuery.hQuery = m_eventQuery; + issueQuery.Flags.End = 1; + m_origVtable.pfnIssueQuery(m_device, &issueQuery); + + if (m_origVtable.pfnFlush1) + { + m_origVtable.pfnFlush1(m_device, 0); + } + else + { + m_origVtable.pfnFlush(m_device); + } + + BOOL result = FALSE; + D3DDDIARG_GETQUERYDATA getQueryData = {}; + getQueryData.hQuery = m_eventQuery; + getQueryData.pData = &result; + while (S_FALSE == m_origVtable.pfnGetQueryData(m_device, &getQueryData)) + { + } + } + std::map Device::s_devices; bool Device::s_isFlushEnabled = true; } diff --git a/DDrawCompat/D3dDdi/Device.h b/DDrawCompat/D3dDdi/Device.h index f843f1f..5760e26 100644 --- a/DDrawCompat/D3dDdi/Device.h +++ b/DDrawCompat/D3dDdi/Device.h @@ -58,6 +58,7 @@ namespace D3dDdi void prepareForGpuWrite(); void setRenderTarget(const D3DDDIARG_SETRENDERTARGET& data); void updateConfig(); + void waitForIdle(); static void add(Adapter& adapter, HANDLE device); static Device& get(HANDLE device) { return s_devices.find(device)->second; } @@ -75,6 +76,7 @@ namespace D3dDdi D3DDDI_DEVICEFUNCS m_origVtable; Adapter& m_adapter; HANDLE m_device; + HANDLE m_eventQuery; std::map> m_resources; Resource* m_renderTarget; UINT m_renderTargetSubResourceIndex; diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.cpp b/DDrawCompat/D3dDdi/KernelModeThunks.cpp index 81846df..424d916 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.cpp +++ b/DDrawCompat/D3dDdi/KernelModeThunks.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -107,6 +106,21 @@ namespace return LOG_RESULT(result); } + NTSTATUS APIENTRY createDevice(D3DKMT_CREATEDEVICE* pData) + { + LOG_FUNC("D3DKMTCreateDevice", pData); + NTSTATUS result = D3DKMTCreateDevice(pData); + if (SUCCEEDED(result)) + { + D3DKMT_SETQUEUEDLIMIT limit = {}; + limit.hDevice = pData->hDevice; + limit.Type = D3DKMT_SET_QUEUEDLIMIT_PRESENT; + limit.QueuedPresentLimit = 1; + 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); @@ -152,7 +166,7 @@ namespace { D3DKMT_GETSCANLINE data = {}; getVidPnSource(data.hAdapter, data.VidPnSourceId); - if (!data.hAdapter || FAILED(D3DKMTGetScanLine(&data)) || data.InVerticalBlank) + if (!data.hAdapter || FAILED(D3DKMTGetScanLine(&data))) { return -1; } @@ -189,19 +203,6 @@ namespace return LOG_RESULT(result); } - void pollForVerticalBlank() - { - int scanLine = getScanLine(); - int prevScanLine = scanLine; - auto qpcStart = Time::queryPerformanceCounter(); - while (scanLine >= prevScanLine && Time::queryPerformanceCounter() - qpcStart < Time::g_qpcFrequency / 60) - { - Sleep(1); - prevScanLine = scanLine; - scanLine = getScanLine(); - } - } - NTSTATUS APIENTRY present(D3DKMT_PRESENT* pData) { LOG_FUNC("D3DKMTPresent", pData); @@ -252,6 +253,7 @@ namespace { LOG_FUNC("D3DKMTSetGammaRamp", pData); UINT vsyncCounter = D3dDdi::KernelModeThunks::getVsyncCounter(); + DDraw::RealPrimarySurface::setUpdateReady(); DDraw::RealPrimarySurface::flush(); HRESULT result = D3DKMTSetGammaRamp(pData); if (SUCCEEDED(result)) @@ -324,30 +326,24 @@ namespace void waitForVerticalBlank() { - if (IsWindows8OrGreater()) + auto qpcStart = Time::queryPerformanceCounter(); + int scanLine = getScanLine(); + int prevScanLine = 0; + while (scanLine >= prevScanLine) { - D3DKMT_WAITFORVERTICALBLANKEVENT data = {}; - - { - Compat::ScopedSrwLockShared lock(g_adapterInfoSrwLock); - data.hAdapter = g_lastOpenAdapterInfo.adapter; - data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId; - } - - if (!data.hAdapter) - { - updateGdiAdapterInfo(); - data.hAdapter = g_gdiAdapterInfo.adapter; - data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId; - } - - if (data.hAdapter && SUCCEEDED(D3DKMTWaitForVerticalBlankEvent(&data))) - { - return; - } + Time::waitForNextTick(); + prevScanLine = scanLine; + scanLine = getScanLine(); } - pollForVerticalBlank(); + if (scanLine < 0) + { + auto msElapsed = static_cast(Time::qpcToMs(Time::queryPerformanceCounter() - qpcStart)); + if (msElapsed < 16) + { + Sleep(16 - msElapsed); + } + } } } @@ -412,6 +408,7 @@ namespace D3dDdi Compat::hookIatFunction(Dll::g_origDDrawModule, "CreateDCA", ddrawCreateDcA); Compat::hookIatFunction(Dll::g_origDDrawModule, "D3DKMTCloseAdapter", closeAdapter); Compat::hookIatFunction(Dll::g_origDDrawModule, "D3DKMTCreateDCFromMemory", createDcFromMemory); + Compat::hookIatFunction(Dll::g_origDDrawModule, "D3DKMTCreateDevice", createDevice); Compat::hookIatFunction(Dll::g_origDDrawModule, "D3DKMTOpenAdapterFromHdc", openAdapterFromHdc); Compat::hookIatFunction(Dll::g_origDDrawModule, "D3DKMTPresent", present); Compat::hookIatFunction(Dll::g_origDDrawModule, "D3DKMTQueryAdapterInfo", queryAdapterInfo); diff --git a/DDrawCompat/D3dDdi/Resource.cpp b/DDrawCompat/D3dDdi/Resource.cpp index 902477a..850bae4 100644 --- a/DDrawCompat/D3dDdi/Resource.cpp +++ b/DDrawCompat/D3dDdi/Resource.cpp @@ -832,6 +832,16 @@ namespace D3dDdi auto rtIndex = rtSurface.resource ? 0 : data.DstSubResourceIndex; auto rtRect = rtSurface.resource ? data.SrcRect : data.DstRect; + if (D3DDDIPOOL_SYSTEMMEM == srcResource->m_fixedData.Pool) + { + srcResource = repo.getTempTexture(srcWidth, srcHeight, getPixelFormat(srcResource->m_fixedData.Format)).resource; + if (!srcResource) + { + return E_OUTOFMEMORY; + } + copySubResourceRegion(*srcResource, 0, data.SrcRect, data.hSrcResource, data.SrcSubResourceIndex, data.SrcRect); + } + if (D3DDDIFMT_P8 == srcResource->m_origData.Format) { auto entries(Gdi::Palette::getHardwarePalette()); diff --git a/DDrawCompat/D3dDdi/Resource.h b/DDrawCompat/D3dDdi/Resource.h index 2d59e1e..a12213a 100644 --- a/DDrawCompat/D3dDdi/Resource.h +++ b/DDrawCompat/D3dDdi/Resource.h @@ -27,6 +27,7 @@ namespace D3dDdi operator HANDLE() const { return m_handle; } const Resource* getCustomResource() { return m_msaaSurface.resource ? m_msaaSurface.resource : m_msaaResolvedSurface.resource; } + Device& getDevice() const { return m_device; } const D3DDDIARG_CREATERESOURCE2& getFixedDesc() const { return m_fixedData; } const D3DDDIARG_CREATERESOURCE2& getOrigDesc() const { return m_origData; } bool isClampable() const { return m_isClampable; } diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index 4f03783..1d44506 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -73,6 +73,7 @@ namespace template HRESULT STDMETHODCALLTYPE WaitForVerticalBlank(TDirectDraw* This, DWORD dwFlags, HANDLE hEvent) { + DDraw::RealPrimarySurface::setUpdateReady(); DDraw::RealPrimarySurface::flush(); return getOrigVtable(This).WaitForVerticalBlank(This, dwFlags, hEvent); } diff --git a/DDrawCompat/DDraw/DirectDrawPalette.cpp b/DDrawCompat/DDraw/DirectDrawPalette.cpp index c04109b..4bf69d7 100644 --- a/DDrawCompat/DDraw/DirectDrawPalette.cpp +++ b/DDrawCompat/DDraw/DirectDrawPalette.cpp @@ -49,7 +49,8 @@ namespace DDraw updatesInLastMs.pop_front(); } - if (updatesInLastMs.size() >= Config::maxPaletteUpdatesPerMs) + const unsigned maxPaletteUpdatesPerMs = 5; + if (updatesInLastMs.size() >= maxPaletteUpdatesPerMs) { Sleep(1); updatesInLastMs.clear(); diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index d5dbe76..046a7e1 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include +#include #include #include #include @@ -29,6 +29,8 @@ namespace { + const unsigned DELAYED_FLIP_MODE_TIMEOUT_MS = 200; + void onRelease(); CompatWeakPtr g_frontBuffer; @@ -40,11 +42,18 @@ namespace bool g_isFullscreen = false; DDraw::Surface* g_lastFlipSurface = nullptr; + Compat::CriticalSection g_presentCs; + bool g_isDelayedFlipPending = false; bool g_isUpdatePending = false; - bool g_waitingForPrimaryUnlock = false; - std::atomic g_qpcLastUpdate = 0; + bool g_isUpdateReady = false; + DWORD g_lastUpdateThreadId = 0; + long long g_qpcLastUpdate = 0; + long long g_qpcUpdateStart = 0; + + long long g_qpcDelayedFlipEnd = 0; UINT g_flipEndVsyncCount = 0; UINT g_presentEndVsyncCount = 0; + HWND g_devicePresentationWindow = nullptr; HWND g_deviceWindow = nullptr; HWND* g_deviceWindowPtr = nullptr; @@ -112,24 +121,14 @@ namespace return 1; } - bool isFlipPending() - { - return static_cast(D3dDdi::KernelModeThunks::getVsyncCounter() - g_flipEndVsyncCount) < 0; - } - - bool isPresentPending() - { - return static_cast(D3dDdi::KernelModeThunks::getVsyncCounter() - g_presentEndVsyncCount) < 0; - } - void onRelease() { LOG_FUNC("RealPrimarySurface::onRelease"); g_frontBuffer = nullptr; + g_lastFlipSurface = nullptr; g_clipper.release(); g_isFullscreen = false; - g_waitingForPrimaryUnlock = false; g_surfaceDesc = {}; DDraw::RealPrimarySurface::updateDevicePresentationWindowPos(); @@ -152,16 +151,20 @@ namespace const bool isFlippable = 0 != (desc.ddsCaps.dwCaps & DDSCAPS_FLIP); g_surfaceDesc = desc; g_isFullscreen = isFlippable; - g_isUpdatePending = true; - g_qpcLastUpdate = Time::queryPerformanceCounter() - Time::msToQpc(Config::delayedFlipModeTimeout); if (isFlippable) { g_frontBuffer->Flip(g_frontBuffer, getLastSurface(), DDFLIP_WAIT); - g_flipEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + 1; - g_presentEndVsyncCount = g_flipEndVsyncCount; - D3dDdi::KernelModeThunks::waitForVsyncCounter(g_flipEndVsyncCount); + D3dDdi::KernelModeThunks::waitForVsyncCounter(D3dDdi::KernelModeThunks::getVsyncCounter() + 1); } + + Compat::ScopedCriticalSection lock(g_presentCs); + g_isUpdatePending = false; + g_isUpdateReady = false; + g_qpcLastUpdate = Time::queryPerformanceCounter() - Time::msToQpc(DELAYED_FLIP_MODE_TIMEOUT_MS); + g_qpcUpdateStart = g_qpcLastUpdate; + g_presentEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter(); + g_flipEndVsyncCount = g_presentEndVsyncCount; } void presentToPrimaryChain(CompatWeakPtr src) @@ -202,11 +205,15 @@ namespace D3dDdi::KernelModeThunks::setDcPaletteOverride(nullptr); } - void updateNow(CompatWeakPtr src, UINT flipInterval) + void updateNow(CompatWeakPtr src) { + { + Compat::ScopedCriticalSection lock(g_presentCs); + g_isUpdatePending = false; + g_isUpdateReady = false; + } + presentToPrimaryChain(src); - g_isUpdatePending = false; - g_waitingForPrimaryUnlock = false; if (g_isFullscreen && g_devicePresentationWindow) { @@ -214,60 +221,26 @@ namespace g_frontBuffer->Flip(g_frontBuffer, getBackBuffer(), DDFLIP_WAIT); *g_deviceWindowPtr = g_deviceWindow; } - g_presentEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + max(flipInterval, 1); - } - - 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) - { - const auto msSinceLastUpdate = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate); - updateNow(primary, msSinceLastUpdate > Config::delayedFlipModeTimeout ? 0 : 1); - } + g_presentEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + 1; } unsigned WINAPI updateThreadProc(LPVOID /*lpParameter*/) { - bool skipWaitForVsync = false; - + int msUntilUpdateReady = 0; while (true) { - if (!skipWaitForVsync) + if (msUntilUpdateReady > 0) + { + Sleep(1); + } + else { D3dDdi::KernelModeThunks::waitForVsyncCounter(D3dDdi::KernelModeThunks::getVsyncCounter() + 1); } - skipWaitForVsync = false; - Sleep(1); DDraw::ScopedThreadLock lock; - Gdi::Caret::blink(); - if (Gdi::Cursor::update()) - { - g_isUpdatePending = true; - } - - if (g_isUpdatePending && !isPresentPending()) - { - auto qpcNow = Time::queryPerformanceCounter(); - auto qpcLastVsync = D3dDdi::KernelModeThunks::getQpcLastVsync(); - if (Time::qpcToMs(qpcNow - qpcLastVsync) < 1 || - Time::qpcToMs(qpcNow - g_qpcLastUpdate) < 1 && Time::qpcToMs(qpcNow - qpcLastVsync) <= 3) - { - skipWaitForVsync = true; - } - else - { - updateNowIfNotBusy(); - } - } + msUntilUpdateReady = DDraw::RealPrimarySurface::flush(); } - - return 0; } } @@ -325,27 +298,19 @@ namespace DDraw HRESULT RealPrimarySurface::flip(CompatPtr surfaceTargetOverride, DWORD flags) { const DWORD flipInterval = getFlipInterval(flags); - if (0 == flipInterval) + if (0 == flipInterval || + Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate) < DELAYED_FLIP_MODE_TIMEOUT_MS) { - g_isUpdatePending = true; - return DD_OK; - } - - const auto msSinceLastUpdate = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate); - const bool isFlipDelayed = msSinceLastUpdate >= 0 && msSinceLastUpdate <= Config::delayedFlipModeTimeout; - if (isFlipDelayed) - { - if (!isPresentPending()) - { - CompatPtr prevPrimarySurface( - surfaceTargetOverride ? surfaceTargetOverride : PrimarySurface::getLastSurface()); - updateNow(prevPrimarySurface, 0); - } - g_isUpdatePending = true; + PrimarySurface::waitForIdle(); + Compat::ScopedCriticalSection lock(g_presentCs); + g_isDelayedFlipPending = true; + g_isUpdatePending = false; + g_isUpdateReady = false; + g_lastUpdateThreadId = GetCurrentThreadId(); } else { - updateNow(PrimarySurface::getPrimary(), flipInterval); + updateNow(PrimarySurface::getPrimary()); } g_flipEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + flipInterval; @@ -358,16 +323,60 @@ namespace DDraw { g_lastFlipSurface = nullptr; } + + g_qpcDelayedFlipEnd = Time::queryPerformanceCounter(); return DD_OK; } - void RealPrimarySurface::flush() + int RealPrimarySurface::flush() { - DDraw::ScopedThreadLock lock; - if (g_isUpdatePending && !isPresentPending()) + auto vsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter(); + if (static_cast(vsyncCount - g_presentEndVsyncCount) < 0) { - updateNowIfNotBusy(); + return -1; } + + { + Compat::ScopedCriticalSection lock(g_presentCs); + if (!g_isUpdateReady) + { + if (g_isUpdatePending) + { + auto msSinceUpdateStart = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcUpdateStart); + if (msSinceUpdateStart < 10) + { + return 10 - static_cast(msSinceUpdateStart); + } + g_isUpdateReady = true; + } + else if (g_isDelayedFlipPending) + { + auto msSinceDelayedFlipEnd = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcDelayedFlipEnd); + if (msSinceDelayedFlipEnd < 3) + { + return 3 - static_cast(msSinceDelayedFlipEnd); + } + g_isDelayedFlipPending = false; + g_isUpdateReady = true; + } + } + + if (!g_isUpdateReady) + { + return -1; + } + } + + auto src(g_isDelayedFlipPending ? g_lastFlipSurface->getDDS() : DDraw::PrimarySurface::getPrimary()); + RECT emptyRect = {}; + HRESULT result = src ? src->BltFast(src, 0, 0, src, &emptyRect, DDBLTFAST_WAIT) : DD_OK; + if (DDERR_SURFACEBUSY == result || DDERR_LOCKEDSURFACES == result) + { + return 1; + } + + updateNow(src); + return 0; } HWND RealPrimarySurface::getDevicePresentationWindow() @@ -441,8 +450,16 @@ namespace DDraw void RealPrimarySurface::scheduleUpdate() { + Compat::ScopedCriticalSection lock(g_presentCs); g_qpcLastUpdate = Time::queryPerformanceCounter(); - g_isUpdatePending = true; + if (!g_isUpdatePending) + { + g_qpcUpdateStart = g_qpcLastUpdate; + g_isUpdatePending = true; + g_isDelayedFlipPending = false; + g_lastUpdateThreadId = GetCurrentThreadId(); + } + g_isUpdateReady = false; } HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData) @@ -457,14 +474,13 @@ namespace DDraw return gammaControl->SetGammaRamp(gammaControl, 0, rampData); } - void RealPrimarySurface::update() + void RealPrimarySurface::setUpdateReady() { - DDraw::ScopedThreadLock lock; - g_qpcLastUpdate = Time::queryPerformanceCounter(); - g_isUpdatePending = true; - if (g_waitingForPrimaryUnlock) + Compat::ScopedCriticalSection lock(g_presentCs); + if ((g_isUpdatePending || g_isDelayedFlipPending) && GetCurrentThreadId() == g_lastUpdateThreadId) { - updateNowIfNotBusy(); + g_isUpdateReady = true; + g_isDelayedFlipPending = false; } } @@ -490,17 +506,23 @@ namespace DDraw }); } - bool RealPrimarySurface::waitForFlip(Surface* surface) + bool RealPrimarySurface::waitForFlip(CompatWeakPtr surface) { auto primary(DDraw::PrimarySurface::getPrimary()); - if (!surface || !primary || - surface != g_lastFlipSurface && - surface != Surface::getSurface(*DDraw::PrimarySurface::getPrimary())) + if (!surface || !primary || !g_lastFlipSurface || + surface != primary && surface != g_lastFlipSurface->getDDS()) { return true; } - D3dDdi::KernelModeThunks::waitForVsyncCounter(g_flipEndVsyncCount); + auto vsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter(); + while (static_cast(vsyncCount - g_flipEndVsyncCount) < 0) + { + flush(); + ++vsyncCount; + D3dDdi::KernelModeThunks::waitForVsyncCounter(vsyncCount); + g_qpcDelayedFlipEnd = Time::queryPerformanceCounter(); + } return true; } } diff --git a/DDrawCompat/DDraw/RealPrimarySurface.h b/DDrawCompat/DDraw/RealPrimarySurface.h index 09174a5..9ddd7a2 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.h +++ b/DDrawCompat/DDraw/RealPrimarySurface.h @@ -16,7 +16,7 @@ namespace DDraw static HRESULT create(CompatRef dd); static HRESULT flip(CompatPtr surfaceTargetOverride, DWORD flags); - static void flush(); + static int flush(); static HWND getDevicePresentationWindow(); static HRESULT getGammaRamp(DDGAMMARAMP* rampData); static RECT getMonitorRect(); @@ -29,8 +29,8 @@ namespace DDraw static HRESULT restore(); static void scheduleUpdate(); static HRESULT setGammaRamp(DDGAMMARAMP* rampData); - static void update(); + static void setUpdateReady(); static void updateDevicePresentationWindowPos(); - static bool waitForFlip(Surface* surface); + static bool waitForFlip(CompatWeakPtr surface); }; } diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp index 02f37bb..7a5f49f 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp @@ -14,6 +14,7 @@ namespace { CompatWeakPtr g_primarySurface; + D3dDdi::Device* g_device = nullptr; HANDLE g_gdiResourceHandle = nullptr; HANDLE g_frontResource = nullptr; DWORD g_origCaps = 0; @@ -28,6 +29,7 @@ namespace DDraw { LOG_FUNC("PrimarySurface::~PrimarySurface"); + g_device = nullptr; g_gdiResourceHandle = nullptr; g_frontResource = nullptr; g_primarySurface = nullptr; @@ -91,6 +93,7 @@ namespace DDraw ResizePalette(g_palette, 256); } + g_device = D3dDdi::Device::findDeviceByResource(DirectDrawSurface::getDriverResourceHandle(*surface)); data->restore(); D3dDdi::Device::updateAllConfig(); return DD_OK; @@ -265,7 +268,15 @@ namespace DDraw ReleaseDC(g_deviceWindow, dc); } - RealPrimarySurface::update(); + RealPrimarySurface::scheduleUpdate(); + } + + void PrimarySurface::waitForIdle() + { + if (g_device) + { + g_device->waitForIdle(); + } } CompatWeakPtr PrimarySurface::s_palette; diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.h b/DDrawCompat/DDraw/Surfaces/PrimarySurface.h index 1f77a09..a2d8aa0 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.h +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.h @@ -31,6 +31,7 @@ namespace DDraw static bool isGdiSurface(TSurface* surface); static void updateFrontResource(); + static void waitForIdle(); virtual void restore(); diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp index 210da24..e8d6f57 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp @@ -91,11 +91,13 @@ namespace DDraw return DDERR_SURFACELOST; } + RealPrimarySurface::flush(); HRESULT result = SurfaceImpl::Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); if (SUCCEEDED(result)) { bltToGdi(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); - RealPrimarySurface::update(); + RealPrimarySurface::scheduleUpdate(); + PrimarySurface::waitForIdle(); } return result; } @@ -109,10 +111,12 @@ namespace DDraw return DDERR_SURFACELOST; } + RealPrimarySurface::flush(); HRESULT result = SurfaceImpl::BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); if (SUCCEEDED(result)) { - RealPrimarySurface::update(); + RealPrimarySurface::scheduleUpdate(); + PrimarySurface::waitForIdle(); } return result; } @@ -120,7 +124,9 @@ namespace DDraw template HRESULT PrimarySurfaceImpl::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags) { - DDraw::RealPrimarySurface::waitForFlip(m_data); + RealPrimarySurface::setUpdateReady(); + RealPrimarySurface::flush(); + RealPrimarySurface::waitForFlip(m_data->getDDS()); auto surfaceTargetOverride(CompatPtr::from(lpDDSurfaceTargetOverride)); const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY); if (isFlipEmulated) @@ -155,6 +161,13 @@ namespace DDraw return result; } + template + HRESULT PrimarySurfaceImpl::GetDC(TSurface* This, HDC* lphDC) + { + RealPrimarySurface::flush(); + return SurfaceImpl::GetDC(This, lphDC); + } + template HRESULT PrimarySurfaceImpl::GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc) { @@ -187,6 +200,7 @@ namespace DDraw return DDERR_SURFACELOST; } + RealPrimarySurface::flush(); HRESULT result = SurfaceImpl::Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); if (SUCCEEDED(result)) { @@ -201,7 +215,7 @@ namespace DDraw HRESULT result = SurfaceImpl::ReleaseDC(This, hDC); if (SUCCEEDED(result)) { - RealPrimarySurface::update(); + RealPrimarySurface::scheduleUpdate(); } return result; } @@ -244,7 +258,7 @@ namespace DDraw HRESULT result = SurfaceImpl::Unlock(This, lpRect); if (SUCCEEDED(result)) { - RealPrimarySurface::update(); + RealPrimarySurface::scheduleUpdate(); } return result; } diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.h b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.h index 5edccce..9ea2b86 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.h +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.h @@ -21,6 +21,7 @@ namespace DDraw TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) override; virtual HRESULT Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags) override; virtual HRESULT GetCaps(TSurface* This, TDdsCaps* lpDDSCaps) override; + virtual HRESULT GetDC(TSurface* This, HDC* lphDC); virtual HRESULT GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc) override; virtual HRESULT IsLost(TSurface* This) override; virtual HRESULT Lock(TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc, diff --git a/DDrawCompat/DDraw/Surfaces/Surface.h b/DDrawCompat/DDraw/Surfaces/Surface.h index d27985c..34f57ae 100644 --- a/DDrawCompat/DDraw/Surfaces/Surface.h +++ b/DDrawCompat/DDraw/Surfaces/Surface.h @@ -30,6 +30,8 @@ namespace DDraw template static Surface* getSurface(TSurface& dds); + CompatWeakPtr getDDS() const { return m_surface; }; + template SurfaceImpl* getImpl() const; diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp index 07ea92e..b9b3280 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp @@ -109,7 +109,7 @@ namespace DDraw TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { - RealPrimarySurface::waitForFlip(m_data); + RealPrimarySurface::waitForFlip(m_data->getDDS()); DirectDrawClipper::update(); return blt(This, lpDDSrcSurface, lpSrcRect, [=](TSurface* This, TSurface* lpDDSrcSurface, LPRECT lpSrcRect) { return getOrigVtable(This).Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); }); @@ -119,7 +119,7 @@ namespace DDraw HRESULT SurfaceImpl::BltFast( TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { - RealPrimarySurface::waitForFlip(m_data); + RealPrimarySurface::waitForFlip(m_data->getDDS()); return blt(This, lpDDSrcSurface, lpSrcRect, [=](TSurface* This, TSurface* lpDDSrcSurface, LPRECT lpSrcRect) { return getOrigVtable(This).BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); }); } @@ -144,7 +144,7 @@ namespace DDraw template HRESULT SurfaceImpl::GetDC(TSurface* This, HDC* lphDC) { - RealPrimarySurface::waitForFlip(m_data); + RealPrimarySurface::waitForFlip(m_data->getDDS()); HRESULT result = getOrigVtable(This).GetDC(This, lphDC); if (SUCCEEDED(result)) { @@ -181,7 +181,7 @@ namespace DDraw TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { - RealPrimarySurface::waitForFlip(m_data); + RealPrimarySurface::waitForFlip(m_data->getDDS()); HRESULT result = getOrigVtable(This).Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); if (SUCCEEDED(result)) { diff --git a/DDrawCompat/Gdi/Cursor.cpp b/DDrawCompat/Gdi/Cursor.cpp index f914677..10f8712 100644 --- a/DDrawCompat/Gdi/Cursor.cpp +++ b/DDrawCompat/Gdi/Cursor.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -222,7 +223,7 @@ namespace Gdi } } - bool update() + void update() { Compat::ScopedCriticalSection lock(g_cs); if (!IsRectEmpty(&g_monitorClipRect)) @@ -238,11 +239,9 @@ namespace Gdi (cursorInfo.hCursor != g_prevCursorInfo.hCursor || cursorInfo.ptScreenPos != g_prevCursorInfo.ptScreenPos)) { g_prevCursorInfo = cursorInfo; - return true; + DDraw::RealPrimarySurface::scheduleUpdate(); } } - - return false; } } } diff --git a/DDrawCompat/Gdi/Cursor.h b/DDrawCompat/Gdi/Cursor.h index d26987a..620bf0a 100644 --- a/DDrawCompat/Gdi/Cursor.h +++ b/DDrawCompat/Gdi/Cursor.h @@ -12,6 +12,6 @@ namespace Gdi HCURSOR setCursor(HCURSOR cursor); void setMonitorClipRect(const RECT& rect); void setEmulated(bool isEmulated); - bool update(); + void update(); } } diff --git a/DDrawCompat/Gdi/GuiThread.cpp b/DDrawCompat/Gdi/GuiThread.cpp index 4f8631b..a024389 100644 --- a/DDrawCompat/Gdi/GuiThread.cpp +++ b/DDrawCompat/Gdi/GuiThread.cpp @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -83,7 +86,7 @@ namespace } MSG msg = {}; - while (GetMessage(&msg, nullptr, 0, 0)) + while (CALL_ORIG_FUNC(GetMessageA)(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); @@ -91,6 +94,16 @@ namespace return 0; } + + unsigned WINAPI updateThreadProc(LPVOID /*lpParameter*/) + { + while (true) + { + Sleep(5); + Gdi::Caret::blink(); + Gdi::Cursor::update(); + } + } } namespace Gdi @@ -154,6 +167,7 @@ namespace Gdi void start() { Dll::createThread(messageWindowThreadProc, &g_threadId, THREAD_PRIORITY_TIME_CRITICAL, 0); + Dll::createThread(updateThreadProc, nullptr, THREAD_PRIORITY_TIME_CRITICAL, 0); } } } diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index 4560f59..40f6a12 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -147,6 +147,23 @@ namespace return ddcWindowProc(hwnd, uMsg, wParam, lParam, CallWindowProcW, getWindowProc(hwnd).wndProcW); } + BOOL WINAPI getMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, + decltype(&GetMessageA) origGetMessage) + { + DDraw::RealPrimarySurface::setUpdateReady(); + return origGetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); + } + + BOOL WINAPI getMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax) + { + return getMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, CALL_ORIG_FUNC(GetMessageA)); + } + + BOOL WINAPI getMessageW(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax) + { + return getMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, CALL_ORIG_FUNC(GetMessageW)); + } + LONG getWindowLong(HWND hWnd, int nIndex, decltype(&GetWindowLongA) origGetWindowLong, WNDPROC(WindowProc::* wndProc)) { @@ -290,6 +307,23 @@ namespace } } + BOOL peekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg, + decltype(&PeekMessageA) origPeekMessage) + { + DDraw::RealPrimarySurface::setUpdateReady(); + return origPeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); + } + + BOOL WINAPI peekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg) + { + return peekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg, CALL_ORIG_FUNC(PeekMessageA)); + } + + BOOL WINAPI peekMessageW(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg) + { + return peekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg, CALL_ORIG_FUNC(PeekMessageW)); + } + BOOL WINAPI setLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags) { LOG_FUNC("SetLayeredWindowAttributes", hwnd, crKey, bAlpha, dwFlags); @@ -455,8 +489,12 @@ namespace Gdi void installHooks() { + HOOK_FUNCTION(user32, GetMessageA, getMessageA); + HOOK_FUNCTION(user32, GetMessageW, getMessageW); HOOK_FUNCTION(user32, GetWindowLongA, getWindowLongA); HOOK_FUNCTION(user32, GetWindowLongW, getWindowLongW); + HOOK_FUNCTION(user32, PeekMessageA, peekMessageA); + HOOK_FUNCTION(user32, PeekMessageW, peekMessageW); HOOK_FUNCTION(user32, SetLayeredWindowAttributes, setLayeredWindowAttributes); HOOK_FUNCTION(user32, SetWindowLongA, setWindowLongA); HOOK_FUNCTION(user32, SetWindowLongW, setWindowLongW);