From 647a4bfcff7bcc8acd60cbfdcc0a731b7ba79027 Mon Sep 17 00:00:00 2001 From: narzoul Date: Sat, 12 Jun 2021 20:49:36 +0200 Subject: [PATCH] Added DisplayResolution setting --- DDrawCompat/Common/Comparison.h | 10 + DDrawCompat/Config/Config.cpp | 1 + DDrawCompat/Config/Config.h | 2 + DDrawCompat/Config/EnumSetting.cpp | 41 +- DDrawCompat/Config/EnumSetting.h | 16 +- DDrawCompat/Config/ListSetting.h | 2 +- DDrawCompat/Config/MappedSetting.h | 56 ++ DDrawCompat/Config/Parser.cpp | 24 + DDrawCompat/Config/Parser.h | 3 + DDrawCompat/Config/Setting.h | 3 +- .../Config/Settings/DisplayResolution.cpp | 39 ++ .../Config/Settings/DisplayResolution.h | 25 + DDrawCompat/D3dDdi/KernelModeThunks.cpp | 60 +- DDrawCompat/D3dDdi/KernelModeThunks.h | 3 +- DDrawCompat/D3dDdi/Resource.cpp | 168 ++++-- DDrawCompat/D3dDdi/Resource.h | 4 +- DDrawCompat/D3dDdi/ShaderBlitter.cpp | 85 +-- DDrawCompat/D3dDdi/ShaderBlitter.h | 5 +- DDrawCompat/D3dDdi/SurfaceRepository.cpp | 98 ++++ DDrawCompat/D3dDdi/SurfaceRepository.h | 36 ++ DDrawCompat/DDraw/DirectDrawClipper.cpp | 3 +- DDrawCompat/DDraw/RealPrimarySurface.cpp | 14 +- DDrawCompat/DDraw/RealPrimarySurface.h | 1 + DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp | 16 +- DDrawCompat/DDraw/Surfaces/PrimarySurface.h | 1 + .../DDraw/Surfaces/PrimarySurfaceImpl.cpp | 2 +- DDrawCompat/DDrawCompat.vcxproj | 5 + DDrawCompat/DDrawCompat.vcxproj.filters | 15 + DDrawCompat/Gdi/Metrics.cpp | 38 +- DDrawCompat/Gdi/WinProc.cpp | 25 + DDrawCompat/Win32/DisplayMode.cpp | 534 +++++++++++++++--- DDrawCompat/Win32/DisplayMode.h | 27 + DDrawCompat/Win32/Log.cpp | 2 +- 33 files changed, 1059 insertions(+), 305 deletions(-) create mode 100644 DDrawCompat/Config/MappedSetting.h create mode 100644 DDrawCompat/Config/Settings/DisplayResolution.cpp create mode 100644 DDrawCompat/Config/Settings/DisplayResolution.h create mode 100644 DDrawCompat/D3dDdi/SurfaceRepository.cpp create mode 100644 DDrawCompat/D3dDdi/SurfaceRepository.h diff --git a/DDrawCompat/Common/Comparison.h b/DDrawCompat/Common/Comparison.h index d5749cc..e58b92c 100644 --- a/DDrawCompat/Common/Comparison.h +++ b/DDrawCompat/Common/Comparison.h @@ -21,3 +21,13 @@ inline auto toTuple(const LUID& luid) { return std::make_tuple(luid.LowPart, luid.HighPart); } + +inline auto toTuple(const POINT& pt) +{ + return std::make_tuple(pt.x, pt.y); +} + +inline auto toTuple(const SIZE& size) +{ + return std::make_tuple(size.cx, size.cy); +} diff --git a/DDrawCompat/Config/Config.cpp b/DDrawCompat/Config/Config.cpp index 2035994..fa98087 100644 --- a/DDrawCompat/Config/Config.cpp +++ b/DDrawCompat/Config/Config.cpp @@ -3,5 +3,6 @@ namespace Config { Settings::CpuAffinity cpuAffinity; + Settings::DisplayResolution displayResolution; Settings::ThreadPriorityBoost threadPriorityBoost; } diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index 4768580..ab1d135 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace Config @@ -10,5 +11,6 @@ namespace Config const unsigned maxPaletteUpdatesPerMs = 5; extern Settings::CpuAffinity cpuAffinity; + extern Settings::DisplayResolution displayResolution; extern Settings::ThreadPriorityBoost threadPriorityBoost; } diff --git a/DDrawCompat/Config/EnumSetting.cpp b/DDrawCompat/Config/EnumSetting.cpp index 92ae068..2546cf9 100644 --- a/DDrawCompat/Config/EnumSetting.cpp +++ b/DDrawCompat/Config/EnumSetting.cpp @@ -3,33 +3,26 @@ #include #include +namespace +{ + std::map createMapping(const std::vector& enumNames) + { + std::map mapping; + unsigned i = 0; + for (const auto& name : enumNames) + { + + mapping[name] = i; + ++i; + } + return mapping; + } +} + namespace Config { EnumSetting::EnumSetting(const std::string& name, unsigned default, const std::vector& enumNames) - : Setting(name) - , m_default(default) - , m_value(default) - , m_enumNames(enumNames) + : MappedSetting(name, default, createMapping(enumNames)) { } - - std::string EnumSetting::getValueStr() const - { - return m_enumNames[m_value]; - } - - void EnumSetting::resetValue() - { - m_value = m_default; - } - - void EnumSetting::setValue(const std::string& value) - { - auto it = std::find(m_enumNames.begin(), m_enumNames.end(), value); - if (it == m_enumNames.end()) - { - throw ParsingError("invalid value: '" + value + "'"); - } - m_value = it - m_enumNames.begin(); - } } diff --git a/DDrawCompat/Config/EnumSetting.h b/DDrawCompat/Config/EnumSetting.h index 2675dc4..525146a 100644 --- a/DDrawCompat/Config/EnumSetting.h +++ b/DDrawCompat/Config/EnumSetting.h @@ -2,25 +2,13 @@ #include -#include +#include namespace Config { - class EnumSetting : public Setting + class EnumSetting : public MappedSetting { - public: - unsigned get() const { return m_value; } - protected: EnumSetting(const std::string& name, unsigned default, const std::vector& enumNames); - - private: - std::string getValueStr() const override; - void resetValue() override; - void setValue(const std::string& value) override; - - unsigned m_default; - unsigned m_value; - const std::vector m_enumNames; }; } diff --git a/DDrawCompat/Config/ListSetting.h b/DDrawCompat/Config/ListSetting.h index 951c990..8036b2c 100644 --- a/DDrawCompat/Config/ListSetting.h +++ b/DDrawCompat/Config/ListSetting.h @@ -11,11 +11,11 @@ namespace Config protected: ListSetting(const std::string& name, const std::string& default); - private: void resetValue() override; void setValue(const std::string& value) override; virtual void setValues(const std::vector& values) = 0; + private: std::string m_default; }; } diff --git a/DDrawCompat/Config/MappedSetting.h b/DDrawCompat/Config/MappedSetting.h new file mode 100644 index 0000000..651430e --- /dev/null +++ b/DDrawCompat/Config/MappedSetting.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include +#include + +namespace Config +{ + template + class MappedSetting : public Setting + { + public: + Value get() const { return m_value; } + + protected: + MappedSetting(const std::string& name, Value default, const std::map& valueMapping) + : Setting(name) + , m_default(default) + , m_value(default) + , m_valueMapping(valueMapping) + { + } + + std::string getValueStr() const override + { + for (const auto& pair : m_valueMapping) + { + if (pair.second == m_value) + { + return pair.first; + } + } + throw ParsingError("MappedSetting::getValueStr(): value not found in mapping"); + } + + void resetValue() override + { + m_value = m_default; + } + + void setValue(const std::string& value) override + { + auto it = m_valueMapping.find(value); + if (it == m_valueMapping.end()) + { + throw ParsingError("invalid value: '" + value + "'"); + } + m_value = it->second; + } + + Value m_default; + Value m_value; + const std::map m_valueMapping; + }; +} diff --git a/DDrawCompat/Config/Parser.cpp b/DDrawCompat/Config/Parser.cpp index 1c49851..45dc03a 100644 --- a/DDrawCompat/Config/Parser.cpp +++ b/DDrawCompat/Config/Parser.cpp @@ -142,6 +142,30 @@ namespace Config } } + SIZE parseResolution(const std::string& value) + { + try + { + auto pos = value.find('x'); + if (pos != std::string::npos) + { + SIZE resolution = {}; + resolution.cx = parseUnsigned(value.substr(0, pos)); + resolution.cy = parseUnsigned(value.substr(pos + 1)); + if (0 != resolution.cx && resolution.cx <= 65535 && + 0 != resolution.cy && resolution.cy <= 65535) + { + return resolution; + } + } + } + catch (ParsingError&) + { + } + + throw ParsingError("invalid resolution: '" + value + "'"); + } + unsigned parseUnsigned(const std::string& value) { if (value.empty() || std::string::npos != value.find_first_not_of("0123456789")) diff --git a/DDrawCompat/Config/Parser.h b/DDrawCompat/Config/Parser.h index 68d880e..f76b4eb 100644 --- a/DDrawCompat/Config/Parser.h +++ b/DDrawCompat/Config/Parser.h @@ -4,6 +4,8 @@ #include #include +#include + namespace Config { class ParsingError : public std::runtime_error @@ -17,6 +19,7 @@ namespace Config namespace Parser { void loadAllConfigFiles(const std::filesystem::path& processPath); + SIZE parseResolution(const std::string& value); unsigned parseUnsigned(const std::string& value); void registerSetting(Setting& setting); std::string trim(const std::string& str); diff --git a/DDrawCompat/Config/Setting.h b/DDrawCompat/Config/Setting.h index a5c369b..4551a94 100644 --- a/DDrawCompat/Config/Setting.h +++ b/DDrawCompat/Config/Setting.h @@ -21,11 +21,12 @@ namespace Config void reset(); void set(const std::string& value, const std::string& source); - private: + protected: virtual std::string getValueStr() const = 0; virtual void resetValue() = 0; virtual void setValue(const std::string& value) = 0; + private: std::string m_name; std::string m_source; }; diff --git a/DDrawCompat/Config/Settings/DisplayResolution.cpp b/DDrawCompat/Config/Settings/DisplayResolution.cpp new file mode 100644 index 0000000..f71b4b6 --- /dev/null +++ b/DDrawCompat/Config/Settings/DisplayResolution.cpp @@ -0,0 +1,39 @@ +#include + +namespace Config +{ + namespace Settings + { + DisplayResolution::DisplayResolution() + : MappedSetting("DisplayResolution", DESKTOP, { {"app", APP}, {"desktop", DESKTOP} }) + { + } + + std::string DisplayResolution::getValueStr() const + { + try + { + return MappedSetting::getValueStr(); + } + catch (const ParsingError&) + { + return std::to_string(m_value.cx) + 'x' + std::to_string(m_value.cy); + } + } + + void DisplayResolution::setValue(const std::string& value) + { + try + { + MappedSetting::setValue(value); + } + catch (const ParsingError&) + { + m_value = Parser::parseResolution(value); + } + } + + const SIZE DisplayResolution::APP = { 0, 0 }; + const SIZE DisplayResolution::DESKTOP = { 1, 0 }; + } +} diff --git a/DDrawCompat/Config/Settings/DisplayResolution.h b/DDrawCompat/Config/Settings/DisplayResolution.h new file mode 100644 index 0000000..32761d2 --- /dev/null +++ b/DDrawCompat/Config/Settings/DisplayResolution.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include +#include + +namespace Config +{ + namespace Settings + { + class DisplayResolution : public MappedSetting + { + public: + static const SIZE APP; + static const SIZE DESKTOP; + + DisplayResolution(); + + protected: + std::string getValueStr() const override; + void setValue(const std::string& value) override; + }; + } +} diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.cpp b/DDrawCompat/D3dDdi/KernelModeThunks.cpp index c84c541..4cf5190 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.cpp +++ b/DDrawCompat/D3dDdi/KernelModeThunks.cpp @@ -23,7 +23,7 @@ namespace D3dDdi::KernelModeThunks::AdapterInfo g_gdiAdapterInfo = {}; D3dDdi::KernelModeThunks::AdapterInfo g_lastOpenAdapterInfo = {}; Compat::SrwLock g_lastOpenAdapterInfoSrwLock; - std::string g_lastDDrawCreateDcDevice; + std::string g_lastDDrawDeviceName; std::atomic g_qpcLastVsync = 0; UINT g_vsyncCounter = 0; @@ -104,49 +104,39 @@ namespace LOG_FUNC("ddrawCreateDCA", pwszDriver, pwszDevice, pszPort, pdm); if (pwszDevice) { - g_lastDDrawCreateDcDevice = pwszDevice; + g_lastDDrawDeviceName = pwszDevice; } else { MONITORINFOEXA mi = {}; mi.cbSize = sizeof(mi); CALL_ORIG_FUNC(GetMonitorInfoA)(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi); - g_lastDDrawCreateDcDevice = mi.szDevice; + g_lastDDrawDeviceName = mi.szDevice; } return LOG_RESULT(CreateDCA(pwszDriver, pwszDevice, pszPort, pdm)); } - BOOL CALLBACK findDDrawMonitorRect(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData) + BOOL CALLBACK findMonitorInfo(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData) { - MONITORINFOEX mi = {}; + MONITORINFOEXW mi = {}; mi.cbSize = sizeof(mi); - GetMonitorInfo(hMonitor, &mi); - if (g_lastDDrawCreateDcDevice == mi.szDevice) + CALL_ORIG_FUNC(GetMonitorInfoW)(hMonitor, &mi); + if (0 == wcscmp(reinterpret_cast(dwData)->szDevice, mi.szDevice)) { - *reinterpret_cast(dwData) = mi.rcMonitor; + *reinterpret_cast(dwData) = mi; return FALSE; } return TRUE; } - D3dDdi::KernelModeThunks::AdapterInfo getAdapterInfo(const D3DKMT_OPENADAPTERFROMHDC& data) + D3dDdi::KernelModeThunks::AdapterInfo getAdapterInfo(const std::string& deviceName, const D3DKMT_OPENADAPTERFROMHDC& data) { D3dDdi::KernelModeThunks::AdapterInfo adapterInfo = {}; adapterInfo.adapter = data.hAdapter; adapterInfo.vidPnSourceId = data.VidPnSourceId; adapterInfo.luid = data.AdapterLuid; - - EnumDisplayMonitors(nullptr, nullptr, findDDrawMonitorRect, - reinterpret_cast(&adapterInfo.monitorRect)); - - if (IsRectEmpty(&adapterInfo.monitorRect)) - { - MONITORINFO mi = {}; - mi.cbSize = sizeof(mi); - GetMonitorInfo(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi); - adapterInfo.monitorRect = mi.rcMonitor; - } - + wcscpy_s(adapterInfo.monitorInfo.szDevice, std::wstring(deviceName.begin(), deviceName.end()).c_str()); + EnumDisplayMonitors(nullptr, nullptr, findMonitorInfo, reinterpret_cast(&adapterInfo.monitorInfo)); return adapterInfo; } @@ -157,7 +147,7 @@ namespace if (SUCCEEDED(result)) { Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock); - g_lastOpenAdapterInfo = getAdapterInfo(*pData); + g_lastOpenAdapterInfo = getAdapterInfo(g_lastDDrawDeviceName, *pData); } return LOG_RESULT(result); } @@ -228,7 +218,7 @@ namespace data.hDc = CreateDC(mi.szDevice, mi.szDevice, nullptr, nullptr); if (SUCCEEDED(D3DKMTOpenAdapterFromHdc(&data))) { - g_gdiAdapterInfo = getAdapterInfo(data); + g_gdiAdapterInfo = getAdapterInfo(mi.szDevice, data); } DeleteDC(data.hDc); @@ -295,30 +285,6 @@ namespace D3dDdi return g_lastOpenAdapterInfo; } - RECT getMonitorRect() - { - auto primary(DDraw::PrimarySurface::getPrimary()); - if (!primary) - { - return {}; - } - - static auto lastDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1; - const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness(); - if (currentDisplaySettingsUniqueness != lastDisplaySettingsUniqueness) - { - lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness; - CompatPtr ddUnk; - primary->GetDDInterface(primary, reinterpret_cast(&ddUnk.getRef())); - CompatPtr dd7(ddUnk); - - DDDEVICEIDENTIFIER2 di = {}; - dd7.get()->lpVtbl->GetDeviceIdentifier(dd7, &di, 0); - } - - return getLastOpenAdapterInfo().monitorRect; - } - long long getQpcLastVsync() { return g_qpcLastVsync; diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.h b/DDrawCompat/D3dDdi/KernelModeThunks.h index 3e11721..072d4f1 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.h +++ b/DDrawCompat/D3dDdi/KernelModeThunks.h @@ -13,12 +13,11 @@ namespace D3dDdi UINT adapter; UINT vidPnSourceId; LUID luid; - RECT monitorRect; + MONITORINFOEXW monitorInfo; }; AdapterInfo getAdapterInfo(CompatRef dd); AdapterInfo getLastOpenAdapterInfo(); - RECT getMonitorRect(); long long getQpcLastVsync(); UINT getVsyncCounter(); void installHooks(); diff --git a/DDrawCompat/D3dDdi/Resource.cpp b/DDrawCompat/D3dDdi/Resource.cpp index a846af8..03765e4 100644 --- a/DDrawCompat/D3dDdi/Resource.cpp +++ b/DDrawCompat/D3dDdi/Resource.cpp @@ -9,46 +9,49 @@ #include #include #include +#include #include +#include +#include #include #include namespace { D3DDDI_RESOURCEFLAGS getResourceTypeFlags(); - void splitToTiles(D3DDDIARG_CREATERESOURCE& data, const UINT tileWidth, const UINT tileHeight); const UINT g_resourceTypeFlags = getResourceTypeFlags().Value; + RECT g_presentationRect = {}; + + RECT calculatePresentationRect() + { + const RECT srcRect = DDraw::PrimarySurface::getMonitorRect(); + const RECT dstRect = DDraw::RealPrimarySurface::getMonitorRect(); + + const int srcWidth = srcRect.right - srcRect.left; + const int srcHeight = srcRect.bottom - srcRect.top; + const int dstWidth = dstRect.right - dstRect.left; + const int dstHeight = dstRect.bottom - dstRect.top; + + RECT rect = { 0, 0, dstWidth, dstHeight }; + if (dstWidth * srcHeight > dstHeight * srcWidth) + { + rect.right = dstHeight * srcWidth / srcHeight; + } + else + { + rect.bottom = dstWidth * srcHeight / srcWidth; + } + + OffsetRect(&rect, (dstWidth - rect.right) / 2, (dstHeight - rect.bottom) / 2); + return rect; + } LONG divCeil(LONG n, LONG d) { return (n + d - 1) / d; } - void fixResourceData(D3dDdi::Device& device, D3DDDIARG_CREATERESOURCE& data) - { - if (data.Flags.Primary) - { - data.Format = D3DDDIFMT_X8R8G8B8; - } - - const bool isOffScreenPlain = 0 == (data.Flags.Value & g_resourceTypeFlags); - if (D3DDDIPOOL_SYSTEMMEM == data.Pool && - (isOffScreenPlain || data.Flags.Texture) && - 1 == data.SurfCount && - 0 == data.pSurfList[0].Depth && - 0 != D3dDdi::getFormatInfo(data.Format).bytesPerPixel) - { - const auto& caps = device.getAdapter().getD3dExtendedCaps(); - const auto& surfaceInfo = data.pSurfList[0]; - if (0 != caps.dwMaxTextureWidth && surfaceInfo.Width > caps.dwMaxTextureWidth || - 0 != caps.dwMaxTextureHeight && surfaceInfo.Height > caps.dwMaxTextureHeight) - { - splitToTiles(data, caps.dwMaxTextureWidth, caps.dwMaxTextureHeight); - } - } - } - D3DDDI_RESOURCEFLAGS getResourceTypeFlags() { D3DDDI_RESOURCEFLAGS flags = {}; @@ -77,32 +80,6 @@ namespace { HeapFree(GetProcessHeap(), 0, p); } - - void splitToTiles(D3DDDIARG_CREATERESOURCE& data, const UINT tileWidth, const UINT tileHeight) - { - static std::vector tiles; - tiles.clear(); - - const UINT bytesPerPixel = D3dDdi::getFormatInfo(data.Format).bytesPerPixel; - - for (UINT y = 0; y < data.pSurfList[0].Height; y += tileHeight) - { - for (UINT x = 0; x < data.pSurfList[0].Width; x += tileWidth) - { - D3DDDI_SURFACEINFO tile = {}; - tile.Width = min(data.pSurfList[0].Width - x, tileWidth); - tile.Height = min(data.pSurfList[0].Height - y, tileHeight); - tile.pSysMem = static_cast(data.pSurfList[0].pSysMem) + - y * data.pSurfList[0].SysMemPitch + x * bytesPerPixel; - tile.SysMemPitch = data.pSurfList[0].SysMemPitch; - tiles.push_back(tile); - } - } - - data.SurfCount = tiles.size(); - data.pSurfList = tiles.data(); - data.Flags.Texture = 0; - } } namespace D3dDdi @@ -133,7 +110,12 @@ namespace D3dDdi throw HResultException(E_FAIL); } - fixResourceData(device, reinterpret_cast(m_fixedData)); + if (m_origData.Flags.Primary) + { + g_presentationRect = calculatePresentationRect(); + } + + fixResourceData(); m_formatInfo = getFormatInfo(m_fixedData.Format); HRESULT result = m_device.createPrivateResource(m_fixedData); @@ -327,7 +309,7 @@ namespace D3dDdi void Resource::createGdiLockResource() { - auto gdiSurfaceDesc(Gdi::VirtualScreen::getSurfaceDesc(D3dDdi::KernelModeThunks::getMonitorRect())); + auto gdiSurfaceDesc(Gdi::VirtualScreen::getSurfaceDesc(DDraw::RealPrimarySurface::getMonitorRect())); if (!gdiSurfaceDesc.lpSurface) { return; @@ -431,6 +413,39 @@ namespace D3dDdi #endif } + void Resource::fixResourceData() + { + if (m_fixedData.Flags.Primary) + { + RECT r = DDraw::RealPrimarySurface::getMonitorRect(); + if (!IsRectEmpty(&r)) + { + for (auto& surface : m_fixedData.surfaceData) + { + surface.Width = r.right - r.left; + surface.Height = r.bottom - r.top; + } + } + m_fixedData.Format = D3DDDIFMT_X8R8G8B8; + } + + const bool isOffScreenPlain = 0 == (m_fixedData.Flags.Value & g_resourceTypeFlags); + if (D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool && + (isOffScreenPlain || m_fixedData.Flags.Texture) && + 1 == m_fixedData.SurfCount && + 0 == m_fixedData.pSurfList[0].Depth && + 0 != D3dDdi::getFormatInfo(m_fixedData.Format).bytesPerPixel) + { + const auto& caps = m_device.getAdapter().getD3dExtendedCaps(); + const auto& surfaceInfo = m_fixedData.pSurfList[0]; + if (0 != caps.dwMaxTextureWidth && surfaceInfo.Width > caps.dwMaxTextureWidth || + 0 != caps.dwMaxTextureHeight && surfaceInfo.Height > caps.dwMaxTextureHeight) + { + splitToTiles(caps.dwMaxTextureWidth, caps.dwMaxTextureHeight); + } + } + } + void* Resource::getLockPtr(UINT subResourceIndex) { return m_lockData.empty() ? nullptr : m_lockData[subResourceIndex].data; @@ -496,16 +511,24 @@ namespace D3dDdi } } - HRESULT Resource::presentationBlt(const D3DDDIARG_BLT& data, Resource& srcResource) + HRESULT Resource::presentationBlt(D3DDDIARG_BLT data, Resource& srcResource) { if (srcResource.m_lockResource && - srcResource.m_lockData[data.SrcSubResourceIndex].isSysMemUpToDate) + srcResource.m_lockData[0].isSysMemUpToDate) { - srcResource.copyToVidMem(data.SrcSubResourceIndex); + srcResource.copyToVidMem(0); } if (D3DDDIFMT_P8 == srcResource.m_origData.Format) { + const auto& si = srcResource.m_fixedData.pSurfList[0]; + auto palettizedBltRenderTarget(SurfaceRepository::get(m_device.getAdapter()).getPaletteBltRenderTarget( + si.Width, si.Height)); + if (!palettizedBltRenderTarget) + { + return E_OUTOFMEMORY; + } + auto entries(Gdi::Palette::getHardwarePalette()); RGBQUAD pal[256] = {}; for (UINT i = 0; i < 256; ++i) @@ -515,10 +538,14 @@ namespace D3dDdi pal[i].rgbBlue = entries[i].peBlue; } - m_device.getShaderBlitter().palettizedBlt(*this, data.DstSubResourceIndex, srcResource, pal); - return S_OK; + m_device.getShaderBlitter().palettizedBlt(*palettizedBltRenderTarget, 0, srcResource, pal); + data.hSrcResource = *palettizedBltRenderTarget; + data.SrcSubResourceIndex = 0; } + data.DstRect = g_presentationRect; + data.Flags.Linear = 1; + data.Flags.Point = 0; return m_device.getOrigVtable().pfnBlt(m_device, &data); } @@ -598,6 +625,31 @@ namespace D3dDdi return LOG_RESULT(S_OK); } + void Resource::splitToTiles(UINT tileWidth, UINT tileHeight) + { + std::vector tiles; + const UINT bytesPerPixel = getFormatInfo(m_fixedData.Format).bytesPerPixel; + + for (UINT y = 0; y < m_fixedData.pSurfList[0].Height; y += tileHeight) + { + for (UINT x = 0; x < m_fixedData.pSurfList[0].Width; x += tileWidth) + { + D3DDDI_SURFACEINFO tile = {}; + tile.Width = min(m_fixedData.pSurfList[0].Width - x, tileWidth); + tile.Height = min(m_fixedData.pSurfList[0].Height - y, tileHeight); + tile.pSysMem = static_cast(m_fixedData.pSurfList[0].pSysMem) + + y * m_fixedData.pSurfList[0].SysMemPitch + x * bytesPerPixel; + tile.SysMemPitch = m_fixedData.pSurfList[0].SysMemPitch; + tiles.push_back(tile); + } + } + + m_fixedData.surfaceData = tiles; + m_fixedData.SurfCount = m_fixedData.surfaceData.size(); + m_fixedData.pSurfList = m_fixedData.surfaceData.data(); + m_fixedData.Flags.Texture = 0; + } + HRESULT Resource::sysMemPreferredBlt(const D3DDDIARG_BLT& data, Resource& srcResource) { if (m_fixedData.Format == srcResource.m_fixedData.Format && diff --git a/DDrawCompat/D3dDdi/Resource.h b/DDrawCompat/D3dDdi/Resource.h index ef7a453..f05e483 100644 --- a/DDrawCompat/D3dDdi/Resource.h +++ b/DDrawCompat/D3dDdi/Resource.h @@ -80,14 +80,16 @@ namespace D3dDdi void createGdiLockResource(); void createLockResource(); void createSysMemResource(const std::vector& surfaceInfo); + void fixResourceData(); bool isOversized() const; bool isValidRect(UINT subResourceIndex, const RECT& rect); - HRESULT presentationBlt(const D3DDDIARG_BLT& data, Resource& srcResource); + HRESULT presentationBlt(D3DDDIARG_BLT data, Resource& srcResource); HRESULT splitBlt(D3DDDIARG_BLT& data, UINT& subResourceIndex, RECT& rect, RECT& otherRect); template HRESULT splitLock(Arg& data, HRESULT(APIENTRY *lockFunc)(HANDLE, Arg*)); + void splitToTiles(UINT tileWidth, UINT tileHeight); HRESULT sysMemPreferredBlt(const D3DDDIARG_BLT& data, Resource& srcResource); Device& m_device; diff --git a/DDrawCompat/D3dDdi/ShaderBlitter.cpp b/DDrawCompat/D3dDdi/ShaderBlitter.cpp index 7fb1780..5a25d06 100644 --- a/DDrawCompat/D3dDdi/ShaderBlitter.cpp +++ b/DDrawCompat/D3dDdi/ShaderBlitter.cpp @@ -1,55 +1,15 @@ -#include - -#include #include #include #include #include #include -#include +#include #include #define CONCAT_(a, b) a##b #define CONCAT(a, b) CONCAT_(a, b) #define SCOPED_STATE(state, ...) DeviceState::Scoped##state CONCAT(scopedState, __LINE__)(m_device.getState(), __VA_ARGS__) -namespace -{ - std::map> g_paletteTextures; - - CompatWeakPtr getPaletteTexture(CompatWeakPtr dd, LUID luid) - { - LOG_FUNC("ShaderBlitter::getPaletteTexture", dd.get(), luid); - if (!dd) - { - LOG_ONCE("Failed to create palette texture: no DirectDraw repository available") - return LOG_RESULT(nullptr); - } - - auto it = g_paletteTextures.find(luid); - if (it == g_paletteTextures.end()) - { - CompatPtr paletteTexture; - DDSURFACEDESC2 desc = {}; - desc.dwSize = sizeof(desc); - desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; - desc.dwWidth = 256; - desc.dwHeight = 1; - desc.ddpfPixelFormat = DDraw::DirectDraw::getRgbPixelFormat(32); - desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY; - - HRESULT result = dd->CreateSurface(dd, &desc, &paletteTexture.getRef(), nullptr); - if (FAILED(result)) - { - LOG_ONCE("Failed to create palette texture: " << Compat::hex(result)); - return nullptr; - } - it = g_paletteTextures.insert({ luid, paletteTexture.detach() }).first; - } - return LOG_RESULT(it->second.get()); - } -} - namespace D3dDdi { ShaderBlitter::ShaderBlitter(Device& device) @@ -124,7 +84,8 @@ namespace D3dDdi dp.PrimitiveType = D3DPT_TRIANGLEFAN; dp.VStart = 0; dp.PrimitiveCount = 2; - m_device.getDrawPrimitive().draw(dp, nullptr); + m_device.pfnDrawPrimitive(&dp, nullptr); + m_device.flushPrimitives(); } HANDLE ShaderBlitter::createPixelShader(const BYTE* code, UINT size) @@ -167,39 +128,33 @@ namespace D3dDdi LOG_FUNC("ShaderBlitter::palettizedBlt", static_cast(dstResource), dstSubResourceIndex, static_cast(srcResource), Compat::array(reinterpret_cast(palette), 256)); - if (m_paletteTexture && FAILED(m_paletteTexture->IsLost(m_paletteTexture))) - { - g_paletteTextures.erase(m_device.getAdapter().getLuid()); - m_paletteTexture->Release(m_paletteTexture); - m_paletteTexture = nullptr; - } - - if (!m_paletteTexture) - { - m_paletteTexture = getPaletteTexture(m_device.getAdapter().getRepository(), m_device.getAdapter().getLuid()); - if (!m_paletteTexture) - { - return; - } - } - - DDSURFACEDESC2 desc = {}; - desc.dwSize = sizeof(desc); - m_paletteTexture->Lock(m_paletteTexture, nullptr, &desc, DDLOCK_DISCARDCONTENTS | DDLOCK_WAIT, nullptr); - if (!desc.lpSurface) + auto paletteTexture(SurfaceRepository::get(m_device.getAdapter()).getPaletteTexture()); + if (!paletteTexture) { return; } - memcpy(desc.lpSurface, palette, 256 * sizeof(RGBQUAD)); - m_paletteTexture->Unlock(m_paletteTexture, nullptr); + D3DDDIARG_LOCK lock = {}; + lock.hResource = *paletteTexture; + lock.Flags.Discard = 1; + m_device.getOrigVtable().pfnLock(m_device, &lock); + if (!lock.pSurfData) + { + return; + } + + memcpy(lock.pSurfData, palette, 256 * sizeof(RGBQUAD)); + + D3DDDIARG_UNLOCK unlock = {}; + unlock.hResource = *paletteTexture; + m_device.getOrigVtable().pfnUnlock(m_device, &unlock); const auto& dstSurface = dstResource.getFixedDesc().pSurfList[dstSubResourceIndex]; const auto& srcSurface = srcResource.getFixedDesc().pSurfList[0]; const RECT dstRect = { 0, 0, static_cast(dstSurface.Width), static_cast(dstSurface.Height) }; const RECT srcRect = { 0, 0, static_cast(srcSurface.Width), static_cast(srcSurface.Height) }; - SCOPED_STATE(Texture, 1, DDraw::DirectDrawSurface::getDriverResourceHandle(*m_paletteTexture), D3DTEXF_POINT); + SCOPED_STATE(Texture, 1, *paletteTexture, D3DTEXF_POINT); blt(dstResource, dstSubResourceIndex, dstRect, srcResource, srcRect, m_psPaletteLookup, D3DTEXF_POINT); } } diff --git a/DDrawCompat/D3dDdi/ShaderBlitter.h b/DDrawCompat/D3dDdi/ShaderBlitter.h index 40ceef8..45d998a 100644 --- a/DDrawCompat/D3dDdi/ShaderBlitter.h +++ b/DDrawCompat/D3dDdi/ShaderBlitter.h @@ -1,8 +1,6 @@ #pragma once -#include - -#include +#include namespace D3dDdi { @@ -29,7 +27,6 @@ namespace D3dDdi HANDLE createVertexShaderDecl(); Device& m_device; - CompatWeakPtr m_paletteTexture; HANDLE m_psPaletteLookup; HANDLE m_vertexShaderDecl; }; diff --git a/DDrawCompat/D3dDdi/SurfaceRepository.cpp b/DDrawCompat/D3dDdi/SurfaceRepository.cpp new file mode 100644 index 0000000..9f4cdd2 --- /dev/null +++ b/DDrawCompat/D3dDdi/SurfaceRepository.cpp @@ -0,0 +1,98 @@ +#include + +#include +#include +#include +#include +#include + +namespace +{ + std::map g_repositories; +} + +namespace D3dDdi +{ + SurfaceRepository::SurfaceRepository(const Adapter& adapter) + : m_adapter(adapter) + { + } + + CompatWeakPtr SurfaceRepository::createSurface( + DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps) + { + auto dd(m_adapter.getRepository()); + if (!dd) + { + LOG_ONCE("ERROR: no DirectDraw repository available"); + return nullptr; + } + + CompatPtr surface; + + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; + desc.dwWidth = width; + desc.dwHeight = height; + desc.ddpfPixelFormat = pf; + desc.ddsCaps.dwCaps = caps; + + HRESULT result = dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr); + if (FAILED(result)) + { + LOG_ONCE("ERROR: Failed to create repository surface: " << Compat::hex(result) << " " << desc); + return nullptr; + } + return surface.detach(); + } + + SurfaceRepository& SurfaceRepository::get(const Adapter& adapter) + { + auto it = g_repositories.find(adapter.getLuid()); + if (it != g_repositories.end()) + { + return it->second; + } + return g_repositories.emplace(adapter.getLuid(), SurfaceRepository(adapter)).first->second; + } + + Resource* SurfaceRepository::getPaletteBltRenderTarget(DWORD width, DWORD height) + { + return getResource(m_paletteBltRenderTarget, width, height, DDraw::DirectDraw::getRgbPixelFormat(32), + DDSCAPS_3DDEVICE | DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY); + } + + Resource* SurfaceRepository::getPaletteTexture() + { + return getResource(m_paletteTexture, 256, 1, DDraw::DirectDraw::getRgbPixelFormat(32), + DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY); + } + + Resource* SurfaceRepository::getResource(Surface& surface, DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps) + { + if (surface.surface) + { + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + surface.surface->GetSurfaceDesc(surface.surface, &desc); + if (desc.dwWidth != width || desc.dwHeight != height || FAILED(surface.surface->IsLost(surface.surface))) + { + surface.surface->Release(surface.surface); + surface = {}; + } + } + + if (!surface.surface) + { + surface.surface = createSurface(width, height, pf, caps); + if (surface.surface) + { + surface.resource = D3dDdi::Device::findResource( + DDraw::DirectDrawSurface::getDriverResourceHandle(*surface.surface)); + } + } + + return surface.resource; + } +} diff --git a/DDrawCompat/D3dDdi/SurfaceRepository.h b/DDrawCompat/D3dDdi/SurfaceRepository.h new file mode 100644 index 0000000..6bc9cf5 --- /dev/null +++ b/DDrawCompat/D3dDdi/SurfaceRepository.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include + +namespace D3dDdi +{ + class Adapter; + class Resource; + + class SurfaceRepository + { + public: + Resource* getPaletteBltRenderTarget(DWORD width, DWORD height); + Resource* getPaletteTexture(); + + static SurfaceRepository& get(const Adapter& adapter); + + private: + struct Surface + { + CompatWeakPtr surface; + Resource* resource; + }; + + SurfaceRepository(const Adapter& adapter); + + CompatWeakPtr createSurface(DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps); + Resource* getResource(Surface& surface, DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps); + + const Adapter& m_adapter; + Surface m_paletteBltRenderTarget; + Surface m_paletteTexture; + }; +} diff --git a/DDrawCompat/DDraw/DirectDrawClipper.cpp b/DDrawCompat/DDraw/DirectDrawClipper.cpp index e4e4097..a1f9fdc 100644 --- a/DDrawCompat/DDraw/DirectDrawClipper.cpp +++ b/DDrawCompat/DDraw/DirectDrawClipper.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,7 @@ namespace GetRandomRgn(dc, rgn, SYSRGN); CALL_ORIG_FUNC(ReleaseDC)(data.hwnd, dc); - RECT primaryRect = D3dDdi::KernelModeThunks::getMonitorRect(); + RECT primaryRect = DDraw::RealPrimarySurface::getMonitorRect(); if (0 != primaryRect.left || 0 != primaryRect.top) { rgn.offset(-primaryRect.left, -primaryRect.top); diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index 3e3ad9c..9ccbe9b 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -27,6 +27,7 @@ namespace CompatWeakPtr g_frontBuffer; CompatWeakPtr g_clipper; + RECT g_monitorRect = {}; DDSURFACEDESC2 g_surfaceDesc = {}; DDraw::IReleaseNotifier g_releaseNotifier(onRelease); @@ -121,6 +122,7 @@ namespace g_isFullScreen = false; g_waitingForPrimaryUnlock = false; g_surfaceDesc = {}; + g_monitorRect = {}; } void onRestore() @@ -164,8 +166,7 @@ namespace return; } - RECT monitorRect = D3dDdi::KernelModeThunks::getMonitorRect(); - Gdi::Region excludeRegion(monitorRect); + Gdi::Region excludeRegion(DDraw::PrimarySurface::getMonitorRect()); Gdi::Window::present(excludeRegion); D3dDdi::KernelModeThunks::setDcPaletteOverride(true); @@ -177,7 +178,7 @@ namespace auto backBuffer(getBackBuffer()); if (backBuffer) { - POINT offset = { -monitorRect.left, -monitorRect.top }; + POINT offset = { -g_monitorRect.left, -g_monitorRect.top }; Gdi::Window::presentLayered(*backBuffer, offset); } } @@ -251,6 +252,7 @@ namespace DDraw HRESULT RealPrimarySurface::create(CompatRef dd) { DDraw::ScopedThreadLock lock; + g_monitorRect = D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr::from(&dd)).monitorInfo.rcMonitor; typename Types::TSurfaceDesc desc = {}; desc.dwSize = sizeof(desc); @@ -272,6 +274,7 @@ namespace DDraw if (FAILED(result)) { Compat::Log() << "ERROR: Failed to create the real primary surface: " << Compat::hex(result); + g_monitorRect = {}; return result; } @@ -349,6 +352,11 @@ namespace DDraw return gammaControl->GetGammaRamp(gammaControl, 0, rampData); } + RECT RealPrimarySurface::getMonitorRect() + { + return g_monitorRect; + } + CompatWeakPtr RealPrimarySurface::getSurface() { return g_frontBuffer; diff --git a/DDrawCompat/DDraw/RealPrimarySurface.h b/DDrawCompat/DDraw/RealPrimarySurface.h index 6de7212..f783d39 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.h +++ b/DDrawCompat/DDraw/RealPrimarySurface.h @@ -18,6 +18,7 @@ namespace DDraw static HRESULT flip(CompatPtr surfaceTargetOverride, DWORD flags); static void flush(); static HRESULT getGammaRamp(DDGAMMARAMP* rampData); + static RECT getMonitorRect(); static CompatWeakPtr getSurface(); static void init(); static bool isFullScreen(); diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp index 2505795..544a0e0 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp @@ -19,6 +19,7 @@ namespace DWORD g_origCaps = 0; HWND g_deviceWindow = nullptr; HPALETTE g_palette = nullptr; + RECT g_monitorRect = {}; } namespace DDraw @@ -37,6 +38,7 @@ namespace DDraw DeleteObject(g_palette); g_palette = nullptr; } + g_monitorRect = {}; s_palette = nullptr; DDraw::RealPrimarySurface::release(); @@ -45,6 +47,11 @@ namespace DDraw template HRESULT PrimarySurface::create(CompatRef dd, TSurfaceDesc desc, TSurface*& surface) { + const auto& dm = DDraw::DirectDraw::getDisplayMode(*CompatPtr::from(&dd)); + g_monitorRect = D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr::from(&dd)).monitorInfo.rcMonitor; + g_monitorRect.right = g_monitorRect.left + dm.dwWidth; + g_monitorRect.bottom = g_monitorRect.top + dm.dwHeight; + HRESULT result = RealPrimarySurface::create(dd); if (FAILED(result)) { @@ -53,7 +60,6 @@ namespace DDraw const DWORD origCaps = desc.ddsCaps.dwCaps; - const auto& dm = DDraw::DirectDraw::getDisplayMode(*CompatPtr::from(&dd)); desc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; desc.dwWidth = dm.dwWidth; desc.dwHeight = dm.dwHeight; @@ -68,6 +74,7 @@ namespace DDraw if (FAILED(result)) { Compat::Log() << "ERROR: Failed to create the compat primary surface: " << Compat::hex(result); + g_monitorRect = {}; RealPrimarySurface::release(); return result; } @@ -180,6 +187,11 @@ namespace DDraw return g_frontResource; } + RECT PrimarySurface::getMonitorRect() + { + return g_monitorRect; + } + DWORD PrimarySurface::getOrigCaps() { return g_origCaps; @@ -210,7 +222,7 @@ namespace DDraw m_surface->GetSurfaceDesc(m_surface, &desc); if (desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) { - DDSURFACEDESC2 gdiDesc = Gdi::VirtualScreen::getSurfaceDesc(D3dDdi::KernelModeThunks::getMonitorRect()); + DDSURFACEDESC2 gdiDesc = Gdi::VirtualScreen::getSurfaceDesc(g_monitorRect); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_LPSURFACE; desc.lPitch = gdiDesc.lPitch; desc.lpSurface = gdiDesc.lpSurface; diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.h b/DDrawCompat/DDraw/Surfaces/PrimarySurface.h index 09dd937..1f77a09 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.h +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.h @@ -21,6 +21,7 @@ namespace DDraw static CompatPtr getGdiSurface(); static CompatPtr getBackBuffer(); static CompatPtr getLastSurface(); + static RECT getMonitorRect(); static CompatWeakPtr getPrimary(); static HANDLE getFrontResource(); static DWORD getOrigCaps(); diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp index 2196a7b..772bbd2 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp @@ -32,7 +32,7 @@ namespace D3dDdi::ScopedCriticalSection lock; Gdi::Region clipRgn(DDraw::DirectDrawClipper::getClipRgn(*clipper)); - RECT monitorRect = D3dDdi::KernelModeThunks::getMonitorRect(); + RECT monitorRect = DDraw::RealPrimarySurface::getMonitorRect(); RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds(); clipRgn.offset(monitorRect.left, monitorRect.top); clipRgn &= virtualScreenBounds; diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 1ffa9c1..a3f2e57 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -210,9 +210,11 @@ + + @@ -234,6 +236,7 @@ + @@ -314,6 +317,7 @@ + @@ -334,6 +338,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 46a9c65..4661c43 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -426,6 +426,15 @@ Header Files\Common + + Header Files\Config\Settings + + + Header Files\Config + + + Header Files\D3dDdi + @@ -668,6 +677,12 @@ Source Files\D3dDdi + + Source Files\Config\Settings + + + Source Files\D3dDdi + diff --git a/DDrawCompat/Gdi/Metrics.cpp b/DDrawCompat/Gdi/Metrics.cpp index df39786..74d5244 100644 --- a/DDrawCompat/Gdi/Metrics.cpp +++ b/DDrawCompat/Gdi/Metrics.cpp @@ -3,18 +3,52 @@ #include #include #include +#include namespace { decltype(&GetSystemMetricsForDpi) g_origGetSystemMetricsForDpi = nullptr; + int getAdjustedDisplayMetrics(int nIndex, int cxIndex) + { + int result = CALL_ORIG_FUNC(GetSystemMetrics)(nIndex); + auto dm = Win32::DisplayMode::getEmulatedDisplayMode(); + if (0 == dm.rect.left && 0 == dm.rect.top) + { + result += (nIndex == cxIndex) ? dm.diff.cx : dm.diff.cy; + } + return result; + } + int WINAPI getSystemMetrics(int nIndex) { LOG_FUNC("GetSystemMetrics", nIndex); - if (SM_CXSIZE == nIndex) + + switch (nIndex) { - nIndex = SM_CYSIZE; + case SM_CXSCREEN: + case SM_CYSCREEN: + { + return LOG_RESULT(getAdjustedDisplayMetrics(nIndex, SM_CXSCREEN)); } + + case SM_CXFULLSCREEN: + case SM_CYFULLSCREEN: + { + return LOG_RESULT(getAdjustedDisplayMetrics(nIndex, SM_CXFULLSCREEN)); + } + + case SM_CXMAXIMIZED: + case SM_CYMAXIMIZED: + { + return LOG_RESULT(getAdjustedDisplayMetrics(nIndex, SM_CXMAXIMIZED)); + } + + case SM_CXSIZE: + nIndex = SM_CYSIZE; + break; + } + return LOG_RESULT(CALL_ORIG_FUNC(GetSystemMetrics)(nIndex)); } diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index f7549a8..87bdcd4 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace { @@ -34,6 +35,7 @@ namespace bool isUser32ScrollBar(HWND hwnd); void onCreateWindow(HWND hwnd); void onDestroyWindow(HWND hwnd); + void onGetMinMaxInfo(MINMAXINFO& mmi); void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp); void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp); void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW); @@ -45,6 +47,20 @@ namespace switch (uMsg) { + case WM_DISPLAYCHANGE: + { + if (0 != wParam) + { + return 0; + } + wParam = Win32::DisplayMode::getBpp(); + break; + } + + case WM_GETMINMAXINFO: + onGetMinMaxInfo(*reinterpret_cast(lParam)); + break; + case WM_SYNCPAINT: if (isTopLevelWindow(hwnd)) { @@ -266,6 +282,15 @@ namespace } } + void onGetMinMaxInfo(MINMAXINFO& mmi) + { + MONITORINFOEXA mi = {}; + mi.cbSize = sizeof(mi); + GetMonitorInfoA(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi); + mmi.ptMaxSize.x = mi.rcMonitor.right - 2 * mmi.ptMaxPosition.x; + mmi.ptMaxSize.y = mi.rcMonitor.bottom - 2 * mmi.ptMaxPosition.y; + } + void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp) { for (auto notifyFunc : g_windowPosChangeNotifyFuncs) diff --git a/DDrawCompat/Win32/DisplayMode.cpp b/DDrawCompat/Win32/DisplayMode.cpp index 5513139..3a52792 100644 --- a/DDrawCompat/Win32/DisplayMode.cpp +++ b/DDrawCompat/Win32/DisplayMode.cpp @@ -1,8 +1,12 @@ +#include #include #include +#include #include #include +#include +#include #include #include #include @@ -10,10 +14,14 @@ #include BOOL WINAPI DWM8And16Bit_IsShimApplied_CallOut() { return FALSE; } +ULONG WINAPI GdiEntry13() { return 0; } BOOL WINAPI SE_COM_HookInterface(CLSID*, GUID*, DWORD, DWORD) { return 0; } namespace { + using Win32::DisplayMode::DisplayMode; + using Win32::DisplayMode::EmulatedDisplayMode; + template struct EnumParams { @@ -31,13 +39,48 @@ namespace } }; - DWORD g_origBpp = 0; - DWORD g_currentBpp = 0; - DWORD g_lastBpp = 0; + struct GetMonitorFromDcEnumArgs + { + POINT org; + HMONITOR hmonitor; + }; + + DWORD g_desktopBpp = 0; + RECT g_cursorRect = {}; + ULONG g_displaySettingsUniquenessBias = 0; + EmulatedDisplayMode g_emulatedDisplayMode = {}; + Compat::SrwLock g_srwLock; BOOL WINAPI dwm8And16BitIsShimAppliedCallOut(); BOOL WINAPI seComHookInterface(CLSID* clsid, GUID* iid, DWORD unk1, DWORD unk2); + template + SIZE getConfiguredResolution(EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName); + + template + std::wstring getDeviceName(const Char* deviceName); + + HMONITOR getMonitorFromDc(HDC dc); + MONITORINFO getMonitorInfo(const std::wstring& deviceName); + + template + std::vector getSupportedDisplayModes( + EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName, DWORD flags); + + void adjustMonitorInfo(MONITORINFO& mi) + { + Compat::ScopedSrwLockShared srwLock(g_srwLock); + if (!g_emulatedDisplayMode.deviceName.empty() && + g_emulatedDisplayMode.rect.left == mi.rcMonitor.left && + g_emulatedDisplayMode.rect.top == mi.rcMonitor.top) + { + mi.rcMonitor.right += g_emulatedDisplayMode.diff.cx; + mi.rcMonitor.bottom += g_emulatedDisplayMode.diff.cy; + mi.rcWork.right += g_emulatedDisplayMode.diff.cx; + mi.rcWork.bottom += g_emulatedDisplayMode.diff.cy; + } + } + template LONG changeDisplaySettingsEx( ChangeDisplaySettingsExFunc origChangeDisplaySettingsEx, @@ -45,6 +88,19 @@ namespace CStr lpszDeviceName, DevMode* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) { DDraw::ScopedThreadLock lock; + DevMode targetDevMode = {}; + if (lpDevMode) + { + targetDevMode = *lpDevMode; + targetDevMode.dmBitsPerPel = 32; + SIZE resolutionOverride = getConfiguredResolution(origEnumDisplaySettingsEx, lpszDeviceName); + if (0 != resolutionOverride.cx) + { + targetDevMode.dmPelsWidth = resolutionOverride.cx; + targetDevMode.dmPelsHeight = resolutionOverride.cy; + } + } + DevMode prevDevMode = {}; if (!(dwflags & CDS_TEST)) { @@ -52,50 +108,84 @@ namespace origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &prevDevMode, 0); } - BOOL result = FALSE; + LONG result = 0; if (lpDevMode) { - DWORD origBpp = lpDevMode->dmBitsPerPel; - lpDevMode->dmBitsPerPel = 32; - result = origChangeDisplaySettingsEx(lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); - lpDevMode->dmBitsPerPel = origBpp; + result = origChangeDisplaySettingsEx(lpszDeviceName, &targetDevMode, hwnd, dwflags, lParam); + if (DISP_CHANGE_SUCCESSFUL != result && + (lpDevMode->dmPelsWidth != targetDevMode.dmPelsWidth || lpDevMode->dmPelsHeight != targetDevMode.dmPelsHeight)) + { + LOG_ONCE("Failed to apply setting: DisplayResolution = " << targetDevMode.dmPelsWidth << 'x' << targetDevMode.dmPelsHeight); + targetDevMode.dmPelsWidth = lpDevMode->dmPelsWidth; + targetDevMode.dmPelsHeight = lpDevMode->dmPelsHeight; + result = origChangeDisplaySettingsEx(lpszDeviceName, &targetDevMode, hwnd, dwflags, lParam); + } } else { - result = origChangeDisplaySettingsEx(lpszDeviceName, lpDevMode, hwnd, dwflags, lParam); + result = origChangeDisplaySettingsEx(lpszDeviceName, nullptr, hwnd, dwflags, lParam); } - if (SUCCEEDED(result) && !(dwflags & CDS_TEST)) + if (dwflags & CDS_TEST) { + return result; + } + + DevMode currDevMode = {}; + currDevMode.dmSize = sizeof(currDevMode); + origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &currDevMode, 0); + + if (0 == memcmp(&currDevMode, &prevDevMode, sizeof(currDevMode))) + { + HANDLE dwmDxFullScreenTransitionEvent = OpenEventW( + EVENT_MODIFY_STATE, FALSE, L"DWM_DX_FULLSCREEN_TRANSITION_EVENT"); + SetEvent(dwmDxFullScreenTransitionEvent); + CloseHandle(dwmDxFullScreenTransitionEvent); + } + + if (DISP_CHANGE_SUCCESSFUL != result) + { + return result; + } + + RECT clipRect = {}; + + { + Compat::ScopedSrwLockExclusive srwLock(g_srwLock); + ++g_displaySettingsUniquenessBias; + clipRect = g_cursorRect; if (lpDevMode) { - g_currentBpp = lpDevMode->dmBitsPerPel; - g_lastBpp = lpDevMode->dmBitsPerPel; + g_emulatedDisplayMode.width = lpDevMode->dmPelsWidth; + g_emulatedDisplayMode.height = lpDevMode->dmPelsHeight; + g_emulatedDisplayMode.bpp = lpDevMode->dmBitsPerPel; + g_emulatedDisplayMode.refreshRate = currDevMode.dmDisplayFrequency; + + g_emulatedDisplayMode.deviceName = getDeviceName(lpszDeviceName); + g_emulatedDisplayMode.rect = getMonitorInfo(g_emulatedDisplayMode.deviceName).rcMonitor; + g_emulatedDisplayMode.rect.right = g_emulatedDisplayMode.rect.left + lpDevMode->dmPelsWidth; + g_emulatedDisplayMode.rect.bottom = g_emulatedDisplayMode.rect.top + lpDevMode->dmPelsHeight; + g_emulatedDisplayMode.diff.cx = lpDevMode->dmPelsWidth - currDevMode.dmPelsWidth; + g_emulatedDisplayMode.diff.cy = lpDevMode->dmPelsHeight - currDevMode.dmPelsHeight; + + IntersectRect(&clipRect, &clipRect, &g_emulatedDisplayMode.rect); } else { - g_currentBpp = g_origBpp; + g_emulatedDisplayMode = {}; + g_emulatedDisplayMode.bpp = g_desktopBpp; } - - DevMode currDevMode = {}; - currDevMode.dmSize = sizeof(currDevMode); - origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &currDevMode, 0); - - if (currDevMode.dmPelsWidth == prevDevMode.dmPelsWidth && - currDevMode.dmPelsHeight == prevDevMode.dmPelsHeight && - currDevMode.dmBitsPerPel == prevDevMode.dmBitsPerPel && - currDevMode.dmDisplayFrequency == prevDevMode.dmDisplayFrequency && - currDevMode.dmDisplayFlags == prevDevMode.dmDisplayFlags) - { - HANDLE dwmDxFullScreenTransitionEvent = OpenEventW( - EVENT_MODIFY_STATE, FALSE, L"DWM_DX_FULLSCREEN_TRANSITION_EVENT"); - SetEvent(dwmDxFullScreenTransitionEvent); - CloseHandle(dwmDxFullScreenTransitionEvent); - } - - Gdi::VirtualScreen::update(); } + CALL_ORIG_FUNC(ClipCursor)(&clipRect); + + auto& dm = lpDevMode ? *lpDevMode : currDevMode; + LPARAM resolution = (dm.dmPelsHeight << 16) | dm.dmPelsWidth; + EnumWindows(sendDisplayChange, resolution); + + SetCursorPos(currDevMode.dmPosition.x + dm.dmPelsWidth / 2, currDevMode.dmPosition.y + dm.dmPelsHeight / 2); + Gdi::VirtualScreen::update(); + return result; } @@ -119,6 +209,31 @@ namespace lpszDeviceName, lpDevMode, hwnd, dwflags, lParam)); } + BOOL WINAPI clipCursor(const RECT* lpRect) + { + LOG_FUNC("ClipCursor", lpRect); + BOOL result = CALL_ORIG_FUNC(ClipCursor)(lpRect); + if (!result) + { + return result; + } + + RECT rect = {}; + CALL_ORIG_FUNC(GetClipCursor)(&rect); + + { + Compat::ScopedSrwLockExclusive srwLock(g_srwLock); + g_cursorRect = rect; + if (!g_emulatedDisplayMode.deviceName.empty()) + { + IntersectRect(&rect, &rect, &g_emulatedDisplayMode.rect); + CALL_ORIG_FUNC(ClipCursor)(&rect); + } + } + + return result; + } + void disableDwm8And16BitMitigation() { auto user32 = GetModuleHandle("user32"); @@ -147,8 +262,7 @@ namespace { if (ENUM_REGISTRY_SETTINGS == iModeNum || !lpDevMode) { - BOOL result = origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags); - return result; + return origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags); } if (ENUM_CURRENT_SETTINGS == iModeNum) @@ -156,46 +270,53 @@ namespace BOOL result = origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags); if (result) { - lpDevMode->dmBitsPerPel = g_currentBpp; + Compat::ScopedSrwLockShared srwLock(g_srwLock); + if (getDeviceName(lpszDeviceName) == g_emulatedDisplayMode.deviceName) + { + lpDevMode->dmBitsPerPel = g_emulatedDisplayMode.bpp; + lpDevMode->dmPelsWidth = g_emulatedDisplayMode.width; + lpDevMode->dmPelsHeight = g_emulatedDisplayMode.height; + } + else + { + lpDevMode->dmBitsPerPel = g_desktopBpp; + } } return result; } - thread_local std::vector devModes; + thread_local std::vector displayModes; thread_local EnumParams lastEnumParams = {}; - EnumParams currentEnumParams = { - lpszDeviceName ? lpszDeviceName : std::basic_string(), dwFlags }; + EnumParams currentEnumParams = { lpszDeviceName ? lpszDeviceName : std::basic_string(), dwFlags }; - if (0 == iModeNum || devModes.empty() || currentEnumParams != lastEnumParams) + if (0 == iModeNum || displayModes.empty() || currentEnumParams != lastEnumParams) { - devModes.clear(); + displayModes = getSupportedDisplayModes(origEnumDisplaySettingsEx, lpszDeviceName, dwFlags); lastEnumParams = currentEnumParams; - - DWORD modeNum = 0; - DevMode dm = {}; - dm.dmSize = sizeof(dm); - while (origEnumDisplaySettingsEx(lpszDeviceName, modeNum, &dm, dwFlags)) - { - if (32 == dm.dmBitsPerPel) - { - dm.dmBitsPerPel = 8; - devModes.push_back(dm); - dm.dmBitsPerPel = 16; - devModes.push_back(dm); - dm.dmBitsPerPel = 32; - devModes.push_back(dm); - } - ++modeNum; - } } - if (iModeNum >= devModes.size()) + if (iModeNum >= displayModes.size() * 3) { return FALSE; } - *lpDevMode = devModes[iModeNum]; + const auto& displayMode = displayModes[iModeNum / 3]; + lpDevMode->dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | + DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY; + lpDevMode->dmDisplayOrientation = DMDO_DEFAULT; + lpDevMode->dmPelsWidth = displayMode.width; + lpDevMode->dmPelsHeight = displayMode.height; + lpDevMode->dmDisplayFlags = 0; + lpDevMode->dmDisplayFrequency = displayMode.refreshRate; + + switch (iModeNum % 3) + { + case 0: lpDevMode->dmBitsPerPel = 8; break; + case 1: lpDevMode->dmBitsPerPel = 16; break; + case 2: lpDevMode->dmBitsPerPel = 32; break; + } + return TRUE; } @@ -215,6 +336,44 @@ namespace lpszDeviceName, iModeNum, lpDevMode, dwFlags)); } + ULONG WINAPI gdiEntry13() + { + Compat::ScopedSrwLockShared lock(g_srwLock); + return CALL_ORIG_FUNC(GdiEntry13)() + g_displaySettingsUniquenessBias; + } + + BOOL WINAPI getClipCursor(LPRECT lpRect) + { + LOG_FUNC("GetClipCursor", lpRect); + BOOL result = CALL_ORIG_FUNC(GetClipCursor)(lpRect); + if (result) + { + *lpRect = g_cursorRect; + } + return LOG_RESULT(result); + } + + template + SIZE getConfiguredResolution(EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName) + { + auto resolution = Config::displayResolution.get(); + if (Config::Settings::DisplayResolution::DESKTOP == resolution) + { + DevMode dm = {}; + dm.dmSize = sizeof(dm); + if (origEnumDisplaySettingsEx(deviceName, ENUM_REGISTRY_SETTINGS, &dm, 0)) + { + resolution.cx = dm.dmPelsWidth; + resolution.cy = dm.dmPelsHeight; + } + else + { + resolution = {}; + } + } + return resolution; + } + int WINAPI getDeviceCaps(HDC hdc, int nIndex) { LOG_FUNC("GetDeviceCaps", hdc, nIndex); @@ -223,34 +382,52 @@ namespace case BITSPIXEL: if (Gdi::isDisplayDc(hdc)) { - return LOG_RESULT(g_currentBpp); + return LOG_RESULT(Win32::DisplayMode::getBpp()); } break; case COLORRES: - if (8 == g_currentBpp && Gdi::isDisplayDc(hdc)) + if (8 == Win32::DisplayMode::getBpp() && Gdi::isDisplayDc(hdc)) { return 24; } break; + case HORZRES: + case VERTRES: + if (Gdi::isDisplayDc(hdc)) + { + MONITORINFO mi = {}; + mi.cbSize = sizeof(mi); + GetMonitorInfo(getMonitorFromDc(hdc), &mi); + if (HORZRES == nIndex) + { + return LOG_RESULT(mi.rcMonitor.right - mi.rcMonitor.left); + } + else + { + return LOG_RESULT(mi.rcMonitor.bottom - mi.rcMonitor.top); + } + } + break; + case NUMCOLORS: case NUMRESERVED: - if (8 == g_currentBpp && Gdi::isDisplayDc(hdc)) + if (8 == Win32::DisplayMode::getBpp() && Gdi::isDisplayDc(hdc)) { return 20; } break; case RASTERCAPS: - if (8 == g_currentBpp && Gdi::isDisplayDc(hdc)) + if (8 == Win32::DisplayMode::getBpp() && Gdi::isDisplayDc(hdc)) { return LOG_RESULT(CALL_ORIG_FUNC(GetDeviceCaps)(hdc, nIndex) | RC_PALETTE); } break; case SIZEPALETTE: - if (8 == g_currentBpp && Gdi::isDisplayDc(hdc)) + if (8 == Win32::DisplayMode::getBpp() && Gdi::isDisplayDc(hdc)) { return 256; } @@ -259,6 +436,191 @@ namespace return LOG_RESULT(CALL_ORIG_FUNC(GetDeviceCaps)(hdc, nIndex)); } + std::size_t getDeviceNameLength(const char* deviceName) + { + return std::strlen(deviceName); + } + + std::size_t getDeviceNameLength(const wchar_t* deviceName) + { + return std::wcslen(deviceName); + } + + template + std::wstring getDeviceName(const Char* deviceName) + { + if (deviceName) + { + return std::wstring(deviceName, deviceName + getDeviceNameLength(deviceName)); + } + + MONITORINFOEXW mi = {}; + mi.cbSize = sizeof(mi); + CALL_ORIG_FUNC(GetMonitorInfoW)(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi); + return mi.szDevice; + } + + BOOL CALLBACK getMonitorFromDcEnum(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData) + { + auto& args = *reinterpret_cast(dwData); + + MONITORINFOEX mi = {}; + mi.cbSize = sizeof(mi); + CALL_ORIG_FUNC(GetMonitorInfoA)(hMonitor, &mi); + + HDC dc = CreateDC(mi.szDevice, nullptr, nullptr, nullptr); + if (dc) + { + POINT org = {}; + GetDCOrgEx(dc, &org); + DeleteDC(dc); + if (org == args.org) + { + args.hmonitor = hMonitor; + return FALSE; + } + } + return TRUE; + } + + HMONITOR getMonitorFromDc(HDC dc) + { + HWND hwnd = CALL_ORIG_FUNC(WindowFromDC)(dc); + if (hwnd) + { + return MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + } + + GetMonitorFromDcEnumArgs args = {}; + GetDCOrgEx(dc, &args.org); + EnumDisplayMonitors(nullptr, nullptr, getMonitorFromDcEnum, reinterpret_cast(&args)); + return args.hmonitor ? args.hmonitor : MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY); + } + + BOOL CALLBACK getMonitorInfoEnum(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData) + { + MONITORINFOEXW mi = {}; + mi.cbSize = sizeof(mi); + CALL_ORIG_FUNC(GetMonitorInfoW)(hMonitor, &mi); + auto& dest = *reinterpret_cast(dwData); + if (0 == wcscmp(mi.szDevice, dest.szDevice)) + { + dest = mi; + return FALSE; + } + return TRUE; + } + + MONITORINFO getMonitorInfo(const std::wstring& deviceName) + { + MONITORINFOEXW mi = {}; + wcscpy_s(mi.szDevice, deviceName.c_str()); + EnumDisplayMonitors(nullptr, nullptr, &getMonitorInfoEnum, reinterpret_cast(&mi)); + return mi; + } + + BOOL WINAPI getMonitorInfoA(HMONITOR hMonitor, LPMONITORINFO lpmi) + { + LOG_FUNC("GetMonitorInfoA", hMonitor, lpmi); + BOOL result = CALL_ORIG_FUNC(GetMonitorInfoA)(hMonitor, lpmi); + if (result) + { + adjustMonitorInfo(*lpmi); + } + return LOG_RESULT(result); + } + + BOOL WINAPI getMonitorInfoW(HMONITOR hMonitor, LPMONITORINFO lpmi) + { + LOG_FUNC("GetMonitorInfoW", hMonitor, lpmi); + BOOL result = CALL_ORIG_FUNC(GetMonitorInfoW)(hMonitor, lpmi); + if (result) + { + adjustMonitorInfo(*lpmi); + } + return LOG_RESULT(result); + } + + template + std::vector getSupportedDisplayModes( + EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName, DWORD flags) + { + std::set displayModes; + DWORD modeNum = 0; + DevMode dm = {}; + dm.dmSize = sizeof(dm); + + while (origEnumDisplaySettingsEx(deviceName, modeNum, &dm, flags)) + { + if (32 == dm.dmBitsPerPel) + { + displayModes.insert({ dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmDisplayFrequency }); + } + ++modeNum; + } + + auto resolutionOverride = getConfiguredResolution(origEnumDisplaySettingsEx, deviceName); + if (0 == resolutionOverride.cx) + { + return { displayModes.begin(), displayModes.end() }; + } + + std::set supportedRefreshRates; + for (const auto& mode : displayModes) + { + if (static_cast(mode.width) == resolutionOverride.cx && + static_cast(mode.height) == resolutionOverride.cy) + { + supportedRefreshRates.insert(mode.refreshRate); + } + } + + if (supportedRefreshRates.empty()) + { + return { displayModes.begin(), displayModes.end() }; + } + + std::vector customDisplayModes; + DWORD prevWidth = 0; + DWORD prevHeight = 0; + for (const auto& mode : displayModes) + { + if (mode.width != prevWidth || mode.height != prevHeight) + { + for (auto refreshRate : supportedRefreshRates) + { + customDisplayModes.push_back({ mode.width, mode.height, mode.bpp, refreshRate }); + } + prevWidth = mode.width; + prevHeight = mode.height; + } + } + return customDisplayModes; + } + + BOOL CALLBACK initMonitor(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM /*dwData*/) + { + MONITORINFOEX mi = {}; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + + DEVMODE dm = {}; + dm.dmSize = sizeof(dm); + if (EnumDisplaySettings(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm)) + { + if (32 != dm.dmBitsPerPel) + { + dm = {}; + dm.dmSize = sizeof(dm); + dm.dmFields = DM_BITSPERPEL; + dm.dmBitsPerPel = 32; + ChangeDisplaySettingsEx(mi.szDevice, &dm, nullptr, 0, nullptr); + } + } + + return TRUE; + } + BOOL WINAPI seComHookInterface(CLSID* clsid, GUID* iid, DWORD unk1, DWORD unk2) { LOG_FUNC("SE_COM_HookInterface", clsid, iid, unk1, unk2); @@ -268,6 +630,17 @@ namespace } return LOG_RESULT(CALL_ORIG_FUNC(SE_COM_HookInterface)(clsid, iid, unk1, unk2)); } + + BOOL CALLBACK sendDisplayChange(HWND hwnd, LPARAM lParam) + { + DWORD pid = 0; + GetWindowThreadProcessId(hwnd, &pid); + if (GetCurrentProcessId() == pid) + { + SendNotifyMessage(hwnd, WM_DISPLAYCHANGE, 0, lParam); + } + return TRUE; + } } namespace Win32 @@ -276,36 +649,41 @@ namespace Win32 { DWORD getBpp() { - return g_currentBpp; + return getEmulatedDisplayMode().bpp; + } + + EmulatedDisplayMode getEmulatedDisplayMode() + { + Compat::ScopedSrwLockShared lock(g_srwLock); + return g_emulatedDisplayMode; } ULONG queryDisplaySettingsUniqueness() { - static auto ddQueryDisplaySettingsUniqueness = reinterpret_cast( - GetProcAddress(GetModuleHandle("gdi32"), "GdiEntry13")); - return ddQueryDisplaySettingsUniqueness(); + return CALL_ORIG_FUNC(GdiEntry13)(); } void installHooks() { - DEVMODEA devMode = {}; - devMode.dmSize = sizeof(devMode); - EnumDisplaySettingsEx(nullptr, ENUM_CURRENT_SETTINGS, &devMode, 0); - g_origBpp = devMode.dmBitsPerPel; - g_currentBpp = g_origBpp; - g_lastBpp = g_origBpp; + DEVMODEA dm = {}; + dm.dmSize = sizeof(dm); + EnumDisplaySettingsEx(nullptr, ENUM_CURRENT_SETTINGS, &dm, 0); + g_desktopBpp = dm.dmBitsPerPel; + g_emulatedDisplayMode.bpp = dm.dmBitsPerPel; - if (32 != devMode.dmBitsPerPel) - { - devMode.dmBitsPerPel = 32; - ChangeDisplaySettings(&devMode, 0); - } + GetClipCursor(&g_cursorRect); + EnumDisplayMonitors(nullptr, nullptr, &initMonitor, 0); HOOK_FUNCTION(user32, ChangeDisplaySettingsExA, changeDisplaySettingsExA); HOOK_FUNCTION(user32, ChangeDisplaySettingsExW, changeDisplaySettingsExW); + HOOK_FUNCTION(user32, ClipCursor, clipCursor); HOOK_FUNCTION(user32, EnumDisplaySettingsExA, enumDisplaySettingsExA); HOOK_FUNCTION(user32, EnumDisplaySettingsExW, enumDisplaySettingsExW); + HOOK_FUNCTION(gdi32, GdiEntry13, gdiEntry13); + HOOK_FUNCTION(user32, GetClipCursor, getClipCursor); HOOK_FUNCTION(gdi32, GetDeviceCaps, getDeviceCaps); + HOOK_FUNCTION(user32, GetMonitorInfoA, getMonitorInfoA); + HOOK_FUNCTION(user32, GetMonitorInfoW, getMonitorInfoW); disableDwm8And16BitMitigation(); } diff --git a/DDrawCompat/Win32/DisplayMode.h b/DDrawCompat/Win32/DisplayMode.h index a38972c..08ad4b8 100644 --- a/DDrawCompat/Win32/DisplayMode.h +++ b/DDrawCompat/Win32/DisplayMode.h @@ -1,14 +1,41 @@ #pragma once +#include + #include +#include + namespace Win32 { namespace DisplayMode { + struct DisplayMode + { + DWORD width; + DWORD height; + DWORD bpp; + DWORD refreshRate; + }; + + struct EmulatedDisplayMode : DisplayMode + { + std::wstring deviceName; + RECT rect; + SIZE diff; + }; + DWORD getBpp(); + EmulatedDisplayMode getEmulatedDisplayMode(); ULONG queryDisplaySettingsUniqueness(); void installHooks(); + + using ::operator<; + + inline auto toTuple(const DisplayMode& dm) + { + return std::make_tuple(dm.width, dm.height, dm.bpp, dm.refreshRate); + } } } diff --git a/DDrawCompat/Win32/Log.cpp b/DDrawCompat/Win32/Log.cpp index ec9c009..dce084c 100644 --- a/DDrawCompat/Win32/Log.cpp +++ b/DDrawCompat/Win32/Log.cpp @@ -32,7 +32,7 @@ namespace << dm.dmPelsHeight << dm.dmBitsPerPel << dm.dmDisplayFrequency - << dm.dmDisplayFlags; + << Compat::hex(dm.dmDisplayFlags); } template