Skip to content

fix(desktop): retain unchanged boundaries on resize - #219

Merged
kolkov merged 4 commits into
gogpu:mainfrom
besmpl:agent/retain-boundaries-on-resize
Aug 13, 2026
Merged

fix(desktop): retain unchanged boundaries on resize#219
kolkov merged 4 commits into
gogpu:mainfrom
besmpl:agent/retain-boundaries-on-resize

Conversation

@besmpl

@besmpl besmpl commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • stop marking the entire widget tree dirty on every window resize
  • retain repaint-boundary textures and the persistent layer tree when their own sizes remain unchanged
  • keep a separate surface-resize work signal so clean macOS live-resize ticks still fully compose and present
  • invalidate only the root scene for direct post-resize rendering while normal constraint changes dirty affected descendants
  • add deterministic regressions for fixed-size cache reuse, constraint-driven invalidation, and selective texture replacement

Why

Window resizing currently dirties every repaint boundary and releases every cached boundary texture, even when most boundaries keep the same dimensions. Allocating or resizing one boundary texture also contaminates the frame-global full-redraw flag. Together these behaviors defeat retained rendering and cause avoidable GPU allocation churn during live resize.

This change makes texture lifetime follow each boundary's physical size. Layout's existing constraint cache remains responsible for dirtying descendants whose constraints genuinely change, while unchanged fixed-size subtrees reuse their recorded scene and texture.

Canvas resize remains a distinct surface-only work reason. It admits an otherwise-clean live-resize frame and forces full-surface composition/damage without feeding root or child scene dirtiness, then clears only after a successful full render.

Verification

  • go test ./... -count=1
  • go test -race ./app ./desktop ./widget
  • focused resize regressions repeated 20 times
  • go build ./...
  • go vet ./...
  • git diff --check

The broad repository race run still reports the pre-existing animation-pumper test race in mockWindowProvider.RequestRedraw; all other packages and the changed paths pass. Live Metal/RSS validation was not available in this environment.

This is intentionally separate from #172, which handles cross-display device-scale changes and may require a small rebase after either PR merges.

Fixes #176

Local CI and Codecov preflight

  • rebased onto current main (273cc1e) and the prior conflict resolved while preserving the upstream pluggable debug-overlay path
  • go test ./... -count=1 with a full coverage profile: pass
  • focused resize/render tests and desktop race suite: pass; the broad app race remains blocked only by the pre-existing TestWindow_AnimPumper_StartsOnInvalidation mock race noted above
  • go build ./..., go vet ./..., gofmt, and diff checks: pass
  • local Codecov-equivalent changed-production coverage: 39/39 statements (100%) (app/window.go 13/13, desktop/desktop.go 26/26); overall coverage 87.0%

Upstream CI run approval is still external to this branch: GitHub marked the fork workflow action_required with zero jobs (run 31435378503). A gogpu maintainer must approve the workflow before Actions and the configured Codecov upload/bot can run.

Review follow-up (2026-08-12)

Removed the obsolete //nolint:nestif suppression and the stray blank line, then rebased onto current main.

@besmpl
besmpl marked this pull request as ready for review August 10, 2026 17:04
@besmpl
besmpl requested a review from kolkov as a code owner August 10, 2026 17:04
@besmpl
besmpl force-pushed the agent/retain-boundaries-on-resize branch from 0a7ce06 to 060b742 Compare August 10, 2026 20:46
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.56098% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
desktop/desktop.go 96.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE (pending lint fix)

Architecturally sound performance fix. The core insight is correct: a swapchain resize does not mean every boundary's physical size changed. The PR correctly separates surface lifecycle from boundary lifecycle.

Key changes:

  • Removes global releaseBoundaryTextures() + MarkRedrawInTree(root) from resize path
  • ensureBoundaryTexture selectively replaces only entries whose size changed
  • New surfaceResizePending flag drives full surface composition without poisoning fullRedrawNeeded
  • Root scene invalidation handles the RenderModeHostManaged path with dirtySuppressor guard

Test quality is strong: fixed-size cache reuse, constraint-driven invalidation, selective texture replacement, and E2E software backend validation.

Lint issues (2, trivial):

  1. desktop/desktop.go:526 -- unused //nolint:nestif directive. Remove the directive.
  2. desktop/resize_surface_e2e_test.go:113 -- trailing newline. Remove the blank line before closing }.

Both are one-line fixes. Once lint passes, this is ready to merge.

@besmpl
besmpl force-pushed the agent/retain-boundaries-on-resize branch from 508aaa5 to bad5206 Compare August 12, 2026 06:52
@kolkov
kolkov merged commit dd95374 into gogpu:main Aug 13, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Window resize releases ALL boundary textures and force-dirties every widget — full-tree GPU texture recreation burst on every resize tick

2 participants