Skip to content

Implement iris-canvas Phase 2: viewport, compositor, and blend pass#7

Merged
kevincarlson merged 3 commits into
mainfrom
claude/wire-appthere-canvas-6UHIr
May 19, 2026
Merged

Implement iris-canvas Phase 2: viewport, compositor, and blend pass#7
kevincarlson merged 3 commits into
mainfrom
claude/wire-appthere-canvas-6UHIr

Conversation

@kevincarlson

Copy link
Copy Markdown
Member

Summary

Implements the core Iris infinite-canvas rendering pipeline (Phase 2 per SPEC.md §6.2):

  • Viewport transform with pan, zoom, and rotation support
  • GPU compositor that tiles visible layers and blends them with Normal mode
  • Dioxus integration via use_wgpu hook and CustomPaintSource bridge
  • WGSL render pipeline for premultiplied-alpha Normal blend

This unblocks Phase 2 delivery and establishes the foundation for Phase 3+ (vector/text layers, additional blend modes).

Key Changes

New modules

  • viewport.rs (221 lines): CanvasViewport struct with screen↔document coordinate transforms, zoom anchoring, and visible-rect culling. Includes 8 unit tests covering round-trip transforms, rotation, and zoom clamping.
  • compositor/mod.rs (66 lines): Compositor wrapper managing lazy BlendPass initialization and the main composite() entry point.
  • compositor/pass.rs (193 lines): BlendPass wgpu render pipeline (vertex/fragment shaders, bind groups, sampler) for Normal blend via PREMULTIPLIED_ALPHA_BLENDING.
  • compositor/composite.rs (133 lines): Core composite logic—layer iteration (DFS bottom-to-top), tile selection via visible rect, NDC quad computation, and blend pass invocation.
  • compositor/upload.rs (95 lines): CPU→GPU tile upload helpers (upload_tile, transparent_tile) for Rgba16Float textures.
  • canvas_widget.rs (136 lines): IrisCanvas Dioxus component with reactive signals for viewport/tree, use_wgpu hook integration, and IrisPageSource for headless rendering.
  • paint_bridge.rs (93 lines): IrisCanvasPaintSource implementing CustomPaintSource to bridge Compositor into Blitz's paint loop.
  • key.rs (41 lines): TileKey cache key type (layer ID + tile coordinate) for future page-cache integration.
  • shaders/normal_blend.wgsl (65 lines): Vertex/fragment shaders with bilinear corner interpolation for rotated tiles and premultiplied-alpha blending.

Modified files

  • lib.rs: Expose public API (CanvasViewport, IrisCanvas, CompositorError, re-exports from appthere-canvas).
  • Cargo.toml: Uncomment appthere-canvas, anyrender-vello, wgpu, kurbo, bytemuck, thiserror dependencies (gate now open per ADR 006).
  • tests/integration.rs: Add 5 integration tests covering viewport transforms, tile selection, and TileKey hashing.
  • ADR/006-shared-canvas-extraction.md: Update status to "Accepted" and document implementation completion.
  • SPEC.md: Update §6.2 to reflect screen-center pan convention (Q1 decision).
  • CLAUDE.md: Update gating table—iris-canvas gate is now open.
  • Root Cargo.toml: Exclude appthere-canvas from workspace (external dependency).

Implementation Details

Pan convention (Q1 decision)

Document-space coordinate visible at the screen center, not top-left. Rotation is always around screen center. This avoids undefined behavior when rotation is non-zero.

Blend mode phasing

Phase 2 supports Normal blend only via hardware PREMULTIPLIED_ALPHA_BLENDING. Non-Normal modes log a warning and skip the layer. Phase 4 will add compute shaders for additional blend modes (SPEC.md §4.8).

