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

Separate GDI and DirectDraw surfaces

This commit is contained in:
narzoul 2018-07-17 20:46:38 +02:00
parent 6183aed7da
commit 785663700d
45 changed files with 1050 additions and 590 deletions

View File

@ -1,11 +1,12 @@
#define WIN32_LEAN_AND_MEAN
#include <algorithm>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <utility>
#include <vector>
#include <Windows.h>
#include <detours.h>
@ -19,7 +20,7 @@ namespace
struct HookedFunctionInfo
{
HMODULE module;
void* trampoline;
void*& origFunction;
void* newFunction;
};
@ -28,7 +29,7 @@ namespace
std::map<void*, HookedFunctionInfo>::iterator findOrigFunc(void* origFunc)
{
return std::find_if(g_hookedFunctions.begin(), g_hookedFunctions.end(),
[=](const auto& i) { return origFunc == i.first || origFunc == i.second.trampoline; });
[=](const auto& i) { return origFunc == i.first || origFunc == i.second.origFunction; });
}
std::vector<HMODULE> getProcessModules(HANDLE process)
@ -111,7 +112,7 @@ namespace
const auto it = findOrigFunc(origFuncPtr);
if (it != g_hookedFunctions.end())
{
origFuncPtr = it->second.trampoline;
origFuncPtr = it->second.origFunction;
return;
}
@ -136,13 +137,14 @@ namespace
HMODULE module = nullptr;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
reinterpret_cast<char*>(hookedFuncPtr), &module);
g_hookedFunctions[hookedFuncPtr] = { module, origFuncPtr, newFuncPtr };
g_hookedFunctions.emplace(
std::make_pair(hookedFuncPtr, HookedFunctionInfo{ module, origFuncPtr, newFuncPtr }));
}
void unhookFunction(const std::map<void*, HookedFunctionInfo>::iterator& hookedFunc)
{
DetourTransactionBegin();
DetourDetach(&hookedFunc->second.trampoline, hookedFunc->second.newFunction);
DetourDetach(&hookedFunc->second.origFunction, hookedFunc->second.newFunction);
DetourTransactionCommit();
if (hookedFunc->second.module)
@ -173,7 +175,9 @@ namespace Compat
if (0 != _stricmp(moduleBaseName.c_str(), moduleName))
{
Compat::Log() << "Disabling external hook to " << funcName << " in " << moduleBaseName;
hookFunction(hookFunc, newFunc);
static std::list<void*> origFuncs;
origFuncs.push_back(hookFunc);
hookFunction(origFuncs.back(), newFunc);
}
}
}

View File

@ -6,6 +6,5 @@ namespace Config
{
const int maxPaletteUpdatesPerMs = 5;
const int minExpectedFlipsPerSec = 5;
const DWORD preallocatedGdiDcCount = 4;
const DWORD primarySurfaceExtraRows = 2;
}

View File

@ -2,12 +2,36 @@
#include "D3dDdi/Device.h"
#include "D3dDdi/DeviceFuncs.h"
#include "D3dDdi/KernelModeThunks.h"
#include "Gdi/AccessGuard.h"
namespace
{
D3DDDI_RESOURCEFLAGS getResourceTypeFlags();
const UINT g_resourceTypeFlags = getResourceTypeFlags().Value;
HANDLE g_gdiResourceHandle = nullptr;
bool g_isReadOnlyGdiLockEnabled = false;
class RenderGuard : public Gdi::DDrawAccessGuard
{
public:
RenderGuard(D3dDdi::Device& device, Gdi::Access access)
: Gdi::DDrawAccessGuard(access)
, m_device(device)
{
device.prepareForRendering();
}
RenderGuard(D3dDdi::Device& device, Gdi::Access access, HANDLE resource, UINT subResourceIndex = UINT_MAX)
: Gdi::DDrawAccessGuard(access, g_gdiResourceHandle == resource)
, m_device(device)
{
device.prepareForRendering(resource, subResourceIndex);
}
private:
D3dDdi::Device & m_device;
};
D3DDDI_RESOURCEFLAGS getResourceTypeFlags()
{
@ -81,8 +105,8 @@ namespace D3dDdi
HRESULT Device::blt(const D3DDDIARG_BLT& data)
{
prepareForRendering(data.hSrcResource, data.SrcSubResourceIndex);
prepareForRendering(data.hDstResource, data.DstSubResourceIndex);
RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource, data.SrcSubResourceIndex);
RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource, data.DstSubResourceIndex);
auto it = m_oversizedResources.find(data.hSrcResource);
if (it != m_oversizedResources.end())
@ -101,13 +125,13 @@ namespace D3dDdi
HRESULT Device::clear(const D3DDDIARG_CLEAR& data, UINT numRect, const RECT* rect)
{
prepareForRendering();
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
return m_origVtable->pfnClear(m_device, &data, numRect, rect);
}
HRESULT Device::colorFill(const D3DDDIARG_COLORFILL& data)
{
prepareForRendering(data.hResource, data.SubResourceIndex);
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE, data.hResource, data.SubResourceIndex);
return m_origVtable->pfnColorFill(m_device, &data);
}
@ -198,6 +222,10 @@ namespace D3dDdi
{
m_sharedPrimary = nullptr;
}
if (resource == g_gdiResourceHandle)
{
g_gdiResourceHandle = nullptr;
}
}
return result;
@ -205,45 +233,49 @@ namespace D3dDdi
HRESULT Device::drawIndexedPrimitive(const D3DDDIARG_DRAWINDEXEDPRIMITIVE& data)
{
prepareForRendering();
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
return m_origVtable->pfnDrawIndexedPrimitive(m_device, &data);
}
HRESULT Device::drawIndexedPrimitive2(const D3DDDIARG_DRAWINDEXEDPRIMITIVE2& data,
UINT indicesSize, const void* indexBuffer, const UINT* flagBuffer)
{
prepareForRendering();
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
return m_origVtable->pfnDrawIndexedPrimitive2(m_device, &data, indicesSize, indexBuffer, flagBuffer);
}
HRESULT Device::drawPrimitive(const D3DDDIARG_DRAWPRIMITIVE& data, const UINT* flagBuffer)
{
prepareForRendering();
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
return m_origVtable->pfnDrawPrimitive(m_device, &data, flagBuffer);
}
HRESULT Device::drawPrimitive2(const D3DDDIARG_DRAWPRIMITIVE2& data)
{
prepareForRendering();
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
return m_origVtable->pfnDrawPrimitive2(m_device, &data);
}
HRESULT Device::drawRectPatch(const D3DDDIARG_DRAWRECTPATCH& data, const D3DDDIRECTPATCH_INFO* info,
const FLOAT* patch)
{
prepareForRendering();
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
return m_origVtable->pfnDrawRectPatch(m_device, &data, info, patch);
}
HRESULT Device::drawTriPatch(const D3DDDIARG_DRAWTRIPATCH& data, const D3DDDITRIPATCH_INFO* info,
const FLOAT* patch)
{
prepareForRendering();
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
return m_origVtable->pfnDrawTriPatch(m_device, &data, info, patch);
}
HRESULT Device::lock(D3DDDIARG_LOCK& data)
{
Gdi::DDrawAccessGuard accessGuard(
(data.Flags.ReadOnly || g_isReadOnlyGdiLockEnabled) ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE,
data.hResource == g_gdiResourceHandle);
auto it = m_renderTargetResources.find(data.hResource);
if (it != m_renderTargetResources.end())
{
@ -269,35 +301,45 @@ namespace D3dDdi
HRESULT Device::present(const D3DDDIARG_PRESENT& data)
{
prepareForRendering(data.hSrcResource, data.SrcSubResourceIndex);
RenderGuard renderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource, data.SrcSubResourceIndex);
return m_origVtable->pfnPresent(m_device, &data);
}
HRESULT Device::present1(D3DDDIARG_PRESENT1& data)
{
bool isGdiResourceInvolved = false;
for (UINT i = 0; i < data.SrcResources && !isGdiResourceInvolved; ++i)
{
isGdiResourceInvolved = data.phSrcResources[i].hResource == g_gdiResourceHandle;
}
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, isGdiResourceInvolved);
for (UINT i = 0; i < data.SrcResources; ++i)
{
prepareForRendering(data.phSrcResources[i].hResource, data.phSrcResources[i].SubResourceIndex);
}
return m_origVtable->pfnPresent1(m_device, &data);
}
HRESULT Device::texBlt(const D3DDDIARG_TEXBLT& data)
{
prepareForRendering(data.hDstResource);
prepareForRendering(data.hSrcResource);
RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource);
RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource);
return m_origVtable->pfnTexBlt(m_device, &data);
}
HRESULT Device::texBlt1(const D3DDDIARG_TEXBLT1& data)
{
prepareForRendering(data.hDstResource);
prepareForRendering(data.hSrcResource);
RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource);
RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource);
return m_origVtable->pfnTexBlt1(m_device, &data);
}
HRESULT Device::unlock(const D3DDDIARG_UNLOCK& data)
{
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, data.hResource == g_gdiResourceHandle);
auto it = m_renderTargetResources.find(data.hResource);
if (it != m_renderTargetResources.end())
{
@ -344,4 +386,14 @@ namespace D3dDdi
prepareForRendering((it++)->second);
}
}
void Device::setGdiResourceHandle(HANDLE resource)
{
g_gdiResourceHandle = resource;
}
void Device::setReadOnlyGdiLock(bool enable)
{
g_isReadOnlyGdiLockEnabled = enable;
}
}

View File

