From 0d2ee634209bb2563ff34d3acaa048d90b8e8aae Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Thu, 7 May 2026 15:28:47 +0200 Subject: [PATCH] perf(themes): add pre-computed ABGR cache on theme records --- HellionChat/Themes/Theme.cs | 45 +++++++++++++++++++++++++++++ HellionChat/Themes/ThemeRegistry.cs | 6 ++++ 2 files changed, 51 insertions(+) diff --git a/HellionChat/Themes/Theme.cs b/HellionChat/Themes/Theme.cs index 18eee74..738a981 100644 --- a/HellionChat/Themes/Theme.cs +++ b/HellionChat/Themes/Theme.cs @@ -1,3 +1,5 @@ +using HellionChat.Util; + namespace HellionChat.Themes; public sealed record Theme( @@ -10,4 +12,47 @@ public sealed record Theme( ThemeTypography Typography, bool IsBuiltIn, ThemeChatColors? ChatColors = null +) +{ + // Pre-computed ABGR mirror of ThemeColors so PushGlobal can skip the + // RgbaToAbgr conversion per slot per frame. + public ThemeAbgrCache AbgrCache { get; private set; } + + public void RecomputeAbgrCache() + { + AbgrCache = new ThemeAbgrCache( + PrimaryDark: ColourUtil.RgbaToAbgr(Colors.PrimaryDark), + Primary: ColourUtil.RgbaToAbgr(Colors.Primary), + PrimaryLight: ColourUtil.RgbaToAbgr(Colors.PrimaryLight), + PrimaryGlow: ColourUtil.RgbaToAbgr(Colors.PrimaryGlow), + AccentDark: ColourUtil.RgbaToAbgr(Colors.AccentDark), + Accent: ColourUtil.RgbaToAbgr(Colors.Accent), + AccentLight: ColourUtil.RgbaToAbgr(Colors.AccentLight), + Identity: ColourUtil.RgbaToAbgr(Colors.Identity), + WindowBg: ColourUtil.RgbaToAbgr(Colors.WindowBg), + ChildBg: ColourUtil.RgbaToAbgr(Colors.ChildBg), + FrameBg: ColourUtil.RgbaToAbgr(Colors.FrameBg), + Surface: ColourUtil.RgbaToAbgr(Colors.Surface), + SurfaceHover: ColourUtil.RgbaToAbgr(Colors.SurfaceHover), + Border: ColourUtil.RgbaToAbgr(Colors.Border), + TextPrimary: ColourUtil.RgbaToAbgr(Colors.TextPrimary), + TextMuted: ColourUtil.RgbaToAbgr(Colors.TextMuted), + TextDim: ColourUtil.RgbaToAbgr(Colors.TextDim), + StatusSuccess: ColourUtil.RgbaToAbgr(Colors.StatusSuccess), + StatusDanger: ColourUtil.RgbaToAbgr(Colors.StatusDanger), + StatusWarning: ColourUtil.RgbaToAbgr(Colors.StatusWarning), + StatusInfo: ColourUtil.RgbaToAbgr(Colors.StatusInfo)); + } +} + +// Mirrors ThemeColors slot-for-slot. The FillsAll21Slots test pins the +// contract — a new slot without its mirror fails the build. +public readonly record struct ThemeAbgrCache( + uint PrimaryDark, uint Primary, uint PrimaryLight, uint PrimaryGlow, + uint AccentDark, uint Accent, uint AccentLight, + uint Identity, + uint WindowBg, uint ChildBg, uint FrameBg, + uint Surface, uint SurfaceHover, uint Border, + uint TextPrimary, uint TextMuted, uint TextDim, + uint StatusSuccess, uint StatusDanger, uint StatusWarning, uint StatusInfo ); diff --git a/HellionChat/Themes/ThemeRegistry.cs b/HellionChat/Themes/ThemeRegistry.cs index 62c0f8a..5b3ce23 100644 --- a/HellionChat/Themes/ThemeRegistry.cs +++ b/HellionChat/Themes/ThemeRegistry.cs @@ -25,6 +25,11 @@ public sealed class ThemeRegistry { ForgeMerchantman.Slug, ForgeMerchantman.Build() }, { MintGrove.Slug, MintGrove.Build() }, }; + + // Centralised so the nine .Build() factories stay free of cache plumbing. + foreach (var theme in _builtIns.Values) + theme.RecomputeAbgrCache(); + _active = _builtIns[DefaultSlug]; _customThemesDir = customThemesDir; } @@ -81,6 +86,7 @@ public sealed class ThemeRegistry try { theme = ThemeJsonLoader.LoadFromFile(path); + theme.RecomputeAbgrCache(); _customCache[key] = (theme, stamp); } catch (Exception ex)