From 2cebaa75090c37234d7c5816db3a4d395b1a250a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 8 Jul 2025 15:08:19 +0200 Subject: [PATCH 1/2] Win32: Paint the window black earlier during startup --- Windows/MainWindow.cpp | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index d225c2a1d4fb..d832af24f3e8 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -152,8 +152,8 @@ namespace MainWindow bool trapMouse = true; // Handles some special cases(alt+tab, win menu) when game is running and mouse is confined #define MAX_LOADSTRING 100 - const TCHAR *szWindowClass = TEXT("PPSSPPWnd"); - const TCHAR *szDisplayClass = TEXT("PPSSPPDisplay"); + const wchar_t *const szWindowClass = L"PPSSPPWnd"; + const wchar_t *const szDisplayClass = L"PPSSPPDisplay"; // Forward declarations of functions included in this code module: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); @@ -180,7 +180,7 @@ namespace MainWindow wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.hInstance = hInstance; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = NULL; // Always covered by display window + wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wcex.lpszMenuName = (LPCWSTR)IDR_MENU1; wcex.lpszClassName = szWindowClass; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_PPSSPP); @@ -544,8 +544,10 @@ namespace MainWindow WindowsRawInput::Init(); - SetFocus(hwndMain); + UpdateWindow(hwndMain); + UpdateWindow(hwndDisplay); + SetFocus(hwndMain); return TRUE; } @@ -615,8 +617,6 @@ namespace MainWindow } LRESULT CALLBACK DisplayProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - static bool firstErase = true; - switch (message) { case WM_SIZE: break; @@ -652,12 +652,7 @@ namespace MainWindow break; case WM_ERASEBKGND: - if (firstErase) { - firstErase = false; - // Paint black on first erase while OpenGL stuff is loading - return DefWindowProc(hWnd, message, wParam, lParam); - } - // Then never erase, let the OpenGL drawing take care of everything. + // Don't erase, let OpenGL take care of it. return 1; // Mouse input. We send asynchronous touch events for minimal latency. @@ -852,8 +847,11 @@ namespace MainWindow return darkResult; } + static bool first = true; + switch (message) { case WM_CREATE: + first = true; if (!IsVistaOrHigher()) { // Remove the D3D11 choice on versions below XP RemoveMenu(GetMenu(hWnd), ID_OPTIONS_DIRECT3D11, MF_BYCOMMAND); @@ -968,8 +966,18 @@ namespace MainWindow break; case WM_ERASEBKGND: - // This window is always covered by DisplayWindow. No reason to erase. - return 0; + { + if (first) { + // Manually clearing on first boot looks better (only a brief white flash which I can't seem + // to get rid of). + HDC hdc = (HDC)wParam; + RECT rc; + GetClientRect(hWnd, &rc); + FillRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH)); + first = false; + } + return 1; + } case WM_MOVE: SavePosition(); @@ -1114,7 +1122,7 @@ namespace MainWindow // TODO: Translate? Or just not bother? MessageBox(hwndMain, L"You can only load one file at a time", L"Error", MB_ICONINFORMATION); } else { - TCHAR filename[1024]; + wchar_t filename[1024]; if (DragQueryFile(hdrop, 0, filename, ARRAY_SIZE(filename)) != 0) { const std::string utf8_filename = ReplaceAll(ConvertWStringToUTF8(filename), "\\", "/"); System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, utf8_filename); From a80756574cc1a08e85edd1856a3e6d3f6fa60168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 8 Jul 2025 20:08:23 +0200 Subject: [PATCH 2/2] Fix really wacky edge case when switching to Auto FrameSkip from "skip buffered" mode. Fixes issue #20596 --- Core/Config.cpp | 3 --- Core/HLE/sceDisplay.cpp | 10 +++++++++- GPU/Common/FramebufferManagerCommon.cpp | 2 ++ GPU/Common/FramebufferManagerCommon.h | 4 ++++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index ad76481c46d2..6542a552d607 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1460,9 +1460,6 @@ void Config::PostLoadCleanup(bool gameSpecific) { if (g_Config.sCustomDriver == "Default") { g_Config.sCustomDriver = ""; } - - // Convert old volume settings. - } void Config::PreSaveCleanup(bool gameSpecific) { diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index aa2258f35d3e..fe6436498c60 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -425,7 +425,7 @@ static void DoFrameTiming(bool throttle, bool *skipFrame, float scaledTimestep, // Auto-frameskip automatically if speed limit is set differently than the default. int frameSkipNum = DisplayCalculateFrameSkip(); - if (g_Config.bAutoFrameSkip) { + if (g_Config.bAutoFrameSkip && !g_Config.bSkipBufferEffects) { // autoframeskip // Argh, we are falling behind! Let's skip a frame and see if we catch up. if (curFrameTime > nextFrameTime && doFrameSkip) { @@ -694,6 +694,14 @@ void __DisplayFlip(int cyclesLate) { } else { gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME; numSkippedFrames = 0; + + // NOTE!! It can happen that if we just toggled frameskip (especially auto), we are still in a state + // where we don't have a framebuffer bound, from the last frame. But framebuffermanager still might think + // that we're in non-buffered mode. + if (gpu->GetFramebufferManagerCommon() && !gpu->GetFramebufferManagerCommon()->UseBufferedRendering() && !g_Config.bSkipBufferEffects) { + gpu->GetFramebufferManagerCommon()->ForceUseBufferedRendering(!g_Config.bSkipBufferEffects); + gstate_c.skipDrawReason &= ~SKIPDRAW_NON_DISPLAYED_FB; + } } // Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame). diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 171e5e72cbbb..02c671621e5a 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1707,6 +1707,8 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) { } else if (useBufferedRendering_) { WARN_LOG(Log::FrameBuf, "Using buffered rendering, and current VFB lacks an FBO: %08x", vfb->fb_address); } else { + // This is OK because here we're in "skip buffered" mode, so even if we haven't presented + // we will have a render target. presentation_->NotifyPresent(); } diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 63a3bf011f84..14241912929d 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -385,6 +385,10 @@ class FramebufferManagerCommon { return useBufferedRendering_; } + void ForceUseBufferedRendering(bool buf) { + useBufferedRendering_ = true; + } + // TODO: Maybe just include the last depth buffer address in this, too. bool MayIntersectFramebufferColor(u32 start) const { // Clear the cache/kernel bits.