@ -48,6 +48,9 @@ namespace D3dDdi
void prepareForRendering(HANDLE resource, UINT subResourceIndex = UINT_MAX);
void prepareForRendering();
static void setGdiResourceHandle(HANDLE resource);
static void setReadOnlyGdiLock(bool enable);
private:
template <typename CreateResourceArg, typename CreateResourceFunc>
HRESULT createOversizedResource(

View File

@ -31,6 +31,7 @@ namespace
std::map<D3DKMT_HANDLE, ContextInfo> g_contexts;
std::map<D3DKMT_HANDLE, DeviceInfo> g_devices;
HMONITOR g_lastOpenAdapterMonitor = nullptr;
decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr;
decltype(D3DKMTSetVidPnSourceOwner1)* g_origD3dKmtSetVidPnSourceOwner1 = nullptr;
@ -123,6 +124,20 @@ namespace
return result;
}
NTSTATUS APIENTRY openAdapterFromHdc(D3DKMT_OPENADAPTERFROMHDC* pData)
{
Compat::LogEnter("D3DKMTOpenAdapterFromHdc", pData);
NTSTATUS result = CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(pData);
if (pData)
{
POINT p = {};
GetDCOrgEx(pData->hDc, &p);
g_lastOpenAdapterMonitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY);
}
Compat::LogLeave("D3DKMTOpenAdapterFromHdc", pData) << result;
return result;
}
bool isPresentReady(D3DKMT_HANDLE device, D3DDDI_VIDEO_PRESENT_SOURCE_ID vidPnSourceId)
{
D3DKMT_GETDEVICESTATE deviceState = {};
@ -253,6 +268,11 @@ namespace D3dDdi
{
namespace KernelModeThunks
{
HMONITOR getLastOpenAdapterMonitor()
{
return g_lastOpenAdapterMonitor;
}
bool isPresentReady()
{
for (auto it : g_devices)
@ -272,6 +292,7 @@ namespace D3dDdi
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);

View File

@ -1,5 +1,13 @@
#pragma once
#define CINTERFACE
#define WIN32_LEAN_AND_MEAN
#include <d3d.h>
#include <d3dumddi.h>
#include <../km/d3dkmthk.h>
#include <Windows.h>
#include "D3dDdi/Log/KernelModeThunksLog.h"
static const auto D3DDDI_FLIPINTERVAL_NOOVERRIDE = static_cast<D3DDDI_FLIPINTERVAL_TYPE>(5);
@ -8,6 +16,7 @@ namespace D3dDdi
{
namespace KernelModeThunks
{
HMONITOR getLastOpenAdapterMonitor();
void installHooks();
bool isPresentReady();
void overrideFlipInterval(D3DDDI_FLIPINTERVAL_TYPE flipInterval);

View File

@ -1,6 +1,13 @@
#include "Common/Log.h"
#include "D3dDdi/Log/KernelModeThunksLog.h"
std::ostream& operator<<(std::ostream& os, const LUID& luid)
{
return Compat::LogStruct(os)
<< Compat::hex(luid.LowPart)
<< Compat::hex(luid.HighPart);
}
std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXT& data)
{
return Compat::LogStruct(os)
@ -73,6 +80,15 @@ std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYDEVICE& data)
<< Compat::hex(data.hDevice);
}
std::ostream& operator<<(std::ostream& os, const D3DKMT_OPENADAPTERFROMHDC& data)
{
return Compat::LogStruct(os)
<< data.hDc
<< Compat::hex(data.hAdapter)
<< data.AdapterLuid
<< data.VidPnSourceId;
}
std::ostream& operator<<(std::ostream& os, const D3DKMT_PRESENT& data)
{
return Compat::LogStruct(os)

View File

@ -8,12 +8,14 @@
#include <d3dumddi.h>
#include <../km/d3dkmthk.h>
std::ostream& operator<<(std::ostream& os, const LUID& luid);
std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXT& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXTVIRTUAL& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATEDCFROMMEMORY& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATEDEVICE& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYCONTEXT& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYDEVICE& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_OPENADAPTERFROMHDC& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_PRESENT& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_SETQUEUEDLIMIT& data);
std::ostream& operator<<(std::ostream& os, const D3DKMT_SETVIDPNSOURCEOWNER& data);

View File

@ -4,6 +4,7 @@
#include "Common/Hook.h"
#include "DDraw/ActivateAppHandler.h"
#include "DDraw/RealPrimarySurface.h"
#include "Gdi/Gdi.h"
#include "Win32/DisplayMode.h"
#include "Win32/FontSmoothing.h"
@ -32,6 +33,7 @@ namespace
{
case WM_ACTIVATEAPP:
{
DDraw::RealPrimarySurface::disableUpdates();
isDisplayChangeNotificationEnabled = false;
if (TRUE == wParam)
{
@ -43,6 +45,7 @@ namespace
}
LRESULT result = g_origDdWndProc(hwnd, uMsg, wParam, lParam);
isDisplayChangeNotificationEnabled = true;
DDraw::RealPrimarySurface::enableUpdates();
return result;
}

View File

@ -8,43 +8,6 @@
namespace
{
DDPIXELFORMAT getRgbPixelFormat(DWORD bpp)
{
DDPIXELFORMAT pf = {};
pf.dwSize = sizeof(pf);
pf.dwFlags = DDPF_RGB;
pf.dwRGBBitCount = bpp;
switch (bpp)
{
case 1:
pf.dwFlags |= DDPF_PALETTEINDEXED1;
break;
case 2:
pf.dwFlags |= DDPF_PALETTEINDEXED2;
break;
case 4:
pf.dwFlags |= DDPF_PALETTEINDEXED4;
break;
case 8:
pf.dwFlags |= DDPF_PALETTEINDEXED8;
break;
case 16:
pf.dwRBitMask = 0xF800;
pf.dwGBitMask = 0x07E0;
pf.dwBBitMask = 0x001F;
break;
case 24:
case 32:
pf.dwRBitMask = 0xFF0000;
pf.dwGBitMask = 0x00FF00;
pf.dwBBitMask = 0x0000FF;
break;
}
return pf;
}
template <typename TDirectDraw>
HRESULT setDisplayMode(TDirectDraw* This, DWORD width, DWORD height, DWORD bpp)
{
@ -100,6 +63,43 @@ namespace DDraw
return dm;
}
DDPIXELFORMAT getRgbPixelFormat(DWORD bpp)
{
DDPIXELFORMAT pf = {};
pf.dwSize = sizeof(pf);
pf.dwFlags = DDPF_RGB;
pf.dwRGBBitCount = bpp;
switch (bpp)
{
case 1:
pf.dwFlags |= DDPF_PALETTEINDEXED1;
break;
case 2:
pf.dwFlags |= DDPF_PALETTEINDEXED2;
break;
case 4:
pf.dwFlags |= DDPF_PALETTEINDEXED4;
break;
case 8:
pf.dwFlags |= DDPF_PALETTEINDEXED8;
break;
case 16:
pf.dwRBitMask = 0xF800;
pf.dwGBitMask = 0x07E0;
pf.dwBBitMask = 0x001F;
break;
case 24:
case 32:
pf.dwRBitMask = 0xFF0000;
pf.dwGBitMask = 0x00FF00;
pf.dwBBitMask = 0x0000FF;
break;
}
return pf;
}
void suppressEmulatedDirectDraw(GUID*& guid)
{
if (reinterpret_cast<GUID*>(DDCREATE_EMULATIONONLY) == guid)

View File

@ -18,6 +18,7 @@ namespace DDraw
void* getDdObject(TDirectDraw& dd);
DDSURFACEDESC2 getDisplayMode(CompatRef<IDirectDraw7> dd);
DDPIXELFORMAT getRgbPixelFormat(DWORD bpp);
void suppressEmulatedDirectDraw(GUID*& guid);
template <typename TDirectDraw>

View File

@ -6,6 +6,7 @@
#include "DDraw/DirectDrawPalette.h"
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "Gdi/AccessGuard.h"
namespace DDraw
{
@ -37,7 +38,7 @@ namespace DDraw
{
std::memcpy(&PrimarySurface::s_paletteEntries[dwStartingEntry], lpEntries,
dwCount * sizeof(PALETTEENTRY));
RealPrimarySurface::updatePalette(dwStartingEntry, dwCount);
RealPrimarySurface::updatePalette();
}
return result;
}

View File

@ -33,6 +33,7 @@ namespace DDraw
SET_COMPAT_METHOD(BltFast);
SET_COMPAT_METHOD(Flip);
SET_COMPAT_METHOD(GetCaps);
SET_COMPAT_METHOD(GetDC);
SET_COMPAT_METHOD(GetSurfaceDesc);
SET_COMPAT_METHOD(IsLost);
SET_COMPAT_METHOD(Lock);

View File

@ -104,6 +104,8 @@ namespace DDraw
{
void installHooks()
{
RealPrimarySurface::init();
Win32::Registry::unsetValue(
HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\DirectDraw", "EmulationOnly");
Win32::Registry::unsetValue(

View File

@ -1,10 +1,12 @@
#include <atomic>
#include <memory>
#include <vector>
#include "Common/CompatPtr.h"
#include "Common/Hook.h"
#include "Common/Time.h"
#include "Config/Config.h"
#include "D3dDdi/Device.h"
#include "D3dDdi/KernelModeThunks.h"
#include "DDraw/DirectDraw.h"
#include "DDraw/DirectDrawSurface.h"
@ -13,11 +15,26 @@
#include "DDraw/ScopedThreadLock.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "DDraw/Types.h"
#include "Gdi/AccessGuard.h"
#include "Gdi/Gdi.h"
#include "Gdi/VirtualScreen.h"
#include "Gdi/Window.h"
#include "Win32/DisplayMode.h"
namespace
{
struct BltToWindowViaGdiArgs
{
std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc;
Gdi::Region* primaryRegion;
BltToWindowViaGdiArgs()
: virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc)
, primaryRegion(nullptr)
{
}
};
void onRelease();
DWORD WINAPI updateThreadProc(LPVOID lpParameter);
@ -42,7 +59,8 @@ namespace
BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam)
{
if (IsWindowVisible(hwnd) && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED))
if (IsWindowVisible(hwnd) && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) &&
!Gdi::Window::isPresentationWindow(hwnd))
{
auto& visibleLayeredWindows = *reinterpret_cast<std::vector<HWND>*>(lParam);
visibleLayeredWindows.push_back(hwnd);
@ -52,7 +70,7 @@ namespace
BOOL CALLBACK bltToWindow(HWND hwnd, LPARAM lParam)
{
if (!IsWindowVisible(hwnd) || (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED))
if (!IsWindowVisible(hwnd) || !Gdi::Window::isPresentationWindow(hwnd))
{
return TRUE;
}
@ -63,6 +81,54 @@ namespace
return TRUE;
}
BOOL CALLBACK bltToWindowViaGdi(HWND hwnd, LPARAM lParam)
{
if (!IsWindowVisible(hwnd) || !Gdi::Window::isPresentationWindow(hwnd))
{
return TRUE;
}
auto window = Gdi::Window::get(GetParent(hwnd));
if (!window)
{
return TRUE;
}
Gdi::Region visibleRegion = window->getVisibleRegion();
if (visibleRegion.isEmpty())
{
return TRUE;
}
auto& args = *reinterpret_cast<BltToWindowViaGdiArgs*>(lParam);
if (args.primaryRegion)
{
visibleRegion -= *args.primaryRegion;
if (visibleRegion.isEmpty())
{
return TRUE;
}
}
if (!args.virtualScreenDc)
{
args.virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
if (!args.virtualScreenDc)
{
return FALSE;
}
}
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_READ);
HDC presentationWindowDc = GetWindowDC(hwnd);
RECT rect = window->getWindowRect();
CALL_ORIG_FUNC(BitBlt)(presentationWindowDc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
args.virtualScreenDc.get(), rect.left, rect.top, SRCCOPY);
ReleaseDC(hwnd, presentationWindowDc);
return TRUE;
}
void bltVisibleLayeredWindowsToBackBuffer()
{
std::vector<HWND> visibleLayeredWindows;
@ -94,7 +160,6 @@ namespace
}
g_backBuffer->ReleaseDC(g_backBuffer, backBufferDc);
DDraw::RealPrimarySurface::update();
}
HRESULT bltToPrimaryChain(CompatRef<IDirectDrawSurface7> src)
@ -112,15 +177,31 @@ namespace
{
Compat::LogEnter("RealPrimarySurface::compatBlt");
bool result = false;
BltToWindowViaGdiArgs bltToWindowViaGdiArgs;
if (!g_frontBuffer || (!g_isFullScreen && DDraw::RealPrimarySurface::isLost()))
{
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
Compat::LogLeave("RealPrimarySurface::compatBlt") << false;
return false;
}
Gdi::Region primaryRegion(DDraw::PrimarySurface::getMonitorRect());
bltToWindowViaGdiArgs.primaryRegion = &primaryRegion;
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
bool result = false;
auto primary(DDraw::PrimarySurface::getPrimary());
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(primary.get()));
if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8)
{
HDC paletteConverterDc = nullptr;
g_paletteConverter->GetDC(g_paletteConverter, &paletteConverterDc);
HDC primaryDc = nullptr;
D3dDdi::Device::setReadOnlyGdiLock(true);
primary->GetDC(primary, &primaryDc);
D3dDdi::Device::setReadOnlyGdiLock(false);
if (paletteConverterDc && primaryDc)
{
@ -203,7 +284,6 @@ namespace
surface->SetClipper(surface, g_clipper);
}
g_qpcFlipModeTimeout = Time::g_qpcFrequency / Config::minExpectedFlipsPerSec;
g_qpcLastFlip = Time::queryPerformanceCounter() - g_qpcFlipModeTimeout;
g_qpcNextUpdate = Time::queryPerformanceCounter();
@ -216,17 +296,6 @@ namespace
}
g_qpcUpdateInterval = Time::g_qpcFrequency / dm.dwRefreshRate;
if (!g_updateEvent)
{
g_updateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
}
if (!g_updateThread)
{
g_updateThread = CreateThread(nullptr, 0, &updateThreadProc, nullptr, 0, nullptr);
SetThreadPriority(g_updateThread, THREAD_PRIORITY_TIME_CRITICAL);
}
surface->SetPrivateData(surface, IID_IReleaseNotifier,
&g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER);
@ -265,6 +334,7 @@ namespace
g_clipper.release();
g_isFullScreen = false;
g_paletteConverter.release();
g_qpcUpdateInterval = Time::g_qpcFrequency / 60;
ZeroMemory(&g_surfaceDesc, sizeof(g_surfaceDesc));
@ -275,6 +345,7 @@ namespace
{
ResetEvent(g_updateEvent);
Gdi::VirtualScreen::update();
if (compatBlt() && g_isFullScreen)
{
D3dDdi::KernelModeThunks::overrideFlipInterval(
@ -349,7 +420,7 @@ namespace DDraw
return result;
}
return init(dd, surface);
return ::init(dd, surface);
}
template HRESULT RealPrimarySurface::create(CompatRef<IDirectDraw>);
@ -408,6 +479,17 @@ namespace DDraw
return g_frontBuffer;
}
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);
}
bool RealPrimarySurface::isFullScreen()
{
return g_isFullScreen;
@ -465,7 +547,7 @@ namespace DDraw
g_frontBuffer->SetPalette(g_frontBuffer, PrimarySurface::s_palette);
}
updatePalette(0, 256);
updatePalette();
}
void RealPrimarySurface::update()
@ -500,9 +582,8 @@ namespace DDraw
}
}
void RealPrimarySurface::updatePalette(DWORD startingEntry, DWORD count)
void RealPrimarySurface::updatePalette()
{
Gdi::updatePalette(startingEntry, count);
if (PrimarySurface::s_palette)
{
update();

View File

@ -20,6 +20,7 @@ namespace DDraw
static HRESULT flip(DWORD flags);
static HRESULT getGammaRamp(DDGAMMARAMP* rampData);
static CompatWeakPtr<IDirectDrawSurface7> getSurface();
static void init();
static bool isFullScreen();
static bool isLost();
static void release();
@ -28,6 +29,6 @@ namespace DDraw
static HRESULT setGammaRamp(DDGAMMARAMP* rampData);
static void setPalette();
static void update();
static void updatePalette(DWORD startingEntry, DWORD count);
static void updatePalette();
};
}

View File

@ -1,5 +1,13 @@
#define CINTERFACE
#define _NO_DDRAWINT_NO_COM
#include <ddraw.h>
#include "Common/CompatPtr.h"
#include "Common/CompatRef.h"
#include "Config/Config.h"
#include "D3dDdi/Device.h"
#include "D3dDdi/KernelModeThunks.h"
#include "DDraw/DirectDraw.h"
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/Surfaces/PrimarySurface.h"
@ -8,11 +16,30 @@
namespace
{
DDSURFACEDESC2 g_primarySurfaceDesc = {};
CompatWeakPtr<IDirectDrawSurface7> g_primarySurface = nullptr;
CompatWeakPtr<IDirectDrawSurface7> g_primarySurface;
HANDLE g_gdiResourceHandle = nullptr;
DWORD g_origCaps = 0;
RECT g_monitorRect = {};
HANDLE getResourceHandle(IDirectDrawSurface7& surface)
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)
{
return reinterpret_cast<HANDLE**>(&surface)[1][2];
}
@ -32,6 +59,7 @@ 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));
@ -75,6 +103,7 @@ 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);
@ -86,6 +115,8 @@ namespace DDraw
}
g_gdiResourceHandle = getResourceHandle(*surface7);
D3dDdi::Device::setGdiResourceHandle(*reinterpret_cast<HANDLE*>(g_gdiResourceHandle));
return DD_OK;
}
@ -135,7 +166,7 @@ namespace DDraw
do
{
if (getResourceHandle(*surface) == g_gdiResourceHandle)
if (isGdiSurface(surface.get()))
{
return CompatPtr<IDirectDrawSurface7>::from(surface.get());
}
@ -150,6 +181,11 @@ namespace DDraw
return nullptr;
}
RECT PrimarySurface::getMonitorRect()
{
return g_monitorRect;
}
CompatWeakPtr<IDirectDrawSurface7> PrimarySurface::getPrimary()
{
return g_primarySurface;
@ -160,6 +196,27 @@ namespace DDraw
return g_origCaps;
}
template <typename TSurface>
static bool PrimarySurface::isGdiSurface(TSurface* surface)
{
return surface && getResourceHandle(*surface) == g_gdiResourceHandle;
}
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface*);
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface2*);
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface3*);
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface4*);
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface7*);
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);
}
void PrimarySurface::resizeBuffers(CompatRef<IDirectDrawSurface7> surface)
{
DDSCAPS2 flipCaps = {};

View File

@ -19,10 +19,13 @@ namespace DDraw
static HRESULT flipToGdiSurface();
static const DDSURFACEDESC2& getDesc();
static CompatPtr<IDirectDrawSurface7> getGdiSurface();
static RECT getMonitorRect();
static CompatWeakPtr<IDirectDrawSurface7> getPrimary();
static DWORD getOrigCaps();
static void onRestore();
void updateGdiSurfacePtr(IDirectDrawSurface* flipTargetOverride);
template <typename TSurface>
static bool isGdiSurface(TSurface* surface);
static CompatWeakPtr<IDirectDrawPalette> s_palette;
static PALETTEENTRY s_paletteEntries[256];

View File

@ -57,22 +57,6 @@ namespace DDraw
HRESULT result = m_impl.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans);
if (SUCCEEDED(result))
{
const LONG x = dwX;
const LONG y = dwY;
RECT destRect = { x, y, x, y };
if (lpSrcRect)
{
destRect.right += lpSrcRect->right - lpSrcRect->left;
destRect.bottom += lpSrcRect->bottom - lpSrcRect->top;
}
else
{
TSurfaceDesc desc = {};
desc.dwSize = sizeof(desc);
CompatVtable<Vtable<TSurface>>::s_origVtable.GetSurfaceDesc(lpDDSrcSurface, &desc);
destRect.right += desc.dwWidth;
destRect.bottom += desc.dwHeight;
}
RealPrimarySurface::update();
}
return result;
@ -195,6 +179,7 @@ namespace DDraw
result = m_impl.Restore(This);
if (SUCCEEDED(result))
{
PrimarySurface::onRestore();
Gdi::redraw(nullptr);
}
}

