From d8ccea3ca020ddc8d0097df36b90404a69ca3d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 15 Feb 2026 19:42:17 +0100 Subject: [PATCH 1/4] Fix crash in crash screen (oops...) --- UI/EmuScreen.cpp | 10 ++++++---- UI/EmuScreen.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 3317196267..aa54dea09c 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1693,6 +1693,10 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { checkPowerDown(); if (hasVisibleUI()) { + if (clearColor_) { + // This is used on the exception bluescreen for example. + draw->Clear(Draw::Aspect::COLOR_BIT, clearColor_, 0.0f, 0); + } cardboardDisableButton_->SetVisibility(displayLayoutConfig.bEnableCardboardVR ? UI::V_VISIBLE : UI::V_GONE); renderUI(); } @@ -1721,8 +1725,8 @@ ScreenRenderFlags EmuScreen::RunEmulation(bool skipBufferEffects) { const Draw::Viewport viewport{0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f}; PSP_UpdateDebugStats((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS || g_Config.bLogFrameDrops); + clearColor_ = 0; bool blockedExecution = Achievements::IsBlockingExecution(); - uint32_t clearColor = 0; if (!blockedExecution) { // We process savestates before running the frame. SaveState::Process(); @@ -1762,9 +1766,7 @@ ScreenRenderFlags EmuScreen::RunEmulation(bool skipBufferEffects) { if (info.type != MIPSExceptionType::NONE) { // Clear to blue background screen bool dangerousSettings = !Reporting::IsSupported(); - clearColor = dangerousSettings ? 0xFF900050 : 0xFF900000; - draw->Clear(Draw::Aspect::COLOR_BIT, clearColor, 0.0f, 0); - // The info is drawn later in renderUI + clearColor_ = dangerousSettings ? 0xFF900050 : 0xFF900000; } else { // If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output. // This won't work in non-buffered, but that's fine. diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 81dd030ae2..2de11fe2af 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -167,6 +167,8 @@ private: bool skipBufferEffects_ = false; // cached state, fetched once per frame. bool isOnTop_ = true; + + uint32_t clearColor_ = 0; }; bool MustRunBehind(); From 1cc2b7233acbce69cf0d1e7e3b20d1dbdfa7bc50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 15 Feb 2026 19:57:16 +0100 Subject: [PATCH 2/4] Fix some minor visual UI issues See #21223 --- Common/GPU/OpenGL/GLFeatures.cpp | 1 - Common/GPU/OpenGL/GLFeatures.h | 1 - Common/Math/geom2d.h | 12 +++++++----- Common/UI/View.cpp | 9 ++++++--- UI/GameScreen.cpp | 3 ++- UI/GameSettingsScreen.cpp | 2 +- UI/MiscScreens.cpp | 4 ++-- 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Common/GPU/OpenGL/GLFeatures.cpp b/Common/GPU/OpenGL/GLFeatures.cpp index 2b798142ca..c28807ad4b 100644 --- a/Common/GPU/OpenGL/GLFeatures.cpp +++ b/Common/GPU/OpenGL/GLFeatures.cpp @@ -360,7 +360,6 @@ bool CheckGLExtensions() { gl_extensions.ARB_conservative_depth = g_set_gl_extensions.count("GL_ARB_conservative_depth") != 0; gl_extensions.ARB_shader_image_load_store = (g_set_gl_extensions.count("GL_ARB_shader_image_load_store") != 0) || (g_set_gl_extensions.count("GL_EXT_shader_image_load_store") != 0); gl_extensions.ARB_shading_language_420pack = (g_set_gl_extensions.count("GL_ARB_shading_language_420pack") != 0); - gl_extensions.EXT_bgra = g_set_gl_extensions.count("GL_EXT_bgra") != 0; gl_extensions.EXT_gpu_shader4 = g_set_gl_extensions.count("GL_EXT_gpu_shader4") != 0; gl_extensions.NV_framebuffer_blit = g_set_gl_extensions.count("GL_NV_framebuffer_blit") != 0; gl_extensions.NV_copy_image = g_set_gl_extensions.count("GL_NV_copy_image") != 0; diff --git a/Common/GPU/OpenGL/GLFeatures.h b/Common/GPU/OpenGL/GLFeatures.h index 49948c8239..acd6cd75bf 100644 --- a/Common/GPU/OpenGL/GLFeatures.h +++ b/Common/GPU/OpenGL/GLFeatures.h @@ -90,7 +90,6 @@ struct GLExtensions { bool EXT_swap_control_tear; bool EXT_discard_framebuffer; bool EXT_unpack_subimage; // always supported on desktop and ES3 - bool EXT_bgra; bool EXT_shader_framebuffer_fetch; bool EXT_gpu_shader4; bool EXT_blend_minmax; diff --git a/Common/Math/geom2d.h b/Common/Math/geom2d.h index 155e34eec7..32b510c03b 100644 --- a/Common/Math/geom2d.h +++ b/Common/Math/geom2d.h @@ -97,28 +97,30 @@ struct Bounds { Point2D Center() const { return Point2D(centerX(), centerY()); } + [[nodiscard]] Bounds Expand(float amount) const { return Bounds(x - amount, y - amount, w + amount * 2, h + amount * 2); } + [[nodiscard]] Bounds Expand(float xAmount, float yAmount) const { return Bounds(x - xAmount, y - yAmount, w + xAmount * 2, h + yAmount * 2); } + [[nodiscard]] Bounds Expand(float left, float top, float right, float bottom) const { return Bounds(x - left, y - top, w + left + right, h + top + bottom); } + [[nodiscard]] Bounds Offset(float xAmount, float yAmount) const { return Bounds(x + xAmount, y + yAmount, w, h); } - Bounds Inset(float left, float top, float right, float bottom) { + [[nodiscard]] + Bounds Inset(float left, float top, float right, float bottom) const { return Bounds(x + left, y + top, w - left - right, h - bottom - top); } - + [[nodiscard]] Bounds Inset(float xAmount, float yAmount) const { return Bounds(x + xAmount, y + yAmount, w - xAmount * 2, h - yAmount * 2); } - Bounds Inset(float left, float top, float right, float bottom) const { - return Bounds(x + left, y + top, w - left - right, h - top - bottom); - } float AspectRatio() const { return w / h; diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index 0e1ce10b92..aa943187a6 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -1059,11 +1059,12 @@ void TextView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz if (bullet_) { availWidth -= bulletOffset; } + availWidth -= pad_.horiz(); const FontStyle *style = GetTextStyle(dc, textSize_); float measuredW; float measuredH; dc.MeasureTextRect(*style, 1.0f, 1.0f, text_, availWidth, &measuredW, &measuredH, textAlign_); - w = measuredW + pad_.horiz(); + w = measuredW; h = measuredH + pad_.vert(); if (bullet_) { w += bulletOffset; @@ -1110,11 +1111,13 @@ void TextView::Draw(UIContext &dc) { textBounds.w -= bulletOffset; } + textBounds = textBounds.Inset(pad_.left, pad_.top, pad_.right, pad_.bottom); + if (shadow_) { uint32_t shadowColor = 0x80000000; - dc.DrawTextRect(text_, textBounds.Offset(1.0f + pad_.left, 1.0f + pad_.top), shadowColor, textAlign_); + dc.DrawTextRect(text_, textBounds.Offset(1.0f, 1.0f), shadowColor, textAlign_); } - dc.DrawTextRect(text_, textBounds.Offset(pad_.left, pad_.top), textColor, textAlign_); + dc.DrawTextRect(text_, textBounds, textColor, textAlign_); if (textSize_ != TextSize::Normal) { // If we changed font style, reset it. dc.SetFontStyle(dc.GetTheme().uiFont); diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index fa809f2233..26856f542f 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -309,12 +309,13 @@ void GameScreen::CreateContentViews(UI::ViewGroup *parent) { if (System_GetPropertyBool(SYSPROP_HAS_TEXT_CLIPBOARD)) { Choice *tvCRCCopy = crcHoriz->Add(new Choice(ImageID("I_FILE_COPY"), new LinearLayoutParams(0.0, Gravity::G_VCENTER))); tvCRCCopy->OnClick.Add([this](UI::EventParams &) { + auto di = GetI18NCategory(I18NCat::DIALOG); u32 crc = Reporting::RetrieveCRC(gamePath_); char buffer[16]; snprintf(buffer, sizeof(buffer), "%08X", crc); System_CopyStringToClipboard(buffer); // Success indication. Not worth a translatable string. - g_OSD.Show(OSDType::MESSAGE_SUCCESS, buffer, 1.0f); + g_OSD.Show(OSDType::MESSAGE_INFO, ApplySafeSubstitutions(di->T("Copied to clipboard: %1"), buffer), 1.0f); }); } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index cfbf412458..c499cf1941 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -116,7 +116,7 @@ public: SettingHint(std::string_view text) : UI::TextView(text, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT)) { SetTextSize(UI::TextSize::Tiny); - SetPadding(UI::Margins(14, 0, 0, 8)); + SetPadding(UI::Margins(14, 0, 12, 8)); SetAlign(FLAG_WRAP_TEXT); } diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index adb47454d3..b86ccfed7f 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -746,8 +746,8 @@ void CreditsScroller::Draw(UIContext &dc) { dc.Begin(); - const Bounds &bounds = bounds_; - bounds.Inset(10.f, 10.f); + Bounds &bounds = bounds_; + bounds = bounds.Inset(10.f, 10.f); const int numItems = ARRAY_SIZE(credits); int itemHeight = 36; int contentsHeight = numItems * itemHeight + bounds.h + 200; From deb3314d46e6dab012b7e780b853ab38380d0ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 15 Feb 2026 20:13:55 +0100 Subject: [PATCH 3/4] Correct alignment in NoticeView See #21223 --- Common/UI/Notice.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Common/UI/Notice.cpp b/Common/UI/Notice.cpp index 9b26fe0b12..60d304b619 100644 --- a/Common/UI/Notice.cpp +++ b/Common/UI/Notice.cpp @@ -44,6 +44,8 @@ NoticeLevel GetNoticeLevel(OSDType type) { } } +constexpr float m = 4.0f; + // Align only matters here for the ASCII-only flag. void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float maxWidth, float *width, float *height, float *height1) { float iconW = 0.0f; @@ -63,7 +65,7 @@ void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text } } - float chromeWidth = iconW + 5.0f + 12.0f; + float chromeWidth = iconW + m * 2.0f + 12.0f; float availableWidth = maxWidth - chromeWidth; // OK, now that we have figured out how much space we have for the text, we can measure it (with wrapping if needed). @@ -84,9 +86,11 @@ void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text *width += chromeWidth; if (height2 == 0.0f && iconH < 2.0f * *height1) { // Center vertically using the icon. - *height1 = std::max(*height1, iconH + 2.0f); + *height1 = std::max(*height1, iconH + m * 2.0f); } - *height = std::max(*height1 + height2 + 8.0f, iconH + 5.0f); + + float heightSum = *height1 + (height2 > 0.0f ? height2 + 8.0f : 0.0f); + *height = std::max(heightSum, iconH + m); } void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float alpha, OSDMessageFlags flags, float timeVal) { @@ -110,7 +114,7 @@ void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level if (texture) { iconW = texture->Width(); iconH = texture->Height(); - dc.Draw()->DrawTexRect(Bounds(bounds.x + 2.5f, bounds.y + 2.5f, iconW, iconH), 0.0f, 0.0f, 1.0f, 1.0f, foreGround); + dc.Draw()->DrawTexRect(Bounds(bounds.x + m, bounds.y + m, iconW, iconH), 0.0f, 0.0f, 1.0f, 1.0f, foreGround); dc.Flush(); dc.RebindTexture(); } @@ -121,7 +125,7 @@ void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level // Atlas icon. dc.Draw()->GetAtlas()->measureImage(iconID, &iconW, &iconH); if (!iconName.empty()) { - Bounds iconBounds = Bounds(bounds.x + 2.5f, bounds.y + 2.5f, iconW, iconH); + Bounds iconBounds = Bounds(bounds.x + m, bounds.y + m, iconW, iconH); // HACK: The RA icon needs some background. if (equals(iconName, "I_RETROACHIEVEMENTS_LOGO")) { dc.FillRect(UI::Drawable(0x50000000), iconBounds.Expand(2.0f)); @@ -130,16 +134,16 @@ void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level if (flags & (OSDMessageFlags::SpinLeft | OSDMessageFlags::SpinRight)) { const float direction = (flags & OSDMessageFlags::SpinLeft) ? -1.5f : 1.5f; - dc.DrawImageRotated(iconID, bounds.x + 2.5f + iconW * 0.5f, bounds.y + 2.5f + iconW * 0.5f, 1.0f, direction * timeVal, foreGround, false); + dc.DrawImageRotated(iconID, bounds.x + m + iconW * 0.5f, bounds.y + m + iconW * 0.5f, 1.0f, direction * timeVal, foreGround, false); } else { - dc.DrawImageVGradient(iconID, foreGround, foreGround, Bounds(bounds.x + 2.5f, bounds.y + 2.5f, iconW, iconH)); + dc.DrawImageVGradient(iconID, foreGround, foreGround, Bounds(bounds.x + m, bounds.y + m, iconW, iconH)); } } } // Make room - bounds.x += iconW + 5.0f; - bounds.w -= iconW + 5.0f; + bounds.x += iconW + m * 2.0f; + bounds.w -= iconW + m * 2.0f; Bounds primaryBounds = bounds; primaryBounds.h = height1; From ad458affb2871526f270dd4a1a0547a8782ef41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 15 Feb 2026 22:50:01 +0100 Subject: [PATCH 4/4] Typo fix --- UI/UIAtlas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/UIAtlas.cpp b/UI/UIAtlas.cpp index c373f499a5..1225b9d613 100644 --- a/UI/UIAtlas.cpp +++ b/UI/UIAtlas.cpp @@ -181,7 +181,7 @@ static const ImageMeta imageIDs[] = { {"I_UNKNOWN_ISO", false}, {"I_UMD_VIDEO_ISO", false}, {"I_APP", false}, - {"I_SHORTCUT, false"}, + {"I_SHORTCUT", false}, }; static std::string PNGNameFromID(std::string_view id) {