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

Handle display scaling while primary surface is lost

This commit is contained in:
narzoul 2022-07-30 14:56:43 +02:00
parent 9bca0a6ba0
commit 524f708a7f
9 changed files with 130 additions and 43 deletions

View File

@ -37,7 +37,7 @@ namespace D3dDdi
, m_runtimeVersion(data.Version)
, m_driverVersion(data.DriverVersion)
, m_luid(KernelModeThunks::getLastOpenAdapterInfo().luid)
, m_deviceName(KernelModeThunks::getLastOpenAdapterInfo().monitorInfo.szDevice)
, m_deviceName(KernelModeThunks::getLastOpenAdapterInfo().deviceName)
, m_repository{}
{
}

View File

@ -139,27 +139,13 @@ namespace
return LOG_RESULT(CreateDCA(pwszDriver, pwszDevice, pszPort, pdm));
}
BOOL CALLBACK findMonitorInfo(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
{
MONITORINFOEXW mi = {};
mi.cbSize = sizeof(mi);
CALL_ORIG_FUNC(GetMonitorInfoW)(hMonitor, &mi);
if (0 == wcscmp(reinterpret_cast<MONITORINFOEXW*>(dwData)->szDevice, mi.szDevice))
{
*reinterpret_cast<MONITORINFOEXW*>(dwData) = mi;
return FALSE;
}
return TRUE;
}
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;
wcscpy_s(adapterInfo.monitorInfo.szDevice, std::wstring(deviceName.begin(), deviceName.end()).c_str());
EnumDisplayMonitors(nullptr, nullptr, findMonitorInfo, reinterpret_cast<LPARAM>(&adapterInfo.monitorInfo));
adapterInfo.deviceName = std::wstring(deviceName.begin(), deviceName.end());
return adapterInfo;
}
@ -266,8 +252,6 @@ namespace
LOG_FUNC("D3DKMTSetGammaRamp", pData);
NTSTATUS result = 0;
UINT vsyncCounter = D3dDdi::KernelModeThunks::getVsyncCounter();
DDraw::RealPrimarySurface::setUpdateReady();
DDraw::RealPrimarySurface::flush();
if (g_isExclusiveFullscreen || D3DDDI_GAMMARAMP_RGB256x3x16 != pData->Type || !pData->pGammaRampRgb256x3x16)
{
D3dDdi::ShaderBlitter::resetGammaRamp();

View File

@ -1,5 +1,7 @@
#pragma once
#include <string>
#include <Windows.h>
#include <winternl.h>
#include <d3dkmthk.h>
@ -17,7 +19,7 @@ namespace D3dDdi
UINT adapter;
UINT vidPnSourceId;
LUID luid;
MONITORINFOEXW monitorInfo;
std::wstring deviceName;
};
void fixPresent(D3DKMT_PRESENT& data);

View File

@ -273,7 +273,8 @@ namespace D3dDdi
const SurfaceRepository::Surface& SurfaceRepository::getTempTexture(DWORD width, DWORD height, const DDPIXELFORMAT& pf)
{
return getTempSurface(m_textures[pf], width, height, pf, DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
return getTempSurface(m_textures[pf], width, height, pf,
(pf.dwRGBBitCount > 8 ? DDSCAPS_TEXTURE : 0) | DDSCAPS_VIDEOMEMORY);
}
bool SurfaceRepository::isLost(Surface& surface)

View File

@ -372,18 +372,14 @@ namespace
}
bool isFullscreen = false;
if (SUCCEEDED(g_frontBuffer->IsLost(g_frontBuffer)))
if (SUCCEEDED(g_frontBuffer->IsLost(g_frontBuffer)) && DDraw::PrimarySurface::getPrimary())
{
auto primary(DDraw::PrimarySurface::getPrimary());
if (primary && SUCCEEDED(primary->IsLost(primary)))
HWND foregroundWindow = GetForegroundWindow();
if (foregroundWindow)
{
HWND foregroundWindow = GetForegroundWindow();
if (foregroundWindow)
{
DWORD pid = 0;
GetWindowThreadProcessId(foregroundWindow, &pid);
isFullscreen = GetCurrentProcessId() == pid && Gdi::Window::hasFullscreenWindow();
}
DWORD pid = 0;
GetWindowThreadProcessId(foregroundWindow, &pid);
isFullscreen = GetCurrentProcessId() == pid && Gdi::Window::hasFullscreenWindow();
}
}
@ -432,7 +428,8 @@ namespace DDraw
{
LOG_FUNC("RealPrimarySurface::create", &dd);
DDraw::ScopedThreadLock lock;
g_monitorRect = D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr<IDirectDraw7>::from(&dd)).monitorInfo.rcMonitor;
g_monitorRect = Win32::DisplayMode::getMonitorInfo(
D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr<IDirectDraw7>::from(&dd)).deviceName).rcMonitor;
DDSURFACEDESC desc = {};
desc.dwSize = sizeof(desc);
@ -593,7 +590,21 @@ namespace DDraw
}
updatePresentationWindowPos();
auto src(g_isDelayedFlipPending ? g_lastFlipSurface->getDDS() : DDraw::PrimarySurface::getPrimary());
auto primary(DDraw::PrimarySurface::getPrimary());
CompatWeakPtr<IDirectDrawSurface7> src;
if (g_isDelayedFlipPending)
{
src = g_lastFlipSurface->getDDS();
}
else if (primary && SUCCEEDED(primary->IsLost(primary)))
{
src = primary;
}
else
{
src = DDraw::PrimarySurface::getGdiPrimary();
}
RECT emptyRect = {};
HRESULT result = src ? src->BltFast(src, 0, 0, src, &emptyRect, DDBLTFAST_WAIT) : DD_OK;
if (DDERR_SURFACEBUSY == result || DDERR_LOCKEDSURFACES == result)

