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

Reduced flickering when changing display modes in scaled fullscreen mode

This commit is contained in:
narzoul 2023-07-15 10:47:38 +02:00
parent 4a40d97fe9
commit 4c4051a844
5 changed files with 91 additions and 93 deletions

View File

@ -371,10 +371,8 @@ namespace D3dDdi
void fixPresent(D3DKMT_PRESENT& data) void fixPresent(D3DKMT_PRESENT& data)
{ {
static RECT rect = {}; static RECT rect = {};
HWND devicePresentationWindow = DDraw::RealPrimarySurface::getDevicePresentationWindow();
HWND presentationWindow = DDraw::RealPrimarySurface::getPresentationWindow(); HWND presentationWindow = DDraw::RealPrimarySurface::getPresentationWindow();
if (devicePresentationWindow && devicePresentationWindow == data.hWindow || if (presentationWindow && presentationWindow == data.hWindow)
presentationWindow && presentationWindow == data.hWindow)
{ {
rect = DDraw::RealPrimarySurface::getMonitorRect(); rect = DDraw::RealPrimarySurface::getMonitorRect();
OffsetRect(&rect, -rect.left, -rect.top); OffsetRect(&rect, -rect.left, -rect.top);

View File

@ -42,6 +42,7 @@ namespace
const unsigned DELAYED_FLIP_MODE_TIMEOUT_MS = 200; const unsigned DELAYED_FLIP_MODE_TIMEOUT_MS = 200;
void onRelease(); void onRelease();
void updatePresentationWindow();
CompatWeakPtr<IDirectDrawSurface7> g_defaultPrimary; CompatWeakPtr<IDirectDrawSurface7> g_defaultPrimary;
@ -69,10 +70,10 @@ namespace
UINT g_flipEndVsyncCount = 0; UINT g_flipEndVsyncCount = 0;
UINT g_presentEndVsyncCount = 0; UINT g_presentEndVsyncCount = 0;
HWND g_devicePresentationWindow = nullptr;
HWND g_deviceWindow = nullptr; HWND g_deviceWindow = nullptr;
HWND* g_deviceWindowPtr = nullptr; HWND* g_deviceWindowPtr = nullptr;
HWND g_presentationWindow = nullptr; HWND g_presentationWindow = nullptr;
long long g_qpcUpdatePresentationWindow = 0;
CompatPtr<IDirectDrawSurface7> getBackBuffer(); CompatPtr<IDirectDrawSurface7> getBackBuffer();
CompatPtr<IDirectDrawSurface7> getLastSurface(); CompatPtr<IDirectDrawSurface7> getLastSurface();
@ -98,6 +99,7 @@ namespace
bbResource->presentationBlt(blt, srcResource); bbResource->presentationBlt(blt, srcResource);
} }
updatePresentationWindow();
Gdi::Window::present(*g_frontBuffer, *g_windowedBackBuffer, *g_clipper); Gdi::Window::present(*g_frontBuffer, *g_windowedBackBuffer, *g_clipper);
return; return;
} }
@ -258,20 +260,31 @@ namespace
return 1; return 1;
} }
bool isProcessActive()
{
const HWND foregroundWindow = GetForegroundWindow();
if (foregroundWindow)
{
DWORD pid = 0;
GetWindowThreadProcessId(foregroundWindow, &pid);
return GetCurrentProcessId() == pid;
}
return false;
}
void onRelease() void onRelease()
{ {
LOG_FUNC("RealPrimarySurface::onRelease"); LOG_FUNC("RealPrimarySurface::onRelease");
if (g_presentationWindow) if (g_windowedBackBuffer)
{ {
auto resource = D3dDdi::Device::findResource( auto resource = D3dDdi::Device::findResource(
DDraw::DirectDrawSurface::getDriverResourceHandle(*g_windowedBackBuffer)); DDraw::DirectDrawSurface::getDriverResourceHandle(*g_windowedBackBuffer));
resource->setFullscreenMode(false); resource->setFullscreenMode(false);
Gdi::GuiThread::destroyWindow(g_presentationWindow);
g_presentationWindow = nullptr;
} }
DDraw::RealPrimarySurface::schedulePresentationWindowUpdate();
g_defaultPrimary = nullptr; g_defaultPrimary = nullptr;
g_frontBuffer = nullptr; g_frontBuffer = nullptr;
g_lastFlipSurface = nullptr; g_lastFlipSurface = nullptr;
@ -281,8 +294,6 @@ namespace
g_surfaceDesc = {}; g_surfaceDesc = {};
g_tagSurface = nullptr; g_tagSurface = nullptr;
DDraw::RealPrimarySurface::updateDevicePresentationWindowPos();
g_devicePresentationWindow = nullptr;
g_deviceWindow = nullptr; g_deviceWindow = nullptr;
g_deviceWindowPtr = nullptr; g_deviceWindowPtr = nullptr;
g_monitorRect = {}; g_monitorRect = {};
@ -316,6 +327,8 @@ namespace
D3dDdi::Device::setGdiResourceHandle(gdiResource); D3dDdi::Device::setGdiResourceHandle(gdiResource);
} }
updatePresentationWindow();
Compat::ScopedCriticalSection lock(g_presentCs); Compat::ScopedCriticalSection lock(g_presentCs);
g_isOverlayUpdatePending = false; g_isOverlayUpdatePending = false;
g_isUpdatePending = false; g_isUpdatePending = false;
@ -378,49 +391,46 @@ namespace
presentToPrimaryChain(src); presentToPrimaryChain(src);
if (g_isFullscreen && g_devicePresentationWindow) if (g_isFullscreen)
{ {
*g_deviceWindowPtr = g_devicePresentationWindow; updatePresentationWindow();
*g_deviceWindowPtr = g_presentationWindow;
g_frontBuffer->Flip(g_frontBuffer, getBackBuffer(), DDFLIP_WAIT); g_frontBuffer->Flip(g_frontBuffer, getBackBuffer(), DDFLIP_WAIT);
*g_deviceWindowPtr = g_deviceWindow; *g_deviceWindowPtr = g_deviceWindow;
} }
g_presentEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + 1; g_presentEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + 1;
} }
void updatePresentationWindowPos() void updatePresentationWindow()
{ {
if (!g_presentationWindow) LOG_FUNC("RealPrimarySurface::updatePresentationWindow");
{ const bool isFullscreen = isProcessActive() && (g_isFullscreen ||
return; g_frontBuffer && DDraw::PrimarySurface::getPrimary() && SUCCEEDED(g_frontBuffer->IsLost(g_frontBuffer)) &&
} Gdi::Window::hasFullscreenWindow());
bool isFullscreen = false; if (g_windowedBackBuffer)
if (SUCCEEDED(g_frontBuffer->IsLost(g_frontBuffer)) && DDraw::PrimarySurface::getPrimary())
{ {
HWND foregroundWindow = GetForegroundWindow(); auto resource = D3dDdi::Device::findResource(
if (foregroundWindow) DDraw::DirectDrawSurface::getDriverResourceHandle(*g_windowedBackBuffer));
{ resource->setFullscreenMode(isFullscreen);
DWORD pid = 0;
GetWindowThreadProcessId(foregroundWindow, &pid);
isFullscreen = GetCurrentProcessId() == pid && Gdi::Window::hasFullscreenWindow();
}
} }
auto resource = D3dDdi::Device::findResource(
DDraw::DirectDrawSurface::getDriverResourceHandle(*g_windowedBackBuffer));
resource->setFullscreenMode(isFullscreen);
Gdi::GuiThread::execute([&]() Gdi::GuiThread::execute([&]()
{ {
if (isFullscreen) if (isFullscreen)
{ {
if (!g_presentationWindow)
{
g_presentationWindow = Gdi::PresentationWindow::create(nullptr);
}
CALL_ORIG_FUNC(SetWindowPos)(g_presentationWindow, HWND_TOPMOST, g_monitorRect.left, g_monitorRect.top, CALL_ORIG_FUNC(SetWindowPos)(g_presentationWindow, HWND_TOPMOST, g_monitorRect.left, g_monitorRect.top,
g_monitorRect.right - g_monitorRect.left, g_monitorRect.bottom - g_monitorRect.top, g_monitorRect.right - g_monitorRect.left, g_monitorRect.bottom - g_monitorRect.top,
SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW | SWP_NOOWNERZORDER | SWP_SHOWWINDOW); SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW | SWP_NOOWNERZORDER | SWP_SHOWWINDOW);
} }
else if (IsWindowVisible(g_presentationWindow)) else if (g_presentationWindow)
{ {
ShowWindow(g_presentationWindow, SW_HIDE); DestroyWindow(g_presentationWindow);
g_presentationWindow = nullptr;
} }
}); });
} }
@ -513,21 +523,8 @@ namespace DDraw
g_deviceWindowPtr = (0 != desc.dwBackBufferCount) ? DDraw::DirectDraw::getDeviceWindowPtr(dd.get()) : nullptr; g_deviceWindowPtr = (0 != desc.dwBackBufferCount) ? DDraw::DirectDraw::getDeviceWindowPtr(dd.get()) : nullptr;
g_deviceWindow = g_deviceWindowPtr ? *g_deviceWindowPtr : nullptr; g_deviceWindow = g_deviceWindowPtr ? *g_deviceWindowPtr : nullptr;
g_devicePresentationWindow = g_deviceWindow ? Gdi::Window::getPresentationWindow(g_deviceWindow) : nullptr;
if (0 == desc.dwBackBufferCount)
{
auto mr = DDraw::PrimarySurface::getMonitorRect();
if (!EqualRect(&mr, &g_monitorRect))
{
g_presentationWindow = Gdi::PresentationWindow::create(nullptr);
updatePresentationWindowPos();
}
}
onRestore(); onRestore();
updateDevicePresentationWindowPos();
return DD_OK; return DD_OK;
} }
@ -598,6 +595,21 @@ namespace DDraw
lastOverlayCheckVsyncCount = vsyncCount; lastOverlayCheckVsyncCount = vsyncCount;
} }
bool isPresentationWindowUpdateNeeded = false;
{
Compat::ScopedCriticalSection lock(g_presentCs);
isPresentationWindowUpdateNeeded =
0 != g_qpcUpdatePresentationWindow && Time::queryPerformanceCounter() - g_qpcUpdatePresentationWindow >= 0 ||
g_presentationWindow && !isProcessActive();
}
if (isPresentationWindowUpdateNeeded)
{
g_qpcUpdatePresentationWindow = 0;
updatePresentationWindow();
}
{ {
Compat::ScopedCriticalSection lock(g_presentCs); Compat::ScopedCriticalSection lock(g_presentCs);
if (!g_isUpdateReady) if (!g_isUpdateReady)
@ -639,7 +651,6 @@ namespace DDraw
{ {
restore(); restore();
} }
updatePresentationWindowPos();
auto primary(DDraw::PrimarySurface::getPrimary()); auto primary(DDraw::PrimarySurface::getPrimary());
CompatWeakPtr<IDirectDrawSurface7> src; CompatWeakPtr<IDirectDrawSurface7> src;
@ -667,11 +678,6 @@ namespace DDraw
return 0; return 0;
} }
HWND RealPrimarySurface::getDevicePresentationWindow()
{
return g_devicePresentationWindow;
}
HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData) HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData)
{ {
DDraw::ScopedThreadLock lock; DDraw::ScopedThreadLock lock;
@ -701,15 +707,7 @@ namespace DDraw
HWND RealPrimarySurface::getTopmost() HWND RealPrimarySurface::getTopmost()
{ {
if (g_isFullscreen && g_devicePresentationWindow) return g_presentationWindow ? g_presentationWindow : HWND_TOPMOST;
{
return g_devicePresentationWindow;
}
if (g_presentationWindow && IsWindowVisible(g_presentationWindow))
{
return g_presentationWindow;
}
return HWND_TOPMOST;
} }
void RealPrimarySurface::init() void RealPrimarySurface::init()
@ -774,6 +772,12 @@ namespace DDraw
g_isUpdateReady = false; g_isUpdateReady = false;
} }
void RealPrimarySurface::schedulePresentationWindowUpdate()
{
Compat::ScopedCriticalSection lock(g_presentCs);
g_qpcUpdatePresentationWindow = Time::queryPerformanceCounter() + Time::g_qpcFrequency / 5;
}
HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData) HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData)
{ {
DDraw::ScopedThreadLock lock; DDraw::ScopedThreadLock lock;
@ -786,6 +790,18 @@ namespace DDraw
return gammaControl->SetGammaRamp(gammaControl, 0, rampData); return gammaControl->SetGammaRamp(gammaControl, 0, rampData);
} }
void RealPrimarySurface::setPresentationWindowTopmost()
{
Gdi::GuiThread::execute([&]()
{
if (g_presentationWindow)
{
CALL_ORIG_FUNC(SetWindowPos)(g_presentationWindow, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW | SWP_NOOWNERZORDER);
}
});
}
void RealPrimarySurface::setUpdateReady() void RealPrimarySurface::setUpdateReady()
{ {
Compat::ScopedCriticalSection lock(g_presentCs); Compat::ScopedCriticalSection lock(g_presentCs);
@ -796,28 +812,6 @@ namespace DDraw
} }
} }
void RealPrimarySurface::updateDevicePresentationWindowPos()
{
if (!g_devicePresentationWindow)
{
return;
}
Gdi::GuiThread::execute([&]()
{
if (IsWindowVisible(g_deviceWindow) && !IsIconic(g_deviceWindow))
{
CALL_ORIG_FUNC(SetWindowPos)(g_devicePresentationWindow, HWND_TOPMOST, g_monitorRect.left, g_monitorRect.top,
g_monitorRect.right - g_monitorRect.left, g_monitorRect.bottom - g_monitorRect.top,
SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW | SWP_NOOWNERZORDER | SWP_SHOWWINDOW);
}
else
{
Gdi::Window::updatePresentationWindowPos(g_devicePresentationWindow, g_deviceWindow);
}
});
}
bool RealPrimarySurface::waitForFlip(CompatWeakPtr<IDirectDrawSurface7> surface) bool RealPrimarySurface::waitForFlip(CompatWeakPtr<IDirectDrawSurface7> surface)
{ {
auto primary(DDraw::PrimarySurface::getPrimary()); auto primary(DDraw::PrimarySurface::getPrimary());

View File

@ -16,7 +16,6 @@ namespace DDraw
static void destroyDefaultPrimary(); static void destroyDefaultPrimary();
static HRESULT flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags); static HRESULT flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags);
static int flush(); static int flush();
static HWND getDevicePresentationWindow();
static HWND getPresentationWindow(); static HWND getPresentationWindow();
static HRESULT getGammaRamp(DDGAMMARAMP* rampData); static HRESULT getGammaRamp(DDGAMMARAMP* rampData);
static RECT getMonitorRect(); static RECT getMonitorRect();
@ -29,9 +28,10 @@ namespace DDraw
static HRESULT restore(); static HRESULT restore();
static void scheduleOverlayUpdate(); static void scheduleOverlayUpdate();
static void scheduleUpdate(); static void scheduleUpdate();
static void schedulePresentationWindowUpdate();
static HRESULT setGammaRamp(DDGAMMARAMP* rampData); static HRESULT setGammaRamp(DDGAMMARAMP* rampData);
static void setPresentationWindowTopmost();
static void setUpdateReady(); static void setUpdateReady();
static void updateDevicePresentationWindowPos();
static bool waitForFlip(CompatWeakPtr<IDirectDrawSurface7> surface); static bool waitForFlip(CompatWeakPtr<IDirectDrawSurface7> surface);
static void waitForFlipFpsLimit(); static void waitForFlipFpsLimit();
}; };

