From 39f5953a7e2e61528e38e0ee2c889a48c45a295b Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 17 Aug 2026 17:04:53 +0700 Subject: [PATCH] VSync joins the video options, the chat dock goes quiet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VSync is a player setting on the Graphics page now — 1.12's own gxVSync, persisted like every other CVar — instead of a dev-HUD checkbox - the perf HUD collapses to one surface that says everything once, at a deliberate, governed width; the badge stops crying wolf - the idle chat dock stops repainting itself every frame — the fade applier gets the gate the reference carries twice over, and hovering still reveals the hidden tabs - the tick can name who spent it: per-handler self/total attribution at the one seam where every handler fires Co-Authored-By: Claude Fable 5 --- crates/benilla-app/assets/ui/ChatFrame.xml | 59 +- crates/benilla-app/assets/ui/OptionsFrame.xml | 18 +- crates/benilla-app/src/autocast_shine.rs | 18 + crates/benilla-app/src/cvars.rs | 43 +- crates/benilla-app/src/lib.rs | 19 +- crates/benilla-app/src/perf/hud.rs | 614 +++++++++++------- crates/benilla-app/src/perf/stats.rs | 139 +++- .../benilla-app/src/ui_script/chat_tests.rs | 68 ++ crates/benilla-app/src/ui_script/mod.rs | 11 + .../src/ui_script/options_tests.rs | 67 +- crates/benilla-app/src/video.rs | 126 ++++ crates/benilla-ui/src/script/event.rs | 6 + crates/benilla-ui/src/script/handler_prof.rs | 397 +++++++++++ crates/benilla-ui/src/script/mod.rs | 5 + .../src/script/tests/handler_prof.rs | 209 ++++++ crates/benilla-ui/src/script/tests/mod.rs | 1 + crates/benilla-ui/src/script/tick.rs | 3 + 17 files changed, 1525 insertions(+), 278 deletions(-) create mode 100644 crates/benilla-app/src/video.rs create mode 100644 crates/benilla-ui/src/script/handler_prof.rs create mode 100644 crates/benilla-ui/src/script/tests/handler_prof.rs diff --git a/crates/benilla-app/assets/ui/ChatFrame.xml b/crates/benilla-app/assets/ui/ChatFrame.xml index 8a499c9b..1b2d500a 100644 --- a/crates/benilla-app/assets/ui/ChatFrame.xml +++ b/crates/benilla-app/assets/ui/ChatFrame.xml @@ -241,6 +241,11 @@ oldX = 0, oldY = 0, reveal = 0, -- 0..1 master fade the tab/texture alphas scale by revealTarget = 0, + -- What the apply block below last WROTE, so an unchanged frame writes nothing + -- (decision 1396). -1 is unreachable for both, so the first pass always applies. + appliedReveal = -1, + appliedSelected = -1, + tabsSettled = false, -- both tabs' host-measured label widths have landed flashTab = 0, -- tab id whose whisper flash runs (0 = none), 0288 P4 wires it flashLeft = 0, -- remaining flash seconds (ref: 60s, blink 0.25/0.25) } @@ -331,14 +336,19 @@ -- PanelTemplates_TabResize(5, tab), re-expressed: the ref sizes every docked tab to its -- label (FCF_DockUpdate) — middle slice = text width + 5, tab = middle + the two 16px -- caps; the flash/highlight bands follow the Left/Right pieces by anchor. The label - -- width is host-measured (GetStringWidth), so early frames may see 0 — the OnUpdate - -- below re-applies until it settles, then the change-guard makes this a no-op. + -- width is host-measured (GetStringWidth), so early frames may see 0. It RETURNS whether + -- the width is known yet — which is what lets FCF_OnUpdate's apply gate keep polling + -- until the measurement lands and then stop for good (decision 1396). Without that + -- answer the gate would have to choose between polling forever and shipping tabs stuck + -- at their authored width on whichever session measured late. function BenillaFCF_TabResize(tab) local w = getglobal(tab:GetName() .. "Text"):GetStringWidth() - if w == 0 or tab.benillaTabTextW == w then return end + if w == 0 then return false end + if tab.benillaTabTextW == w then return true end tab.benillaTabTextW = w getglobal(tab:GetName() .. "Middle"):SetWidth(w + 5) tab:SetWidth(w + 5 + 32) + return true end -- The hover rect: the dock's rect expanded by the ref's MouseIsOver pads @@ -389,16 +399,43 @@ -- Apply: tabs at 1.0 (selected) / 0.5 (unselected) × reveal; the selected window's -- background+borders at 0.25 × reveal (ref: fade oldAlpha↔DEFAULT_CHATFRAME_ALPHA). - for i = 1, 2 do - local tab = getglobal("ChatFrame" .. i .. "Tab") - if tab then - local a = (i == BenillaFCF.selected) and 1.0 or 0.5 - tab:SetAlpha(a * s.reveal) - BenillaFCF_TabResize(tab) + -- + -- GATED on everything the writes below depend on — the reveal, the selected tab, and + -- whether the label measurements have landed (decision 1396). **The reference needs + -- no gate because it does not do this here at all**: ref FCF_OnUpdate hands the fade + -- to UIFrameFadeIn/Out, and UIFrameFade_OnUpdate walks only the frames still fading, + -- so a settled dock costs nothing. This transcription inlined the ramp and lost that + -- property with it, and ~24 getglobals, two table builds and ~20 SetAlpha calls then + -- ran every frame of every session to write the values that were already there — + -- 36 us/frame, the largest single handler in the client (1395 measured it). The + -- guard restores the reference's own behaviour rather than inventing one. + if s.reveal ~= s.appliedReveal or s.selected ~= s.appliedSelected then + for i = 1, 2 do + local tab = getglobal("ChatFrame" .. i .. "Tab") + if tab then + tab:SetAlpha(((i == s.selected) and 1.0 or 0.5) * s.reveal) + end + for _, t in ipairs(BenillaFCF_Textures(i)) do + t:SetAlpha(0.25 * s.reveal) + end end - for _, t in ipairs(BenillaFCF_Textures(i)) do - t:SetAlpha(0.25 * s.reveal) + s.appliedReveal = s.reveal + s.appliedSelected = s.selected + end + + -- The tab widths are a SEPARATE trigger and get their own latch, deliberately: they + -- depend on a host measurement rather than on the fade, and folding them into the + -- gate above would tie the alphas to a width that can legitimately never arrive (an + -- empty label measures 0 forever) — the apply block would then run every frame again + -- for a reason having nothing to do with what it writes. Polls until both land, then + -- stops for good. + if not s.tabsSettled then + local settled = true + for i = 1, 2 do + local tab = getglobal("ChatFrame" .. i .. "Tab") + if tab and not BenillaFCF_TabResize(tab) then settled = false end end + s.tabsSettled = settled end -- The whisper flash blink (0.25 on / 0.25 off, 60s — ref UIFrameFlash args). diff --git a/crates/benilla-app/assets/ui/OptionsFrame.xml b/crates/benilla-app/assets/ui/OptionsFrame.xml index 6bb59125..2b467237 100644 --- a/crates/benilla-app/assets/ui/OptionsFrame.xml +++ b/crates/benilla-app/assets/ui/OptionsFrame.xml @@ -615,7 +615,7 @@ "RowEnableAll", "RowMaster", "RowSound", "RowMusic", "RowAmbience", "RowEnableMusic", "RowEnableAmbience", }, - Graphics = { "RowUiScale", "RowWorldDetail" }, + Graphics = { "RowUiScale", "RowWorldDetail", "RowVerticalSync" }, Nameplates = { "RowPlayerNames", "RowNpcNames", "RowOwnName" }, -- The stock globals that were already defined AND consumed when 1134 opened the store -- (1136; Buff Durations 1139), in the reference's own on-screen order — its Basic @@ -2075,6 +2075,22 @@ }) + + + + + + + -- Vertical Sync: 1.12's own Video Options checkbox + -- 5 (gxVSync), over the window's present mode. + -- The reference row carries gxRestart = 1; ours + -- does not and must not be marked deferred -- + -- wgpu swaps the presentation interval live, so + -- the box takes effect as you click it. + OptionsRow_OnLoad(self, "gxVSync", "Vertical Sync", + "OPTION_TOOLTIP_VERTICAL_SYNC") + +