VSync joins the video options, the chat dock goes quiet

- 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 <noreply@anthropic.com>
This commit is contained in:
Sam
2026-08-17 17:04:53 +07:00
co-authored by Claude Fable 5
parent 46275aeb37
commit 71a3efb55c
17 changed files with 1525 additions and 278 deletions
@@ -648,3 +648,71 @@ fn fcf_select_dock_frame_selects_by_frame_and_raises_for_an_undocked_one() {
"an undocked window raises — benilla has no dock model for it (0288 §2)"
);
}
/// **The idle dock writes nothing** (decision 1396). `FCF_OnUpdate`'s apply block used to run
/// unconditionally — ~24 `getglobal`s, two table builds and ~20 `SetAlpha` calls every frame of
/// every session, writing the values that were already there, at 36 µs/frame (1395 measured it as
/// the largest single handler in the client).
///
/// The probe is a sentinel the applier can never write: it only ever writes `{0, 0.5, 1.0} × reveal`,
/// and `reveal` is pinned at 0 with the cursor away from the dock. If the gate is removed, the very
/// next tick overwrites 0.42 with 0 and this goes red.
#[test]
fn an_idle_dock_stops_rewriting_the_tab_alpha_every_frame() {
let mut s = chat_frame();
s.mouse_move(1500.0, 850.0); // far from the dock: no hover, so `reveal` stays 0
for _ in 0..8 {
s.tick(0.016); // let the label measurements land and the applier reach its resting write
s.resolve();
}
s.run("ChatFrame1Tab:SetAlpha(0.42)").unwrap();
for _ in 0..10 {
s.tick(0.016);
s.resolve();
}
assert!(s.errors().is_empty(), "{:?}", s.errors());
let alpha: f64 = s.eval("return ChatFrame1Tab:GetAlpha()").unwrap();
assert!(
(alpha - 0.42).abs() < 1e-6,
"a settled dock must not rewrite its tab alpha — got {alpha}"
);
}
/// The control for the guard above: the gate must not cost the reveal it is gating. A stationary
/// cursor over the dock for longer than `CHAT_TAB_SHOW_DELAY` fades the selected tab to full.
#[test]
fn hovering_the_dock_still_reveals_the_tabs() {
let mut s = chat_frame();
s.mouse_move(1500.0, 850.0);
for _ in 0..4 {
s.tick(0.016);
s.resolve();
}
assert!(
s.eval::<f64>("return ChatFrame1Tab:GetAlpha()").unwrap() < 1e-6,
"the dock starts concealed"
);
// The dock's own centre, asked of the layout rather than hardcoded.
let (x, y): (f32, f32) = s
.eval(
"return (ChatFrame1:GetLeft() + ChatFrame1:GetRight()) / 2, \
(ChatFrame1:GetBottom() + ChatFrame1:GetTop()) / 2",
)
.unwrap();
s.mouse_move(x, y);
// 0.2 s stationary to arm the reveal, then the 0.15 s ramp — 25 frames covers both with room.
for _ in 0..25 {
s.tick(0.016);
s.resolve();
}
assert!(s.errors().is_empty(), "{:?}", s.errors());
let alpha: f64 = s.eval("return ChatFrame1Tab:GetAlpha()").unwrap();
assert!(
(alpha - 1.0).abs() < 1e-6,
"a stationary hover reveals the selected tab at full alpha — got {alpha}"
);
}
+11
View File
@@ -218,6 +218,17 @@ impl Default for UiScaleCvar {
/// The shipped default (the reference's slider spans 0.64..1.0; 1.0 read oversized to the
/// director's eye — the taste call this dial exists for).
///
/// **It also happens to be what the reference itself lands on above ~853 px tall**, which we did
/// not know when it was chosen (wow-re `system/ui/scratch/modelframe-uiscale-law.md`, out of the
/// autocast-shine thread): `0x492f70` computes `max((H > 768) ? 768/H : 1.0, 0.9)` and is called
/// **both** on a mode set and from the `useUiScale`-OFF leg (`0x4908ad`) — and OFF is the shipped
/// default (`0x8430c0` = `"0"`). So the reference is 1.0 at 768 and below, 0.96 at 1280×800, and
/// **0.9 at 1080p and 1440p**. Our flat 0.9 therefore agrees with it on every modern window and
/// diverges only *below* ~853 px tall, where the reference is nearer 1.0. Left alone deliberately:
/// this constant is a director taste call, and the divergence is confined to window heights we
/// do not ship against. The reference's own law is written here so the next session inherits it
/// rather than re-deriving it.
pub(crate) const DEFAULT_UI_SCALE: f32 = 0.9;
/// The app's `uiScale`: `WOW_UI_SCALE=` if set (clamped to the plausible dial range), else
@@ -822,6 +822,56 @@ fn the_graphics_page_reads_the_cvar_table_on_select() {
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
/// **Vertical Sync** (1394) — the Graphics page's third row, and the first option in this window
/// that reaches the *window* rather than a gameplay or UI knob. It is 1.12's own Video Options
/// checkbox 5 (`gxVSync`), which lived on the perf HUD as a dev checkbox until it turned out to be
/// unreachable in a player build (the HUD is `#[cfg(feature = "dev")]`) and to be a setting rather
/// than an instrument in the first place.
///
/// The row is deliberately **not** deferred, unlike the UI Scale slider above it: the reference's
/// row carries `gxRestart = 1` because its device could not swap the presentation interval live,
/// and wgpu can — so the click commits, like every other checkbox here.
#[test]
fn the_vertical_sync_row_reads_and_writes_the_present_mode_cvar() {
let mut s = audio_harness();
s.set_cvar_host("gxVSync", "0");
let mut s = harness_on(s);
s.run("ShowUIPanel(OptionsFrame)").unwrap();
s.run("OptionsFrameCategoryListRowGraphics:Click()")
.unwrap();
// Read from the table, not from a restated default: the harness seeded it off.
assert!(!s
.eval::<bool>("return OptionsFrameContainerBodyGraphicsRowVerticalSyncCheck:GetChecked()")
.unwrap());
assert_eq!(
s.eval::<String>("return OptionsFrameContainerBodyGraphicsRowVerticalSyncLabel:GetText()")
.unwrap(),
"Vertical Sync"
);
let _ = s.take_cvar_changes();
// A click commits immediately — nothing stages it behind Apply.
s.run("OptionsFrameContainerBodyGraphicsRowVerticalSyncCheck:Click()")
.unwrap();
assert_eq!(
s.take_cvar_changes(),
vec![("gxVSync".to_string(), "1".to_string())],
"the checkbox writes the cvar on click, not on Apply"
);
// Defaults walks it back to the registered "1" — which `cvars::tests` welds to
// `VideoConfig::default()`, and `video::tests` welds in turn to the window's boot mode.
s.run("OptionsFrameContainerBodyGraphicsRowVerticalSyncCheck:Click()")
.unwrap();
let _ = s.take_cvar_changes();
s.run("OptionsFrameContainerDefaults:Click()").unwrap();
assert!(s
.eval::<bool>("return OptionsFrameContainerBodyGraphicsRowVerticalSyncCheck:GetChecked()")
.unwrap());
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
/// The dropdown row (0992, the first): the capsule reads the CVar on select (text from the
/// row's own entries), the capsule button opens the shared list at the OWNER's effective scale
/// (the kit's 0992 uiScale correction — the window rides SetScale) with the current value's
@@ -1647,10 +1697,11 @@ fn every_row_tooltip_key_resolves_in_the_real_global_strings() {
);
checked += 1;
}
// 19 CVar rows (Social's two bubble switches are 1139's; Status Bar Text, Mouse Sensitivity
// and Max Camera Distance 1140's) + the Combat page's 14 saved-variable rows (1134) + the Interface
// page's 4 (3 from 1136, Buff Durations 1139) and the Action Bars page's 1 (1136).
assert_eq!(checked, 38, "every row but one carries a live 1.12 key");
// 20 CVar rows (Social's two bubble switches are 1139's; Status Bar Text, Mouse Sensitivity
// and Max Camera Distance 1140's; Graphics' Vertical Sync is 1394's) + the Combat page's 14
// saved-variable rows (1134) + the Interface page's 4 (3 from 1136, Buff Durations 1139) and
// the Action Bars page's 1 (1136).
assert_eq!(checked, 39, "every row but one carries a live 1.12 key");
assert_eq!(
untipped,
vec!["ControlsRowAutoLoot".to_string()],
@@ -1739,10 +1790,10 @@ fn every_flavor_of_row_raises_its_plate_from_the_page_it_lives_on() {
s.errors()
);
}
// 19 of the 20 CVar rows (Social's two are 1139's; Status Bar Text, Mouse Sensitivity and
// Max Camera Distance 1140's), plus the Combat page's 14 saved-variable rows (1134), the Interface page's 4
// (1136, + Buff Durations 1139) and Action Bars' 1 (1136).
assert_eq!(raised, 38, "every row but Auto Loot has a 1.12 description");
// 20 of the 21 CVar rows (Social's two are 1139's; Status Bar Text, Mouse Sensitivity and
// Max Camera Distance 1140's; Vertical Sync 1394's), plus the Combat page's 14 saved-variable
// rows (1134), the Interface page's 4 (1136, + Buff Durations 1139) and Action Bars' 1 (1136).
assert_eq!(raised, 39, "every row but Auto Loot has a 1.12 description");
}
/// The **Combat page** (decision 1134) — the first rows in this window whose store is a