Skip to content

feat: Per-Boundary GPU Textures + 0% GPU Idle (v0.1.19)#95

Merged
kolkov merged 9 commits into
mainfrom
release/v0.1.19
May 10, 2026
Merged

feat: Per-Boundary GPU Textures + 0% GPU Idle (v0.1.19)#95
kolkov merged 9 commits into
mainfrom
release/v0.1.19

Conversation

@kolkov
Copy link
Copy Markdown
Contributor

@kolkov kolkov commented May 10, 2026

Summary

  • Per-boundary GPU textures (ADR-007 Phase 7): each RepaintBoundary rendered into own offscreen MSAA texture. Clean boundaries reuse previous texture (0 GPU work). Compositor blits via non-MSAA path.
  • 0% GPU idle: frame skip in desktop.draw when nothing dirty. Offscreen boundary culling stops animation pumper. Verified on all 6 examples.
  • 8% GPU with visible spinner (was 8% always, even without animations). Pumper isolation prevents data tickers from restarting 30fps rendering.
  • SVG icons + DPI-aware rendering (ADR-026): 2-level LRU IconCache, Qt6/Chromium pattern.
  • 34 integration tests for render loop pipeline (6 suites: multi-frame lifecycle, visibility matrix, damage rects, recording order).
  • Dependencies: gg v0.46.4, gogpu v0.34.0, gpucontext v0.18.0.

GPU Performance (Intel Iris Xe)

Scenario Before (v0.1.18) After (v0.1.19)
Static UI (no animations) 8% 0%
Spinner visible (30fps) 8% 8%
Spinner offscreen 8% 0%

Test plan

  • go build ./... — pass
  • go test ./... — 56 packages, 0 FAIL
  • golangci-lint run — 0 new issues
  • go vet ./... — clean
  • go mod verify — all modules verified
  • Visual: gallery, hello, ide, signals, taskmanager, modular-compositor — all OK
  • GPU verified: 0% idle, 0-1% taskmanager, 0% compositor, 8% gallery spinner

kolkov added 7 commits May 10, 2026 14:39
- gg v0.44.1 → v0.46.4 (LCD ClearType, TagText scene text, atlas zoom
  resilience, deferred ortho projection, blit scissor groups)
- gogpu v0.31.0 → v0.34.0 (ClearType, SubpixelLayout, D2 render loop)
- gpucontext v0.16.0 → v0.18.0 (SubpixelLayout, AdapterInfo)
Retained-mode compositor with per-boundary GPU textures:

- compositor/ package: Layer Tree types (OffsetLayer, PictureLayer,
  ClipRectLayer, OpacityLayer, Compositor) — implemented, tested,
  not yet connected to production pipeline (future optimization)
- PaintBoundaryLayers: re-records dirty boundaries (Flutter flushPaint)
- renderBoundaryTextures: per-boundary offscreen MSAA textures
- compositeTextures: blit all textures via non-MSAA path
- DrawChild skip (Flutter paintChild): child boundaries SKIPPED during
  parent recording, composed as separate textures
- Compositor scissor clipping for ScrollView viewport
- RepaintBoundary as WidgetBase property (ADR-024)
- Spinner auto-boundary (indeterminate progress)
- AnimationScheduler (Flutter scheduleFrame pattern)
- Scrollbar Qt6 timing (500ms initial, 50ms repeat)
- Upward dirty propagation to nearest boundary (O(depth))
- SVG icons in SceneCanvas via SVGRenderer + SVGFiller interfaces
- CPU rasterization via RasterizerAnalytic (bypasses GPU queueing)
- 2-level LRU IconCache: L1 parsed svg.Document, L2 rasterized scene.Image
  by (ptr, w, h, color) with 256-entry eviction. 7.5ms → <1µs per frame.
- DPI-aware: render at ceil(logicalSize × deviceScale) physical pixels
- DeviceScaler interface propagates scale to SceneCanvas
- Qt6/Chromium/IntelliJ enterprise pattern
- CrossAxisAlignment for VBox/HBox: Center, Start, End, Stretch
- Collapsible: header as internal widget for dirty tracking
- Collapsible: TitleSignal + TitleReadonlySignal for reactive headers
- TextField/Slider/LineChart: fill MaxWidth from constraints (was hardcoded)
- Per-item InvalidateRect in ListView (not full bounds)
- Leaf-dirty pattern in dirty collector
- ScreenOriginBase on Canvas for screen-space dirty tracking
Six professional test suites:
1. MultiFrameSpinnerLifecycle — 5-frame simulation, per-frame invariants
2. DataTickerDoesNotTriggerOffscreenSpinnerRecording — isolation verified
3. BoundaryRecordingOrder_RootBeforeChildren — depth-first order
4. ScreenBoundsAccuracyAfterRecording — screen-space damage coords
5. CleanStateEarlyReturn — zero Draw calls on clean pass
6. VisibilityMatrix — 14 table-driven subtests (all positions)

Plus: hover E2E, spinner E2E, DrawChild skip, compositor clip,
  boundary recording, screen origin, dirty overlay, first frame tests
- CHANGELOG: per-boundary GPU textures, 0% GPU idle, offscreen culling,
  SVG icons, DPI rendering, CrossAxisAlignment, 34 integration tests,
  damage rect fixes, pumper isolation, scrollbar Qt6 timing
- ARCHITECTURE: production pipeline with frame skip + culling
- ROADMAP: performance targets (0% idle achieved), new widget plans,
  optimization roadmap (LoadOpLoad P0, parent chain P1, Vello P3)
- Test adaptations for new boundary/compositor infrastructure
Mark unused code with nolint:unused and task references:
- compositor/ layer asContainer overrides (TASK-UI-OPT-005)
- replayLayerTree in desktop.go (TASK-UI-OPT-005)
- dirty collector debug field (enterprise logging)
- testSceneRecorderForOriginTests (future test variants)
@codecov
Copy link
Copy Markdown

codecov Bot commented May 10, 2026

kolkov added 2 commits May 10, 2026 15:10
- desktop.go: nolint:gocyclo,nestif on render loop (pipeline orchestration)
- desktop.go: ifElseChain → switch in compositeTextures
- desktop.go: nolint:gocognit,nestif on renderBoundaryTexturesRecursive
- listview: nolint:nestif on cache/widget (type assertion chains)
- collector.go: nolint:nestif on viewport dirty collection
- collapsible_test.go: preallocate texts slice
- progress_test.go: remove empty if block
- collector_test.go: remove unused x parameter
- .golangci.yml: exclude tmp/ from analysis

Result: golangci-lint v2.12.2 reports 0 issues.
@kolkov kolkov merged commit d9462d7 into main May 10, 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.

1 participant