View File

@ -4,24 +4,60 @@
#include <D3dDdi/Device.h>
#include <D3dDdi/KernelModeThunks.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/SurfaceRepository.h>
#include <DDraw/DirectDraw.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurfaceImpl.h>
#include <DDraw/Surfaces/TagSurface.h>
#include <Gdi/Palette.h>
#include <Gdi/VirtualScreen.h>
#include <Win32/DisplayMode.h>
namespace
{
CompatWeakPtr<IDirectDrawSurface7> g_primarySurface;
CompatWeakPtr<IDirectDrawSurface7> g_gdiPrimarySurface;
D3dDdi::Device* g_device = nullptr;
HANDLE g_gdiResourceHandle = nullptr;
HANDLE g_frontResource = nullptr;
DWORD g_origCaps = 0;
HWND g_deviceWindow = nullptr;
HPALETTE g_palette = nullptr;
std::wstring g_deviceName;
RECT g_monitorRect = {};
CompatPtr<IDirectDrawSurface7> createGdiPrimarySurface(CompatRef<IDirectDraw> dd)
{
LOG_FUNC("PrimarySurface::createGdiPrimarySurface", &dd);
auto ddLcl = DDraw::DirectDraw::getInt(dd.get()).lpLcl;
auto tagSurface = DDraw::TagSurface::get(ddLcl);
if (!tagSurface)
{
return LOG_RESULT(nullptr);
}
auto resource = DDraw::DirectDrawSurface::getDriverResourceHandle(*tagSurface->getDDS());
if (!resource)
{
return LOG_RESULT(nullptr);
}
auto device = D3dDdi::Device::findDeviceByResource(resource);
if (!device)
{
return LOG_RESULT(nullptr);
}
auto& repo = D3dDdi::SurfaceRepository::get(device->getAdapter());
D3dDdi::SurfaceRepository::Surface surface = {};
repo.getSurface(surface, 1, 1, DDraw::DirectDraw::getRgbPixelFormat(32), DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY);
LOG_RESULT(surface.surface.get());
return surface.surface;
}
}
namespace DDraw
@ -34,6 +70,7 @@ namespace DDraw
g_gdiResourceHandle = nullptr;
g_frontResource = nullptr;
g_primarySurface = nullptr;
g_gdiPrimarySurface.release();
g_origCaps = 0;
g_deviceWindow = nullptr;
if (g_palette)
@ -41,6 +78,7 @@ namespace DDraw
DeleteObject(g_palette);
g_palette = nullptr;
}
g_deviceName.clear();
g_monitorRect = {};
s_palette = nullptr;
@ -59,13 +97,16 @@ namespace DDraw
}
const auto& dm = DDraw::DirectDraw::getDisplayMode(*CompatPtr<IDirectDraw7>::from(&dd));
g_monitorRect = D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr<IDirectDraw7>::from(&dd)).monitorInfo.rcMonitor;
g_deviceName = D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr<IDirectDraw7>::from(&dd)).deviceName;
g_monitorRect = Win32::DisplayMode::getMonitorInfo(g_deviceName).rcMonitor;
g_monitorRect.right = g_monitorRect.left + dm.dwWidth;
g_monitorRect.bottom = g_monitorRect.top + dm.dwHeight;
HRESULT result = RealPrimarySurface::create(*CompatPtr<IDirectDraw>::from(&dd));
if (FAILED(result))
{
g_deviceName.clear();
g_monitorRect = {};
return LOG_RESULT(result);
}
@ -85,6 +126,7 @@ namespace DDraw
if (FAILED(result))
{
LOG_INFO << "ERROR: Failed to create the compat primary surface: " << Compat::hex(result);
g_deviceName.clear();
g_monitorRect = {};
RealPrimarySurface::release();
return LOG_RESULT(result);
@ -92,6 +134,7 @@ namespace DDraw
g_origCaps = origCaps;
g_deviceWindow = *DDraw::DirectDraw::getDeviceWindowPtr(dd.get());
g_gdiPrimarySurface = createGdiPrimarySurface(*CompatPtr<IDirectDraw>::from(&dd)).detach();
if (desc.ddpfPixelFormat.dwRGBBitCount <= 8)
{
@ -190,6 +233,51 @@ namespace DDraw
return surface;
}
CompatWeakPtr<IDirectDrawSurface7> PrimarySurface::getGdiPrimary()
{
LOG_FUNC("PrimarySurface::getGdiPrimary");
if (!g_primarySurface || !g_gdiPrimarySurface)
{
return LOG_RESULT(nullptr);
}
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
g_primarySurface->GetSurfaceDesc(g_primarySurface, &desc);
g_monitorRect = Win32::DisplayMode::getMonitorInfo(g_deviceName).rcMonitor;
g_monitorRect.right = g_monitorRect.left + desc.dwWidth;
g_monitorRect.bottom = g_monitorRect.top + desc.dwHeight;
desc = Gdi::VirtualScreen::getSurfaceDesc(g_monitorRect);
DDSURFACEDESC2 prevDesc = {};
prevDesc.dwSize = sizeof(prevDesc);
g_gdiPrimarySurface->Lock(g_gdiPrimarySurface, nullptr, &prevDesc, DDLOCK_WAIT, nullptr);
g_gdiPrimarySurface->Unlock(g_gdiPrimarySurface, nullptr);
if (desc.dwWidth != prevDesc.dwWidth ||
desc.dwHeight != prevDesc.dwHeight ||
desc.lPitch != prevDesc.lPitch ||
desc.lpSurface != prevDesc.lpSurface ||
0 != memcmp(&desc.ddpfPixelFormat, &prevDesc.ddpfPixelFormat, sizeof(desc.ddpfPixelFormat)))
{
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_LPSURFACE | DDSD_PIXELFORMAT;
if (FAILED(g_gdiPrimarySurface->SetSurfaceDesc(g_gdiPrimarySurface, &desc, 0)))
{
return LOG_RESULT(nullptr);
}
if (desc.dwWidth != prevDesc.dwWidth ||
desc.dwHeight != prevDesc.dwHeight)
{
DDraw::RealPrimarySurface::restore();
}
}
return LOG_RESULT(g_gdiPrimarySurface.get());
}
CompatWeakPtr<IDirectDrawSurface7> PrimarySurface::getPrimary()
{
return g_primarySurface;

View File

@ -22,6 +22,7 @@ namespace DDraw
static CompatPtr<IDirectDrawSurface7> getBackBuffer();
static CompatPtr<IDirectDrawSurface7> getLastSurface();
static RECT getMonitorRect();
static CompatWeakPtr<IDirectDrawSurface7> getGdiPrimary();
static CompatWeakPtr<IDirectDrawSurface7> getPrimary();
static HANDLE getFrontResource();
static DWORD getOrigCaps();

View File

@ -73,7 +73,6 @@ namespace
std::wstring getDeviceName(const Char* deviceName);
HMONITOR getMonitorFromDc(HDC dc);
MONITORINFO getMonitorInfo(const std::wstring& deviceName);
template <typename Char>
std::map<SIZE, std::set<DWORD>> getSupportedDisplayModeMap(const Char* deviceName, DWORD flags);
@ -224,7 +223,7 @@ namespace
g_emulatedDisplayMode.refreshRate = currDevMode.dmDisplayFrequency;
g_emulatedDisplayMode.deviceName = getDeviceName(lpszDeviceName);
g_emulatedDisplayMode.rect = getMonitorInfo(g_emulatedDisplayMode.deviceName).rcMonitor;
g_emulatedDisplayMode.rect = Win32::DisplayMode::getMonitorInfo(g_emulatedDisplayMode.deviceName).rcMonitor;
g_emulatedDisplayMode.rect.right = g_emulatedDisplayMode.rect.left + emulatedResolution.cx;
g_emulatedDisplayMode.rect.bottom = g_emulatedDisplayMode.rect.top + emulatedResolution.cy;
g_emulatedDisplayMode.diff.cx = emulatedResolution.cx - currDevMode.dmPelsWidth;
@ -542,14 +541,6 @@ namespace
return TRUE;
}
MONITORINFO getMonitorInfo(const std::wstring& deviceName)
{
MONITORINFOEXW mi = {};
wcscpy_s(mi.szDevice, deviceName.c_str());
EnumDisplayMonitors(nullptr, nullptr, &getMonitorInfoEnum, reinterpret_cast<LPARAM>(&mi));
return mi;
}
BOOL WINAPI getMonitorInfoA(HMONITOR hMonitor, LPMONITORINFO lpmi)
{
LOG_FUNC("GetMonitorInfoA", hMonitor, lpmi);
@ -739,6 +730,14 @@ namespace Win32
return g_emulatedDisplayMode;
}
MONITORINFOEXW getMonitorInfo(const std::wstring& deviceName)
{
MONITORINFOEXW mi = {};
wcscpy_s(mi.szDevice, deviceName.c_str());
EnumDisplayMonitors(nullptr, nullptr, &getMonitorInfoEnum, reinterpret_cast<LPARAM>(&mi));
return mi;
}
ULONG queryDisplaySettingsUniqueness()
{
return CALL_ORIG_FUNC(GdiEntry13)();

View File

@ -27,6 +27,7 @@ namespace Win32
DWORD getBpp();
EmulatedDisplayMode getEmulatedDisplayMode();
MONITORINFOEXW getMonitorInfo(const std::wstring& deviceName);
ULONG queryDisplaySettingsUniqueness();
void installHooks();