Tile rendering

  • Tiles are uploaded as Rgba16Float textures (f16 RGBA, straight alpha

https://claude.ai/code/session_01X8kb2QAMhu3VtT8C2CkdYa

claude added 3 commits May 19, 2026 02:59
- Exclude crates/appthere-canvas from workspace members (external path dep)
- Fix workspace appthere-canvas path (was ../appthere-canvas, now crates/appthere-canvas)
- Add features = ["gpu", "font-cache"] to appthere-canvas workspace dep
- Uncomment appthere-canvas (+ dioxus feature), vello, wgpu in iris-canvas/Cargo.toml
- Replace iris-canvas/src/lib.rs TODO stub: re-export CacheKey, CacheTier,
  ScrollPhase, ScrollState, GpuTexture, PageSource, RenderError from appthere_canvas;
  compile-time assertion that TileCoord satisfies CacheKey
- ADR/006: status Proposed → Accepted; add Implementation notes section
- CLAUDE.md: iris-canvas gate updated to GATE OPEN (ADR 006)

cargo check --workspace: zero errors
cargo test --workspace: 55 tests, 0 failures

https://claude.ai/code/session_01X8kb2QAMhu3VtT8C2CkdYa
…ompositor, IrisCanvas

- TileKey: (LayerId, TileCoord) composite key satisfying appthere_canvas::CacheKey
- CanvasViewport: screen-center-anchored pan, zoom/rotation math, screen↔doc
  round-trip transforms, visible_doc_rect AABB for culling, zoom_to anchor-stable
- Compositor: lazy BlendPass pipeline (Normal blend via PREMULTIPLIED_ALPHA_BLENDING
  render pipeline), DFS bottom-to-top layer iteration, tile upload to Rgba16Float,
  tile_params NDC quad corners per viewport transform
- IrisCanvas Dioxus component (placeholder div, BLOCKED on appthere-canvas paint hook)
  with complete IrisPageSource / PageSource<Key = ()> implementation
- Integration tests: viewport round-trips, TileKey hash, LayerTree structure
- Upgrade wgpu workspace dep 22→26 to match appthere-canvas; add bytemuck, dioxus deps
- Split compositor/mod.rs under 100-line ceiling into mod.rs + composite.rs
- All 11 iris-canvas tests pass; clippy -D warnings clean; cargo test --workspace green

https://claude.ai/code/session_01X8kb2QAMhu3VtT8C2CkdYa
…urce

- Add anyrender_vello = "0.6.2" dep to iris-canvas (Option B: bridge in iris-canvas)
- New paint_bridge.rs: IrisCanvasPaintSource implements anyrender_vello::CustomPaintSource
  - resume(): clones DeviceHandle (wgpu::Device + Queue are Clone in wgpu 26)
  - suspend(): drops device handle and last registered TextureHandle
  - render(): reads viewport/tree from Arc<Mutex<>>, calls compositor.composite(),
    unregisters old TextureHandle, calls ctx.register_texture(gpu_texture.inner),
    returns Some(TextureHandle) — None skips the frame (Blitz reuses last texture)
- Update canvas_widget.rs: replace placeholder div with working use_wgpu integration
  - shared_viewport and shared_tree are Arc<Mutex<>> updated on each Dioxus re-render
  - use_wgpu captures cloned Arcs via FnOnce; auto-unregisters on component drop
  - RSX: <canvas "src"="{canvas_id}"> (quoted attr bypasses Dioxus schema;
    blitz-dom reads "src" on <canvas> elements to load custom paint sources)
- Add #[derive(Clone)] to LayerTree in iris-pixel (all fields already Clone)
- All 11 iris-canvas tests pass; clippy -D warnings clean; cargo test --workspace green

API discoveries vs audit assumptions:
  - DeviceHandle.device / .queue are public fields (not getters) — direct field access
  - TextureHandle: Clone — can clone for storage while returning from render()
  - anyrender_vello uses wgpu = "26" — same as iris-canvas, types compatible
  - use_wgpu is at dioxus::native::use_wgpu (not dioxus::prelude)
  - canvas src attribute must be quoted "src" in RSX (not in Dioxus element schema)

https://claude.ai/code/session_01X8kb2QAMhu3VtT8C2CkdYa
@kevincarlson kevincarlson merged commit fb83b09 into main May 19, 2026
1 check failed
@kevincarlson kevincarlson deleted the claude/wire-appthere-canvas-6UHIr branch May 19, 2026 05:39
@kevincarlson kevincarlson self-assigned this May 19, 2026
@AppThere AppThere locked as resolved and limited conversation to collaborators May 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants