From 14eebd9b0987f5c66fc6f8fe6cf6cf749a5dee82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 14:18:48 +0100 Subject: [PATCH 1/9] Add Gitaroo Man Lives! to compat.ini for CenterLines, fixing title screen bug --- assets/compat.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assets/compat.ini b/assets/compat.ini index 3a4a6f3022..2a016625e9 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1316,6 +1316,13 @@ WEBF00752 = true ULUS10202 = true ULES00645 = true +# Gitaroo Man (title screen circles) +ULES00383 = true +ULKS41071 = true +ULUS10207 = true +ULJM05130 = true +NPJH50035 = true + [ZZT3SelectHack] # Bypass softlock on Zettai Zetsumei Toshi 3 character select screen #4901 # This problem affects the game also on PS3 From e7660457ca5f02683b11d645fc1cab83443d28e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 14:23:50 +0100 Subject: [PATCH 2/9] Fix font bug in InfoItem --- Common/UI/View.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index 31535a9713..55e55dbfcf 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -666,6 +666,7 @@ void InfoItem::Draw(UIContext &dc) { } dc.FillRoundRect(style.background, bounds_, dc.GetTheme().choiceRoundRadius, roundCorners_); + dc.SetFontStyle(dc.GetTheme().uiFont); if (rightText_.empty()) { int paddingX = 12; @@ -680,7 +681,6 @@ void InfoItem::Draw(UIContext &dc) { float leftWidth, leftHeight; dc.MeasureTextRect(dc.GetTheme().uiFont, 1.0f, 1.0f, text_, padBounds.w, &leftWidth, &leftHeight, ALIGN_VCENTER); - dc.SetFontStyle(dc.GetTheme().uiFont); dc.DrawTextRect(text_, padBounds, style.fgColor, ALIGN_VCENTER); // The info item should not look too clickable even if it has a background. Make the right side dimmed a little. From 822bae58e6c01703435a29f2a0d4d1480759372e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 14:31:41 +0100 Subject: [PATCH 3/9] UI: Pass the font style to each text draw, drop UIContext's current style What a draw used depended on whatever was set last, often by another view, so several draws only got the right font because something before them happened to reset it. Those now pass the theme's font, which is what they got in practice. DrawTextRectSqueeze also measured with the theme's font whatever style it drew with, so small text could be squeezed more than it needed to be. Co-Authored-By: Claude Opus 5.5 --- Common/UI/Context.cpp | 52 +++++++++++--------------- Common/UI/Context.h | 15 +++----- Common/UI/Notice.cpp | 8 +--- Common/UI/PopupScreens.cpp | 5 +-- Common/UI/View.cpp | 68 ++++++++++++---------------------- GPU/Vulkan/DebugVisVulkan.cpp | 5 ++- UI/DevScreens.cpp | 13 +++---- UI/EmuScreen.cpp | 2 +- UI/GPUDriverTestScreen.cpp | 28 +++++++------- UI/GameButton.cpp | 23 +++++------- UI/GamePacker.cpp | 5 +-- UI/GameScreen.cpp | 3 +- UI/ImposeHome.cpp | 5 +-- UI/JoystickHistoryView.cpp | 2 +- UI/MainScreen.cpp | 4 +- UI/MemStickScreen.cpp | 5 +-- UI/MiscScreens.cpp | 13 +++---- UI/MiscViews.cpp | 2 +- UI/OnScreenDisplay.cpp | 9 ++--- UI/ProfilerDraw.cpp | 9 ++--- UI/RetroAchievementScreens.cpp | 43 +++++++-------------- UI/SavedataScreen.cpp | 7 ++-- 22 files changed, 129 insertions(+), 197 deletions(-) diff --git a/Common/UI/Context.cpp b/Common/UI/Context.cpp index 030af9206d..428cca2e5d 100644 --- a/Common/UI/Context.cpp +++ b/Common/UI/Context.cpp @@ -16,14 +16,12 @@ #include "Common/Render/AtlasGen.h" UIContext::UIContext() { - fontStyle_ = new FontStyle(); bounds_ = Bounds(0, 0, g_display.dp_xres, g_display.dp_yres); } UIContext::~UIContext() { if (sampler_) sampler_->Release(); - delete fontStyle_; delete textDrawer_; if (uitexture_) uitexture_->Release(); @@ -43,10 +41,6 @@ void UIContext::Init(Draw::DrawContext *thin3d, Draw::Pipeline *uipipe, Draw::Pi void UIContext::BeginFrame() { frameStartTime_ = time_now_d(); - // Text drawn before anything set a style uses the theme's. - if (fontStyle_->sizePts <= 0 && theme) { - *fontStyle_ = theme->uiFont; - } if (atlasInvalid_ || !uitexture_) { if (uitexture_) { uitexture_->Release(); @@ -281,10 +275,6 @@ void UIContext::SetFontScale(float scaleX, float scaleY) { fontScaleY_ = scaleY; } -void UIContext::SetFontStyle(const FontStyle &fontStyle) { - *fontStyle_ = fontStyle; -} - static FontID AtlasFontFromStyle(const FontStyle &style) { // For now, we only have one font in the atlas. return FontID("UBUNTU24"); @@ -312,59 +302,59 @@ void UIContext::MeasureTextRect(const FontStyle &style, float scaleX, float scal } } -void UIContext::DrawText(std::string_view str, float x, float y, uint32_t color, int align) { +void UIContext::DrawText(const FontStyle &style, std::string_view str, float x, float y, uint32_t color, int align) { _dbg_assert_(str.data() != nullptr); if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) { // Use the font texture if this font is in that texture instead. - bool useFontTexture = Draw()->GetFontAtlas()->getFont(AtlasFontFromStyle(*fontStyle_)) != nullptr; + bool useFontTexture = Draw()->GetFontAtlas()->getFont(AtlasFontFromStyle(style)) != nullptr; if (useFontTexture) { Flush(); BindFontTexture(); } - float sizeFactor = (float)fontStyle_->sizePts / 24.0f; + float sizeFactor = (float)style.sizePts / 24.0f; Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor); - Draw()->DrawText(AtlasFontFromStyle(*fontStyle_), str, x, y, color, align); + Draw()->DrawText(AtlasFontFromStyle(style), str, x, y, color, align); if (useFontTexture) Flush(); } else { - textDrawer_->DrawString(*Draw(), *fontStyle_, fontScaleX_, fontScaleY_, str, x, y, color, align); + textDrawer_->DrawString(*Draw(), style, fontScaleX_, fontScaleY_, str, x, y, color, align); } RebindTexture(); } -void UIContext::DrawTextShadow(std::string_view str, float x, float y, uint32_t color, int align) { +void UIContext::DrawTextShadow(const FontStyle &style, std::string_view str, float x, float y, uint32_t color, int align) { uint32_t alpha = (color >> 1) & 0xFF000000; - DrawText(str, x + 2, y + 2, alpha, align); - DrawText(str, x, y, color, align); + DrawText(style, str, x + 2, y + 2, alpha, align); + DrawText(style, str, x, y, color, align); } -void UIContext::DrawTextRect(std::string_view str, const Bounds &bounds, uint32_t color, int align) { +void UIContext::DrawTextRect(const FontStyle &style, std::string_view str, const Bounds &bounds, uint32_t color, int align) { if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) { // Use the font texture if this font is in that texture instead. - bool useFontTexture = Draw()->GetFontAtlas()->getFont(AtlasFontFromStyle(*fontStyle_)) != nullptr; + bool useFontTexture = Draw()->GetFontAtlas()->getFont(AtlasFontFromStyle(style)) != nullptr; if (useFontTexture) { Flush(); BindFontTexture(); } - float sizeFactor = (float)fontStyle_->sizePts / 24.0f; + float sizeFactor = (float)style.sizePts / 24.0f; Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor); - Draw()->DrawTextRect(AtlasFontFromStyle(*fontStyle_), str, bounds.x, bounds.y, bounds.w, bounds.h, color, align); + Draw()->DrawTextRect(AtlasFontFromStyle(style), str, bounds.x, bounds.y, bounds.w, bounds.h, color, align); if (useFontTexture) Flush(); } else { Bounds rounded = bounds; rounded.x = floorf(rounded.x); rounded.y = floorf(rounded.y); - textDrawer_->DrawStringRect(*Draw(), *fontStyle_, fontScaleX_, fontScaleY_, str, rounded, color, align); + textDrawer_->DrawStringRect(*Draw(), style, fontScaleX_, fontScaleY_, str, rounded, color, align); } RebindTexture(); } static constexpr float MIN_TEXT_SCALE = 0.7f; -float UIContext::CalculateTextScale(std::string_view str, float availWidth) const { +float UIContext::CalculateTextScale(const FontStyle &style, std::string_view str, float availWidth) const { float actualWidth, actualHeight; - MeasureTextRect(theme->uiFont, 1.0f, 1.0f, str, availWidth, &actualWidth, &actualHeight, ALIGN_VCENTER); + MeasureTextRect(style, 1.0f, 1.0f, str, availWidth, &actualWidth, &actualHeight, ALIGN_VCENTER); if (actualWidth > availWidth) { // TODO: Return feedback that wrapping was needed. // Shave off a little, since float rounding can otherwise leave the scaled text a hair @@ -374,25 +364,25 @@ float UIContext::CalculateTextScale(std::string_view str, float availWidth) cons return 1.0f; } -void UIContext::DrawTextRectSqueeze(std::string_view str, const Bounds &bounds, uint32_t color, int align) { +void UIContext::DrawTextRectSqueeze(const FontStyle &style, std::string_view str, const Bounds &bounds, uint32_t color, int align) { if (bounds.w <= 0 || bounds.h <= 0) { // Probably mid-layout. return; } float origScaleX = fontScaleX_; float origScaleY = fontScaleY_; - float scale = CalculateTextScale(str, bounds.w / origScaleX); + float scale = CalculateTextScale(style, str, bounds.w / origScaleX); SetFontScale(scale * origScaleX, scale * origScaleY); Bounds textBounds(bounds.x, bounds.y, bounds.w, bounds.h); - DrawTextRect(str, textBounds, color, align); + DrawTextRect(style, str, textBounds, color, align); SetFontScale(origScaleX, origScaleY); } -void UIContext::DrawTextShadowRect(std::string_view str, const Bounds &bounds, uint32_t color, int align) { +void UIContext::DrawTextShadowRect(const FontStyle &style, std::string_view str, const Bounds &bounds, uint32_t color, int align) { uint32_t alpha = (color >> 1) & 0xFF000000; Bounds shadowBounds(bounds.x+2, bounds.y+2, bounds.w, bounds.h); - DrawTextRect(str, shadowBounds, alpha, align); - DrawTextRect(str, bounds, color, align); + DrawTextRect(style, str, shadowBounds, alpha, align); + DrawTextRect(style, str, bounds, color, align); } void UIContext::FillRoundRect(const UI::Drawable &drawable, const Bounds &bounds, float radius) { diff --git a/Common/UI/Context.h b/Common/UI/Context.h index 996420d09e..e8448aa064 100644 --- a/Common/UI/Context.h +++ b/Common/UI/Context.h @@ -94,19 +94,17 @@ public: // High level drawing functions. They generally assume the default texture to be bounds. - void SetFontStyle(const FontStyle &style); - const FontStyle &GetFontStyle() { return *fontStyle_; } void SetFontScale(float scaleX, float scaleY); void MeasureText(const FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align = 0) const; void MeasureTextRect(const FontStyle &style, float scaleX, float scaleY, std::string_view str, float maxWidth, float *x, float *y, int align = 0) const; - void DrawText(std::string_view str, float x, float y, uint32_t color, int align = 0); - void DrawTextShadow(std::string_view str, float x, float y, uint32_t color, int align = 0); - void DrawTextRect(std::string_view str, const Bounds &bounds, uint32_t color, int align = 0); - void DrawTextShadowRect(std::string_view str, const Bounds &bounds, uint32_t color, int align = 0); + void DrawText(const FontStyle &style, std::string_view str, float x, float y, uint32_t color, int align = 0); + void DrawTextShadow(const FontStyle &style, std::string_view str, float x, float y, uint32_t color, int align = 0); + void DrawTextRect(const FontStyle &style, std::string_view str, const Bounds &bounds, uint32_t color, int align = 0); + void DrawTextShadowRect(const FontStyle &style, std::string_view str, const Bounds &bounds, uint32_t color, int align = 0); // Will squeeze the text into the bounds if needed. - void DrawTextRectSqueeze(std::string_view str, const Bounds &bounds, uint32_t color, int align = 0); + void DrawTextRectSqueeze(const FontStyle &style, std::string_view str, const Bounds &bounds, uint32_t color, int align = 0); - float CalculateTextScale(std::string_view str, float availWidth) const; + float CalculateTextScale(const FontStyle &style, std::string_view str, float availWidth) const; void FillRoundRect(const UI::Drawable &drawable, const Bounds &bounds, float radius); void FillRoundRect(const UI::Drawable &drawable, const Bounds &bounds, float radius, RoundCorners corners); @@ -152,7 +150,6 @@ private: float fontScaleX_ = 1.0f; float fontScaleY_ = 1.0f; - FontStyle *fontStyle_ = nullptr; TextDrawer *textDrawer_ = nullptr; Draw::SamplerState *sampler_ = nullptr; diff --git a/Common/UI/Notice.cpp b/Common/UI/Notice.cpp index 106d5d1d1a..1ff3689bec 100644 --- a/Common/UI/Notice.cpp +++ b/Common/UI/Notice.cpp @@ -95,8 +95,6 @@ void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text 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, float padding, float radius, RoundCorners corners) { UI::Drawable background = UI::Drawable(colorAlpha(GetNoticeBackgroundColor(level), alpha)); - dc.SetFontStyle(dc.GetTheme().uiFont); - uint32_t foreGround = whiteAlpha(alpha); if (!(flags & OSDMessageFlags::Transparent)) { @@ -152,7 +150,7 @@ void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level titleAlign |= FLAG_ELLIPSIZE_TEXT; } - dc.DrawTextShadowRect(text, primaryBounds.Inset(2.0f, 0.0f, 1.0f, 0.0f), foreGround, titleAlign); + dc.DrawTextShadowRect(dc.GetTheme().uiFont, text, primaryBounds.Inset(2.0f, 0.0f, 1.0f, 0.0f), foreGround, titleAlign); if (!details.empty()) { Bounds bottomTextBounds = bounds.Inset(3.0f, height1 + 5.0f, 3.0f, 3.0f); @@ -160,8 +158,6 @@ void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level UI::Drawable backgroundDark = UI::Drawable(colorAlpha(darkenColor(GetNoticeBackgroundColor(level)), alpha)); dc.FillRect(backgroundDark, bottomTextBounds); } - dc.SetFontStyle(dc.GetTheme().uiFontSmall); - dc.DrawTextRect(details, bottomTextBounds.Inset(1.0f, 1.0f), foreGround, (align & FLAG_DYNAMIC_ASCII) | ALIGN_LEFT | FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFontSmall, details, bottomTextBounds.Inset(1.0f, 1.0f), foreGround, (align & FLAG_DYNAMIC_ASCII) | ALIGN_LEFT | FLAG_WRAP_TEXT); } - dc.SetFontStyle(dc.GetTheme().uiFont); } diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index a30a27541b..0130bed583 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -1073,7 +1073,6 @@ void AbstractChoiceWithValueDisplay::Draw(UIContext &dc) { style = dc.GetTheme().itemDisabledStyle; } int paddingX = 12; - dc.SetFontStyle(dc.GetTheme().uiFont); bool shadow; std::string valueText = ValueText(&shadow); @@ -1105,7 +1104,7 @@ void AbstractChoiceWithValueDisplay::Draw(UIContext &dc) { } dc.SetFontScale(scale, scale); Bounds valueBounds(bounds.x2() - textPadding_.right - imagePadding, bounds.y, w, bounds.h); - dc.DrawTextRect(valueText, valueBounds, color, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFont, valueText, valueBounds,color, ALIGN_RIGHT | ALIGN_VCENTER | FLAG_WRAP_TEXT); dc.SetFontScale(1.0f, 1.0f); } else { Choice::Draw(dc); @@ -1119,7 +1118,7 @@ void AbstractChoiceWithValueDisplay::Draw(UIContext &dc) { float scale = CalculateValueScale(dc, valueText, bounds.w); dc.SetFontScale(scale, scale); - dc.DrawTextRect(valueText, bounds.Expand(-paddingX, 0.0f), color, ALIGN_LEFT | ALIGN_VCENTER | FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFont, valueText, bounds.Expand(-paddingX, 0.0f), color, ALIGN_LEFT | ALIGN_VCENTER | FLAG_WRAP_TEXT); dc.SetFontScale(1.0f, 1.0f); } } diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index 55e55dbfcf..66d19b7941 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -484,13 +484,11 @@ void ClickableItem::DrawHint(UIContext &dc, const Style &style) const { if (hintText_.empty()) { return; } - dc.SetFontStyle(dc.GetTheme().uiFontTiny); dc.SetFontScale(1.0f, 1.0f); const float textHeight = hintHeight_ - HINT_PADDING_BOTTOM + HINT_TITLE_OVERLAP; const float indent = HintIndent(); Bounds hintBounds(bounds_.x + indent, bounds_.y2() - HINT_PADDING_BOTTOM - textHeight, bounds_.w - indent - HINT_PADDING_X, textHeight); - dc.DrawTextRect(hintText_, hintBounds, style.fgColor, ALIGN_TOPLEFT | FLAG_WRAP_TEXT); - dc.SetFontStyle(dc.GetTheme().uiFont); + dc.DrawTextRect(dc.GetTheme().uiFontTiny, hintText_, hintBounds, style.fgColor, ALIGN_TOPLEFT | FLAG_WRAP_TEXT); } float ClickableItem::HintIndent() const { @@ -521,7 +519,7 @@ void Choice::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, } if (horiz.type != EXACTLY && layoutParams_->width > 0.0f && availWidth > layoutParams_->width) availWidth = layoutParams_->width; - float scale = dc.CalculateTextScale(text_, availWidth); + float scale = dc.CalculateTextScale(dc.GetTheme().uiFont, text_, availWidth); float textW = 0.0f, textH = 0.0f; dc.MeasureTextRect(dc.GetTheme().uiFont, scale, scale, text_, availWidth, &textW, &textH, FLAG_WRAP_TEXT); textW += 4; // We need a small fudge factor here, not sure why yet. @@ -561,8 +559,6 @@ void Choice::Draw(UIContext &dc) { DrawIconShine(dc, b.Inset(5.0f, 5.0f), 0.65f, false); } } else if (!text_.empty() && !hideTitle_) { - dc.SetFontStyle(dc.GetTheme().uiFont); - int paddingLeft = 12; int paddingRight = 12; @@ -587,7 +583,7 @@ void Choice::Draw(UIContext &dc) { float availWidth = bounds.w - (paddingLeft + paddingRight + textPadding_.horiz()); if (centered_) { - dc.DrawTextRectSqueeze(text_, bounds, style.fgColor, ALIGN_CENTER | FLAG_WRAP_TEXT | drawTextFlags_); + dc.DrawTextRectSqueeze(dc.GetTheme().uiFont, text_, bounds, style.fgColor, ALIGN_CENTER | FLAG_WRAP_TEXT | drawTextFlags_); } else { if (rightIconImage_.isValid()) { uint32_t col = rightIconKeepColor_ ? 0xffffffff : style.fgColor; // Don't apply theme to gold icon @@ -601,7 +597,7 @@ void Choice::Draw(UIContext &dc) { dc.Draw()->DrawImageRotated(rightIconImage_, iconX, bounds.centerY(), rightIconScale_, rightIconRot_, col, rightIconFlipH_); } Bounds textBounds(bounds.x + paddingLeft + textPadding_.left, bounds.y, availWidth, bounds.h); - dc.DrawTextRectSqueeze(text_, textBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT | drawTextFlags_); + dc.DrawTextRectSqueeze(dc.GetTheme().uiFont, text_, textBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT | drawTextFlags_); } dc.SetFontScale(1.0f, 1.0f); } @@ -666,12 +662,11 @@ void InfoItem::Draw(UIContext &dc) { } dc.FillRoundRect(style.background, bounds_, dc.GetTheme().choiceRoundRadius, roundCorners_); - dc.SetFontStyle(dc.GetTheme().uiFont); if (rightText_.empty()) { int paddingX = 12; Bounds padBounds = bounds_.Expand(-paddingX, 0); - dc.DrawTextRect(text_, padBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFont, text_, padBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); return; } @@ -681,11 +676,11 @@ void InfoItem::Draw(UIContext &dc) { float leftWidth, leftHeight; dc.MeasureTextRect(dc.GetTheme().uiFont, 1.0f, 1.0f, text_, padBounds.w, &leftWidth, &leftHeight, ALIGN_VCENTER); - dc.DrawTextRect(text_, padBounds, style.fgColor, ALIGN_VCENTER); + dc.DrawTextRect(dc.GetTheme().uiFont, text_, padBounds, style.fgColor, ALIGN_VCENTER); // The info item should not look too clickable even if it has a background. Make the right side dimmed a little. Bounds rightBounds(padBounds.x + leftWidth, padBounds.y, padBounds.w - leftWidth, padBounds.h); - dc.DrawTextRect(rightText_, rightBounds, alphaMul(style.fgColor, 0.75f), ALIGN_VCENTER | ALIGN_RIGHT | FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFont, rightText_, rightBounds, alphaMul(style.fgColor, 0.75f), ALIGN_VCENTER | ALIGN_RIGHT | FLAG_WRAP_TEXT); } std::string InfoItem::DescribeText() const { @@ -700,11 +695,11 @@ ItemHeader::ItemHeader(std::string_view text, LayoutParams *layoutParams) } void ItemHeader::Draw(UIContext &dc) { - dc.SetFontStyle(large_ ? dc.GetTheme().uiFont : dc.GetTheme().uiFontSmall); + const FontStyle &fontStyle = large_ ? dc.GetTheme().uiFont : dc.GetTheme().uiFontSmall; const UI::Style &style = popupStyle_ ? dc.GetTheme().popupStyle : dc.GetTheme().itemStyle; dc.FillRoundRect(style.background, bounds_, dc.GetTheme().choiceRoundRadius, roundCorners_); - dc.DrawText(text_, bounds_.x + 12, bounds_.centerY(), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER); + dc.DrawText(fontStyle, text_, bounds_.x + 12, bounds_.centerY(), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER); dc.Draw()->DrawImageCenterTexel(dc.GetTheme().whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), style.fgColor); } @@ -743,8 +738,7 @@ void PopupHeader::Draw(UIContext &dc) { const float availableWidth = bounds_.w; float tw, th; - dc.SetFontStyle(dc.GetTheme().uiFontBig); - dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text_, &tw, &th, 0); + dc.MeasureText(dc.GetTheme().uiFontBig, 1.0f, 1.0f, text_, &tw, &th, 0); float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f; @@ -761,7 +755,7 @@ void PopupHeader::Draw(UIContext &dc) { // Header background dc.FillRoundRect(dc.GetTheme().popupTitleStyle.background, bounds_, dc.GetTheme().choiceRoundRadius, roundCorners_); // Header title text - dc.DrawText(text_, bounds_.x + tx, bounds_.centerY(), dc.GetTheme().popupTitleStyle.fgColor, ALIGN_LEFT | ALIGN_VCENTER); + dc.DrawText(dc.GetTheme().uiFontBig, text_, bounds_.x + tx, bounds_.centerY(), dc.GetTheme().popupTitleStyle.fgColor, ALIGN_LEFT | ALIGN_VCENTER); // Underline. Skipping it with the new style. // dc.Draw()->DrawImageCenterTexel(dc.GetTheme().whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.GetTheme().popupTitleStyle.fgColor); @@ -792,8 +786,7 @@ void CollapsibleHeader::Draw(UIContext &dc) { float xoff = 37.0f; - dc.SetFontStyle(dc.GetTheme().uiFontSmall); - dc.DrawText(text_, bounds_.x + 6 + xoff, bounds_.centerY(), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER); + dc.DrawText(dc.GetTheme().uiFontSmall, text_, bounds_.x + 6 + xoff, bounds_.centerY(), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER); if (underline_) { dc.Draw()->DrawImageCenterTexel(dc.GetTheme().whiteImage, bounds_.x, bounds_.y2() - 2, bounds_.x2(), bounds_.y2(), style.fgColor); } @@ -905,7 +898,6 @@ void CheckBox::Draw(UIContext &dc) { } } - dc.SetFontStyle(dc.GetTheme().uiFont); DrawBG(dc, style, dc.GetTheme().choiceRoundRadius); @@ -919,7 +911,7 @@ void CheckBox::Draw(UIContext &dc) { const Bounds bounds = ContentBounds(); if (!text_.empty()) { Bounds textBounds(bounds.x + paddingX, bounds.y, availWidth, bounds.h); - dc.DrawTextRectSqueeze(text_, textBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); + dc.DrawTextRectSqueeze(dc.GetTheme().uiFont, text_, textBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); } dc.Draw()->DrawImage(image, bounds.x2() - paddingX, bounds.centerY(), 1.0f, style.fgColor, ALIGN_RIGHT | ALIGN_VCENTER); DrawHint(dc, style); @@ -962,7 +954,7 @@ void CheckBox::GetContentDimensions(const UIContext &dc, float &w, float &h) con } if (!text_.empty()) { - float scale = dc.CalculateTextScale(text_, availWidth); + float scale = dc.CalculateTextScale(dc.GetTheme().uiFont, text_, availWidth); float actualWidth, actualHeight; dc.MeasureTextRect(dc.GetTheme().uiFont, scale, scale, text_, availWidth, &actualWidth, &actualHeight, ALIGN_VCENTER | FLAG_WRAP_TEXT); @@ -1065,7 +1057,6 @@ void Button::Draw(UIContext &dc) { if (tw > bounds_.w || imageId.isValid()) { dc.PushScissor(bounds_); } - dc.SetFontStyle(dc.GetTheme().uiFont); dc.SetFontScale(scale_, scale_); if (imageId.isValid() && (ignoreText_ || text_.empty())) { dc.Draw()->DrawImage(imageId, bounds_.centerX(), bounds_.centerY(), scale_, style.fgColor, ALIGN_CENTER); @@ -1078,7 +1069,7 @@ void Button::Draw(UIContext &dc) { textX += img->w / 2.0f; } } - dc.DrawText(text_, textX, bounds_.centerY(), style.fgColor, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, text_, textX, bounds_.centerY(), style.fgColor, ALIGN_CENTER); } dc.SetFontScale(1.0f, 1.0f); @@ -1134,12 +1125,11 @@ void RadioButton::Draw(UIContext &dc) { const ImageID image(checked ? "I_RADIO_SELECTED" : "I_RADIO_EMPTY"); dc.Draw()->DrawImage(image, bounds.x + paddingW_ + radioRadius_, bounds.centerY(), 1.0f, style.fgColor, ALIGN_CENTER); - dc.SetFontStyle(dc.GetTheme().uiFont); if (!text_.empty()) { const float textX = bounds.x + paddingW_ * 2.0f + radioRadius_ * 2.0f; const Bounds textBounds(textX, bounds.y, bounds.x2() - paddingW_ - textX, bounds.h); - dc.DrawTextRect(text_, textBounds, style.fgColor, ALIGN_LEFT | ALIGN_VCENTER | FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFont, text_, textBounds, style.fgColor, ALIGN_LEFT | ALIGN_VCENTER | FLAG_WRAP_TEXT); } DrawHint(dc, style); @@ -1177,8 +1167,7 @@ void SimpleTextView::GetContentDimensions(const UIContext &dc, float &w, float & void SimpleTextView::Draw(UIContext &dc) { uint32_t textColor = dc.GetTheme().itemStyle.fgColor; - dc.SetFontStyle(*ComputeStyle(dc)); - dc.DrawText(text_, bounds_.x, bounds_.y, textColor, 0); + dc.DrawText(*ComputeStyle(dc), text_, bounds_.x, bounds_.y, textColor, 0); } const FontStyle *SimpleTextView::ComputeStyle(const UIContext &dc) const { @@ -1264,7 +1253,6 @@ void TextView::Draw(UIContext &dc) { } const FontStyle *style = GetTextStyle(dc, textSize_); - dc.SetFontStyle(*style); Bounds textBounds = bounds_; @@ -1287,14 +1275,10 @@ void TextView::Draw(UIContext &dc) { } else if (shadow_) { // Old-fashioned double-draw shadow. uint32_t shadowColor = 0x80000000; - dc.DrawTextRect(text_, textBounds.Offset(1.0f, 1.0f), shadowColor, textFlags); + dc.DrawTextRect(*style, text_, textBounds.Offset(1.0f, 1.0f), shadowColor, textFlags); } - dc.DrawTextRect(text_, textBounds, textColor, textFlags); - if (textSize_ != TextSize::Normal) { - // If we changed font style, reset it. - dc.SetFontStyle(dc.GetTheme().uiFont); - } + dc.DrawTextRect(*style, text_, textBounds, textColor, textFlags); if (clip) { dc.PopScissor(); } @@ -1384,7 +1368,6 @@ void TextEdit::FocusChanged(FocusFlags focusFlags) { void TextEdit::Draw(UIContext &dc) { Bounds textBounds = bounds_.Inset(padding_.left, padding_.top, padding_.right, padding_.bottom); - dc.SetFontStyle(dc.GetTheme().uiFont); // TODO: make background themeable? dc.FillRoundRect(HasFocus() ? UI::Drawable(0x80000000) : UI::Drawable(0x30000000), bounds_, dc.GetTheme().choiceRoundRadius * 0.5f); @@ -1406,10 +1389,10 @@ void TextEdit::Draw(UIContext &dc) { if (text_.empty()) { if (placeholderText_.size()) { uint32_t c = textColor & 0x50FFFFFF; - dc.DrawTextRect(placeholderText_, origTextBounds, c, ALIGN_CENTER); + dc.DrawTextRect(dc.GetTheme().uiFont, placeholderText_, origTextBounds, c, ALIGN_CENTER); } } else { - dc.DrawTextRect(textToDisplay, textBounds, textColor, ALIGN_VCENTER | ALIGN_LEFT | align_); + dc.DrawTextRect(dc.GetTheme().uiFont, textToDisplay, textBounds, textColor, ALIGN_VCENTER | ALIGN_LEFT | align_); } // Hack to find the caret position. Might want to find a better way... @@ -1660,8 +1643,7 @@ void ProgressBar::Draw(UIContext &dc) { done.w = std::max(bounds_.w * progress_, std::min(bounds_.w, radius * 2.0f)); dc.FillRoundRect(dc.GetTheme().itemFocusedStyle.background, done, radius, roundCorners_); } - dc.SetFontStyle(dc.GetTheme().uiFont); - dc.DrawTextRect(temp, bounds_, 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawTextRect(dc.GetTheme().uiFont, temp, bounds_, 0xFFFFFFFF, ALIGN_CENTER); } std::string ProgressBar::DescribeText() const { @@ -1881,8 +1863,7 @@ void Slider::Draw(UIContext &dc) { snprintf(temp, sizeof(temp), "%d%%", *value_); else snprintf(temp, sizeof(temp), "%d", *value_); - dc.SetFontStyle(dc.GetTheme().uiFont); - dc.DrawText(temp, bounds_.x2() - 22, bounds_.centerY(), sliderColor, ALIGN_CENTER | FLAG_DYNAMIC_ASCII); + dc.DrawText(dc.GetTheme().uiFont, temp, bounds_.x2() - 22, bounds_.centerY(), sliderColor, ALIGN_CENTER | FLAG_DYNAMIC_ASCII); } std::string Slider::DescribeText() const { @@ -2038,8 +2019,7 @@ void SliderFloat::Draw(UIContext &dc) { dc.Draw()->DrawImage(dc.GetTheme().sliderKnob, knobX, bounds_.centerY(), 1.0f, sliderColor, ALIGN_CENTER); char temp[64]; snprintf(temp, sizeof(temp), "%0.2f", *value_); - dc.SetFontStyle(dc.GetTheme().uiFont); - dc.DrawText(temp, bounds_.x2() - 22, bounds_.centerY(), sliderColor, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, temp, bounds_.x2() - 22, bounds_.centerY(), sliderColor, ALIGN_CENTER); } std::string SliderFloat::DescribeText() const { diff --git a/GPU/Vulkan/DebugVisVulkan.cpp b/GPU/Vulkan/DebugVisVulkan.cpp index 989a031a96..3186b95555 100644 --- a/GPU/Vulkan/DebugVisVulkan.cpp +++ b/GPU/Vulkan/DebugVisVulkan.cpp @@ -23,6 +23,7 @@ #include "Common/GPU/thin3d.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/UI/Context.h" +#include "Common/UI/View.h" #include "Common/System/System.h" #include "ext/vma/vk_mem_alloc.h" @@ -88,7 +89,7 @@ void DrawGPUMemoryVis(UIContext *ui, GPUCommon *gpu) { int y = starty; ui->SetFontScale(0.7f, 0.7f); - ui->DrawTextShadow(str.str(), x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ui->DrawTextShadow(ui->GetTheme().uiFont, str.str(), x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); ui->SetFontScale(1.0f, 1.0f); ui->Flush(); } @@ -111,7 +112,7 @@ void DrawGPUProfilerVis(UIContext *ui, GPUCommon *gpu) { std::string text = ui->GetDrawContext()->GetGpuProfileString(); ui->SetFontScale(0.4f, 0.4f); - ui->DrawTextShadow(text, x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ui->DrawTextShadow(ui->GetTheme().uiFont, text, x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); ui->SetFontScale(1.0f, 1.0f); ui->Flush(); } diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 7ca740db4c..90a0377514 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -861,7 +861,7 @@ void TouchTestScreen::DrawForeground(UIContext &dc) { if (touches_[i].id != -1) { dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0); snprintf(buffer, sizeof(buffer), "%0.1fx%0.1f", touches_[i].x, touches_[i].y); - dc.DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > g_display.dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII); + dc.DrawText(dc.GetTheme().uiFont, buffer, touches_[i].x, touches_[i].y + (touches_[i].y > g_display.dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII); } } @@ -882,7 +882,7 @@ void TouchTestScreen::DrawForeground(UIContext &dc) { extra_debug); // On Android, also add joystick debug data. - dc.DrawTextShadow(buffer, bounds.centerX(), bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + dc.DrawTextShadow(dc.GetTheme().uiFont, buffer, bounds.centerX(), bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); dc.Flush(); } @@ -1066,14 +1066,13 @@ void ColorTestView::Draw(UIContext &dc) { dc.Flush(); dc.Begin(); - dc.SetFontStyle(*GetTextStyle(dc, TextSize::Tiny)); + const FontStyle &tinyFont = *GetTextStyle(dc, TextSize::Tiny); for (const Label &label : labels) { - dc.DrawText(label.text, label.x, label.y, label.color, ALIGN_CENTER | FLAG_DYNAMIC_ASCII); + dc.DrawText(tinyFont, label.text, label.x, label.y, label.color, ALIGN_CENTER | FLAG_DYNAMIC_ASCII); } - dc.DrawText("Blurred, the lines match 188 if averaged in linear light, 128 if averaged as encoded", gammaX, y, 0xFFFFFFFF, ALIGN_TOPLEFT); + dc.DrawText(tinyFont, "Blurred, the lines match 188 if averaged in linear light, 128 if averaged as encoded", gammaX, y, 0xFFFFFFFF, ALIGN_TOPLEFT); const std::string header = StringFromFormat("%s%s, %dx%d pixels", dc.GetDrawContext()->GetInfoString(Draw::InfoField::APINAME).c_str(), IsVREnabled() ? ", VR" : "", g_display.pixel_xres, g_display.pixel_yres); - dc.SetFontStyle(*GetTextStyle(dc, TextSize::Small)); - dc.DrawText(header, bounds_.x + margin, bounds_.y + 4.0f, 0xFFFFFFFF, ALIGN_TOPLEFT); + dc.DrawText(*GetTextStyle(dc, TextSize::Small), header, bounds_.x + margin, bounds_.y + 4.0f, 0xFFFFFFFF, ALIGN_TOPLEFT); dc.Flush(); dc.Begin(); } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 4c1ab9c18b..075d1bdfd1 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1619,7 +1619,7 @@ void EmuScreen::drawGameOnOutputMessage() { auto di = GetI18NCategory(I18NCat::DIALOG); const Bounds &bounds = dc.GetBounds(); dc.Begin(); - dc.DrawText(di->T("Playing on TV"), bounds.centerX(), bounds.centerY(), 0xFF505050, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, di->T("Playing on TV"), bounds.centerX(), bounds.centerY(), 0xFF505050, ALIGN_CENTER); dc.Flush(); } diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index 673d20becb..174cfbf43a 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -463,9 +463,9 @@ void GPUDriverTestScreen::DiscardTest(UIContext &dc) { std::string apiName = screenManager()->getDrawContext()->GetInfoString(InfoField::APINAME); std::string vendor = screenManager()->getDrawContext()->GetInfoString(InfoField::VENDORSTRING); std::string driver = screenManager()->getDrawContext()->GetInfoString(InfoField::DRIVER); - dc.DrawText(apiName, layoutBounds.centerX(), 20, 0xFFFFFFFF, ALIGN_CENTER); - dc.DrawText(vendor, layoutBounds.centerX(), 60, 0xFFFFFFFF, ALIGN_CENTER); - dc.DrawText(driver, layoutBounds.centerX(), 100, 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, apiName, layoutBounds.centerX(), 20, 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, vendor, layoutBounds.centerX(), 60, 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, driver, layoutBounds.centerX(), 100, 0xFFFFFFFF, ALIGN_CENTER); dc.Flush(); float testW = 170.f; @@ -476,14 +476,14 @@ void GPUDriverTestScreen::DiscardTest(UIContext &dc) { for (int j = 0; j < numWriteModes; j++, y += 120.f + padding) { float x = layoutBounds.x + (layoutBounds.w - (float)numTests * testW - (float)(numTests - 1) * padding) / 2.0f; dc.Begin(); - dc.DrawText(writeModeNames[j], layoutBounds.x + padding, y + 40, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + dc.DrawText(dc.GetTheme().uiFont, writeModeNames[j], layoutBounds.x + padding, y + 40, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); dc.Flush(); for (int i = 0; i < numTests; i++, x += testW + padding) { if (!validCombinations[j][i]) continue; dc.Begin(); Bounds bounds = { x, y + 40, testW, 70 }; - dc.DrawText(testNames[i], bounds.x, y, style.fgColor, FLAG_DYNAMIC_ASCII); + dc.DrawText(dc.GetTheme().uiFont, testNames[i], bounds.x, y, style.fgColor, FLAG_DYNAMIC_ASCII); dc.Flush(); dc.BeginPipeline(writePipelines[j], samplerNearest_); @@ -497,7 +497,7 @@ void GPUDriverTestScreen::DiscardTest(UIContext &dc) { draw->SetStencilParams(0xff, 0xFF, 0xFF); dc.SetCurZ(0.9f); - dc.DrawTextRect("TEST OK", bounds, textColorBAD, ALIGN_HCENTER | ALIGN_VCENTER | FLAG_DYNAMIC_ASCII); + dc.DrawTextRect(dc.GetTheme().uiFont, "TEST OK", bounds, textColorBAD, ALIGN_HCENTER | ALIGN_VCENTER | FLAG_DYNAMIC_ASCII); dc.Flush(); // Draw rectangle that should result in the text @@ -569,15 +569,15 @@ void GPUDriverTestScreen::ShaderTest(UIContext &dc) { std::string apiName = screenManager()->getDrawContext()->GetInfoString(InfoField::APINAME); std::string vendor = screenManager()->getDrawContext()->GetInfoString(InfoField::VENDORSTRING); std::string driver = screenManager()->getDrawContext()->GetInfoString(InfoField::DRIVER); - dc.DrawText(apiName, layoutBounds.centerX(), 20, 0xFFFFFFFF, ALIGN_CENTER); - dc.DrawText(vendor, layoutBounds.centerX(), 60, 0xFFFFFFFF, ALIGN_CENTER); - dc.DrawText(driver, layoutBounds.centerX(), 100, 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, apiName, layoutBounds.centerX(), 20, 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, vendor, layoutBounds.centerX(), 60, 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, driver, layoutBounds.centerX(), 100, 0xFFFFFFFF, ALIGN_CENTER); dc.Flush(); float y = layoutBounds.y + 150; float x = layoutBounds.x + 10; dc.Begin(); - dc.DrawText("Adreno logic", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + dc.DrawText(dc.GetTheme().uiFont, "Adreno logic", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); dc.Flush(); float testW = 170.f; @@ -591,14 +591,14 @@ void GPUDriverTestScreen::ShaderTest(UIContext &dc) { // Draw text on it using the shader. dc.BeginPipeline(adrenoLogicDiscardPipeline_, samplerNearest_); - dc.DrawTextRect("TEST OK", bounds, textColorOK, ALIGN_HCENTER | ALIGN_VCENTER | FLAG_DYNAMIC_ASCII); + dc.DrawTextRect(dc.GetTheme().uiFont, "TEST OK", bounds, textColorOK, ALIGN_HCENTER | ALIGN_VCENTER | FLAG_DYNAMIC_ASCII); dc.Flush(); y += 100; dc.Begin(); - dc.DrawText("Flat shaded tex", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); - dc.DrawText("(TEST OK if logo but no gradient!)", x + 400, y, 0xFFFFFFFF, ALIGN_LEFT); + dc.DrawText(dc.GetTheme().uiFont, "Flat shaded tex", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + dc.DrawText(dc.GetTheme().uiFont, "(TEST OK if logo but no gradient!)", x + 400, y, 0xFFFFFFFF, ALIGN_LEFT); dc.Flush(); bounds = { x + 200, y, 100, 100 }; @@ -613,7 +613,7 @@ void GPUDriverTestScreen::ShaderTest(UIContext &dc) { y += 120; dc.Begin(); - dc.DrawText("Test done", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + dc.DrawText(dc.GetTheme().uiFont, "Test done", x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); dc.Flush(); } diff --git a/UI/GameButton.cpp b/UI/GameButton.cpp index 8a971b84e6..22c93305fc 100644 --- a/UI/GameButton.cpp +++ b/UI/GameButton.cpp @@ -279,8 +279,8 @@ void GameButton::Draw(UIContext &dc) { idStr = ga->T("SaveData"); } dc.SetFontScale(0.5f * g_Config.fGameGridScale, 0.5f * g_Config.fGameGridScale); - dc.DrawText(idStr, bounds_.x + 5, iconBounds.y + 1, 0xFF000000, ALIGN_TOPLEFT); - dc.DrawText(idStr, bounds_.x + 4, iconBounds.y, dc.GetTheme().infoStyle.fgColor, ALIGN_TOPLEFT); + dc.DrawText(dc.GetTheme().uiFont, idStr, bounds_.x + 5, iconBounds.y + 1, 0xFF000000, ALIGN_TOPLEFT); + dc.DrawText(dc.GetTheme().uiFont, idStr, bounds_.x + 4, iconBounds.y, dc.GetTheme().infoStyle.fgColor, ALIGN_TOPLEFT); dc.SetFontScale(1.0f, 1.0f); } @@ -288,10 +288,9 @@ void GameButton::Draw(UIContext &dc) { Bounds bounds = bounds_; float tw, th; - dc.MeasureText(dc.GetFontStyle(), g_Config.fGameGridScale, g_Config.fGameGridScale, text, &tw, &th, 0); + dc.MeasureText(dc.GetTheme().uiFont, g_Config.fGameGridScale, g_Config.fGameGridScale, text, &tw, &th, 0); dc.PushScissor(bounds); const FontStyle *fontStyle = GetTextStyle(dc, UI::TextSize::Small); - dc.SetFontStyle(*GetTextStyle(dc, UI::TextSize::Small)); int iconSize = imageIcon == ImageID("I_UP_DIRECTORY") ? (float)dc.Draw()->GetAtlas()->getImage(imageIcon)->h : bounds.h * 0.3f; float textWidth = 0.0f; @@ -309,15 +308,14 @@ void GameButton::Draw(UIContext &dc) { } if (imageIcon != ImageID("I_UP_DIRECTORY") && imageIcon != ImageID("I_PIN") && imageIcon != ImageID("I_UNPIN")) { - dc.DrawTextRect(text, bounds.Inset(5, y + iconSize + 4, 5, 2), style.fgColor, ALIGN_HCENTER | FLAG_WRAP_TEXT); + dc.DrawTextRect(*fontStyle, text, bounds.Inset(5, y + iconSize + 4, 5, 2), style.fgColor, ALIGN_HCENTER | FLAG_WRAP_TEXT); } dc.PopScissor(); - dc.SetFontStyle(dc.GetTheme().uiFont); } else if (!texture) { // Just draw the text, centered. dc.Draw()->Flush(); dc.PushScissor(bounds_); - dc.DrawText(title_, bounds_.x + 4, bounds_.centerY(), style.fgColor, ALIGN_VCENTER); + dc.DrawText(dc.GetTheme().uiFont, title_, bounds_.x + 4, bounds_.centerY(), style.fgColor, ALIGN_VCENTER); dc.Draw()->Flush(); dc.PopScissor(); } @@ -327,7 +325,7 @@ void GameButton::Draw(UIContext &dc) { std::string_view rightText = g_Config.bShowIDOnGameIcon ? std::string_view(id_version) : std::string_view(); float tw, th; - dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text, &tw, &th, 0); + dc.MeasureText(dc.GetTheme().uiFont, 1.0f, 1.0f, text, &tw, &th, 0); if (imageIcon.isValid()) { // Dirs are fixed height. @@ -339,10 +337,10 @@ void GameButton::Draw(UIContext &dc) { int availableWidth = bounds.w - textX - LIST_TEXT_RIGHT_MARGIN; if (!rightText.empty()) { float vw, vh; - dc.MeasureText(dc.GetFontStyle(), 0.7f, 0.7f, rightText, &vw, &vh, 0); + dc.MeasureText(dc.GetTheme().uiFont, 0.7f, 0.7f, rightText, &vw, &vh, 0); availableWidth -= vw + 20; dc.SetFontScale(0.7f, 0.7f); - dc.DrawText(rightText, bounds.x + availableWidth + textX + 10, bounds.centerY(), style.fgColor, ALIGN_VCENTER); + dc.DrawText(dc.GetTheme().uiFont, rightText, bounds.x + availableWidth + textX + 10, bounds.centerY(), style.fgColor, ALIGN_VCENTER); dc.SetFontScale(1.0f, 1.0f); } float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f; @@ -353,7 +351,7 @@ void GameButton::Draw(UIContext &dc) { tb.w = availableWidth; dc.PushScissor(tb); } - dc.DrawText(text, bounds.x + tx, bounds.centerY(), style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); + dc.DrawText(dc.GetTheme().uiFont, text, bounds.x + tx, bounds.centerY(), style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); if (availableWidth < tw) { dc.PopScissor(); } @@ -421,10 +419,9 @@ void GameButton::Draw(UIContext &dc) { auto ga = GetI18NCategory(I18NCat::GAME); dc.FillRect(Drawable(0xD0505050), iconBounds); const float scale = 0.8f * (viewMode_ == GameBrowserViewMode::GRID ? g_Config.fGameGridScale : g_Config.fGameListScale); - dc.SetFontStyle(dc.GetTheme().uiFont); dc.SetFontScale(scale, scale); const std::string text = std::string(ga->T("Compressing...")) + StringFromFormat("\n%d%%", (int)(packStatus.progress * 100.0f)); - dc.DrawTextRectSqueeze(text, iconBounds, 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawTextRectSqueeze(dc.GetTheme().uiFont, text, iconBounds, 0xFFFFFFFF, ALIGN_CENTER); dc.SetFontScale(1.0f, 1.0f); } diff --git a/UI/GamePacker.cpp b/UI/GamePacker.cpp index 0bcfe10eef..83623f8a9a 100644 --- a/UI/GamePacker.cpp +++ b/UI/GamePacker.cpp @@ -400,10 +400,9 @@ public: const float textX = bounds_.x + 60.0f; const float textW = bounds_.w - 70.0f; - dc.SetFontStyle(dc.GetTheme().uiFont); - dc.DrawTextRectSqueeze(title_, Bounds(textX, bounds_.y + 6.0f, textW, 32.0f), style.fgColor, ALIGN_VCENTER); + dc.DrawTextRectSqueeze(dc.GetTheme().uiFont, title_, Bounds(textX, bounds_.y + 6.0f, textW, 32.0f), style.fgColor, ALIGN_VCENTER); dc.SetFontScale(0.7f, 0.7f); - dc.DrawTextRectSqueeze(description_, Bounds(textX, bounds_.y + 36.0f, textW, 28.0f), alphaMul(style.fgColor, 0.75f), ALIGN_VCENTER); + dc.DrawTextRectSqueeze(dc.GetTheme().uiFont, description_, Bounds(textX, bounds_.y + 36.0f, textW, 28.0f), alphaMul(style.fgColor, 0.75f), ALIGN_VCENTER); dc.SetFontScale(1.0f, 1.0f); } diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index c4abce42de..c3015a6ca9 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -673,9 +673,8 @@ void GameScreen::DrawForeground(UIContext &dc) { std::string eta = seconds >= 3600 ? StringFromFormat("%d:%02d:%02d", seconds / 3600, (seconds / 60) % 60, seconds % 60) : StringFromFormat("%d:%02d", seconds / 60, seconds % 60); text += " " + ApplySafeSubstitutions(ga->T("PackTimeLeft", "~%1 left"), eta); } - dc.SetFontStyle(dc.GetTheme().uiFont); dc.SetFontScale(0.8f, 0.8f); - dc.DrawTextRectSqueeze(text, track.Inset(8.0f, 0.0f), 0xFFFFFFFF, ALIGN_CENTER); + dc.DrawTextRectSqueeze(dc.GetTheme().uiFont, text, track.Inset(8.0f, 0.0f), 0xFFFFFFFF, ALIGN_CENTER); dc.SetFontScale(1.0f, 1.0f); } diff --git a/UI/ImposeHome.cpp b/UI/ImposeHome.cpp index dfc99e7160..a4b593a9cb 100644 --- a/UI/ImposeHome.cpp +++ b/UI/ImposeHome.cpp @@ -783,7 +783,6 @@ void ImposeHomeScreen::Draw(UIContext &dc, const Bounds &screen) { // No firmware: the same screen with our font, at the firmware's positions (English layout). auto di = GetI18NCategory(I18NCat::DIALOG); const UI::Theme &theme = dc.GetTheme(); - dc.SetFontStyle(theme.uiFont); auto FitText = [&](std::string_view text, const Bounds &bounds, uint32_t color, int align) { // Scale the font to the bitmap's height, then squeeze if the translation is longer. float w, h; @@ -794,7 +793,7 @@ void ImposeHomeScreen::Draw(UIContext &dc, const Bounds &screen) { } dc.SetFontScale(scale, scale); const float x = (align & ALIGN_HCENTER) ? bounds.centerX() : bounds.x; - dc.DrawText(text, x, bounds.centerY(), color, (align & ALIGN_HCENTER) ? ALIGN_CENTER : (ALIGN_LEFT | ALIGN_VCENTER)); + dc.DrawText(theme.uiFont, text, x, bounds.centerY(), color, (align & ALIGN_HCENTER) ? ALIGN_CENTER : (ALIGN_LEFT | ALIGN_VCENTER)); }; // Status bar: the clock right-aligned at the top, the battery as its level in quarters. @@ -815,7 +814,7 @@ void ImposeHomeScreen::Draw(UIContext &dc, const Bounds &screen) { dc.MeasureText(theme.uiFont, 1.0f, 1.0f, clock, &w, &h); const float scale = h > 0.0f ? 15.0f * sy / h : 1.0f; dc.SetFontScale(scale, scale); - dc.DrawText(clock, screen.x + (clockRight - 6.0f) * sx, screen.y + 12.5f * sy, 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_VCENTER); + dc.DrawText(theme.uiFont, clock, screen.x + (clockRight - 6.0f) * sx, screen.y + 12.5f * sy, 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_VCENTER); } // The lines above and below the question. diff --git a/UI/JoystickHistoryView.cpp b/UI/JoystickHistoryView.cpp index 72f171a29b..82b98c0a13 100644 --- a/UI/JoystickHistoryView.cpp +++ b/UI/JoystickHistoryView.cpp @@ -19,7 +19,7 @@ void JoystickHistoryView::Draw(UIContext &dc) { dc.Flush(); dc.Begin(); Bounds textBounds(bounds_.x, bounds_.centerY() + minRadius + 5.0, bounds_.w, bounds_.h / 2 - minRadius - 5.0); - dc.DrawTextShadowRect(title_, textBounds, 0xFFFFFFFF, ALIGN_TOP | ALIGN_HCENTER | FLAG_WRAP_TEXT); + dc.DrawTextShadowRect(dc.GetTheme().uiFont, title_, textBounds, 0xFFFFFFFF, ALIGN_TOP | ALIGN_HCENTER | FLAG_WRAP_TEXT); dc.Flush(); dc.BeginNoTex(); dc.Draw()->RectOutline(bounds_.centerX() - minRadius, bounds_.centerY() - minRadius, minRadius * 2.0f, minRadius * 2.0f, 0x80FFFFFF); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 4f8247dfbb..6af1642838 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -330,12 +330,10 @@ public: const bool tiny = versionString.size() > 10; const FontStyle *style = GetTextStyle(dc, tiny ? TextSize::Tiny : TextSize::Small); - dc.SetFontStyle(*style); - dc.DrawText(versionString, + dc.DrawText(*style, versionString, bounds_.x + iconImg->w + 8, bounds_.y + logoImg->h + (tiny ? 8 : 6), dc.GetTheme().infoStyle.fgColor); - dc.SetFontStyle(dc.GetTheme().uiFont); } void GetContentDimensions(const UIContext &dc, float &w, float &h) const override { diff --git a/UI/MemStickScreen.cpp b/UI/MemStickScreen.cpp index 3c8fa46787..247402ffce 100644 --- a/UI/MemStickScreen.cpp +++ b/UI/MemStickScreen.cpp @@ -555,11 +555,10 @@ public: void Draw(UIContext &dc) override { const UI::Style &style = dc.GetTheme().itemStyle; dc.FillRoundRect(style.background, bounds_, dc.GetTheme().choiceRoundRadius, roundCorners_); - dc.SetFontStyle(dc.GetTheme().uiFont); if (minLines_ > 1) { - dc.DrawTextRect(text_, bounds_.Expand(-PADDING_X, -PADDING_Y), style.fgColor, FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFont, text_, bounds_.Expand(-PADDING_X, -PADDING_Y), style.fgColor, FLAG_WRAP_TEXT); } else { - dc.DrawTextRect(text_, bounds_.Expand(-PADDING_X, 0), style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFont, text_, bounds_.Expand(-PADDING_X, 0), style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT); } } diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index cffe0042a0..829faf0d8d 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -116,7 +116,7 @@ bool BackgroundScreen::renderOutput(const OutputDisplay &output) { } const Bounds &bounds = dc.GetBounds(); dc.Begin(); - dc.DrawText(text, bounds.centerX(), bounds.centerY(), 0xFF505050, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, text, bounds.centerX(), bounds.centerY(), 0xFF505050, ALIGN_CENTER); dc.Flush(); return true; } @@ -509,11 +509,10 @@ void LogoScreen::DrawForeground(UIContext &dc) { dc.Draw()->DrawImage(ImageID("I_LOGO"), bounds.centerX() + 45, startY, 1.5f, 0xFFFFFFFF, ALIGN_CENTER); //dc.Draw()->DrawTextShadow(UBUNTU48, "PPSSPP", bounds.w / 2, bounds.h / 2 - 30, textColor, ALIGN_CENTER); dc.SetFontScale(1.0f, 1.0f); - dc.SetFontStyle(dc.GetTheme().uiFont); - dc.DrawText(temp, bounds.centerX(), startY + 70, textColor, ALIGN_CENTER); - dc.DrawText(cr->T_cstr("license", "Free Software under GPL 2.0+"), bounds.centerX(), startY + 110, textColor, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, temp, bounds.centerX(), startY + 70, textColor, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, cr->T_cstr("license", "Free Software under GPL 2.0+"), bounds.centerX(), startY + 110, textColor, ALIGN_CENTER); - dc.DrawText("www.ppsspp.org", bounds.centerX(), startY + 160, textColor, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, "www.ppsspp.org", bounds.centerX(), startY + 160, textColor, ALIGN_CENTER); #if !PPSSPP_PLATFORM(UWP) || defined(_DEBUG) // Draw the graphics API, except on UWP where it's always D3D11 @@ -523,7 +522,7 @@ void LogoScreen::DrawForeground(UIContext &dc) { // Add some emoji for testing. apiName += CodepointToUTF8(0x1F41B) + CodepointToUTF8(0x1F41C) + CodepointToUTF8(0x1F914); #endif - dc.DrawText(apiName, bounds.centerX(), startY + 200, textColor, ALIGN_CENTER); + dc.DrawText(dc.GetTheme().uiFont, apiName, bounds.centerX(), startY + 200, textColor, ALIGN_CENTER); #endif dc.Flush(); @@ -827,7 +826,7 @@ void CreditsScroller::Draw(UIContext &dc) { if (alpha > 0.0f) { dc.SetFontScale(ease(alpha), ease(alpha)); - dc.DrawText(credits[i], bounds.centerX(), y + bounds.y, textColor, ALIGN_HCENTER); + dc.DrawText(dc.GetTheme().uiFont, credits[i], bounds.centerX(), y + bounds.y, textColor, ALIGN_HCENTER); dc.SetFontScale(1.0f, 1.0f); } } diff --git a/UI/MiscViews.cpp b/UI/MiscViews.cpp index e91b4a2983..79eb57eb31 100644 --- a/UI/MiscViews.cpp +++ b/UI/MiscViews.cpp @@ -338,7 +338,7 @@ void SearchBar::Draw(UIContext &dc) { dc.Draw()->DrawImage(searchIcon, bounds_.x + leftMargin, bounds_.centerY(), 1.0f, 0xFFFFFFFF, ALIGN_VCENTER); leftMargin += image->w + 10; } - dc.DrawText(searchFilter_, bounds_.x + leftMargin, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER); + dc.DrawText(dc.GetTheme().uiFont, searchFilter_, bounds_.x + leftMargin, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER); } SearchBar::SearchBar(UI::LayoutParams *params) : UI::InertView(params) { diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 195d85c540..06e2ec1393 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -89,14 +89,13 @@ static void RenderOSDProgressBar(UIContext &dc, const OnScreenDisplay::Entry &en dc.FillRoundRect(background, bounds, radius); } - dc.SetFontStyle(dc.GetTheme().uiFont); dc.SetFontScale(1.0f, 1.0f); - dc.DrawTextShadowRect(entry.text, bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER); + dc.DrawTextShadowRect(dc.GetTheme().uiFont, entry.text, bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER); } static void MeasureLeaderboardTracker(UIContext &dc, const std::string &text, float *width, float *height) { - dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text, width, height); + dc.MeasureText(dc.GetTheme().uiFont, 1.0f, 1.0f, text, width, height); *width += 16.0f; *height += 10.0f; } @@ -107,9 +106,8 @@ static void RenderLeaderboardTracker(UIContext &dc, const Bounds &bounds, const UI::Drawable background = UI::Drawable(backgroundColor); dc.DrawRectDropShadow(bounds, 12.0f, 0.7f * alpha); dc.FillRoundRect(background, bounds, dc.GetTheme().choiceRoundRadius * 0.75f); - dc.SetFontStyle(dc.GetTheme().uiFont); dc.SetFontScale(1.0f, 1.0f); - dc.DrawTextShadowRect(text, bounds.Inset(5.0f, 5.0f), colorAlpha(0xFFFFFFFF, alpha), ALIGN_VCENTER | ALIGN_HCENTER); + dc.DrawTextShadowRect(dc.GetTheme().uiFont, text, bounds.Inset(5.0f, 5.0f), colorAlpha(0xFFFFFFFF, alpha), ALIGN_VCENTER | ALIGN_HCENTER); } void OnScreenMessagesView::Draw(UIContext &dc) { @@ -170,7 +168,6 @@ void OnScreenMessagesView::Draw(UIContext &dc) { typeEdges[(size_t)OSDType::STATUS_ICON] = ScreenEdgePosition::TOP_LEFT; typeEdges[(size_t)OSDType::PROGRESS_BAR] = ScreenEdgePosition::TOP_CENTER; // These only function at the top currently, needs fixing. - dc.SetFontStyle(dc.GetTheme().uiFont); dc.SetFontScale(1.0f, 1.0f); // Indexed by the enum ScreenEdgePosition. diff --git a/UI/ProfilerDraw.cpp b/UI/ProfilerDraw.cpp index aa5727fefe..52efda7379 100644 --- a/UI/ProfilerDraw.cpp +++ b/UI/ProfilerDraw.cpp @@ -60,7 +60,6 @@ void DrawProfile(UIContext &ui) { int numThreads = Profiler_GetNumThreads(); int historyLength = Profiler_GetHistoryLength(); - ui.SetFontStyle(ui.theme->uiFont); static float lastMaxVal = 1.0f / 60.0f; float legendMinVal = lastMaxVal * (1.0f / 120.0f); @@ -92,7 +91,7 @@ void DrawProfile(UIContext &ui) { // So they don't move horizontally, we always measure. float w = 0.0f, h = 0.0f; - ui.MeasureText(ui.GetFontStyle(), 1.0f, 1.0f, name, &w, &h); + ui.MeasureText(ui.GetTheme().uiFont, 1.0f, 1.0f, name, &w, &h); if (w > legendWidth) { legendWidth = w; } @@ -117,7 +116,7 @@ void DrawProfile(UIContext &ui) { if (catStatus[i] == PROFILE_CAT_VISIBLE) { float y = legendStartY + legendNum++ * rowH; ui.FillRect(UI::Drawable(opacity | color), Bounds(legendStartX, y, rowH - 2, rowH - 2)); - ui.DrawTextShadow(name, legendStartX + rowH + 2, y, 0xFFFFFFFF, ALIGN_VBASELINE); + ui.DrawTextShadow(ui.GetTheme().uiFont, name, legendStartX + rowH + 2, y, 0xFFFFFFFF, ALIGN_VBASELINE); } } @@ -147,8 +146,8 @@ void DrawProfile(UIContext &ui) { ui.FillRect(UI::Drawable(0x80FFFF00), Bounds(0, y_60th, graphWidth, 2)); ui.FillRect(UI::Drawable(0x80FFFF00), Bounds(0, y_1ms, graphWidth, 2)); - ui.DrawTextShadow("1/60s", 5, y_60th, 0x80FFFF00); - ui.DrawTextShadow("1ms", 5, y_1ms, 0x80FFFF00); + ui.DrawTextShadow(ui.GetTheme().uiFont, "1/60s", 5, y_60th, 0x80FFFF00); + ui.DrawTextShadow(ui.GetTheme().uiFont, "1ms", 5, y_1ms, 0x80FFFF00); std::vector total; total.resize(historyLength); diff --git a/UI/RetroAchievementScreens.cpp b/UI/RetroAchievementScreens.cpp index e62d4ce446..7dadb108ea 100644 --- a/UI/RetroAchievementScreens.cpp +++ b/UI/RetroAchievementScreens.cpp @@ -607,7 +607,6 @@ void RenderAchievement(UIContext &dc, const rc_client_achievement_t *achievement dc.Flush(); dc.Begin(); - dc.SetFontStyle(*GetTextStyle(dc, UI::TextSize::Normal)); char temp[512]; @@ -636,31 +635,28 @@ void RenderAchievement(UIContext &dc, const rc_client_achievement_t *achievement break; } - dc.DrawTextRect(title, bounds.Inset(iconSpace + 12.0f, 2.0f, padding, padding), fgColor, ALIGN_TOPLEFT); + dc.DrawTextRect(*GetTextStyle(dc, UI::TextSize::Normal), title, bounds.Inset(iconSpace + 12.0f, 2.0f, padding, padding), fgColor, ALIGN_TOPLEFT); - dc.SetFontStyle(*GetTextStyle(dc, UI::TextSize::Small)); - dc.DrawTextRectSqueeze(DeNull(achievement->description), bounds.Inset(iconSpace + 12.0f, 39.0f, padding, padding), fgColor, ALIGN_TOPLEFT); + dc.DrawTextRectSqueeze(*GetTextStyle(dc, UI::TextSize::Small), DeNull(achievement->description), bounds.Inset(iconSpace + 12.0f, 39.0f, padding, padding), fgColor, ALIGN_TOPLEFT); - dc.DrawTextRect(badge, bounds, fgColor, ALIGN_TOPRIGHT); + dc.DrawTextRect(*GetTextStyle(dc, UI::TextSize::Small), badge, bounds, fgColor, ALIGN_TOPRIGHT); if (style == AchievementRenderStyle::LISTED && strlen(achievement->measured_progress) > 0) { - dc.SetFontStyle(*GetTextStyle(dc, UI::TextSize::Normal)); - dc.DrawTextRect(achievement->measured_progress, bounds.Inset(iconSpace + 12.0f, padding, padding + 100.0f, padding), fgColor, ALIGN_VCENTER | ALIGN_RIGHT); + dc.DrawTextRect(*GetTextStyle(dc, UI::TextSize::Normal), achievement->measured_progress, bounds.Inset(iconSpace + 12.0f, padding, padding + 100.0f, padding), fgColor, ALIGN_VCENTER | ALIGN_RIGHT); } // TODO: Draw measured_progress / measured_percent in a cute way snprintf(temp, sizeof(temp), "%d", achievement->points); // The points number to the right. - dc.SetFontStyle(*GetTextStyle(dc, UI::TextSize::Big)); - dc.DrawTextRect(temp, bounds.Expand(-5.0f, -5.0f), fgColor, ALIGN_RIGHT | (badge.empty() ? ALIGN_VCENTER : ALIGN_BOTTOMRIGHT)); + dc.DrawTextRect(*GetTextStyle(dc, UI::TextSize::Big), temp, bounds.Expand(-5.0f, -5.0f), fgColor, ALIGN_RIGHT | (badge.empty() ? ALIGN_VCENTER : ALIGN_BOTTOMRIGHT)); dc.Flush(); break; } case AchievementRenderStyle::PROGRESS_INDICATOR: // TODO: Also render a progress bar. - dc.DrawTextRect(achievement->measured_progress, bounds.Inset(iconSpace + padding * 2.0f, padding, padding, padding), fgColor, ALIGN_LEFT | ALIGN_VCENTER); + dc.DrawTextRect(*GetTextStyle(dc, UI::TextSize::Normal), achievement->measured_progress, bounds.Inset(iconSpace + padding * 2.0f, padding, padding, padding), fgColor, ALIGN_LEFT | ALIGN_VCENTER); // Show the unlocked icon. iconState = RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED; break; @@ -679,7 +675,6 @@ void RenderAchievement(UIContext &dc, const rc_client_achievement_t *achievement dc.Draw()->DrawTexRect(Bounds(bounds.x + padding, bounds.y + padding, iconSpace, iconSpace), 0.0f, 0.0f, 1.0f, 1.0f, whiteAlpha(alpha)); } } - dc.SetFontStyle(*GetTextStyle(dc, UI::TextSize::Normal)); dc.Flush(); dc.RebindTexture(); dc.PopScissor(); @@ -712,16 +707,13 @@ static void RenderGameAchievementSummary(UIContext &dc, const Bounds &bounds, fl dc.FillRect(background, bounds); dc.SetFontScale(1.0f, 1.0f); - dc.SetFontStyle(dc.GetTheme().uiFont); - dc.DrawTextRect(gameInfo->title, bounds.Inset(iconSpace + 5.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT | FLAG_ELLIPSIZE_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFont, gameInfo->title, bounds.Inset(iconSpace + 5.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT | FLAG_ELLIPSIZE_TEXT); std::string description = Achievements::GetGameAchievementSummary(subsetId); - dc.SetFontStyle(dc.GetTheme().uiFontSmall); - dc.DrawTextRect(description, bounds.Inset(iconSpace + 5.0f, 38.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT | FLAG_WRAP_TEXT); + dc.DrawTextRect(dc.GetTheme().uiFontSmall, description, bounds.Inset(iconSpace + 5.0f, 38.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT | FLAG_WRAP_TEXT); - dc.SetFontStyle(dc.GetTheme().uiFont); dc.Flush(); if (gameInfo->badge_url && strlen(gameInfo->badge_url) > 0) { @@ -758,23 +750,20 @@ static void RenderLeaderboardSummary(UIContext &dc, const rc_client_leaderboard_ dc.FillRect(background, bounds); dc.SetFontScale(1.0f, 1.0f); - dc.SetFontStyle(dc.GetTheme().uiFont); - dc.DrawTextRect(DeNull(leaderboard->title), bounds.Inset(12.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT); + dc.DrawTextRect(dc.GetTheme().uiFont, DeNull(leaderboard->title), bounds.Inset(12.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT); - dc.SetFontStyle(dc.GetTheme().uiFontSmall); - dc.DrawTextRectSqueeze(DeNull(leaderboard->description), bounds.Inset(12.0f, 39.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT); + dc.DrawTextRectSqueeze(dc.GetTheme().uiFontSmall, DeNull(leaderboard->description), bounds.Inset(12.0f, 39.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT); /* char temp[64]; snprintf(temp, sizeof(temp), "%d", leaderboard.points); dc.SetFontScale(1.5f, 1.5f); - dc.DrawTextRect(temp, bounds.Expand(-5.0f, -5.0f), fgColor, ALIGN_RIGHT | ALIGN_VCENTER); + dc.DrawTextRect(dc.GetTheme().uiFontSmall, temp, bounds.Expand(-5.0f, -5.0f), fgColor, ALIGN_RIGHT | ALIGN_VCENTER); dc.Flush(); */ - dc.SetFontStyle(dc.GetTheme().uiFont); dc.Flush(); dc.RebindTexture(); @@ -809,17 +798,13 @@ static void RenderLeaderboardEntry(UIContext &dc, const rc_client_leaderboard_en dc.FillRect(background, bounds); dc.SetFontScale(1.0f, 1.0f); - dc.SetFontStyle(dc.GetTheme().uiFontBig); - dc.DrawTextRect(StringFromFormat("%d", entry->rank), Bounds(bounds.x + 4.0f, bounds.y + 4.0f, numberSpace - 10.0f, bounds.h - 4.0f * 2.0f), fgColor, ALIGN_TOPRIGHT); + dc.DrawTextRect(dc.GetTheme().uiFontBig, StringFromFormat("%d", entry->rank), Bounds(bounds.x + 4.0f, bounds.y + 4.0f, numberSpace - 10.0f, bounds.h - 4.0f * 2.0f), fgColor, ALIGN_TOPRIGHT); - dc.SetFontStyle(dc.GetTheme().uiFont); - dc.DrawTextRect(entry->user, bounds.Inset(iconSpace + 5.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT); + dc.DrawTextRect(dc.GetTheme().uiFont, entry->user, bounds.Inset(iconSpace + 5.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT); - dc.SetFontStyle(dc.GetTheme().uiFontSmall); - dc.DrawTextRect(DeNull(entry->display), bounds.Inset(iconSpace + 5.0f, 38.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT); + dc.DrawTextRect(dc.GetTheme().uiFontSmall, DeNull(entry->display), bounds.Inset(iconSpace + 5.0f, 38.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT); - dc.SetFontStyle(dc.GetTheme().uiFont); dc.Flush(); // Come up with a unique name for the icon entry. diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 5bbab0cc07..2ece491bc5 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -374,14 +374,13 @@ void SavedataButton::Draw(UIContext &dc) { dc.Draw()->Flush(); dc.RebindTexture(); - dc.SetFontStyle(dc.GetTheme().uiFont); float tw, th; dc.Draw()->Flush(); dc.PushScissor(bounds_); UpdateText(ginfo); - dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, title_, &tw, &th, 0); + dc.MeasureText(dc.GetTheme().uiFont, 1.0f, 1.0f, title_, &tw, &th, 0); int availableWidth = bounds_.w - 150; float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f; @@ -395,9 +394,9 @@ void SavedataButton::Draw(UIContext &dc) { tb.w = std::max(1.0f, bounds_.w - 150.0f); dc.PushScissor(tb); } - dc.DrawText(title_, bounds_.x + tx, bounds_.y + 4, style.fgColor, ALIGN_TOPLEFT); + dc.DrawText(dc.GetTheme().uiFont, title_, bounds_.x + tx, bounds_.y + 4, style.fgColor, ALIGN_TOPLEFT); dc.SetFontScale(0.6f, 0.6f); - dc.DrawText(subtitle_, bounds_.x + tx, bounds_.y2() - 7, style.fgColor, ALIGN_BOTTOM); + dc.DrawText(dc.GetTheme().uiFont, subtitle_, bounds_.x + tx, bounds_.y2() - 7, style.fgColor, ALIGN_BOTTOM); dc.SetFontScale(1.0f, 1.0f); if (availableWidth < tw) { From 59c9084fed70d84bccc3303acd0af3964364b8b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 14:33:12 +0100 Subject: [PATCH 4/9] Firmware: Create the NAND's .nomedia on every install Installing from an updater file left it out, so Android's media scanner indexed the firmware. Also only creates it when missing. Co-Authored-By: Claude Opus 5.5 --- Core/Util/PSARUnpack.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Core/Util/PSARUnpack.cpp b/Core/Util/PSARUnpack.cpp index 2ff1ed082d..c4973c2cd0 100644 --- a/Core/Util/PSARUnpack.cpp +++ b/Core/Util/PSARUnpack.cpp @@ -1621,6 +1621,16 @@ std::string ReadMountedDiscUpdaterVersion() { return ReadUpdaterVersionFromArchiveHeader(); } +// Keeps Android's media scanner out of the firmware. +static void CreateNoMediaFile(const Path &dir) { +#if PPSSPP_PLATFORM(ANDROID) + const Path noMedia = dir / ".nomedia"; + if (!File::Exists(noMedia) && !File::CreateEmptyFile(noMedia)) { + WARN_LOG(Log::Loader, "Couldn't create %s", noMedia.c_str()); + } +#endif +} + bool UnpackUpdaterFromMountedDisc(const Path &outputDir, const PSARUnpackOptions &options, PSARUnpackStats *stats, std::string *error) { std::string localError; if (!error) { @@ -1633,10 +1643,7 @@ bool UnpackUpdaterFromMountedDisc(const Path &outputDir, const PSARUnpackOptions return false; } - // Write a nomedia file to avoid Android indexing the files. -#if PPSSPP_PLATFORM(ANDROID) - File::CreateEmptyFile(outputDir / ".nomedia"); -#endif + CreateNoMediaFile(outputDir); INFO_LOG(Log::Loader, "Found a %d byte updater on the mounted disc", (int)psar.size()); return UnpackPSAR(psar.data(), psar.size(), outputDir, options, stats, error); @@ -1750,6 +1757,9 @@ bool InstallFirmware(const Path &updater, const Path &nandRoot, const PSARUnpack ok = SwapStagingIntoPlace(nandRoot, staging, error); } if (ok) { + // Whichever way the firmware came, and also into a NAND that was set up without one. + CreateNoMediaFile(nandRoot); + // The shell refuses to install a theme without this directory, and the updater's archive // doesn't have it (6.61's has nothing under flash1), so the real updater must create it. const Path themeDir = nandRoot / "flash1/vsh/theme"; @@ -1786,11 +1796,7 @@ bool AutoInstallFirmwareFromDisc() { } const Path nandRoot = GetSysDirectory(DIRECTORY_NAND); - - // Write a nomedia file to avoid Android indexing the files. -#if PPSSPP_PLATFORM(ANDROID) - File::CreateEmptyFile(nandRoot / ".nomedia"); -#endif + CreateNoMediaFile(nandRoot); InstalledFirmwareInfo installed; ReadInstalledFirmwareInfo(nandRoot, &installed, false); From 3c491b74235a9ac655dc741df0d31f5d402c9fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 15:00:39 +0100 Subject: [PATCH 5/9] Vulkan: Retry with the system driver when a custom driver fails Before falling back to OpenGL. Covers the availability check, a library that won't load, a failed InitAPI and a crash on the last run. The setting is cleared and a notice names the driver. Co-Authored-By: Claude Opus 5.5 --- Common/GPU/Vulkan/VulkanLoader.cpp | 48 +++++++++++++++++++++++++++++- Common/GPU/Vulkan/VulkanLoader.h | 5 ++++ UI/NativeApp.cpp | 17 +++++++++++ android/jni/app-android.cpp | 16 +++++++++- 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanLoader.cpp b/Common/GPU/Vulkan/VulkanLoader.cpp index 94fc47e97a..8e5f5448a7 100644 --- a/Common/GPU/Vulkan/VulkanLoader.cpp +++ b/Common/GPU/Vulkan/VulkanLoader.cpp @@ -362,6 +362,24 @@ static const char * const so_names[] = { }; #endif +// The custom driver that didn't work, for VulkanTakeFailedCustomDriver. +static std::string g_failedCustomDriver; + +static bool CustomDriverInUse() { +#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64) + return !g_Config.sCustomDriver.empty() && g_Config.sCustomDriver != "Default" && VulkanCustomDriverAllowedByOS(); +#else + return false; +#endif +} + +// Forgets the custom driver, so that the next try (and the next run) uses the system's. +static void ForgetCustomDriver() { + ERROR_LOG(Log::G3D, "Custom driver '%s' failed, switching to the system driver", g_Config.sCustomDriver.c_str()); + g_failedCustomDriver = g_Config.sCustomDriver; + g_Config.sCustomDriver.clear(); +} + static VulkanLibraryHandle VulkanLoadLibrary(std::string *errorString) { #if PPSSPP_PLATFORM(SWITCH) // Always unavailable, for now. @@ -376,7 +394,7 @@ static VulkanLibraryHandle VulkanLoadLibrary(std::string *errorString) { #if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64) if (!g_Config.sCustomDriver.empty() && g_Config.sCustomDriver != "Default" && !VulkanCustomDriverAllowedByOS()) { WARN_LOG(Log::G3D, "Custom driver '%s' ignored, not supported on this Android version", g_Config.sCustomDriver.c_str()); - } else if (!g_Config.sCustomDriver.empty() && g_Config.sCustomDriver != "Default") { + } else if (CustomDriverInUse()) { const Path driverPath = g_Config.internalDataDirectory / "drivers" / g_Config.sCustomDriver; json::JsonReader meta = json::JsonReader((driverPath / "meta.json").c_str()); @@ -402,6 +420,12 @@ static VulkanLibraryHandle VulkanLoadLibrary(std::string *errorString) { } else { INFO_LOG(Log::G3D, "Vulkan library loaded with AdrenoTools ('%s')", g_Config.sCustomDriver.c_str()); } + } else { + ERROR_LOG(Log::G3D, "Custom driver '%s' has no readable meta.json", g_Config.sCustomDriver.c_str()); + } + if (!lib) { + // The system's driver is loaded below instead. + ForgetCustomDriver(); } } #endif @@ -435,6 +459,22 @@ static void VulkanFreeLibrary(VulkanLibraryHandle &h) { } } +bool VulkanDisableCustomDriver() { + if (!CustomDriverInUse()) { + return false; + } + ForgetCustomDriver(); + VulkanFreeLibrary(vulkanLibrary); + g_vulkanAvailabilityChecked = false; + return true; +} + +std::string VulkanTakeFailedCustomDriver() { + std::string driver = std::move(g_failedCustomDriver); + g_failedCustomDriver.clear(); + return driver; +} + void VulkanSetAvailable(bool available) { INFO_LOG(Log::G3D, "Setting Vulkan availability to true"); g_vulkanAvailabilityChecked = true; @@ -480,6 +520,8 @@ bool VulkanMayBeAvailable() { std::string errorStr; VulkanLibraryHandle lib = VulkanLoadLibrary(&errorStr); + // After the load, which forgets a custom driver that didn't load at all. + const bool usedCustomDriver = CustomDriverInUse(); if (!lib) { INFO_LOG(Log::G3D, "Vulkan loader: Library not available: %s", errorStr.c_str()); g_vulkanAvailabilityChecked = true; @@ -678,6 +720,10 @@ bail: } if (!g_vulkanMayBeAvailable) { WARN_LOG(Log::G3D, "Vulkan with working device not detected."); + // Try the system's driver before giving up on Vulkan. + if (usedCustomDriver && VulkanDisableCustomDriver()) { + return VulkanMayBeAvailable(); + } } return g_vulkanMayBeAvailable; #endif diff --git a/Common/GPU/Vulkan/VulkanLoader.h b/Common/GPU/Vulkan/VulkanLoader.h index 97785cee27..738fb404da 100644 --- a/Common/GPU/Vulkan/VulkanLoader.h +++ b/Common/GPU/Vulkan/VulkanLoader.h @@ -286,6 +286,11 @@ void VulkanApplyCustomDriverEnvVars(AdrenoEnvVar envVars); void VulkanSetNativeLibDir(std::string_view nativeLibDir); bool VulkanMayBeAvailable(); void VulkanSetAvailable(bool available); +// When Vulkan didn't work with a custom driver: clears the setting and unloads the library, so the next +// try gets the system's driver. Returns false if no custom driver was in use. +bool VulkanDisableCustomDriver(); +// The custom driver that failed (and was cleared) since the last call, if any. +std::string VulkanTakeFailedCustomDriver(); bool VulkanLoad(std::string *errorStr); // Alternative to VulkanLoad() for adopting a Vulkan loader that a host application (e.g. a libretro diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 628646b54c..444807d2d4 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -65,6 +65,7 @@ #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/GPU/thin3d.h" #include "Common/GPU/Vulkan/FixedSPIRVCache.h" +#include "Common/GPU/Vulkan/VulkanLoader.h" #include "Common/UI/UI.h" #include "Common/UI/Screen.h" #include "Common/UI/ScreenManager.h" @@ -538,6 +539,22 @@ static void CheckFailedGPUBackends() { ERROR_LOG(Log::Loader, "Failed graphics backends: %s", g_Config.sFailedGPUBackends.c_str()); } +#if PPSSPP_PLATFORM(ANDROID) + // A crash in Vulkan with a custom driver gets another try with the system's driver before leaving Vulkan. + std::vector failed; + SplitString(g_Config.sFailedGPUBackends, ',', failed); + if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN && ContainsNoCase(failed, "VULKAN") && VulkanDisableCustomDriver()) { + RemoveNoCase(failed, "VULKAN"); + g_Config.sFailedGPUBackends.clear(); + for (const std::string &backend : failed) { + if (!g_Config.sFailedGPUBackends.empty()) { + g_Config.sFailedGPUBackends += ","; + } + g_Config.sFailedGPUBackends += backend; + } + } +#endif + // Okay, let's not try a backend in the failed list. g_Config.iGPUBackend = g_Config.NextValidBackend(); if (lastBackend != g_Config.iGPUBackend) { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index c05fa5ffb6..4259f33e3f 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -61,6 +61,7 @@ struct JNIEnv {}; #define JNI_VERSION_1_6 16 #endif +#include "Common/Data/Text/I18n.h" #include "Common/Log.h" #include "Common/LogReporting.h" @@ -839,10 +840,15 @@ retry: GraphicsContext *ctx = new VulkanGraphicsContext(); std::string errorMessage; if (!ctx->InitAPI(nullptr, &g_Config.sVulkanDevice, &errorMessage)) { + delete ctx; + // Only fall back to OpenGL when the system's driver doesn't work either. + if (VulkanDisableCustomDriver()) { + ERROR_LOG(Log::System, "Failed to initialize Vulkan with a custom driver, retrying with the system's: %s", errorMessage.c_str()); + goto retry; + } ERROR_LOG(Log::System, "Failed to initialize Vulkan, switching to OpenGL: %s", errorMessage.c_str()); g_Config.iGPUBackend = (int)GPUBackend::OPENGL; SetGPUBackend(GPUBackend::OPENGL); - delete ctx; goto retry; } ((VulkanContext *)ctx->GetAPIContext())->SetCbGetDrawSize([]() { @@ -858,6 +864,14 @@ retry: g_Config.iGPUBackend = (int)GPUBackend::OPENGL; goto retry; } + + // Here rather than where the driver failed, which can be before the translations are loaded. + const std::string failedDriver = VulkanTakeFailedCustomDriver(); + if (!failedDriver.empty()) { + auto gr = GetI18NCategory(I18NCat::GRAPHICS); + g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The custom driver failed, so the default driver is used instead"), failedDriver, 10.0f); + g_Config.Save("CustomDriverFailed"); + } } extern "C" void Java_org_ppsspp_ppsspp_NativeApp_setAchievementsHostOverride(JNIEnv *env, jclass, jstring jhost) { From b44e93b3c652f81f321b8ff4431418368727f523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 15:09:16 +0100 Subject: [PATCH 6/9] Android: Check for a crashing custom driver on Android 11+ too There the backend is never given up on, but a custom driver that crashed Vulkan last time is cleared, and Vulkan is tried again with the system's. Co-Authored-By: Claude Opus 5.5 --- UI/NativeApp.cpp | 17 ++++++++++++----- android/jni/app-android.cpp | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 444807d2d4..5f5e0f976b 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -507,12 +507,17 @@ static void CheckFailedGPUBackends() { return; #endif + // Only a custom driver is given up on, never the backend. + bool customDriverOnly = false; #if PPSSPP_PLATFORM(ANDROID) if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) { - // In Android 11 or later, Vulkan is as stable as OpenGL, so let's not even bother. - // Have also seen unexplained issues with random fallbacks to OpenGL for no good reason, - // especially when debugging. - return; + // In Android 11 or later, Vulkan is as stable as OpenGL, so the only thing to check for is a custom + // driver that crashes. Have also seen unexplained issues with random fallbacks to OpenGL for no good + // reason, especially when debugging. + if (g_Config.iGPUBackend != (int)GPUBackend::VULKAN || g_Config.sCustomDriver.empty()) { + return; + } + customDriverOnly = true; } #endif @@ -556,7 +561,9 @@ static void CheckFailedGPUBackends() { #endif // Okay, let's not try a backend in the failed list. - g_Config.iGPUBackend = g_Config.NextValidBackend(); + if (!customDriverOnly) { + g_Config.iGPUBackend = g_Config.NextValidBackend(); + } if (lastBackend != g_Config.iGPUBackend) { // This is the expected path. std::string param = GPUBackendToString((GPUBackend)lastBackend) + " -> " + GPUBackendToString((GPUBackend)g_Config.iGPUBackend); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 4259f33e3f..cd646f9f1e 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -869,7 +869,7 @@ retry: const std::string failedDriver = VulkanTakeFailedCustomDriver(); if (!failedDriver.empty()) { auto gr = GetI18NCategory(I18NCat::GRAPHICS); - g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The custom driver failed, so the default driver is used instead"), failedDriver, 10.0f); + g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The custom Vulkan driver failed to load. Switching back to default."), failedDriver, 10.0f); g_Config.Save("CustomDriverFailed"); } } From e361ba7ae1725eb2f0eda94a151901ca2f675ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 15:09:16 +0100 Subject: [PATCH 7/9] Translate the custom Vulkan driver failure notice Co-Authored-By: Claude Opus 5.5 --- assets/lang/ar_AE.ini | 1 + assets/lang/az_AZ.ini | 1 + assets/lang/be_BY.ini | 1 + assets/lang/bg_BG.ini | 1 + assets/lang/ca_ES.ini | 1 + assets/lang/cz_CZ.ini | 1 + assets/lang/da_DK.ini | 1 + assets/lang/de_DE.ini | 1 + assets/lang/dr_ID.ini | 1 + assets/lang/en_US.ini | 1 + assets/lang/es_ES.ini | 1 + assets/lang/es_LA.ini | 1 + assets/lang/fa_IR.ini | 1 + assets/lang/fi_FI.ini | 1 + assets/lang/fr_FR.ini | 1 + assets/lang/gl_ES.ini | 1 + assets/lang/gr_EL.ini | 1 + assets/lang/he_IL.ini | 1 + assets/lang/hr_HR.ini | 1 + assets/lang/hu_HU.ini | 1 + assets/lang/id_ID.ini | 1 + assets/lang/it_IT.ini | 1 + assets/lang/ja_JP.ini | 1 + assets/lang/jv_ID.ini | 1 + assets/lang/km_KH.ini | 1 + assets/lang/ko_KR.ini | 1 + assets/lang/ku_SO.ini | 1 + assets/lang/lo_LA.ini | 1 + assets/lang/lt-LT.ini | 1 + assets/lang/ms_MY.ini | 1 + assets/lang/nl_NL.ini | 1 + assets/lang/nn_NO.ini | 1 + assets/lang/no_NO.ini | 1 + assets/lang/pl_PL.ini | 1 + assets/lang/pt_BR.ini | 1 + assets/lang/pt_PT.ini | 1 + assets/lang/ro_RO.ini | 1 + assets/lang/ru_RU.ini | 1 + assets/lang/sv_SE.ini | 1 + assets/lang/tg_PH.ini | 1 + assets/lang/th_TH.ini | 1 + assets/lang/tr_TR.ini | 1 + assets/lang/uk_UA.ini | 1 + assets/lang/vi_VN.ini | 1 + assets/lang/zh_CN.ini | 1 + assets/lang/zh_TW.ini | 1 + 46 files changed, 46 insertions(+) diff --git a/assets/lang/ar_AE.ini b/assets/lang/ar_AE.ini index 4f58256078..7996038b9f 100644 --- a/assets/lang/ar_AE.ini +++ b/assets/lang/ar_AE.ini @@ -920,6 +920,7 @@ Texture Filtering = مفلتر الرسوم Texture replacement pack activated = تم تفعيل حزمة استبدال الرسوم # AI translated Texture upscaling = تكبير الرسوم The chosen ZIP file doesn't contain a valid driver = ملف ZIP المختار لا يحتوي على تعريف صالح # AI translated +The custom Vulkan driver failed to load. Switching back to default. = تعذّر تحميل برنامج تشغيل Vulkan المخصص. جارٍ الرجوع إلى الافتراضي. # AI translated Unlimited = لا محدود Up to 1 = حتى 1 # AI translated Up to 2 = حتى 2 # AI translated diff --git a/assets/lang/az_AZ.ini b/assets/lang/az_AZ.ini index bb44e6ab65..ac21aeb0c0 100644 --- a/assets/lang/az_AZ.ini +++ b/assets/lang/az_AZ.ini @@ -912,6 +912,7 @@ Texture Filtering = Toxuma süzgüsü Texture replacement pack activated = Toxuma yerdəyişməsi bağlaması işə salınıb Texture upscaling = Toxuma ölçəklənişi The chosen ZIP file doesn't contain a valid driver = Seçilmiş ZIP fayl keçərli sürücü daşımır +The custom Vulkan driver failed to load. Switching back to default. = Fərdi Vulkan drayveri yüklənmədi. Standart drayverə qayıdılır. # AI translated Unlimited = Sınırsız Up to 1 = 1'ə qədər Up to 2 = 2'yə qədər diff --git a/assets/lang/be_BY.ini b/assets/lang/be_BY.ini index 0c22324e78..44b011463e 100644 --- a/assets/lang/be_BY.ini +++ b/assets/lang/be_BY.ini @@ -912,6 +912,7 @@ Texture Filtering = Фільтраванне тэкстур Texture replacement pack activated = Пакет замены тэкстур актываваны # AI translated Texture upscaling = Маштабаванне тэкстур The chosen ZIP file doesn't contain a valid driver = Выбраны ZIP-файл не змяшчае прыдатнага драйвера # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Не ўдалося загрузіць карыстальніцкі драйвер Vulkan. Вяртаемся да стандартнага. # AI translated Unlimited = Неабмежаваны Up to 1 = Да 1 Up to 2 = Да 2 diff --git a/assets/lang/bg_BG.ini b/assets/lang/bg_BG.ini index 237076d0c8..b72abe744c 100644 --- a/assets/lang/bg_BG.ini +++ b/assets/lang/bg_BG.ini @@ -912,6 +912,7 @@ Texture Filtering = Текстурно филтриране Texture replacement pack activated = Пакетът за замяна на текстури е включен # AI translated Texture upscaling = Текстурно мащабиране The chosen ZIP file doesn't contain a valid driver = Избраният ZIP файл не съдържа валиден драйвер +The custom Vulkan driver failed to load. Switching back to default. = Персонализираният драйвер за Vulkan не можа да се зареди. Връщане към стандартния. # AI translated Unlimited = Няма ограничения Up to 1 = До 1 Up to 2 = До 2 diff --git a/assets/lang/ca_ES.ini b/assets/lang/ca_ES.ini index 9f9018b532..715482fb63 100644 --- a/assets/lang/ca_ES.ini +++ b/assets/lang/ca_ES.ini @@ -912,6 +912,7 @@ Texture Filtering = Filtrat de textures Texture replacement pack activated = Paquet de reemplaçament de textures activat # AI translated Texture upscaling = Escalat de textures The chosen ZIP file doesn't contain a valid driver = El fitxer ZIP triat no conté cap controlador vàlid # AI translated +The custom Vulkan driver failed to load. Switching back to default. = No s'ha pogut carregar el controlador Vulkan personalitzat. Es torna al predeterminat. # AI translated Unlimited = Il·limitat Up to 1 = Fins a 1 Up to 2 = Fins a 2 diff --git a/assets/lang/cz_CZ.ini b/assets/lang/cz_CZ.ini index fd70ffba04..2f43f5674c 100644 --- a/assets/lang/cz_CZ.ini +++ b/assets/lang/cz_CZ.ini @@ -912,6 +912,7 @@ Texture Filtering = Filtrování textur Texture replacement pack activated = Balíček náhradních textur aktivován # AI translated Texture upscaling = Zvětšení textur # AI translated The chosen ZIP file doesn't contain a valid driver = Zvolený soubor ZIP neobsahuje platný ovladač # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Vlastní ovladač Vulkan se nepodařilo načíst. Přepínám zpět na výchozí. # AI translated Unlimited = Neomezené Up to 1 = Až 1 # AI translated Up to 2 = Až 2 # AI translated diff --git a/assets/lang/da_DK.ini b/assets/lang/da_DK.ini index edf90e26ee..d0a9e04c94 100644 --- a/assets/lang/da_DK.ini +++ b/assets/lang/da_DK.ini @@ -912,6 +912,7 @@ Texture Filtering = Texturefiltrering # AI translated Texture replacement pack activated = Pakke med erstatningstextures aktiveret # AI translated Texture upscaling = Texture-opskalering # AI translated The chosen ZIP file doesn't contain a valid driver = Den valgte ZIP-fil indeholder ikke en gyldig driver # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Den brugerdefinerede Vulkan-driver kunne ikke indlæses. Skifter tilbage til standard. # AI translated Unlimited = Ubegrænset Up to 1 = Op til 1 # AI translated Up to 2 = Op til 2 # AI translated diff --git a/assets/lang/de_DE.ini b/assets/lang/de_DE.ini index 7063dc19c4..85080919a8 100644 --- a/assets/lang/de_DE.ini +++ b/assets/lang/de_DE.ini @@ -911,6 +911,7 @@ Texture Filtering = Texturfilterung Texture replacement pack activated = Textur-Ersatzpaket aktiviert Texture upscaling = Texturskalierung The chosen ZIP file doesn't contain a valid driver = Die gewählte ZIP-Datei enthält keinen gültigen Treiber +The custom Vulkan driver failed to load. Switching back to default. = Der benutzerdefinierte Vulkan-Treiber konnte nicht geladen werden. Es wird wieder der Standardtreiber verwendet. # AI translated Unlimited = Unbegrenzt Up to 1 = Bis zu 1 Up to 2 = Bis zu 2 diff --git a/assets/lang/dr_ID.ini b/assets/lang/dr_ID.ini index aa81f7d593..93d4069f62 100644 --- a/assets/lang/dr_ID.ini +++ b/assets/lang/dr_ID.ini @@ -912,6 +912,7 @@ Texture Filtering = Saring i Texture Texture replacement pack activated = Texture replacement pack activated Texture upscaling = Petonggoi Texture The chosen ZIP file doesn't contain a valid driver = The chosen ZIP file doesn't contain a valid driver +The custom Vulkan driver failed to load. Switching back to default. = The custom Vulkan driver failed to load. Switching back to default. Unlimited = Unlimited Up to 1 = Up to 1 Up to 2 = Up to 2 diff --git a/assets/lang/en_US.ini b/assets/lang/en_US.ini index 3ce06d1a89..88fb41c31d 100644 --- a/assets/lang/en_US.ini +++ b/assets/lang/en_US.ini @@ -936,6 +936,7 @@ Texture Filtering = Texture filtering Texture replacement pack activated = Texture replacement pack activated Texture upscaling = Texture upscaling The chosen ZIP file doesn't contain a valid driver = The chosen ZIP file doesn't contain a valid driver +The custom Vulkan driver failed to load. Switching back to default. = The custom Vulkan driver failed to load. Switching back to default. Unlimited = Unlimited Up to 1 = Up to 1 Up to 2 = Up to 2 diff --git a/assets/lang/es_ES.ini b/assets/lang/es_ES.ini index 5100ad2cc8..deaff51758 100644 --- a/assets/lang/es_ES.ini +++ b/assets/lang/es_ES.ini @@ -913,6 +913,7 @@ Texture Filtering = Filtrado de texturas Texture replacement pack activated = Paquete de reemplazo de textura activado Texture upscaling = Escalado de texturas The chosen ZIP file doesn't contain a valid driver = El archivo ZIP seleccionado no contiene un controlador válido +The custom Vulkan driver failed to load. Switching back to default. = No se ha podido cargar el controlador Vulkan personalizado. Se vuelve al predeterminado. # AI translated Unlimited = Ilimitado Up to 1 = Hasta 1 Up to 2 = Hasta 2 diff --git a/assets/lang/es_LA.ini b/assets/lang/es_LA.ini index 76b9390fce..41987fcbe7 100644 --- a/assets/lang/es_LA.ini +++ b/assets/lang/es_LA.ini @@ -912,6 +912,7 @@ Texture Filtering = Filtrado de texturas Texture replacement pack activated = Paquete de reemplazo de texturas activado # AI translated Texture upscaling = Escalado de texturas The chosen ZIP file doesn't contain a valid driver = El archivo ZIP elegido no contiene un controlador válido # AI translated +The custom Vulkan driver failed to load. Switching back to default. = No se pudo cargar el controlador Vulkan personalizado. Se regresa al predeterminado. # AI translated Unlimited = Ilimitado Up to 1 = Hasta 1 Up to 2 = Hasta 2 diff --git a/assets/lang/fa_IR.ini b/assets/lang/fa_IR.ini index d88cdb837f..764fc4f476 100644 --- a/assets/lang/fa_IR.ini +++ b/assets/lang/fa_IR.ini @@ -912,6 +912,7 @@ Texture Filtering = فیلتر کردن بافت Texture replacement pack activated = بستهٔ جایگزینی بافت فعال شد # AI translated Texture upscaling = افزایش سایز بافت‌ها # AI translated The chosen ZIP file doesn't contain a valid driver = فایل ZIP انتخاب‌شده درایور معتبری ندارد # AI translated +The custom Vulkan driver failed to load. Switching back to default. = درایور سفارشی Vulkan بارگذاری نشد. بازگشت به درایور پیش‌فرض. # AI translated Unlimited = نامحدود Up to 1 = حداکثر ۱ # AI translated Up to 2 = حداکثر ۲ # AI translated diff --git a/assets/lang/fi_FI.ini b/assets/lang/fi_FI.ini index 38e1cb6f07..81395493a1 100644 --- a/assets/lang/fi_FI.ini +++ b/assets/lang/fi_FI.ini @@ -912,6 +912,7 @@ Texture Filtering = Tekstuurin suodatus Texture replacement pack activated = Tekstuurin korvauspaketti aktivoitu # AI translated Texture upscaling = Tekstuurin skaalaus The chosen ZIP file doesn't contain a valid driver = Valittu ZIP-tiedosto ei sisällä kelvollista ajuria # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Mukautetun Vulkan-ajurin lataaminen epäonnistui. Palataan oletukseen. # AI translated Unlimited = Rajoittamaton Up to 1 = Enintään 1 Up to 2 = Enintään 2 diff --git a/assets/lang/fr_FR.ini b/assets/lang/fr_FR.ini index 41b04a25eb..0be68e3cf7 100644 --- a/assets/lang/fr_FR.ini +++ b/assets/lang/fr_FR.ini @@ -936,6 +936,7 @@ Texture Filtering = Filtrage des textures Texture replacement pack activated = Pack de remplacement de textures activé # AI translated Texture upscaling = Mise à l'échelle des textures The chosen ZIP file doesn't contain a valid driver = Le fichier ZIP sélectionné ne contient pas de pilote valide +The custom Vulkan driver failed to load. Switching back to default. = Le pilote Vulkan personnalisé n'a pas pu être chargé. Retour au pilote par défaut. # AI translated Unlimited = Illimité Up to 1 = Max 1 Up to 2 = Max 2 diff --git a/assets/lang/gl_ES.ini b/assets/lang/gl_ES.ini index 85404853a6..c0f399f775 100644 --- a/assets/lang/gl_ES.ini +++ b/assets/lang/gl_ES.ini @@ -912,6 +912,7 @@ Texture Filtering = Filtrado de texturas Texture replacement pack activated = Paquete de substitución de texturas activado # AI translated Texture upscaling = Escalado de texturas The chosen ZIP file doesn't contain a valid driver = O ficheiro ZIP escollido non contén un controlador válido # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Non se puido cargar o controlador Vulkan personalizado. Vólvese ao predeterminado. # AI translated Unlimited = Ilimitado # AI translated Up to 1 = Ata 1 # AI translated Up to 2 = Ata 2 # AI translated diff --git a/assets/lang/gr_EL.ini b/assets/lang/gr_EL.ini index 9bd57a7ccb..d2670a0f2e 100644 --- a/assets/lang/gr_EL.ini +++ b/assets/lang/gr_EL.ini @@ -912,6 +912,7 @@ Texture Filtering = Φιλτράρισμα Υφών Texture replacement pack activated = Το πακέτο αντικατάστασης υφών ενεργοποιήθηκε # AI translated Texture upscaling = Κλιμάκωση Υφών The chosen ZIP file doesn't contain a valid driver = Το επιλεγμένο αρχείο ZIP δεν περιέχει έγκυρο οδηγό # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Δεν ήταν δυνατή η φόρτωση του προσαρμοσμένου προγράμματος οδήγησης Vulkan. Γίνεται επιστροφή στο προεπιλεγμένο. # AI translated Unlimited = Απεριόριστο Up to 1 = Έως 1 # AI translated Up to 2 = Έως 2 # AI translated diff --git a/assets/lang/he_IL.ini b/assets/lang/he_IL.ini index ac93a6d1d0..4441f8f53c 100644 --- a/assets/lang/he_IL.ini +++ b/assets/lang/he_IL.ini @@ -912,6 +912,7 @@ Texture Filtering = סינון טקסטורות # AI translated Texture replacement pack activated = חבילת החלפת טקסטורות הופעלה # AI translated Texture upscaling = הגדלת טקסטורות # AI translated The chosen ZIP file doesn't contain a valid driver = קובץ ה-ZIP שנבחר אינו מכיל מנהל התקן תקין # AI translated +The custom Vulkan driver failed to load. Switching back to default. = טעינת מנהל ההתקן המותאם אישית של Vulkan נכשלה. חוזרים לברירת המחדל. # AI translated Unlimited = ללא הגבלה # AI translated Up to 1 = עד 1 # AI translated Up to 2 = עד 2 # AI translated diff --git a/assets/lang/hr_HR.ini b/assets/lang/hr_HR.ini index 8ac969dd09..ee514d36c2 100644 --- a/assets/lang/hr_HR.ini +++ b/assets/lang/hr_HR.ini @@ -912,6 +912,7 @@ Texture Filtering = Filtriranje tekstura Texture replacement pack activated = Paket zamjenskih tekstura aktiviran # AI translated Texture upscaling = Povećanje tekstura # AI translated The chosen ZIP file doesn't contain a valid driver = Odabrana ZIP datoteka ne sadrži ispravan driver # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Prilagođeni Vulkan upravljački program nije se uspio učitati. Vraćanje na zadani. # AI translated Unlimited = Neograničeno Up to 1 = Do 1 # AI translated Up to 2 = Do 2 # AI translated diff --git a/assets/lang/hu_HU.ini b/assets/lang/hu_HU.ini index 8a72cfad67..7b4589860a 100644 --- a/assets/lang/hu_HU.ini +++ b/assets/lang/hu_HU.ini @@ -912,6 +912,7 @@ Texture Filtering = Textúra szűrés Texture replacement pack activated = Textúracsere-csomag aktiválva # AI translated Texture upscaling = Textúra felskálázás The chosen ZIP file doesn't contain a valid driver = A kiválasztott ZIP fájl nem tartalmaz érvényes drivert # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Az egyéni Vulkan-illesztőprogramot nem sikerült betölteni. Visszaváltás az alapértelmezettre. # AI translated Unlimited = Korlátlan Up to 1 = Legfeljebb 1 Up to 2 = Legfeljebb 2 diff --git a/assets/lang/id_ID.ini b/assets/lang/id_ID.ini index f2b65d4747..f390dad47f 100644 --- a/assets/lang/id_ID.ini +++ b/assets/lang/id_ID.ini @@ -912,6 +912,7 @@ Texture Filtering = Pemfilteran tekstur Texture replacement pack activated = Paket penggantian tekstur diaktifkan Texture upscaling = Penskala tekstur The chosen ZIP file doesn't contain a valid driver = File ZIP yang dipilih tidak berisi driver yang valid +The custom Vulkan driver failed to load. Switching back to default. = Driver Vulkan kustom gagal dimuat. Beralih kembali ke bawaan. # AI translated Unlimited = Tak terbatas Up to 1 = Hingga 1 Up to 2 = Hingga 2 diff --git a/assets/lang/it_IT.ini b/assets/lang/it_IT.ini index 47d8b22fb8..f90d5922ac 100644 --- a/assets/lang/it_IT.ini +++ b/assets/lang/it_IT.ini @@ -913,6 +913,7 @@ Texture Filtering = Filtraggio delle texture Texture replacement pack activated = Pacchetto di sostituzione delle texture attivato Texture upscaling = Scalatura delle texture The chosen ZIP file doesn't contain a valid driver = Il file ZIP scelto non contiene un driver valido +The custom Vulkan driver failed to load. Switching back to default. = Impossibile caricare il driver Vulkan personalizzato. Si torna a quello predefinito. # AI translated Unlimited = Illimitato Up to 1 = Max 1 Up to 2 = Max 2 diff --git a/assets/lang/ja_JP.ini b/assets/lang/ja_JP.ini index 82f3ca9f2c..af60342ce8 100644 --- a/assets/lang/ja_JP.ini +++ b/assets/lang/ja_JP.ini @@ -912,6 +912,7 @@ Texture Filtering = テクスチャフィルタリング Texture replacement pack activated = テクスチャ置換パックが有効化されました Texture upscaling = テクスチャスケーリング The chosen ZIP file doesn't contain a valid driver = 選択されたZIPファイルには有効なドライバが含まれていません +The custom Vulkan driver failed to load. Switching back to default. = カスタムVulkanドライバーを読み込めませんでした。デフォルトに戻します。 # AI translated Unlimited = 無制限 Up to 1 = 最大1 Up to 2 = 最大2 diff --git a/assets/lang/jv_ID.ini b/assets/lang/jv_ID.ini index fd03f903b4..a01a35736b 100644 --- a/assets/lang/jv_ID.ini +++ b/assets/lang/jv_ID.ini @@ -912,6 +912,7 @@ Texture Filtering = Penyaring Tekstur Texture replacement pack activated = Texture replacement pack activated Texture upscaling = Skala Tekstur The chosen ZIP file doesn't contain a valid driver = The chosen ZIP file doesn't contain a valid driver +The custom Vulkan driver failed to load. Switching back to default. = The custom Vulkan driver failed to load. Switching back to default. Unlimited = Ora Ana Watesan Up to 1 = Up to 1 Up to 2 = Up to 2 diff --git a/assets/lang/km_KH.ini b/assets/lang/km_KH.ini index 4cd26993d0..4dcec79088 100644 --- a/assets/lang/km_KH.ini +++ b/assets/lang/km_KH.ini @@ -912,6 +912,7 @@ Texture Filtering =តម្រងវាយនភាព Texture replacement pack activated =កញ្ចប់ជំនួសវាយនភាពត្រូវបានធ្វើឱ្យសកម្ម Texture upscaling =ការបង្កើនទំហំផ្ទៃ The chosen ZIP file doesn't contain a valid driver =ឯកសារ ZIP ដែលបានជ្រើសរើសមិនមានកម្មវិធីបញ្ជាត្រឹមត្រូវទេ។ +The custom Vulkan driver failed to load. Switching back to default. = The custom Vulkan driver failed to load. Switching back to default. Unlimited =គ្មានដែនកំណត់ Up to 1 =រហូតដល់ 1 Up to 2 =រហូតដល់ 2 diff --git a/assets/lang/ko_KR.ini b/assets/lang/ko_KR.ini index a069e4629f..d5f5cfa0b9 100644 --- a/assets/lang/ko_KR.ini +++ b/assets/lang/ko_KR.ini @@ -912,6 +912,7 @@ Texture Filtering = 텍스처 필터링 Texture replacement pack activated = 텍스처 교체 팩 활성화 Texture upscaling = 텍스처 스케일링 The chosen ZIP file doesn't contain a valid driver = 선택한 ZIP 파일에는 유효한 드라이버가 포함되어 있지 않습니다. +The custom Vulkan driver failed to load. Switching back to default. = 사용자 지정 Vulkan 드라이버를 불러오지 못했습니다. 기본 드라이버로 되돌립니다. # AI translated Unlimited = 무제한 Up to 1 = 최대 1까지 Up to 2 = 최대 2까지 diff --git a/assets/lang/ku_SO.ini b/assets/lang/ku_SO.ini index f4e82d6fd1..bc0f6727bc 100644 --- a/assets/lang/ku_SO.ini +++ b/assets/lang/ku_SO.ini @@ -926,6 +926,7 @@ Texture Filtering = Texture filtering Texture replacement pack activated = Texture replacement pack activated Texture upscaling = Texture upscaling The chosen ZIP file doesn't contain a valid driver = The chosen ZIP file doesn't contain a valid driver +The custom Vulkan driver failed to load. Switching back to default. = The custom Vulkan driver failed to load. Switching back to default. Unlimited = Unlimited Up to 1 = Up to 1 Up to 2 = Up to 2 diff --git a/assets/lang/lo_LA.ini b/assets/lang/lo_LA.ini index def577c06c..3ba7ecb517 100644 --- a/assets/lang/lo_LA.ini +++ b/assets/lang/lo_LA.ini @@ -912,6 +912,7 @@ Texture Filtering = ຕອງພື້ນຜິວ Texture replacement pack activated = Texture replacement pack activated Texture upscaling = ປັບພື້ນຜິວ The chosen ZIP file doesn't contain a valid driver = The chosen ZIP file doesn't contain a valid driver +The custom Vulkan driver failed to load. Switching back to default. = The custom Vulkan driver failed to load. Switching back to default. Unlimited = ບໍ່ຈຳກັດ Up to 1 = Up to 1 Up to 2 = Up to 2 diff --git a/assets/lang/lt-LT.ini b/assets/lang/lt-LT.ini index a4012ea857..887f2c094b 100644 --- a/assets/lang/lt-LT.ini +++ b/assets/lang/lt-LT.ini @@ -912,6 +912,7 @@ Texture Filtering = Tekstūrų filtravimas Texture replacement pack activated = Tekstūrų pakeitimo paketas aktyvuotas # AI translated Texture upscaling = Tekstūrų didinimas # AI translated The chosen ZIP file doesn't contain a valid driver = Pasirinktame ZIP faile nėra tinkamos tvarkyklės # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Nepavyko įkelti pasirinktinės Vulkan tvarkyklės. Grįžtama prie numatytosios. # AI translated Unlimited = Neribota # AI translated Up to 1 = Iki 1 # AI translated Up to 2 = Iki 2 # AI translated diff --git a/assets/lang/ms_MY.ini b/assets/lang/ms_MY.ini index daffc78c24..9a781010d7 100644 --- a/assets/lang/ms_MY.ini +++ b/assets/lang/ms_MY.ini @@ -912,6 +912,7 @@ Texture Filtering = Penapisan Tekstur Texture replacement pack activated = Pek penggantian tekstur diaktifkan Texture upscaling = Penskalaan atas tekstur The chosen ZIP file doesn't contain a valid driver = Fail ZIP yang dipilih tidak mengandungi pemacu yang sah +The custom Vulkan driver failed to load. Switching back to default. = Pemacu Vulkan tersuai gagal dimuatkan. Beralih kembali ke lalai. # AI translated Unlimited = Tanpa had Up to 1 = Sehingga 1 Up to 2 = Sehingga 2 diff --git a/assets/lang/nl_NL.ini b/assets/lang/nl_NL.ini index 8db5ec2d45..029fbde54d 100644 --- a/assets/lang/nl_NL.ini +++ b/assets/lang/nl_NL.ini @@ -912,6 +912,7 @@ Texture Filtering = Texturefilter Texture replacement pack activated = Texturevervangingspakket geactiveerd # AI translated Texture upscaling = Textures opschalen # AI translated The chosen ZIP file doesn't contain a valid driver = Het gekozen ZIP-bestand bevat geen geldige driver # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Het aangepaste Vulkan-stuurprogramma kon niet worden geladen. Terug naar de standaard. # AI translated Unlimited = Onbeperkt Up to 1 = Maximaal 1 # AI translated Up to 2 = Maximaal 2 # AI translated diff --git a/assets/lang/nn_NO.ini b/assets/lang/nn_NO.ini index 04d3d81376..9957f93131 100644 --- a/assets/lang/nn_NO.ini +++ b/assets/lang/nn_NO.ini @@ -936,6 +936,7 @@ Texture Filtering = Teksturfiltrering Texture replacement pack activated = Teksturerstatningspakke aktivert Texture upscaling = Teksturoppskalering The chosen ZIP file doesn't contain a valid driver = Den valde ZIP-fila inneheld ikkje ein gyldig drivar +The custom Vulkan driver failed to load. Switching back to default. = Den eigendefinerte Vulkan-drivaren kunne ikkje lastast. Byter tilbake til standard. # AI translated Unlimited = Uavgrensa Up to 1 = Opptil 1 Up to 2 = Opptil 2 diff --git a/assets/lang/no_NO.ini b/assets/lang/no_NO.ini index 84f9e016a6..8b6957d75f 100644 --- a/assets/lang/no_NO.ini +++ b/assets/lang/no_NO.ini @@ -936,6 +936,7 @@ Texture Filtering = Tekstur filtrering Texture replacement pack activated = Teksturerstatningspakke aktivert Texture upscaling = Teksturoppskalering # AI translated The chosen ZIP file doesn't contain a valid driver = Den valgte ZIP-filen inneholder ikke en gyldig driver +The custom Vulkan driver failed to load. Switching back to default. = Den egendefinerte Vulkan-driveren kunne ikke lastes inn. Bytter tilbake til standard. # AI translated Unlimited = Ubegrenset Up to 1 = Opptil 1 Up to 2 = Opptil 2 diff --git a/assets/lang/pl_PL.ini b/assets/lang/pl_PL.ini index 94138c0a62..8db11b797e 100644 --- a/assets/lang/pl_PL.ini +++ b/assets/lang/pl_PL.ini @@ -912,6 +912,7 @@ Texture Filtering = Filtrowanie tekstur Texture replacement pack activated = Paczka podmienionych tekstur aktywowana Texture upscaling = Skalowanie tekstur The chosen ZIP file doesn't contain a valid driver = Wybrany plik ZIP nie zawiera prawidłowego sterownika +The custom Vulkan driver failed to load. Switching back to default. = Nie udało się wczytać niestandardowego sterownika Vulkan. Przywracanie domyślnego. # AI translated Unlimited = Bez limitu Up to 1 = Maks. jeden Up to 2 = Maks. dwa diff --git a/assets/lang/pt_BR.ini b/assets/lang/pt_BR.ini index adfe67291e..3538f85486 100644 --- a/assets/lang/pt_BR.ini +++ b/assets/lang/pt_BR.ini @@ -937,6 +937,7 @@ Texture Filtering = Filtragem das texturas Texture replacement pack activated = Pacote de substituição das texturas ativado Texture upscaling = Ampliação das texturas The chosen ZIP file doesn't contain a valid driver = O arquivo ZIP escolhido não contém um driver válido +The custom Vulkan driver failed to load. Switching back to default. = Não foi possível carregar o driver Vulkan personalizado. Voltando ao padrão. # AI translated Unlimited = Ilimitado Up to 1 = Até 1 Up to 2 = Até 2 diff --git a/assets/lang/pt_PT.ini b/assets/lang/pt_PT.ini index 4124ea402d..59928e7e90 100644 --- a/assets/lang/pt_PT.ini +++ b/assets/lang/pt_PT.ini @@ -936,6 +936,7 @@ Texture Filtering = Filtração das texturas Texture replacement pack activated = Pacote de substituição de texturas ativado Texture upscaling = Dimensionamento das texturas The chosen ZIP file doesn't contain a valid driver = O ficheiro .zip escolhido não contém um driver válido +The custom Vulkan driver failed to load. Switching back to default. = Não foi possível carregar o controlador Vulkan personalizado. A voltar ao predefinido. # AI translated Unlimited = Ilimitado Up to 1 = Até 1 Up to 2 = Até 2 diff --git a/assets/lang/ro_RO.ini b/assets/lang/ro_RO.ini index 02b258794b..c64da7abed 100644 --- a/assets/lang/ro_RO.ini +++ b/assets/lang/ro_RO.ini @@ -913,6 +913,7 @@ Texture Filtering = Filtrare texturi Texture replacement pack activated = Pachet de înlocuire texturi activat # AI translated Texture upscaling = Suprascalare texturi # AI translated The chosen ZIP file doesn't contain a valid driver = Fișierul ZIP ales nu conține un driver valid # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Driverul Vulkan personalizat nu a putut fi încărcat. Se revine la cel implicit. # AI translated Unlimited = Nelimitat # AI translated Up to 1 = Până la 1 # AI translated Up to 2 = Până la 2 # AI translated diff --git a/assets/lang/ru_RU.ini b/assets/lang/ru_RU.ini index a34c3d11be..430f89e85f 100644 --- a/assets/lang/ru_RU.ini +++ b/assets/lang/ru_RU.ini @@ -912,6 +912,7 @@ Texture Filtering = Фильтрация текстур Texture replacement pack activated = Пакет замены текстур активирован Texture upscaling = Масштабирование текстур The chosen ZIP file doesn't contain a valid driver = Выбранный файл ZIP не содержит корректный драйвер +The custom Vulkan driver failed to load. Switching back to default. = Не удалось загрузить пользовательский драйвер Vulkan. Возврат к стандартному. # AI translated Unlimited = Без ограничений Up to 1 = До 1 Up to 2 = До 2 diff --git a/assets/lang/sv_SE.ini b/assets/lang/sv_SE.ini index 615d504553..74e0ce303e 100644 --- a/assets/lang/sv_SE.ini +++ b/assets/lang/sv_SE.ini @@ -913,6 +913,7 @@ Texture Filtering = Texturfiltrering Texture replacement pack activated = Texturersättningspaket aktiverat Texture upscaling = Texturuppskalning The chosen ZIP file doesn't contain a valid driver = Den valda ZIP-filen innehåller inte en giltig drivrutin +The custom Vulkan driver failed to load. Switching back to default. = Den anpassade Vulkan-drivrutinen kunde inte läsas in. Byter tillbaka till standard. # AI translated Unlimited = Obegränsad Up to 1 = Upp till 1 Up to 2 = Upp till 2 diff --git a/assets/lang/tg_PH.ini b/assets/lang/tg_PH.ini index 29d3ced9da..fa08229342 100644 --- a/assets/lang/tg_PH.ini +++ b/assets/lang/tg_PH.ini @@ -913,6 +913,7 @@ Texture Filtering = Pagsala sa Texture Texture replacement pack activated = Na-activate ang texture replacement pack # AI translated Texture upscaling = Pag-upscale ng texture # AI translated The chosen ZIP file doesn't contain a valid driver = Walang wastong driver sa napiling ZIP file # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Hindi na-load ang custom na Vulkan driver. Bumabalik sa default. # AI translated Unlimited = Walang hangganan Up to 1 = Hanggang 1 # AI translated Up to 2 = Hanggang 2 # AI translated diff --git a/assets/lang/th_TH.ini b/assets/lang/th_TH.ini index 9d83712530..ce61537619 100644 --- a/assets/lang/th_TH.ini +++ b/assets/lang/th_TH.ini @@ -918,6 +918,7 @@ Texture Filtering = การกรองพื้นผิว Texture replacement pack activated = แพ็คการแทนที่พื้นผิวถูกเปิดใช้งาน Texture upscaling = การปรับสเกลพื้นผิว The chosen ZIP file doesn't contain a valid driver = ไฟล์ ZIP ที่เลือกไว้ ไม่มีข้อมูลไดรเวอร์ที่ถูกต้อง +The custom Vulkan driver failed to load. Switching back to default. = โหลดไดรเวอร์ Vulkan แบบกำหนดเองไม่สำเร็จ กำลังกลับไปใช้ค่าเริ่มต้น # AI translated Unlimited = ไม่จำกัด Up to 1 = เพิ่มไปที่ 1 Up to 2 = เพิ่มไปที่ 2 diff --git a/assets/lang/tr_TR.ini b/assets/lang/tr_TR.ini index 72b8ecd18a..804b0fe6f2 100644 --- a/assets/lang/tr_TR.ini +++ b/assets/lang/tr_TR.ini @@ -914,6 +914,7 @@ Texture Filtering = Doku Filtreleme Texture replacement pack activated = Doku değiştirme paketi etkinleştirildi Texture upscaling = Doku ölçekleme The chosen ZIP file doesn't contain a valid driver = Seçilen ZIP dosyası geçerli bir sürücü içermiyor +The custom Vulkan driver failed to load. Switching back to default. = Özel Vulkan sürücüsü yüklenemedi. Varsayılana geri dönülüyor. # AI translated Unlimited = Sınırsız Up to 1 = 1 Kadar Up to 2 = 2 Kadar diff --git a/assets/lang/uk_UA.ini b/assets/lang/uk_UA.ini index a708522818..2a62b11ea3 100644 --- a/assets/lang/uk_UA.ini +++ b/assets/lang/uk_UA.ini @@ -912,6 +912,7 @@ Texture Filtering = Фільтрація текстур Texture replacement pack activated = Пакет заміни текстур активовано Texture upscaling = Масштабування текстур The chosen ZIP file doesn't contain a valid driver = Обраний ZIP файл не містить дійсного драйвера +The custom Vulkan driver failed to load. Switching back to default. = Не вдалося завантажити користувацький драйвер Vulkan. Повернення до стандартного. # AI translated Unlimited = Необмежено Up to 1 = До 1 Up to 2 = До 2 diff --git a/assets/lang/vi_VN.ini b/assets/lang/vi_VN.ini index 73f76f10bc..2b60c7e9e4 100644 --- a/assets/lang/vi_VN.ini +++ b/assets/lang/vi_VN.ini @@ -912,6 +912,7 @@ Texture Filtering = Lọc texture Texture replacement pack activated = Đã kích hoạt gói thay thế texture # AI translated Texture upscaling = Upscale texture # AI translated The chosen ZIP file doesn't contain a valid driver = Tệp ZIP đã chọn không chứa driver hợp lệ # AI translated +The custom Vulkan driver failed to load. Switching back to default. = Không thể tải trình điều khiển Vulkan tùy chỉnh. Đang chuyển về mặc định. # AI translated Unlimited = Không giới hạn Up to 1 = Tối đa 1 # AI translated Up to 2 = Tối đa 2 # AI translated diff --git a/assets/lang/zh_CN.ini b/assets/lang/zh_CN.ini index cf0881206e..a7b295b1b6 100644 --- a/assets/lang/zh_CN.ini +++ b/assets/lang/zh_CN.ini @@ -912,6 +912,7 @@ Texture Filtering = 纹理过滤 Texture replacement pack activated = 纹理替换包已生效 Texture upscaling = 纹理缩放 The chosen ZIP file doesn't contain a valid driver = 选择的ZIP文件里没有可用的驱动程序。 +The custom Vulkan driver failed to load. Switching back to default. = 无法加载自定义 Vulkan 驱动。正在切换回默认驱动。 # AI translated Unlimited = 无限制 Up to 1 = 最高缓冲1帧 Up to 2 = 最高缓冲2帧 diff --git a/assets/lang/zh_TW.ini b/assets/lang/zh_TW.ini index decbcd4454..1d741aeda2 100644 --- a/assets/lang/zh_TW.ini +++ b/assets/lang/zh_TW.ini @@ -912,6 +912,7 @@ Texture Filtering = 紋理過濾 Texture replacement pack activated = 紋理取代套件已啟用 Texture upscaling = 紋理縮放 The chosen ZIP file doesn't contain a valid driver = 選擇的 ZIP 檔案不包含有效的驅動程式 +The custom Vulkan driver failed to load. Switching back to default. = 無法載入自訂 Vulkan 驅動程式。正在切換回預設驅動程式。 # AI translated Unlimited = 無限制 Up to 1 = 最多至 1 Up to 2 = 最多至 2 From 879933abce4498cd153c861c28118e5454c76856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 16:13:47 +0100 Subject: [PATCH 8/9] Fix the Linux build: keep the Vulkan headers away from the UI ones X11, which the Vulkan headers bring in, defines Screen. NativeApp gets the custom driver functions from VulkanDriverVars.h instead of VulkanLoader.h, and the GPU debug overlays take their font from the caller instead of including View.h for the theme. Co-Authored-By: Claude Opus 5.5 --- Common/GPU/Vulkan/VulkanDriverVars.h | 10 ++++++++++ Common/GPU/Vulkan/VulkanLoader.h | 5 ----- GPU/Vulkan/DebugVisVulkan.cpp | 9 ++++----- GPU/Vulkan/DebugVisVulkan.h | 6 ++++-- UI/DebugOverlay.cpp | 4 ++-- UI/NativeApp.cpp | 2 +- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanDriverVars.h b/Common/GPU/Vulkan/VulkanDriverVars.h index 8ad15da167..e516601475 100644 --- a/Common/GPU/Vulkan/VulkanDriverVars.h +++ b/Common/GPU/Vulkan/VulkanDriverVars.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "Common/Common.h" @@ -18,3 +19,12 @@ const AdrenoEnvVarDef *GetAdrenoEnvVarDefs(int *count); // False where AdrenoTools' linker namespace tricks are known or suspected to break (Android 17 and its previews). bool VulkanCustomDriverAllowedByOS(); + +// Declared here rather than in VulkanLoader.h, whose Vulkan headers bring in X11 on Linux, and its Screen +// clashes with ours. + +// When Vulkan didn't work with a custom driver: clears the setting and unloads the library, so the next +// try gets the system's driver. Returns false if no custom driver was in use. +bool VulkanDisableCustomDriver(); +// The custom driver that failed (and was cleared) since the last call, if any. +std::string VulkanTakeFailedCustomDriver(); diff --git a/Common/GPU/Vulkan/VulkanLoader.h b/Common/GPU/Vulkan/VulkanLoader.h index 738fb404da..97785cee27 100644 --- a/Common/GPU/Vulkan/VulkanLoader.h +++ b/Common/GPU/Vulkan/VulkanLoader.h @@ -286,11 +286,6 @@ void VulkanApplyCustomDriverEnvVars(AdrenoEnvVar envVars); void VulkanSetNativeLibDir(std::string_view nativeLibDir); bool VulkanMayBeAvailable(); void VulkanSetAvailable(bool available); -// When Vulkan didn't work with a custom driver: clears the setting and unloads the library, so the next -// try gets the system's driver. Returns false if no custom driver was in use. -bool VulkanDisableCustomDriver(); -// The custom driver that failed (and was cleared) since the last call, if any. -std::string VulkanTakeFailedCustomDriver(); bool VulkanLoad(std::string *errorStr); // Alternative to VulkanLoad() for adopting a Vulkan loader that a host application (e.g. a libretro diff --git a/GPU/Vulkan/DebugVisVulkan.cpp b/GPU/Vulkan/DebugVisVulkan.cpp index 3186b95555..f92c96cc03 100644 --- a/GPU/Vulkan/DebugVisVulkan.cpp +++ b/GPU/Vulkan/DebugVisVulkan.cpp @@ -23,7 +23,6 @@ #include "Common/GPU/thin3d.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/UI/Context.h" -#include "Common/UI/View.h" #include "Common/System/System.h" #include "ext/vma/vk_mem_alloc.h" @@ -43,7 +42,7 @@ bool comparePushBufferNames(const GPUMemoryManager *a, const GPUMemoryManager *b return strcmp(a->Name(), b->Name()) < 0; } -void DrawGPUMemoryVis(UIContext *ui, GPUCommon *gpu) { +void DrawGPUMemoryVis(UIContext *ui, const FontStyle &font, GPUCommon *gpu) { // This one will simply display stats. Draw::DrawContext *draw = ui->GetDrawContext(); @@ -89,12 +88,12 @@ void DrawGPUMemoryVis(UIContext *ui, GPUCommon *gpu) { int y = starty; ui->SetFontScale(0.7f, 0.7f); - ui->DrawTextShadow(ui->GetTheme().uiFont, str.str(), x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ui->DrawTextShadow(font, str.str(), x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); ui->SetFontScale(1.0f, 1.0f); ui->Flush(); } -void DrawGPUProfilerVis(UIContext *ui, GPUCommon *gpu) { +void DrawGPUProfilerVis(UIContext *ui, const FontStyle &font, GPUCommon *gpu) { using namespace Draw; const int padding = 10 + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT); const int starty = 50 + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP); @@ -112,7 +111,7 @@ void DrawGPUProfilerVis(UIContext *ui, GPUCommon *gpu) { std::string text = ui->GetDrawContext()->GetGpuProfileString(); ui->SetFontScale(0.4f, 0.4f); - ui->DrawTextShadow(ui->GetTheme().uiFont, text, x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ui->DrawTextShadow(font, text, x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); ui->SetFontScale(1.0f, 1.0f); ui->Flush(); } diff --git a/GPU/Vulkan/DebugVisVulkan.h b/GPU/Vulkan/DebugVisVulkan.h index f86eb8aa51..6746a82b27 100644 --- a/GPU/Vulkan/DebugVisVulkan.h +++ b/GPU/Vulkan/DebugVisVulkan.h @@ -21,7 +21,9 @@ class GPUCommon; class UIContext; +struct FontStyle; // gpu MUST be an instance of GPU_Vulkan. If not, will definitely crash. -void DrawGPUMemoryVis(UIContext *ui, GPUCommon *gpu); -void DrawGPUProfilerVis(UIContext *ui, GPUCommon *gpu); +// Takes the font, since the UI headers that define the theme can't be included with the Vulkan ones (X11). +void DrawGPUMemoryVis(UIContext *ui, const FontStyle &font, GPUCommon *gpu); +void DrawGPUProfilerVis(UIContext *ui, const FontStyle &font, GPUCommon *gpu); diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index b67e030b2d..d7beceb2fa 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -292,12 +292,12 @@ void DrawDebugOverlay(UIContext *ctx, const Bounds &bounds, DebugOverlay overlay #if !PPSSPP_PLATFORM(UWP) && !PPSSPP_PLATFORM(SWITCH) case DebugOverlay::GPU_PROFILE: if (inGame && (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL)) { - DrawGPUProfilerVis(ctx, gpu); + DrawGPUProfilerVis(ctx, ctx->GetTheme().uiFont, gpu); } break; case DebugOverlay::GPU_ALLOCATOR: if (inGame && (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL)) { - DrawGPUMemoryVis(ctx, gpu); + DrawGPUMemoryVis(ctx, ctx->GetTheme().uiFont, gpu); } break; #endif diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 5f5e0f976b..0ef6df30b2 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -65,7 +65,7 @@ #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/GPU/thin3d.h" #include "Common/GPU/Vulkan/FixedSPIRVCache.h" -#include "Common/GPU/Vulkan/VulkanLoader.h" +#include "Common/GPU/Vulkan/VulkanDriverVars.h" #include "Common/UI/UI.h" #include "Common/UI/Screen.h" #include "Common/UI/ScreenManager.h" From f53b614be45573126f55b2be406f58aec1c6cfb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Nov 2026 16:15:36 +0100 Subject: [PATCH 9/9] AGENTS.md: Don't include VulkanLoader.h from UI Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 6c0d1e600b..bf5fbe3b6a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -221,6 +221,12 @@ UWP/PPSSPP_UWPMain.cpp android/jni/app-android.cpp libretro/libretro.cpp +**Never include `Common/GPU/Vulkan/VulkanLoader.h` directly from `UI/`.** On Linux the Vulkan headers +bring in X11, whose definitions (`Screen` among them) break `Common/UI/Screen.h`. Windows and Android +builds don't show it, so it only fails on CI. The same goes for including UI headers (`View.h`, `Screen.h`) in a +file that has the Vulkan ones. Put what UI code needs in a header without Vulkan includes, like +`Common/GPU/Vulkan/VulkanDriverVars.h`, or pass the value in from the UI side. + ## Reverse-engineering the firmware When a question about hardware behaviour can't be settled from the docs or from JPCSP - what a field in