View File

@ -2,8 +2,10 @@
#include "Common/CompatRef.h"
#include "DDraw/Repository.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "DDraw/Surfaces/Surface.h"
#include "DDraw/Surfaces/SurfaceImpl.h"
#include "Gdi/AccessGuard.h"
namespace
{
@ -159,6 +161,8 @@ namespace DDraw
TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect,
DWORD dwFlags, LPDDBLTFX lpDDBltFx)
{
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);
if (DDERR_UNSUPPORTED == result || DDERR_GENERIC == result)
{
@ -178,6 +182,8 @@ namespace DDraw
HRESULT SurfaceImpl<TSurface>::BltFast(
TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans)
{
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);
if (DDERR_UNSUPPORTED == result || DDERR_GENERIC == result)
{
@ -221,6 +227,13 @@ namespace DDraw
return s_origVtable.GetCaps(This, lpDDSCaps);
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetDC(TSurface* This, HDC* lphDC)
{
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_WRITE);
return s_origVtable.GetDC(This, lphDC);
}
template <typename TSurface>
HRESULT SurfaceImpl2<TSurface>::GetDDInterface(TSurface* /*This*/, LPVOID* lplpDD)
{
@ -250,6 +263,8 @@ namespace DDraw
TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc,
DWORD dwFlags, HANDLE hEvent)
{
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);
if (DDERR_SURFACELOST == result)
{
@ -269,6 +284,7 @@ namespace DDraw
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::ReleaseDC(TSurface* This, HDC hDC)
{
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(This));
return s_origVtable.ReleaseDC(This, hDC);
}
@ -287,6 +303,7 @@ namespace DDraw
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::Unlock(TSurface* This, TUnlockParam lpRect)
{
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(This));
return s_origVtable.Unlock(This, lpRect);
}

