From 6183aed7da6baf3ecb72c77e283c1b1037ba0ddc Mon Sep 17 00:00:00 2001 From: narzoul Date: Thu, 7 Jun 2018 23:07:58 +0200 Subject: [PATCH] Optimize GDI redraw on window position changes --- DDrawCompat/DDraw/DirectDrawClipper.cpp | 7 +- DDrawCompat/DDrawCompat.vcxproj | 4 + DDrawCompat/DDrawCompat.vcxproj.filters | 12 ++ DDrawCompat/Gdi/Dc.cpp | 23 +++- DDrawCompat/Gdi/DcFunctions.cpp | 2 +- DDrawCompat/Gdi/Gdi.cpp | 27 ++--- DDrawCompat/Gdi/Gdi.h | 2 +- DDrawCompat/Gdi/Region.cpp | 148 ++++++++++++++++++++++++ DDrawCompat/Gdi/Region.h | 40 +++++++ DDrawCompat/Gdi/WinProc.cpp | 123 ++++++-------------- DDrawCompat/Gdi/Window.cpp | 128 ++++++++++++++++++++ DDrawCompat/Gdi/Window.h | 40 +++++++ DDrawCompat/Win32/DisplayMode.cpp | 7 ++ DDrawCompat/Win32/DisplayMode.h | 1 + 14 files changed, 456 insertions(+), 108 deletions(-) create mode 100644 DDrawCompat/Gdi/Region.cpp create mode 100644 DDrawCompat/Gdi/Region.h create mode 100644 DDrawCompat/Gdi/Window.cpp create mode 100644 DDrawCompat/Gdi/Window.h diff --git a/DDrawCompat/DDraw/DirectDrawClipper.cpp b/DDrawCompat/DDraw/DirectDrawClipper.cpp index c240329..bb0a3c6 100644 --- a/DDrawCompat/DDraw/DirectDrawClipper.cpp +++ b/DDrawCompat/DDraw/DirectDrawClipper.cpp @@ -17,14 +17,11 @@ namespace void updateWindowClipList(CompatRef clipper, ClipperData& data); - void onWindowPosChange(HWND /*hwnd*/, const RECT& oldWindowRect, const RECT& newWindowRect) + void onWindowPosChange() { for (auto& clipperData : g_clipperData) { - if (!IsRectEmpty(&oldWindowRect) || !IsRectEmpty(&newWindowRect)) - { - updateWindowClipList(*clipperData.first, clipperData.second); - } + updateWindowClipList(*clipperData.first, clipperData.second); } } diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 6a76fed..3f4957f 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -221,9 +221,11 @@ + + @@ -278,9 +280,11 @@ + + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 60f26de..45fd920 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -315,6 +315,12 @@ Header Files\D3dDdi + + Header Files\Gdi + + + Header Files\Gdi + @@ -482,6 +488,12 @@ Source Files\D3dDdi + + Source Files\Gdi + + + Source Files\Gdi + diff --git a/DDrawCompat/Gdi/Dc.cpp b/DDrawCompat/Gdi/Dc.cpp index 00345cf..459458c 100644 --- a/DDrawCompat/Gdi/Dc.cpp +++ b/DDrawCompat/Gdi/Dc.cpp @@ -7,6 +7,7 @@ #include "Gdi/Dc.h" #include "Gdi/DcCache.h" #include "Gdi/Gdi.h" +#include "Gdi/Window.h" namespace { @@ -95,7 +96,20 @@ namespace SelectClipRgn(compatDc, clipRgn); } DeleteObject(clipRgn); + } + void updateWindow(HWND wnd) + { + RECT windowRect = {}; + GetWindowRect(wnd, &windowRect); + + auto& window = Gdi::Window::get(wnd); + RECT cachedWindowRect = window.getWindowRect(); + + if (!EqualRect(&windowRect, &cachedWindowRect)) + { + Gdi::Window::updateAll(); + } } } @@ -119,6 +133,13 @@ namespace Gdi return it->second.dc; } + const HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(origDc); + const HWND rootWnd = wnd ? GetAncestor(wnd, GA_ROOT) : nullptr; + if (rootWnd && GetDesktopWindow() != rootWnd) + { + updateWindow(rootWnd); + } + CompatDc compatDc(Gdi::DcCache::getDc()); if (!compatDc.dc) { @@ -130,7 +151,7 @@ namespace Gdi compatDc.savedState = SaveDC(compatDc.dc); copyDcAttributes(compatDc, origDc, origin); - setClippingRegion(compatDc.dc, origDc, CALL_ORIG_FUNC(WindowFromDC)(origDc), origin); + setClippingRegion(compatDc.dc, origDc, wnd, origin); compatDc.refCount = 1; compatDc.origDc = origDc; diff --git a/DDrawCompat/Gdi/DcFunctions.cpp b/DDrawCompat/Gdi/DcFunctions.cpp index b777784..7828dc9 100644 --- a/DDrawCompat/Gdi/DcFunctions.cpp +++ b/DDrawCompat/Gdi/DcFunctions.cpp @@ -242,7 +242,7 @@ namespace } HWND hwnd = WindowFromDC(hdc); - if (!hwnd || (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) + if (!hwnd || hwnd == GetDesktopWindow() || (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) { return 1; } diff --git a/DDrawCompat/Gdi/Gdi.cpp b/DDrawCompat/Gdi/Gdi.cpp index 6248899..32d7e9b 100644 --- a/DDrawCompat/Gdi/Gdi.cpp +++ b/DDrawCompat/Gdi/Gdi.cpp @@ -195,24 +195,25 @@ namespace Gdi void redrawWindow(HWND hwnd, HRGN rgn) { - if (!IsWindowVisible(hwnd)) + if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) { return; } - if (!rgn) - { - RedrawWindow(hwnd, nullptr, nullptr, - RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); - return; - } - POINT origin = {}; - ClientToScreen(hwnd, &origin); - OffsetRgn(rgn, -origin.x, -origin.y); - RedrawWindow(hwnd, nullptr, rgn, - RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); - OffsetRgn(rgn, origin.x, origin.y); + if (rgn) + { + ClientToScreen(hwnd, &origin); + OffsetRgn(rgn, -origin.x, -origin.y); + } + + RedrawWindow(hwnd, nullptr, rgn, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); + RedrawWindow(hwnd, nullptr, rgn, RDW_ERASENOW); + + if (rgn) + { + OffsetRgn(rgn, origin.x, origin.y); + } } void unhookWndProc(LPCSTR className, WNDPROC oldWndProc) diff --git a/DDrawCompat/Gdi/Gdi.h b/DDrawCompat/Gdi/Gdi.h index bf5ff47..4cae620 100644 --- a/DDrawCompat/Gdi/Gdi.h +++ b/DDrawCompat/Gdi/Gdi.h @@ -6,7 +6,7 @@ namespace Gdi { - typedef void(*WindowPosChangeNotifyFunc)(HWND, const RECT&, const RECT&); + typedef void(*WindowPosChangeNotifyFunc)(); bool beginGdiRendering(DWORD lockFlags = 0); void endGdiRendering(); diff --git a/DDrawCompat/Gdi/Region.cpp b/DDrawCompat/Gdi/Region.cpp new file mode 100644 index 0000000..e355a1e --- /dev/null +++ b/DDrawCompat/Gdi/Region.cpp @@ -0,0 +1,148 @@ +#include + +#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(dwData); + Gdi::Region monitorRegion(*lprcMonitor); + virtualScreenRegion |= monitorRegion; + return TRUE; + } + + Gdi::Region calculateVirtualScreenRegion() + { + Gdi::Region region; + EnumDisplayMonitors(nullptr, nullptr, addMonitorRectToRegion, reinterpret_cast(®ion)); + return region; + } + + Gdi::Region combineRegions(const Gdi::Region& rgn1, const Gdi::Region& rgn2, int mode) + { + Gdi::Region region; + CombineRgn(region, rgn1, rgn2, mode); + return region; + } +} + +namespace Gdi +{ + Region::Region(const RECT& rect) + : m_region(CreateRectRgnIndirect(&rect)) + { + } + + Region::~Region() + { + if (m_region) + { + DeleteObject(m_region); + } + } + + Region::Region(const Region& other) + : Region() + { + CombineRgn(m_region, other, nullptr, RGN_COPY); + } + + Region::Region(Region&& other) + : m_region(other.m_region) + { + other.m_region = nullptr; + } + + Region& Region::operator=(Region other) + { + swap(*this, other); + return *this; + } + + bool Region::isEmpty() const + { + return sizeof(RGNDATAHEADER) == GetRegionData(m_region, 0, nullptr); + } + + void Region::offset(int x, int y) + { + OffsetRgn(m_region, x, y); + } + + Region::operator HRGN() const + { + return m_region; + } + + Region Region::operator&(const Region& other) const + { + return combineRegions(*this, other, RGN_AND); + } + + Region Region::operator|(const Region& other) const + { + return combineRegions(*this, other, RGN_OR); + } + + Region Region::operator-(const Region& other) const + { + return combineRegions(*this, other, RGN_DIFF); + } + + Region Region::operator&=(const Region& other) + { + return combine(other, RGN_AND); + } + + Region Region::operator|=(const Region& other) + { + return combine(other, RGN_OR); + } + + Region Region::operator-=(const Region& other) + { + return combine(other, RGN_DIFF); + } + + void swap(Region& rgn1, Region& rgn2) + { + std::swap(rgn1.m_region, rgn2.m_region); + } + + Region operator&(const Region& rgn1, const Region& rgn2) + { + return combineRegions(rgn1, rgn2, RGN_AND); + } + + Region operator|(const Region& rgn1, const Region& rgn2) + { + return combineRegions(rgn1, rgn2, RGN_OR); + } + + Region operator-(const Region& rgn1, const Region& rgn2) + { + return combineRegions(rgn1, rgn2, RGN_DIFF); + } + + Region& Region::combine(const Region& other, int mode) + { + 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; + } +} diff --git a/DDrawCompat/Gdi/Region.h b/DDrawCompat/Gdi/Region.h new file mode 100644 index 0000000..9fa4bea --- /dev/null +++ b/DDrawCompat/Gdi/Region.h @@ -0,0 +1,40 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include + +namespace Gdi +{ + class Region + { + public: + Region(const RECT& rect = RECT{ 0, 0, 0, 0 }); + ~Region(); + Region(const Region& other); + Region(Region&& other); + Region& operator=(Region other); + + bool isEmpty() const; + void offset(int x, int y); + + operator HRGN() const; + + Region operator&(const Region& other) const; + Region operator|(const Region& other) const; + Region operator-(const Region& other) const; + + Region operator&=(const Region& other); + Region operator|=(const Region& other); + Region operator-=(const Region& other); + + friend void swap(Region& rgn1, Region& rgn2); + + private: + Region& combine(const Region& other, int mode); + + HRGN m_region; + }; + + const Region& getVirtualScreenRegion(); +} diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index a1e258e..7ed7762 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -1,8 +1,8 @@ #define WIN32_LEAN_AND_MEAN +#include #include #include -#include #include #include @@ -13,29 +13,22 @@ #include "Gdi/ScrollBar.h" #include "Gdi/ScrollFunctions.h" #include "Gdi/TitleBar.h" +#include "Gdi/Window.h" #include "Gdi/WinProc.h" namespace { - struct WindowData - { - RECT wndRect; - std::shared_ptr sysClipRgn; - }; - HHOOK g_callWndRetProcHook = nullptr; HWINEVENTHOOK g_objectStateChangeEventHook = nullptr; - std::unordered_map g_windowData; std::set g_windowPosChangeNotifyFuncs; void disableDwmAttributes(HWND hwnd); void onActivate(HWND hwnd); + void onCreateWindow(HWND hwnd); + void onDestroyWindow(HWND hwnd); void onMenuSelect(); void onWindowPosChanged(HWND hwnd); - void redrawChangedWindowRegion(HWND hwnd, const WindowData& prevData, const WindowData& data); - void redrawUncoveredRegion(const WindowData& prevData, const WindowData& data); void removeDropShadow(HWND hwnd); - BOOL CALLBACK updateWindowData(HWND hwnd, LPARAM lParam); LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam) { @@ -46,13 +39,11 @@ namespace { if (WM_CREATE == ret->message) { - disableDwmAttributes(ret->hwnd); - removeDropShadow(ret->hwnd); + onCreateWindow(ret->hwnd); } else if (WM_DESTROY == ret->message) { - Compat::ScopedCriticalSection lock(Gdi::g_gdiCriticalSection); - g_windowData.erase(ret->hwnd); + onDestroyWindow(ret->hwnd); } else if (WM_WINDOWPOSCHANGED == ret->message) { @@ -92,18 +83,15 @@ namespace &disableTransitions, sizeof(disableTransitions)); } - WindowData getWindowData(HWND hwnd) + BOOL CALLBACK initTopLevelWindow(HWND hwnd, LPARAM /*lParam*/) { - WindowData data; - if (IsWindowVisible(hwnd) && !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) + onCreateWindow(hwnd); + if (!(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) { - GetWindowRect(hwnd, &data.wndRect); - data.sysClipRgn.reset(CreateRectRgnIndirect(&data.wndRect), DeleteObject); - HDC dc = GetWindowDC(hwnd); - GetRandomRgn(dc, data.sysClipRgn.get(), SYSRGN); - ReleaseDC(hwnd, dc); + RedrawWindow(hwnd, nullptr, nullptr, + RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_UPDATENOW); } - return data; + return TRUE; } void CALLBACK objectStateChangeEvent( @@ -164,6 +152,26 @@ namespace DeleteObject(ncRgn); } + void onCreateWindow(HWND hwnd) + { + if (Gdi::isTopLevelWindow(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); + } + } + void onMenuSelect() { HWND menuWindow = FindWindow(reinterpret_cast(0x8000), nullptr); @@ -189,67 +197,12 @@ namespace Compat::ScopedCriticalSection lock(Gdi::g_gdiCriticalSection); - WindowData prevData = g_windowData[hwnd]; - EnumThreadWindows(Gdi::getGdiThreadId(), updateWindowData, 0); - WindowData& data = g_windowData[hwnd]; - for (auto notifyFunc : g_windowPosChangeNotifyFuncs) { - notifyFunc(hwnd, prevData.wndRect, data.wndRect); + notifyFunc(); } - if (!prevData.sysClipRgn && !data.sysClipRgn) - { - return; - } - - redrawUncoveredRegion(prevData, data); - redrawChangedWindowRegion(hwnd, prevData, data); - } - - void redrawChangedWindowRegion(HWND hwnd, const WindowData& prevData, const WindowData& data) - { - if (!data.sysClipRgn) - { - return; - } - - if (!prevData.sysClipRgn) - { - Gdi::redrawWindow(hwnd, data.sysClipRgn.get()); - return; - } - - if (EqualRect(&prevData.wndRect, &data.wndRect)) - { - HRGN rgn = CreateRectRgn(0, 0, 0, 0); - CombineRgn(rgn, data.sysClipRgn.get(), prevData.sysClipRgn.get(), RGN_DIFF); - Gdi::redrawWindow(hwnd, rgn); - DeleteObject(rgn); - } - else - { - Gdi::redrawWindow(hwnd, data.sysClipRgn.get()); - } - } - - void redrawUncoveredRegion(const WindowData& prevData, const WindowData& data) - { - if (!prevData.sysClipRgn) - { - return; - } - - if (!data.sysClipRgn) - { - Gdi::redraw(prevData.sysClipRgn.get()); - return; - } - - HRGN rgn = CreateRectRgn(0, 0, 0, 0); - CombineRgn(rgn, prevData.sysClipRgn.get(), data.sysClipRgn.get(), RGN_DIFF); - Gdi::redraw(rgn); - DeleteObject(rgn); + Gdi::Window::updateAll(); } void removeDropShadow(HWND hwnd) @@ -260,12 +213,6 @@ namespace SetClassLongPtr(hwnd, GCL_STYLE, style ^ CS_DROPSHADOW); } } - - BOOL CALLBACK updateWindowData(HWND hwnd, LPARAM /*lParam*/) - { - g_windowData[hwnd] = getWindowData(hwnd); - return TRUE; - } } namespace Gdi @@ -278,6 +225,8 @@ namespace Gdi g_callWndRetProcHook = SetWindowsHookEx(WH_CALLWNDPROCRET, callWndRetProc, nullptr, threadId); g_objectStateChangeEventHook = SetWinEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE, nullptr, &objectStateChangeEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); + + EnumThreadWindows(threadId, initTopLevelWindow, 0); } void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc) diff --git a/DDrawCompat/Gdi/Window.cpp b/DDrawCompat/Gdi/Window.cpp new file mode 100644 index 0000000..020a45e --- /dev/null +++ b/DDrawCompat/Gdi/Window.cpp @@ -0,0 +1,128 @@ +#include "Gdi/Gdi.h" +#include "Gdi/Window.h" + +namespace Gdi +{ + Window::Window(HWND hwnd) + : m_hwnd(hwnd) + , m_windowRect{ 0, 0, 0, 0 } + , m_isUpdating(false) + { + update(); + } + + Window& Window::add(HWND hwnd) + { + auto it = s_windows.find(hwnd); + if (it != s_windows.end()) + { + return it->second; + } + + return s_windows.emplace(hwnd, hwnd).first->second; + } + + void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion) + { + if (IsRectEmpty(&m_windowRect) || m_visibleRegion.isEmpty()) + { + m_invalidatedRegion = Region(); + return; + } + + m_invalidatedRegion = m_visibleRegion; + + if (m_windowRect.right - m_windowRect.left == oldWindowRect.right - oldWindowRect.left && + m_windowRect.bottom - m_windowRect.top == oldWindowRect.bottom - oldWindowRect.top) + { + Region preservedRegion(oldVisibleRegion); + preservedRegion.offset(m_windowRect.left - oldWindowRect.left, m_windowRect.top - oldWindowRect.top); + preservedRegion &= m_visibleRegion; + + if (!preservedRegion.isEmpty()) + { + HDC screenDc = GetDC(nullptr); + 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); + + m_invalidatedRegion -= preservedRegion; + } + } + } + + Window& Window::get(HWND hwnd) + { + return add(hwnd); + } + + Region Window::getVisibleRegion() const + { + return m_visibleRegion; + } + + RECT Window::getWindowRect() const + { + return m_windowRect; + } + + void Window::remove(HWND hwnd) + { + s_windows.erase(hwnd); + } + + void Window::update() + { + if (m_isUpdating) + { + return; + } + m_isUpdating = true; + + RECT newWindowRect = {}; + Region newVisibleRegion; + + if (IsWindowVisible(m_hwnd) && !IsIconic(m_hwnd)) + { + GetWindowRect(m_hwnd, &newWindowRect); + if (!IsRectEmpty(&newWindowRect)) + { + HDC windowDc = GetWindowDC(m_hwnd); + GetRandomRgn(windowDc, newVisibleRegion, SYSRGN); + ReleaseDC(m_hwnd, windowDc); + newVisibleRegion &= getVirtualScreenRegion(); + } + } + + std::swap(m_windowRect, newWindowRect); + swap(m_visibleRegion, newVisibleRegion); + + calcInvalidatedRegion(newWindowRect, newVisibleRegion); + + m_isUpdating = false; + } + + void Window::updateAll() + { + for (auto& windowPair : s_windows) + { + windowPair.second.update(); + } + + for (auto& windowPair : s_windows) + { + if (!windowPair.second.m_invalidatedRegion.isEmpty()) + { + POINT clientOrigin = {}; + ClientToScreen(windowPair.first, &clientOrigin); + windowPair.second.m_invalidatedRegion.offset(-clientOrigin.x, -clientOrigin.y); + RedrawWindow(windowPair.first, nullptr, windowPair.second.m_invalidatedRegion, + RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASENOW); + } + } + } + + std::map Window::s_windows; +} diff --git a/DDrawCompat/Gdi/Window.h b/DDrawCompat/Gdi/Window.h new file mode 100644 index 0000000..180b673 --- /dev/null +++ b/DDrawCompat/Gdi/Window.h @@ -0,0 +1,40 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include + +#include + +#include "Gdi/Region.h" + +namespace Gdi +{ + class Window + { + public: + Window(HWND hwnd); + Window(const Window&) = delete; + Window& operator=(const Window&) = delete; + + Region getVisibleRegion() const; + RECT getWindowRect() const; + + static Window& add(HWND hwnd); + static Window& get(HWND hwnd); + static void remove(HWND hwnd); + static void updateAll(); + + private: + void calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion); + void update(); + + HWND m_hwnd; + RECT m_windowRect; + Region m_visibleRegion; + Region m_invalidatedRegion; + bool m_isUpdating; + + static std::map s_windows; + }; +} diff --git a/DDrawCompat/Win32/DisplayMode.cpp b/DDrawCompat/Win32/DisplayMode.cpp index e8afbb0..45694ff 100644 --- a/DDrawCompat/Win32/DisplayMode.cpp +++ b/DDrawCompat/Win32/DisplayMode.cpp @@ -372,6 +372,13 @@ namespace Win32 return result; } + ULONG queryDisplaySettingsUniqueness() + { + static auto ddQueryDisplaySettingsUniqueness = reinterpret_cast( + GetProcAddress(GetModuleHandle("gdi32"), "GdiEntry13")); + return ddQueryDisplaySettingsUniqueness(); + } + void setDDrawBpp(DWORD bpp) { g_ddrawBpp = bpp; diff --git a/DDrawCompat/Win32/DisplayMode.h b/DDrawCompat/Win32/DisplayMode.h index 5ea21a3..6f4d39b 100644 --- a/DDrawCompat/Win32/DisplayMode.h +++ b/DDrawCompat/Win32/DisplayMode.h @@ -13,6 +13,7 @@ namespace Win32 const void* lpbInit, const BITMAPINFO* lpbmi, UINT fuUsage); HBITMAP WINAPI createDiscardableBitmap(HDC hdc, int nWidth, int nHeight); + ULONG queryDisplaySettingsUniqueness(); void setDDrawBpp(DWORD bpp); void disableDwm8And16BitMitigation();