View File

@ -431,6 +431,7 @@ namespace
if (Gdi::Window::isTopLevelWindow(hwnd)) if (Gdi::Window::isTopLevelWindow(hwnd))
{ {
DDraw::RealPrimarySurface::setPresentationWindowTopmost();
Gdi::Window::updateAll(); Gdi::Window::updateAll();
} }
@ -578,6 +579,7 @@ namespace
BOOL WINAPI setWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags) BOOL WINAPI setWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags)
{ {
LOG_FUNC("SetWindowPos", hWnd, hWndInsertAfter, X, Y, cx, cy, Compat::hex(uFlags)); LOG_FUNC("SetWindowPos", hWnd, hWndInsertAfter, X, Y, cx, cy, Compat::hex(uFlags));
if (uFlags & SWP_NOSENDCHANGING) if (uFlags & SWP_NOSENDCHANGING)
{ {
WINDOWPOS wp = {}; WINDOWPOS wp = {};
@ -591,6 +593,18 @@ namespace
onWindowPosChanging(hWnd, wp); onWindowPosChanging(hWnd, wp);
uFlags = wp.flags; uFlags = wp.flags;
} }
if ((uFlags & SWP_NOACTIVATE) && !(uFlags && SWP_NOZORDER) &&
(HWND_TOP == hWndInsertAfter || HWND_TOPMOST == hWndInsertAfter) &&
(GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_TOPMOST))
{
const HWND topmost = DDraw::RealPrimarySurface::getTopmost();
if (topmost != HWND_TOPMOST)
{
hWndInsertAfter = topmost;
}
}
return LOG_RESULT(CALL_ORIG_FUNC(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags)); return LOG_RESULT(CALL_ORIG_FUNC(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags));
} }

View File

@ -350,15 +350,7 @@ namespace
Gdi::GuiThread::setWindowRgn(it->second.presentationWindow, it->second.windowRegion); Gdi::GuiThread::setWindowRgn(it->second.presentationWindow, it->second.windowRegion);
} }
const HWND devicePresentationWindow = DDraw::RealPrimarySurface::getDevicePresentationWindow(); Gdi::Window::updatePresentationWindowPos(it->second.presentationWindow, hwnd);
if (DDraw::RealPrimarySurface::isFullscreen() && devicePresentationWindow == it->second.presentationWindow)
{
DDraw::RealPrimarySurface::updateDevicePresentationWindowPos();
}
else
{
Gdi::Window::updatePresentationWindowPos(it->second.presentationWindow, hwnd);
}
} }
} }
return TRUE; return TRUE;