View File

@ -43,6 +43,7 @@ namespace DDraw
TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans);
virtual HRESULT Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags);
virtual HRESULT GetCaps(TSurface* This, TDdsCaps* lpDDSCaps);
virtual HRESULT GetDC(TSurface* This, HDC* lphDC);
virtual HRESULT GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc);
virtual HRESULT IsLost(TSurface* This);
virtual HRESULT Lock(TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc,

View File

@ -215,6 +215,7 @@
<ClInclude Include="Direct3d\Visitors\Direct3dViewportVtblVisitor.h" />
<ClInclude Include="Direct3d\Visitors\Direct3dVtblVisitor.h" />
<ClInclude Include="Dll\Procs.h" />
<ClInclude Include="Gdi\AccessGuard.h" />
<ClInclude Include="Gdi\Gdi.h" />
<ClInclude Include="Gdi\Caret.h" />
<ClInclude Include="Gdi\Dc.h" />
@ -225,6 +226,7 @@
<ClInclude Include="Gdi\ScrollBar.h" />
<ClInclude Include="Gdi\ScrollFunctions.h" />
<ClInclude Include="Gdi\TitleBar.h" />
<ClInclude Include="Gdi\VirtualScreen.h" />
<ClInclude Include="Gdi\Window.h" />
<ClInclude Include="Gdi\WinProc.h" />
<ClInclude Include="Win32\DisplayMode.h" />
@ -274,6 +276,7 @@
<ClCompile Include="Dll\Procs.cpp" />
<ClCompile Include="Dll\DllMain.cpp" />
<ClCompile Include="Dll\UnmodifiedProcs.cpp" />
<ClCompile Include="Gdi\AccessGuard.cpp" />
<ClCompile Include="Gdi\Gdi.cpp" />
<ClCompile Include="Gdi\Caret.cpp" />
<ClCompile Include="Gdi\Dc.cpp" />
@ -284,6 +287,7 @@
<ClCompile Include="Gdi\ScrollBar.cpp" />
<ClCompile Include="Gdi\ScrollFunctions.cpp" />
<ClCompile Include="Gdi\TitleBar.cpp" />
<ClCompile Include="Gdi\VirtualScreen.cpp" />
<ClCompile Include="Gdi\Window.cpp" />
<ClCompile Include="Gdi\WinProc.cpp" />
<ClCompile Include="Win32\DisplayMode.cpp" />

View File

@ -321,6 +321,12 @@
<ClInclude Include="Gdi\Region.h">
<Filter>Header Files\Gdi</Filter>
</ClInclude>
<ClInclude Include="Gdi\VirtualScreen.h">
<Filter>Header Files\Gdi</Filter>
</ClInclude>
<ClInclude Include="Gdi\AccessGuard.h">
<Filter>Header Files\Gdi</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp">
@ -494,6 +500,12 @@
<ClCompile Include="Gdi\Region.cpp">
<Filter>Source Files\Gdi</Filter>
</ClCompile>
<ClCompile Include="Gdi\VirtualScreen.cpp">
<Filter>Source Files\Gdi</Filter>
</ClCompile>
<ClCompile Include="Gdi\AccessGuard.cpp">
<Filter>Source Files\Gdi</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="Dll\DDrawCompat.def">

View File

@ -15,6 +15,7 @@
#include "Direct3d/Hooks.h"
#include "Dll/Procs.h"
#include "Gdi/Gdi.h"
#include "Gdi/VirtualScreen.h"
#include "Win32/DisplayMode.h"
#include "Win32/FontSmoothing.h"
#include "Win32/MsgHooks.h"
@ -39,14 +40,15 @@ namespace
Win32::Registry::installHooks();
Compat::Log() << "Installing Direct3D driver hooks";
D3dDdi::installHooks();
Compat::Log() << "Installing display mode hooks";
Win32::DisplayMode::installHooks(g_origDDrawModule);
Gdi::VirtualScreen::init();
Compat::Log() << "Installing DirectDraw hooks";
DDraw::installHooks();
Compat::Log() << "Installing Direct3D hooks";
Direct3d::installHooks();
Compat::Log() << "Installing GDI hooks";
Gdi::installHooks();
Compat::Log() << "Installing display mode hooks";
Win32::DisplayMode::installHooks(g_origDDrawModule);
Compat::Log() << "Finished installing hooks";
isAlreadyInstalled = true;
}

View File

@ -0,0 +1,152 @@
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/ScopedThreadLock.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "Gdi/AccessGuard.h"
#include "Gdi/VirtualScreen.h"
namespace
{
struct Accessor
{
DWORD readAccessDepth;
DWORD writeAccessDepth;
bool isSynced;
Accessor(bool isSynced) : readAccessDepth(0), writeAccessDepth(0), isSynced(isSynced) {}
};
Accessor g_ddrawAccessor(false);
Accessor g_gdiAccessor(true);
bool g_isSyncing = false;
bool synchronize(Gdi::User user);
void beginAccess(Gdi::User user, Gdi::Access access)
{
Compat::LogEnter("beginAccess", user, access);
Accessor& accessor = Gdi::USER_DDRAW == user ? g_ddrawAccessor : g_gdiAccessor;
DWORD& accessDepth = Gdi::ACCESS_READ == access ? accessor.readAccessDepth : accessor.writeAccessDepth;
++accessDepth;
accessor.isSynced = accessor.isSynced || synchronize(user);
if (accessor.isSynced && Gdi::ACCESS_WRITE == access)
{
Accessor& otherAccessor = Gdi::USER_DDRAW == user ? g_gdiAccessor : g_ddrawAccessor;
otherAccessor.isSynced = false;
}
Compat::LogLeave("beginAccess", user, access) << accessor.isSynced;
}
void endAccess(Gdi::User user, Gdi::Access access)
{
Compat::LogEnter("endAccess", user, access);
Accessor& accessor = Gdi::USER_DDRAW == user ? g_ddrawAccessor : g_gdiAccessor;
DWORD& accessDepth = Gdi::ACCESS_READ == access ? accessor.readAccessDepth : accessor.writeAccessDepth;
--accessDepth;
if (Gdi::USER_DDRAW == user &&
0 == g_ddrawAccessor.readAccessDepth && 0 == g_ddrawAccessor.writeAccessDepth &&
(0 != g_gdiAccessor.readAccessDepth || 0 != g_gdiAccessor.writeAccessDepth))
{
g_gdiAccessor.isSynced = g_gdiAccessor.isSynced || synchronize(Gdi::USER_GDI);
if (g_gdiAccessor.isSynced && 0 != g_gdiAccessor.writeAccessDepth)
{
g_ddrawAccessor.isSynced = false;
}
}
if (0 == accessDepth && Gdi::ACCESS_WRITE == access && Gdi::USER_GDI == user &&
0 == g_ddrawAccessor.writeAccessDepth)
{
auto primary(DDraw::PrimarySurface::getPrimary());
if (!primary || DDraw::PrimarySurface::isGdiSurface(primary.get()))
{
DDraw::RealPrimarySurface::update();
}
}
Compat::LogLeave("endAccess", user, access);
}
bool synchronize(Gdi::User user)
{
auto ddrawSurface(DDraw::PrimarySurface::getGdiSurface());
if (!ddrawSurface)
{
return false;
}
auto gdiSurface(Gdi::VirtualScreen::createSurface(DDraw::PrimarySurface::getMonitorRect()));
if (!gdiSurface)
{
return false;
}
bool result = true;
g_isSyncing = true;
if (Gdi::USER_DDRAW == user)
{
CompatPtr<IDirectDrawClipper> clipper;
ddrawSurface->GetClipper(ddrawSurface, &clipper.getRef());
ddrawSurface->SetClipper(ddrawSurface, nullptr);
result = SUCCEEDED(ddrawSurface->BltFast(
ddrawSurface, 0, 0, gdiSurface, nullptr, DDBLTFAST_WAIT));
ddrawSurface->SetClipper(ddrawSurface, clipper);
}
else
{
result = SUCCEEDED(gdiSurface->BltFast(
gdiSurface, 0, 0, ddrawSurface, nullptr, DDBLTFAST_WAIT));
}
g_isSyncing = false;
return result;
}
}
namespace Gdi
{
AccessGuard::AccessGuard(User user, Access access, bool condition)
: m_user(user)
, m_access(access)
, m_condition(condition)
{
if (m_condition)
{
DDraw::ScopedThreadLock lock;
if (g_isSyncing)
{
m_condition = false;
return;
}
beginAccess(user, access);
}
}
AccessGuard::~AccessGuard()
{
if (m_condition)
{
DDraw::ScopedThreadLock lock;
endAccess(m_user, m_access);
}
}
DDrawAccessGuard::DDrawAccessGuard(Access access, bool condition)
: AccessGuard(USER_DDRAW, access, condition)
{
}
GdiAccessGuard::GdiAccessGuard(Access access, bool condition)
: AccessGuard(USER_GDI, access, condition)
{
}
}

View File

@ -0,0 +1,40 @@
#pragma once
namespace Gdi
{
enum Access
{
ACCESS_READ,
ACCESS_WRITE
};
enum User
{
USER_DDRAW,
USER_GDI
};
class AccessGuard
{
protected:
AccessGuard(User user, Access access, bool condition = true);
~AccessGuard();
private:
User m_user;
Access m_access;
bool m_condition;
};
class DDrawAccessGuard : public AccessGuard
{
public:
DDrawAccessGuard(Access access, bool condition = true);
};
class GdiAccessGuard : public AccessGuard
{
public:
GdiAccessGuard(Access access, bool condition = true);
};
}

View File

@ -4,6 +4,7 @@
#include "Common/Hook.h"
#include "Common/ScopedCriticalSection.h"
#include "Gdi/AccessGuard.h"
#include "Gdi/Caret.h"
#include "Gdi/Dc.h"
#include "Gdi/Gdi.h"
@ -103,11 +104,11 @@ namespace
return;
}
if ((g_caret.isVisible || newCaret.isVisible) && Gdi::beginGdiRendering())
if ((g_caret.isVisible || newCaret.isVisible))
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
drawCaret(g_caret);
drawCaret(newCaret);
Gdi::endGdiRendering();
}
g_caret = newCaret;

