perf(effects): cull off-screen card effects and sample border by arc

Glass cards scrolled out of view still ran their full effect path, and
the rainbow border always used a segment count derived from a fixed
per-pixel budget, so wide cards drew far more line segments than their
slowly changing gradient needed.

- A glass panel whose rectangle lies wholly outside the draw list's clip
  rect now returns early, so a scrolled-out card draws nothing (its
  border and ember passes are skipped along with the fill).
- The rainbow border is split into segments by perimeter arc length:
  straight edges get one segment per ~32 dp, corner arcs get a few
  chords each to stay round, and a fully transparent border is skipped
  outright. A wide card now draws tens of segments instead of a couple
  hundred, with no visible change.

Tests: the segment fractions are ascending and bounded, each corner arc
keeps at least three chords, and the rect-cull margin test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 13:30:20 -05:00
co-authored by Claude Opus 4.8
parent fbc429f914
commit cd83038de5
5 changed files with 110 additions and 18 deletions
+28
View File
@@ -70,6 +70,8 @@
#include "ui/node_status_banner.h"
#include "ui/staleness_badge.h"
#include "ui/effects/effect_clock.h"
#include "ui/effects/border_sampling.h"
#include "ui/material/rect_cull.h"
#include "ui/notifications.h"
#include "data/seed_migration_resume.h"
#include "data/pin_vault_policy.h"
@@ -13513,6 +13515,30 @@ void testEffectClock() {
EXPECT_NEAR(radSin(1e6, 0.0, 1.5707963267948966), 1.0f, 1e-6f);
}
void testBorderSampling() {
using namespace dragonx::ui::effects; float fr[129];
int n = BorderSampleFractions(500, 150, 8, 32, fr, 129);
EXPECT_TRUE(n >= 40 && n <= 80); // old formula: ~210
EXPECT_EQ(fr[0], 0.0f); EXPECT_EQ(fr[n], 1.0f);
for (int i = 1; i <= n; ++i) EXPECT_TRUE(fr[i] > fr[i - 1]);
n = BorderSampleFractions(3000, 800, 12, 32, fr, 129); EXPECT_TRUE(n <= 128); // old: 256
n = BorderSampleFractions(300, 100, 0, 32, fr, 129); EXPECT_TRUE(n >= 4 && n <= 40); // r=0 rectangle
// each corner arc keeps >= 3 chords: top-right arc spans [s1/P, s2/P]
{ float r = 12, w = 400, h = 120, P = 2*(w-2*r) + 2*(h-2*r) + 4*(3.14159265f*r*0.5f);
n = BorderSampleFractions(w, h, r, 32, fr, 129);
float lo = (w-2*r)/P, hi = lo + 3.14159265f*r*0.5f/P; int c = 0;
for (int i = 1; i <= n; ++i) if (fr[i] > lo + 1e-5f && fr[i] <= hi + 1e-5f) ++c;
EXPECT_TRUE(c >= 3); }
n = BorderSampleFractions(0, 50, 4, 32, fr, 129); EXPECT_EQ(n, 1); // degenerate
}
void testRectCull() {
using namespace dragonx::ui::material; ImVec2 a(0,0), b(100,100);
EXPECT_TRUE(!RectOutsideRect(ImVec2(10,10), ImVec2(50,50), a, b, 0));
EXPECT_TRUE(RectOutsideRect(ImVec2(0,150), ImVec2(80,250), a, b, 16)); // below
EXPECT_TRUE(!RectOutsideRect(ImVec2(0,110), ImVec2(80,250), a, b, 16)); // inside margin
EXPECT_TRUE(RectOutsideRect(ImVec2(-90,0), ImVec2(-20,50), a, b, 16)); // left
}
// The effective pool list = official pools ∪ saved favorites ∪ current custom pool,
// endpoint-deduped, with synthetic user rows flagged official=false.
void testEffectivePools()
@@ -18249,6 +18275,8 @@ int main()
testPoolFeeParsing();
testEffectivePools();
testEffectClock();
testBorderSampling();
testRectCull();
testFormatFeePercent();
testPoolWeightedSelection();
testCoalescingWriter();