View File

@ -1,5 +1,6 @@
#include <algorithm>
#include <unordered_map>
#include <vector>
#include "Common/Hook.h"
#include "Common/Log.h"
@ -7,24 +8,27 @@
#include "Gdi/Dc.h"
#include "Gdi/DcCache.h"
#include "Gdi/Gdi.h"
#include "Gdi/VirtualScreen.h"
#include "Gdi/Window.h"
namespace
{
using Gdi::DcCache::CachedDc;
struct CompatDc : CachedDc
struct CompatDc
{
CompatDc(const CachedDc& cachedDc = {}) : CachedDc(cachedDc) {}
HDC dc;
DWORD refCount;
HDC origDc;
int savedState;
};
typedef std::unique_ptr<HDC__, void(*)(HDC)> OrigDc;
typedef std::unordered_map<HDC, CompatDc> CompatDcMap;
CompatDcMap g_origDcToCompatDc;
void copyDcAttributes(CompatDc& compatDc, HDC origDc, POINT& origin)
CRITICAL_SECTION g_cs;
CompatDcMap g_origDcToCompatDc;
thread_local std::vector<OrigDc> g_threadDcs;
void copyDcAttributes(const CompatDc& compatDc, HDC origDc, const POINT& origin)
{
SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_FONT));
SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_BRUSH));
@ -76,6 +80,15 @@ namespace
MoveToEx(compatDc.dc, currentPos.x, currentPos.y, nullptr);
}
void deleteDc(HDC origDc)
{
Compat::ScopedCriticalSection lock(g_cs);
auto it = g_origDcToCompatDc.find(origDc);
RestoreDC(it->second.dc, it->second.savedState);
Gdi::DcCache::deleteDc(it->second.dc);
g_origDcToCompatDc.erase(origDc);
}
void setClippingRegion(HDC compatDc, HDC origDc, HWND hwnd, const POINT& origin)
{
if (hwnd)
@ -100,12 +113,16 @@ namespace
void updateWindow(HWND wnd)
{
auto window = Gdi::Window::get(wnd);
if (!window)
{
return;
}
RECT windowRect = {};
GetWindowRect(wnd, &windowRect);
auto& window = Gdi::Window::get(wnd);
RECT cachedWindowRect = window.getWindowRect();
RECT cachedWindowRect = window->getWindowRect();
if (!EqualRect(&windowRect, &cachedWindowRect))
{
Gdi::Window::updateAll();
@ -124,8 +141,7 @@ namespace Gdi
return nullptr;
}
Compat::ScopedCriticalSection gdiLock(Gdi::g_gdiCriticalSection);
Compat::ScopedCriticalSection lock(g_cs);
auto it = g_origDcToCompatDc.find(origDc);
if (it != g_origDcToCompatDc.end())
{
@ -140,7 +156,8 @@ namespace Gdi
updateWindow(rootWnd);
}
CompatDc compatDc(Gdi::DcCache::getDc());
CompatDc compatDc;
compatDc.dc = Gdi::DcCache::getDc();
if (!compatDc.dc)
{
return nullptr;
@ -148,6 +165,9 @@ namespace Gdi
POINT origin = {};
GetDCOrgEx(origDc, &origin);
RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
origin.x -= virtualScreenBounds.left;
origin.y -= virtualScreenBounds.top;
compatDc.savedState = SaveDC(compatDc.dc);
copyDcAttributes(compatDc, origDc, origin);
@ -156,21 +176,27 @@ namespace Gdi
compatDc.refCount = 1;
compatDc.origDc = origDc;
g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc));
g_threadDcs.emplace_back(origDc, &deleteDc);
return compatDc.dc;
}
HDC getOrigDc(HDC dc)
{
Compat::ScopedCriticalSection lock(g_cs);
const auto it = std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(),
[dc](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == dc; });
return it != g_origDcToCompatDc.end() ? it->first : dc;
}
void init()
{
InitializeCriticalSection(&g_cs);
}
void releaseDc(HDC origDc)
{
Compat::ScopedCriticalSection gdiLock(Gdi::g_gdiCriticalSection);
Compat::ScopedCriticalSection lock(g_cs);
auto it = g_origDcToCompatDc.find(origDc);
if (it == g_origDcToCompatDc.end())
{
@ -181,8 +207,13 @@ namespace Gdi
--compatDc.refCount;
if (0 == compatDc.refCount)
{
auto threadDcIter = std::find_if(g_threadDcs.begin(), g_threadDcs.end(),
[origDc](const OrigDc& dc) { return dc.get() == origDc; });
threadDcIter->release();
g_threadDcs.erase(threadDcIter);
RestoreDC(compatDc.dc, compatDc.savedState);
Gdi::DcCache::releaseDc(compatDc);
Gdi::DcCache::releaseDc(compatDc.dc);
g_origDcToCompatDc.erase(origDc);
}
}

View File

@ -10,6 +10,7 @@ namespace Gdi
{
HDC getDc(HDC origDc);
HDC getOrigDc(HDC dc);
void init();
void releaseDc(HDC origDc);
}
}

View File

@ -1,227 +1,45 @@
#include <cstring>
#include <memory>
#include <vector>
#include "Common/CompatPtr.h"
#include "Common/Log.h"
#include "Config/Config.h"
#include "DDraw/Repository.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "Dll/Procs.h"
#include "Gdi/DcCache.h"
#include "Gdi/VirtualScreen.h"
namespace
{
using Gdi::DcCache::CachedDc;
std::vector<CachedDc> g_cache;
DWORD g_cacheSize = 0;
DWORD g_cacheId = 0;
DWORD g_maxUsedCacheSize = 0;
DWORD g_ddLockThreadId = 0;
typedef std::unique_ptr<HDC__, void(*)(HDC)> CachedDc;
CompatWeakPtr<IDirectDrawPalette> g_palette;
PALETTEENTRY g_paletteEntries[256] = {};
void* g_surfaceMemory = nullptr;
LONG g_pitch = 0;
CompatPtr<IDirectDrawSurface7> createGdiSurface();
CachedDc createCachedDc()
{
CachedDc cachedDc = {};
CompatPtr<IDirectDrawSurface7> surface(createGdiSurface());
if (!surface)
{
return cachedDc;
}
HDC dc = nullptr;
HRESULT result = surface->GetDC(surface, &dc);
if (FAILED(result))
{
LOG_ONCE("Failed to create a GDI DC: " << result);
return cachedDc;
}
// Release DD critical section acquired by IDirectDrawSurface7::GetDC to avoid deadlocks
Dll::g_origProcs.ReleaseDDThreadLock();
cachedDc.surface = surface.detach();
cachedDc.dc = dc;
cachedDc.cacheId = g_cacheId;
return cachedDc;
}
CompatPtr<IDirectDrawSurface7> createGdiSurface()
{
DDSURFACEDESC2 desc = DDraw::PrimarySurface::getDesc();
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE;
desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
desc.lPitch = g_pitch;
desc.lpSurface = g_surfaceMemory;
auto dd(DDraw::Repository::getDirectDraw());
CompatPtr<IDirectDrawSurface7> surface;
HRESULT result = dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr);
if (FAILED(result))
{
LOG_ONCE("Failed to create a GDI surface: " << result);
return nullptr;
}
if (desc.ddpfPixelFormat.dwRGBBitCount <= 8)
{
surface->SetPalette(surface, g_palette);
}
return surface;
}
void extendCache()
{
if (g_cacheSize >= Config::preallocatedGdiDcCount)
{
LOG_ONCE("Warning: Preallocated GDI DC count is insufficient. This may lead to graphical issues.");
}
if (GetCurrentThreadId() != g_ddLockThreadId)
{
return;
}
for (DWORD i = 0; i < Config::preallocatedGdiDcCount; ++i)
{
CachedDc cachedDc = createCachedDc();
if (!cachedDc.dc)
{
return;
}
g_cache.push_back(cachedDc);
++g_cacheSize;
}
}
void releaseCachedDc(CachedDc cachedDc)
{
// Reacquire DD critical section that was temporarily released after IDirectDrawSurface7::GetDC
Dll::g_origProcs.AcquireDDThreadLock();
HRESULT result = cachedDc.surface->ReleaseDC(cachedDc.surface, cachedDc.dc);
if (FAILED(result))
{
LOG_ONCE("Failed to release a cached DC: " << result);
}
cachedDc.surface.release();
}
thread_local std::vector<CachedDc> g_cache;
}
namespace Gdi
{
namespace DcCache
{
void clear()
void deleteDc(HDC cachedDc)
{
for (auto& cachedDc : g_cache)
{
releaseCachedDc(cachedDc);
}
g_cache.clear();
g_cacheSize = 0;
++g_cacheId;
Gdi::VirtualScreen::deleteDc(cachedDc);
}
CachedDc getDc()
HDC getDc()
{
CachedDc cachedDc = {};
if (!g_surfaceMemory)
{
return cachedDc;
}
HDC cachedDc = nullptr;
if (g_cache.empty())
{
extendCache();
cachedDc = Gdi::VirtualScreen::createDc();
}
if (!g_cache.empty())
else
{
cachedDc = g_cache.back();
cachedDc = g_cache.back().release();
g_cache.pop_back();
const DWORD usedCacheSize = g_cacheSize - g_cache.size();
if (usedCacheSize > g_maxUsedCacheSize)
{
g_maxUsedCacheSize = usedCacheSize;
Compat::Log() << "GDI used DC cache size: " << g_maxUsedCacheSize;
}
}
return cachedDc;
}
bool init()
void releaseDc(HDC cachedDc)
{
auto dd(DDraw::Repository::getDirectDraw());
dd->CreatePalette(dd,
DDPCAPS_8BIT | DDPCAPS_ALLOW256, g_paletteEntries, &g_palette.getRef(), nullptr);
return nullptr != g_palette;
}
void releaseDc(const CachedDc& cachedDc)
{
if (cachedDc.cacheId == g_cacheId)
{
g_cache.push_back(cachedDc);
}
else
{
releaseCachedDc(cachedDc);
}
}
void setDdLockThreadId(DWORD ddLockThreadId)
{
g_ddLockThreadId = ddLockThreadId;
}
void setSurfaceMemory(void* surfaceMemory, LONG pitch)
{
if (g_surfaceMemory == surfaceMemory && g_pitch == pitch)
{
return;
}
g_surfaceMemory = surfaceMemory;
g_pitch = pitch;
clear();
}
void updatePalette(DWORD startingEntry, DWORD count)
{
PALETTEENTRY entries[256] = {};
std::memcpy(&entries[startingEntry],
&DDraw::PrimarySurface::s_paletteEntries[startingEntry],
count * sizeof(PALETTEENTRY));
for (DWORD i = startingEntry; i < startingEntry + count; ++i)
{
if (entries[i].peFlags & PC_RESERVED)
{
entries[i] = DDraw::PrimarySurface::s_paletteEntries[0];
entries[i].peFlags = DDraw::PrimarySurface::s_paletteEntries[i].peFlags;
}
}
if (0 != std::memcmp(&g_paletteEntries[startingEntry], &entries[startingEntry],
count * sizeof(PALETTEENTRY)))
{
std::memcpy(&g_paletteEntries[startingEntry], &entries[startingEntry],
count * sizeof(PALETTEENTRY));
g_palette->SetEntries(g_palette, 0, startingEntry, count, g_paletteEntries);
clear();
}
g_cache.emplace_back(CachedDc(cachedDc, &deleteDc));
}
}
}

View File

@ -1,30 +1,15 @@
#pragma once
#define CINTERFACE
#define WIN32_LEAN_AND_MEAN
#include <ddraw.h>
#include <Windows.h>
#include "Common/CompatWeakPtr.h"
namespace Gdi
{
namespace DcCache
{
struct CachedDc
{
CompatWeakPtr<IDirectDrawSurface7> surface;
HDC dc;
DWORD cacheId;
};
void clear();
CachedDc getDc();
bool init();
void releaseDc(const CachedDc& cachedDc);
void setDdLockThreadId(DWORD ddLockThreadId);
void setSurfaceMemory(void* surfaceMemory, LONG pitch);
void updatePalette(DWORD startingEntry, DWORD count);
void deleteDc(HDC cachedDc);
HDC getDc();
void releaseDc(HDC cachedDc);
}
}

View File

@ -2,9 +2,11 @@
#include "Common/Hook.h"
#include "Common/Log.h"
#include "Gdi/AccessGuard.h"
#include "Gdi/Dc.h"
#include "Gdi/DcFunctions.h"
#include "Gdi/Gdi.h"
#include "Gdi/VirtualScreen.h"
#include "Win32/DisplayMode.h"
namespace
@ -80,21 +82,18 @@ namespace
Compat::LogEnter(g_funcNames[origFunc], params...);
#endif
if (!hasDisplayDcArg(params...) ||
!Gdi::beginGdiRendering(getDdLockFlags<OrigFuncPtr, origFunc>(params...)))
Result result = 0;
if (hasDisplayDcArg(params...))
{
Result result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(params...);
#ifdef _DEBUG
Compat::LogLeave(g_funcNames[origFunc], params...) << result;
#endif
return result;
const bool isReadOnlyAccess = getDdLockFlags<OrigFuncPtr, origFunc>(params...) & DDLOCK_READONLY;
Gdi::GdiAccessGuard accessGuard(isReadOnlyAccess ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE);
result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(replaceDc(params)...);
releaseDc(params...);
}
else
{
result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(params...);
}
Result result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(replaceDc(params)...);
releaseDc(params...);
Gdi::endGdiRendering();
#ifdef _DEBUG
Compat::LogLeave(g_funcNames[origFunc], params...) << result;
@ -254,6 +253,13 @@ namespace
return 1;
}
UINT WINAPI realizePalette(HDC hdc)
{
UINT result = CALL_ORIG_FUNC(RealizePalette)(hdc);
Gdi::VirtualScreen::updatePalette();
return result;
}
HWND WINAPI windowFromDc(HDC dc)
{
return CALL_ORIG_FUNC(WindowFromDC)(Gdi::Dc::getOrigDc(dc));
@ -311,6 +317,9 @@ namespace Gdi
// Clipping functions
HOOK_FUNCTION(gdi32, GetRandomRgn, getRandomRgn);
// Color functions
HOOK_SHIM_FUNCTION(RealizePalette, realizePalette);
// Device context functions
HOOK_GDI_DC_FUNCTION(gdi32, DrawEscape);
HOOK_FUNCTION(user32, WindowFromDC, windowFromDc);

View File

@ -1,152 +1,37 @@
#include "Common/ScopedCriticalSection.h"
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "Dll/Procs.h"
#include "Gdi/Caret.h"
#include "Gdi/DcCache.h"
#include "Gdi/Dc.h"
#include "Gdi/DcFunctions.h"
#include "Gdi/Gdi.h"
#include "Gdi/PaintHandlers.h"
#include "Gdi/ScrollFunctions.h"
#include "Gdi/Window.h"
#include "Gdi/WinProc.h"
namespace
{
DWORD g_gdiThreadId = 0;
DWORD g_renderingRefCount = 0;
DWORD g_ddLockFlags = 0;
DWORD g_ddLockThreadRenderingRefCount = 0;
DWORD g_ddLockThreadId = 0;
HANDLE g_ddUnlockBeginEvent = nullptr;
HANDLE g_ddUnlockEndEvent = nullptr;
bool g_isDelayedUnlockPending = false;
bool lockGdiSurface(DWORD lockFlags)
{
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
auto gdiSurface(DDraw::PrimarySurface::getGdiSurface());
if (!gdiSurface || FAILED(gdiSurface.get()->lpVtbl->Lock(
gdiSurface, nullptr, &desc, lockFlags | DDLOCK_WAIT, nullptr)))
{
return false;
}
g_ddLockFlags = lockFlags;
if (0 != lockFlags)
{
EnterCriticalSection(&Gdi::g_gdiCriticalSection);
}
g_ddLockThreadId = GetCurrentThreadId();
Gdi::DcCache::setDdLockThreadId(g_ddLockThreadId);
Gdi::DcCache::setSurfaceMemory(desc.lpSurface, desc.lPitch);
return true;
}
HDC g_screenDc = nullptr;
BOOL CALLBACK redrawWindowCallback(HWND hwnd, LPARAM lParam)
{
Gdi::redrawWindow(hwnd, reinterpret_cast<HRGN>(lParam));
return TRUE;
}
void unlockGdiSurface()
{
GdiFlush();
auto gdiSurface(DDraw::PrimarySurface::getGdiSurface());
if (gdiSurface)
{
gdiSurface.get()->lpVtbl->Unlock(gdiSurface, nullptr);
if (DDLOCK_READONLY != g_ddLockFlags)
{
DDraw::RealPrimarySurface::update();
}
}
if (0 != g_ddLockFlags)
{
LeaveCriticalSection(&Gdi::g_gdiCriticalSection);
}
g_ddLockFlags = 0;
Dll::g_origProcs.ReleaseDDThreadLock();
}
}
namespace Gdi
{
CRITICAL_SECTION g_gdiCriticalSection;
bool beginGdiRendering(DWORD lockFlags)
{
Compat::ScopedCriticalSection gdiLock(g_gdiCriticalSection);
if (0 == g_renderingRefCount)
{
LeaveCriticalSection(&g_gdiCriticalSection);
Dll::g_origProcs.AcquireDDThreadLock();
EnterCriticalSection(&g_gdiCriticalSection);
if (!lockGdiSurface(lockFlags))
{
Dll::g_origProcs.ReleaseDDThreadLock();
return false;
}
}
if (GetCurrentThreadId() == g_ddLockThreadId)
{
++g_ddLockThreadRenderingRefCount;
}
++g_renderingRefCount;
return true;
}
void endGdiRendering()
{
Compat::ScopedCriticalSection gdiLock(g_gdiCriticalSection);
if (GetCurrentThreadId() == g_ddLockThreadId)
{
if (1 == g_renderingRefCount)
{
unlockGdiSurface();
g_ddLockThreadRenderingRefCount = 0;
g_renderingRefCount = 0;
}
else if (1 == g_ddLockThreadRenderingRefCount)
{
g_isDelayedUnlockPending = true;
gdiLock.unlock();
WaitForSingleObject(g_ddUnlockBeginEvent, INFINITE);
unlockGdiSurface();
g_ddLockThreadRenderingRefCount = 0;
g_renderingRefCount = 0;
SetEvent(g_ddUnlockEndEvent);
}
else
{
--g_ddLockThreadRenderingRefCount;
--g_renderingRefCount;
}
}
else
{
--g_renderingRefCount;
if (1 == g_renderingRefCount && g_isDelayedUnlockPending)
{
SetEvent(g_ddUnlockBeginEvent);
WaitForSingleObject(g_ddUnlockEndEvent, INFINITE);
g_isDelayedUnlockPending = false;
}
}
}
DWORD getGdiThreadId()
{
return g_gdiThreadId;
}
HDC getScreenDc()
{
return g_screenDc;
}
HRGN getVisibleWindowRgn(HWND hwnd)
{
return DcFunctions::getVisibleWindowRgn(hwnd);
@ -163,39 +48,24 @@ namespace Gdi
void installHooks()
{
g_gdiThreadId = GetCurrentThreadId();
InitializeCriticalSection(&g_gdiCriticalSection);
if (Gdi::DcCache::init())
{
g_ddUnlockBeginEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
g_ddUnlockEndEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!g_ddUnlockBeginEvent || !g_ddUnlockEndEvent)
{
Compat::Log() << "Failed to create the unlock events for GDI";
return;
}
g_screenDc = GetDC(nullptr);
Gdi::DcFunctions::installHooks();
Gdi::PaintHandlers::installHooks();
Gdi::ScrollFunctions::installHooks();
Gdi::WinProc::installHooks();
Gdi::Caret::installHooks();
}
}
bool isTopLevelWindow(HWND hwnd)
{
return !(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) ||
GetParent(hwnd) == GetDesktopWindow();
Gdi::Dc::init();
Gdi::DcFunctions::installHooks();
Gdi::PaintHandlers::installHooks();
Gdi::ScrollFunctions::installHooks();
Gdi::WinProc::installHooks();
Gdi::Caret::installHooks();
}
void redraw(HRGN rgn)
{
EnumThreadWindows(GetCurrentThreadId(), &redrawWindowCallback, reinterpret_cast<LPARAM>(rgn));
EnumThreadWindows(g_gdiThreadId, &redrawWindowCallback, reinterpret_cast<LPARAM>(rgn));
}
void redrawWindow(HWND hwnd, HRGN rgn)
{
if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
if (!IsWindowVisible(hwnd) || IsIconic(hwnd) || Window::isPresentationWindow(hwnd))
{
return;
}
@ -228,14 +98,7 @@ namespace Gdi
Gdi::Caret::uninstallHooks();
Gdi::WinProc::uninstallHooks();
Gdi::PaintHandlers::uninstallHooks();
}
void updatePalette(DWORD startingEntry, DWORD count)
{
if (DDraw::PrimarySurface::s_palette)
{
Gdi::DcCache::updatePalette(startingEntry, count);
}
ReleaseDC(nullptr, g_screenDc);
}
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc)

View File

@ -8,20 +8,14 @@ namespace Gdi
{
typedef void(*WindowPosChangeNotifyFunc)();
bool beginGdiRendering(DWORD lockFlags = 0);
void endGdiRendering();
DWORD getGdiThreadId();
HDC getScreenDc();
HRGN getVisibleWindowRgn(HWND hwnd);
void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc);
void installHooks();
bool isTopLevelWindow(HWND hwnd);
void redraw(HRGN rgn);
void redrawWindow(HWND hwnd, HRGN rgn);
void unhookWndProc(LPCSTR className, WNDPROC oldWndProc);
void uninstallHooks();
void updatePalette(DWORD startingEntry, DWORD count);
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc);
extern CRITICAL_SECTION g_gdiCriticalSection;
};

View File

@ -1,6 +1,7 @@
#include "Common/Hook.h"
#include "Common/Log.h"
#include "DDraw/RealPrimarySurface.h"
#include "Gdi/AccessGuard.h"
#include "Gdi/Dc.h"
#include "Gdi/Gdi.h"
#include "Gdi/PaintHandlers.h"
@ -185,19 +186,15 @@ namespace
LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc)
{
if (hwnd && Gdi::beginGdiRendering())
if (hwnd)
{
LRESULT result = 0;
HDC compatDc = Gdi::Dc::getDc(dc);
if (compatDc)
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
result = CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(compatDc), 0);
Gdi::Dc::releaseDc(dc);
}
Gdi::endGdiRendering();
if (compatDc)
{
return result;
}
}
@ -207,7 +204,7 @@ namespace
LRESULT onMenuPaint(HWND hwnd, WNDPROC origWndProc)
{
if (!hwnd || !Gdi::beginGdiRendering())
if (!hwnd)
{
return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0);
}
@ -216,6 +213,7 @@ namespace
HDC compatDc = Gdi::Dc::getDc(dc);
if (compatDc)
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
CallWindowProc(origWndProc, hwnd, WM_PRINT, reinterpret_cast<WPARAM>(compatDc),
PRF_NONCLIENT | PRF_ERASEBKGND | PRF_CLIENT);
ValidateRect(hwnd, nullptr);
@ -227,13 +225,12 @@ namespace
}
ReleaseDC(hwnd, dc);
Gdi::endGdiRendering();
return 0;
}
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc)
{
if (!hwnd || !Gdi::beginGdiRendering())
if (!hwnd)
{
return CallWindowProc(origWndProc, hwnd, WM_NCPAINT, wParam, 0);
}
@ -243,6 +240,7 @@ namespace
if (compatDc)
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
Gdi::TitleBar titleBar(hwnd, compatDc);
titleBar.drawAll();
titleBar.excludeFromClipRegion();
@ -257,13 +255,12 @@ namespace
}
ReleaseDC(hwnd, windowDc);
Gdi::endGdiRendering();
return 0;
}
LRESULT onPaint(HWND hwnd, WNDPROC origWndProc)
{
if (!hwnd || !Gdi::beginGdiRendering())
if (!hwnd)
{
return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0);
}
@ -274,6 +271,7 @@ namespace
if (compatDc)
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
CallWindowProc(origWndProc, hwnd, WM_PRINTCLIENT,
reinterpret_cast<WPARAM>(compatDc), PRF_CLIENT);
Gdi::Dc::releaseDc(dc);
@ -285,21 +283,16 @@ namespace
EndPaint(hwnd, &paint);
Gdi::endGdiRendering();
return 0;
}
LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc)
{
if (!Gdi::beginGdiRendering())
{
return CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast<WPARAM>(dc), flags);
}
LRESULT result = 0;
HDC compatDc = Gdi::Dc::getDc(dc);
if (compatDc)
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
result = CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast<WPARAM>(compatDc), flags);
Gdi::Dc::releaseDc(dc);
}
@ -307,8 +300,6 @@ namespace
{
result = CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast<WPARAM>(dc), flags);
}
Gdi::endGdiRendering();
return result;
}

View File

@ -1,26 +1,9 @@
#include <utility>
#include "Gdi/Region.h"
#include "Win32/DisplayMode.h"
namespace
{
BOOL CALLBACK addMonitorRectToRegion(
HMONITOR /*hMonitor*/, HDC /*hdcMonitor*/, LPRECT lprcMonitor, LPARAM dwData)
{
Gdi::Region& virtualScreenRegion = *reinterpret_cast<Gdi::Region*>(dwData);
Gdi::Region monitorRegion(*lprcMonitor);
virtualScreenRegion |= monitorRegion;
return TRUE;
}
Gdi::Region calculateVirtualScreenRegion()
{
Gdi::Region region;
EnumDisplayMonitors(nullptr, nullptr, addMonitorRectToRegion, reinterpret_cast<LPARAM>(&region));
return region;
}
Gdi::Region combineRegions(const Gdi::Region& rgn1, const Gdi::Region& rgn2, int mode)
{
Gdi::Region region;
@ -132,17 +115,4 @@ namespace Gdi
CombineRgn(m_region, m_region, other, mode);
return *this;
}
const Region& getVirtualScreenRegion()
{
static Region virtualScreenRegion;
static ULONG displaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
const ULONG currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
if (currentDisplaySettingsUniqueness != displaySettingsUniqueness)
{
virtualScreenRegion = calculateVirtualScreenRegion();
displaySettingsUniqueness = currentDisplaySettingsUniqueness;
}
return virtualScreenRegion;
}
}

View File

@ -35,6 +35,4 @@ namespace Gdi
HRGN m_region;
};
const Region& getVirtualScreenRegion();
}

View File

@ -0,0 +1,244 @@
#include <set>
#include "Common/ScopedCriticalSection.h"
#include "DDraw/DirectDraw.h"
#include "DDraw/Repository.h"
#include "Gdi/Gdi.h"
#include "Gdi/Region.h"
#include "Gdi/VirtualScreen.h"
#include "Win32/DisplayMode.h"
namespace
{
CRITICAL_SECTION g_cs = {};
Gdi::Region g_region;
RECT g_bounds = {};
DWORD g_bpp = 0;
LONG g_width = 0;
LONG g_height = 0;
DWORD g_pitch = 0;
HANDLE g_surfaceFileMapping = nullptr;
void* g_surfaceView = nullptr;
HGDIOBJ g_stockBitmap = nullptr;
RGBQUAD g_systemPalette[256] = {};
std::set<HDC> g_dcs;
BOOL CALLBACK addMonitorRectToRegion(
HMONITOR /*hMonitor*/, HDC /*hdcMonitor*/, LPRECT lprcMonitor, LPARAM dwData)
{
Gdi::Region& virtualScreenRegion = *reinterpret_cast<Gdi::Region*>(dwData);
Gdi::Region monitorRegion(*lprcMonitor);
virtualScreenRegion |= monitorRegion;
return TRUE;
}
}
namespace Gdi
{
namespace VirtualScreen
{
HDC createDc()
{
Compat::ScopedCriticalSection lock(g_cs);
std::unique_ptr<void, decltype(&DeleteObject)> dib(createDib(), DeleteObject);
if (!dib)
{
return nullptr;
}
std::unique_ptr<HDC__, decltype(&DeleteDC)> dc(CreateCompatibleDC(nullptr), DeleteDC);
if (!dc)
{
return nullptr;
}
HGDIOBJ stockBitmap = SelectObject(dc.get(), dib.get());
if (!stockBitmap)
{
return nullptr;
}
dib.release();
g_stockBitmap = stockBitmap;
g_dcs.insert(dc.get());
return dc.release();
}
HBITMAP createDib()
{
Compat::ScopedCriticalSection lock(g_cs);
if (!g_surfaceFileMapping)
{
return nullptr;
}
struct BITMAPINFO256 : public BITMAPINFO
{
RGBQUAD bmiRemainingColors[255];
};
BITMAPINFO256 bmi = {};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = g_width;
bmi.bmiHeader.biHeight = -g_height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = static_cast<WORD>(g_bpp);
bmi.bmiHeader.biCompression = 8 == g_bpp ? BI_RGB : BI_BITFIELDS;
if (8 == g_bpp)
{
memcpy(bmi.bmiColors, g_systemPalette, sizeof(g_systemPalette));
}
else
{
const auto pf = DDraw::getRgbPixelFormat(g_bpp);
reinterpret_cast<DWORD&>(bmi.bmiColors[0]) = pf.dwRBitMask;
reinterpret_cast<DWORD&>(bmi.bmiColors[1]) = pf.dwGBitMask;
reinterpret_cast<DWORD&>(bmi.bmiColors[2]) = pf.dwBBitMask;
}
void* bits = nullptr;
return CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, g_surfaceFileMapping, 0);
}
CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect)
{
if (rect.left < g_bounds.left || rect.top < g_bounds.top ||
rect.right > g_bounds.right || rect.bottom > g_bounds.bottom)
{
return nullptr;
}
Compat::ScopedCriticalSection lock(g_cs);
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE;
desc.dwWidth = rect.right - rect.left;
desc.dwHeight = rect.bottom - rect.top;
desc.ddpfPixelFormat = DDraw::getRgbPixelFormat(g_bpp);
desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
desc.lPitch = g_pitch;
desc.lpSurface = static_cast<unsigned char*>(g_surfaceView) +
(rect.top - g_bounds.top) * g_pitch +
(rect.left - g_bounds.left) * g_bpp / 8;
CompatPtr<IDirectDrawSurface7> surface;
auto dd(DDraw::Repository::getDirectDraw());
dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr);
return surface;
}
void deleteDc(HDC dc)
{
if (!dc)
{
return;
}
Compat::ScopedCriticalSection lock(g_cs);
DeleteObject(SelectObject(dc, g_stockBitmap));
DeleteDC(dc);
g_dcs.erase(dc);
}
RECT getBounds()
{
Compat::ScopedCriticalSection lock(g_cs);
return g_bounds;
}
const Region& getRegion()
{
Compat::ScopedCriticalSection lock(g_cs);
return g_region;
}
void init()
{
InitializeCriticalSection(&g_cs);
update();
updatePalette();
}
bool update()
{
Compat::LogEnter("VirtualScreen::update");
Compat::ScopedCriticalSection lock(g_cs);
static auto prevDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
if (currentDisplaySettingsUniqueness == prevDisplaySettingsUniqueness)
{
Compat::LogLeave("VirtualScreen::update") << false;
return false;
}
prevDisplaySettingsUniqueness = currentDisplaySettingsUniqueness;
g_region = Region();
EnumDisplayMonitors(nullptr, nullptr, addMonitorRectToRegion, reinterpret_cast<LPARAM>(&g_region));
GetRgnBox(g_region, &g_bounds);
g_bpp = Win32::DisplayMode::getBpp();
g_width = g_bounds.right - g_bounds.left;
g_height = g_bounds.bottom - g_bounds.top;
g_pitch = (g_width * g_bpp / 8 + 3) & ~3;
if (g_surfaceFileMapping)
{
for (HDC dc : g_dcs)
{
DeleteObject(SelectObject(dc, g_stockBitmap));
}
UnmapViewOfFile(g_surfaceView);
CloseHandle(g_surfaceFileMapping);
}
g_surfaceFileMapping = CreateFileMapping(
INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, g_pitch * g_height, nullptr);
g_surfaceView = MapViewOfFile(g_surfaceFileMapping, FILE_MAP_WRITE, 0, 0, 0);
for (HDC dc : g_dcs)
{
SelectObject(dc, createDib());
}
Gdi::redraw(nullptr);
Compat::LogLeave("VirtualScreen::update") << true;
return true;
}
void updatePalette()
{
Compat::ScopedCriticalSection lock(g_cs);
if (8 != g_bpp)
{
return;
}
PALETTEENTRY pal[256] = {};
GetSystemPaletteEntries(nullptr, 0, 256, pal);
RGBQUAD systemPalette[256] = {};
for (int i = 0; i < 256; ++i)
{
systemPalette[i].rgbRed = pal[i].peRed;
systemPalette[i].rgbGreen = pal[i].peGreen;
systemPalette[i].rgbBlue = pal[i].peBlue;
}
if (0 != memcmp(g_systemPalette, systemPalette, sizeof(systemPalette)))
{
memcpy(g_systemPalette, systemPalette, sizeof(systemPalette));
for (HDC dc : g_dcs)
{
SetDIBColorTable(dc, 0, 256, systemPalette);
}
}
}
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "Common/CompatPtr.h"
namespace Gdi
{
class Region;
namespace VirtualScreen
{
HDC createDc();
HBITMAP createDib();
CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect);
void deleteDc(HDC dc);
RECT getBounds();
const Region& getRegion();
void init();
bool update();
void updatePalette();
}
}

View File

@ -9,6 +9,7 @@
#include "Common/Log.h"
#include "Common/ScopedCriticalSection.h"
#include "Gdi/AccessGuard.h"
#include "Gdi/Dc.h"
#include "Gdi/ScrollBar.h"
#include "Gdi/ScrollFunctions.h"
@ -94,6 +95,12 @@ namespace
return TRUE;
}
bool isTopLevelNonLayeredWindow(HWND hwnd)
{
return !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) &&
(!(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow());
}
void CALLBACK objectStateChangeEvent(
HWINEVENTHOOK /*hWinEventHook*/,
DWORD /*event*/,
@ -105,7 +112,7 @@ namespace
{
if (OBJID_TITLEBAR == idObject || OBJID_HSCROLL == idObject || OBJID_VSCROLL == idObject)
{
if (!hwnd || !Gdi::beginGdiRendering())
if (!hwnd)
{
return;
}
@ -114,6 +121,7 @@ namespace
HDC compatDc = Gdi::Dc::getDc(windowDc);
if (compatDc)
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
if (OBJID_TITLEBAR == idObject)
{
Gdi::TitleBar(hwnd, compatDc).drawButtons();
@ -128,9 +136,7 @@ namespace
}
Gdi::Dc::releaseDc(windowDc);
}
ReleaseDC(hwnd, windowDc);
Gdi::endGdiRendering();
}
}
@ -154,22 +160,17 @@ namespace
void onCreateWindow(HWND hwnd)
{
if (Gdi::isTopLevelWindow(hwnd))
if (isTopLevelNonLayeredWindow(hwnd))
{
disableDwmAttributes(hwnd);
removeDropShadow(hwnd);
Compat::ScopedCriticalSection lock(Gdi::g_gdiCriticalSection);
Gdi::Window::add(hwnd);
}
}
void onDestroyWindow(HWND hwnd)
{
if (Gdi::isTopLevelWindow(hwnd))
{
Compat::ScopedCriticalSection lock(Gdi::g_gdiCriticalSection);
Gdi::Window::remove(hwnd);
}
Gdi::Window::remove(hwnd);
}
void onMenuSelect()
@ -190,19 +191,15 @@ namespace
SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
}
if (!Gdi::isTopLevelWindow(hwnd))
{
return;
}
Compat::ScopedCriticalSection lock(Gdi::g_gdiCriticalSection);
for (auto notifyFunc : g_windowPosChangeNotifyFuncs)
{
notifyFunc();
}
Gdi::Window::updateAll();
if (isTopLevelNonLayeredWindow(hwnd))
{
Gdi::Window::updateAll();
}
}
void removeDropShadow(HWND hwnd)

View File

@ -1,6 +1,34 @@
#include "Gdi/Gdi.h"
#include "Gdi/VirtualScreen.h"
#include "Gdi/Window.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace
{
ATOM registerPresentationWindowClass();
ATOM getPresentationWindowClassAtom()
{
static ATOM atom = registerPresentationWindowClass();
return atom;
}
LRESULT CALLBACK presentationWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return CALL_ORIG_FUNC(DefWindowProc)(hwnd, uMsg, wParam, lParam);
}
ATOM registerPresentationWindowClass()
{
WNDCLASS wc = {};
wc.lpfnWndProc = &presentationWindowProc;
wc.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
wc.lpszClassName = "DDrawCompatPresentationWindow";
return RegisterClass(&wc);
}
}
namespace Gdi
{
Window::Window(HWND hwnd)
@ -8,18 +36,34 @@ namespace Gdi
, m_windowRect{ 0, 0, 0, 0 }
, m_isUpdating(false)
{
m_presentationWindow = CreateWindowEx(
WS_EX_LAYERED | WS_EX_TRANSPARENT,
reinterpret_cast<const char*>(getPresentationWindowClassAtom()),
nullptr,
WS_DISABLED | WS_POPUP,
0, 0, 1, 1,
m_hwnd,
nullptr,
nullptr,
nullptr);
SetLayeredWindowAttributes(m_presentationWindow, 0, 255, LWA_ALPHA);
update();
}
Window& Window::add(HWND hwnd)
Window* Window::add(HWND hwnd)
{
auto it = s_windows.find(hwnd);
if (it != s_windows.end())
{
return it->second;
return &it->second;
}
return s_windows.emplace(hwnd, hwnd).first->second;
if (isPresentationWindow(hwnd))
{
return nullptr;
}
return &s_windows.emplace(hwnd, hwnd).first->second;
}
void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion)
@ -41,21 +85,22 @@ namespace Gdi
if (!preservedRegion.isEmpty())
{
HDC screenDc = GetDC(nullptr);
HDC screenDc = Gdi::getScreenDc();
SelectClipRgn(screenDc, preservedRegion);
BitBlt(screenDc, m_windowRect.left, m_windowRect.top,
oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top,
screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY);
ReleaseDC(nullptr, screenDc);
SelectClipRgn(screenDc, nullptr);
m_invalidatedRegion -= preservedRegion;
}
}
}
Window& Window::get(HWND hwnd)
Window* Window::get(HWND hwnd)
{
return add(hwnd);
auto it = s_windows.find(hwnd);
return it != s_windows.end() ? &it->second : nullptr;
}
Region Window::getVisibleRegion() const
@ -68,6 +113,11 @@ namespace Gdi
return m_windowRect;
}
bool Window::isPresentationWindow(HWND hwnd)
{
return GetClassLong(hwnd, GCW_ATOM) == getPresentationWindowClassAtom();
}
void Window::remove(HWND hwnd)
{
s_windows.erase(hwnd);
@ -92,8 +142,12 @@ namespace Gdi
HDC windowDc = GetWindowDC(m_hwnd);
GetRandomRgn(windowDc, newVisibleRegion, SYSRGN);
ReleaseDC(m_hwnd, windowDc);
newVisibleRegion &= getVirtualScreenRegion();
newVisibleRegion &= VirtualScreen::getRegion();
}
SetWindowPos(m_presentationWindow, nullptr, newWindowRect.left, newWindowRect.top,
newWindowRect.right - newWindowRect.left, newWindowRect.bottom - newWindowRect.top,
SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW);
}
std::swap(m_windowRect, newWindowRect);

View File

@ -20,9 +20,11 @@ namespace Gdi
Region getVisibleRegion() const;
RECT getWindowRect() const;
static Window& add(HWND hwnd);
static Window& get(HWND hwnd);
static Window* add(HWND hwnd);
static Window* get(HWND hwnd);
static void remove(HWND hwnd);
static bool isPresentationWindow(HWND hwnd);
static void updateAll();
private:
@ -30,6 +32,7 @@ namespace Gdi
void update();
HWND m_hwnd;
HWND m_presentationWindow;
RECT m_windowRect;
Region m_visibleRegion;
Region m_invalidatedRegion;

View File

@ -372,6 +372,11 @@ namespace Win32
return result;
}
DWORD getBpp()
{
return g_currentBpp;
}
ULONG queryDisplaySettingsUniqueness()
{
static auto ddQueryDisplaySettingsUniqueness = reinterpret_cast<ULONG(APIENTRY*)()>(

View File

@ -13,6 +13,7 @@ namespace Win32
const void* lpbInit, const BITMAPINFO* lpbmi, UINT fuUsage);
HBITMAP WINAPI createDiscardableBitmap(HDC hdc, int nWidth, int nHeight);
DWORD getBpp();
ULONG queryDisplaySettingsUniqueness();
void setDDrawBpp(DWORD bpp);

View File

@ -52,8 +52,7 @@ namespace Win32
reinterpret_cast<void*>(settings.orientation), 0);
const char* regKey = "FontSmoothing";
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETFONTSMOOTHING,
reinterpret_cast<LPARAM>(regKey), SMTO_BLOCK, 100, nullptr);
PostMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETFONTSMOOTHING, reinterpret_cast<LPARAM>(regKey));
RedrawWindow(nullptr, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
}
}