Extract GPU canvas infrastructure into appthere-canvas crate#14
Merged
Conversation
Phase A — Generalise PageCache over key type: - Add CacheKey marker trait (src/key.rs) with blanket impl - Generalise PageCache<K>, PageGeometry<K>, RetierResult<K> - Update PageSource trait: type Key: CacheKey associated type - RenderError::NoSuchPage(String) — doc-model-agnostic - Add #![forbid(unsafe_code)] to lib.rs - Update loki-renderer to use explicit PageCache<PageIndex> type params Phase B — Cache BlitPipeline to avoid per-call pipeline compilation: - Add BlitPipeline struct (src/blit.rs): new(device) builds pipeline once, downsample(device, queue, src, scale) reuses it per call - Slim downsample_texture free function to delegate to BlitPipeline::new - Export BlitPipeline from lib.rs under #[cfg(feature = "gpu")] All 25 unit tests pass; cargo check --workspace clean. https://claude.ai/code/session_017aWU3s4V3bdXh9vjBonxvk
…enderer, loki-vello, loki-text Phase A: new appthere-canvas crate with CacheKey trait, PageCache<K>, CacheTier, PageGeometry, RetierResult, ScrollState/ScrollPhase, PageSource trait, GpuTexture, BlitPipeline, FontDataCache, and event-driven use_settle_detector (tokio watch channel, no polling loop). Full Apache-2.0 headers, #![forbid(unsafe_code)], 300-line ceiling. Phase B: loki-renderer now depends on appthere-canvas instead of loki-render-cache. Shared vello::Renderer via Arc<Mutex<Option<vello::Renderer>>> across LokiPageSource instances. RendererState stores Arc<watch::Sender<ScrollPhase>> for event-driven settling. All imports migrated to appthere_canvas::. Phase C: loki-vello re-exports FontDataCache from appthere-canvas for API stability. Phase D: loki-text migrated to loki-renderer::DocumentView. DocumentState moved to loki-text/src/editing/state.rs with GPU fields removed. All editor modules updated to import from crate::editing::state. WgpuSurface replaced with DocumentView in editor_canvas.rs. document_source.rs and wgpu_surface.rs deleted. Zero grep hits for document_source|wgpu_surface|LokiDocumentSource|WgpuSurface. cargo check, cargo test, cargo clippy -D warnings all pass. https://claude.ai/code/session_017aWU3s4V3bdXh9vjBonxvk
…View RendererState::new was calling use_signal() inside a use_hook() closure, causing "hook list already borrowed: BorrowMutError" at document open. use_hook mutably borrows the hook list for its entire closure body; any nested hook call (including use_signal) triggers a double-borrow panic. Fix: use_signal is now called unconditionally at the top level of DocumentView and the resulting Signal passed into RendererState::new, which no longer calls any Dioxus hooks itself. https://claude.ai/code/session_017aWU3s4V3bdXh9vjBonxvk
Fix 1 — document_view.rs: apply PTS_TO_CSS_PX (96.0/72.0) to every use of p.page_size.width/height in the page geometry loop. loki-layout stores page dimensions in typographic points; they were being used directly as CSS pixels, making every canvas element ~25% too small in each dimension and causing the rightmost/bottom 25% of each page to be clipped by the render target. Fix 2 — document_view.rs: replace position:absolute (confirmed unsupported in Blitz) with block flow + margin:auto for horizontal centring. Remove the accumulated `top` offset and `total_height` from the inner container; block flow determines the container height automatically. Inter-page gap moved from absolute-top arithmetic to margin-bottom on each PageTile. Remove the `top` field from PageTileProps. Fix 3 — renderer_state.rs: apply the same PTS_TO_CSS_PX conversion in on_settle() when building PageGeometry top_px/bottom_px. Without this, tier assignment compared point-unit page positions against CSS-pixel scroll positions, incorrectly assigning Hot/Warm/Cold tiers. Fix 4 — document_view.rs: remove overflow-y:auto from DocumentView's outer div. It was creating a nested scroll container inside editor_canvas.rs's existing overflow-y:auto container. The parent container owns scroll behaviour; DocumentView is now a non-scrolling block. Fix 5 — editor_canvas.rs: document the hardcoded viewport_height_px:800.0 with a TODO comment. Affects cache tier zones only, not visual correctness. https://claude.ai/code/session_017aWU3s4V3bdXh9vjBonxvk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR extracts generic GPU canvas infrastructure (render cache, scroll state, texture management, and Dioxus integration) from
loki-render-cacheandloki-textinto a new sharedappthere-canvascrate. This enables code reuse across the AppThere suite (Loki Text, Loki Calc, Loki Slides, Iris Photo, Iris Draw) while keeping Loki-specific rendering logic inloki-renderer.Key Changes
New crate:
appthere-canvascache/— Generic tiered page-render cache (tier assignment, retier logic, metadata store)scroll.rs— Scroll phase tracking and hot-zone geometrykey.rs—CacheKeymarker trait for generic cache keyssource.rs—PageSourcetrait andRenderErrorfor render queuetexture.rs— GPU texture allocation utilitiesblit.rs— Cached bilinear-blit pipeline for texture downsamplingfont_cache.rs— Font data cache (moved fromloki-vello)dioxus/scroll_driver.rs— Event-driven scroll-settle detector for DioxusRemoved from
loki-textsrc/components/document_source.rs(1210 lines) — GPU paint source; logic moved toloki-renderersrc/components/wgpu_surface.rs(355 lines) — WGPU canvas component; replaced byloki_renderer::DocumentViewNew in
loki-textsrc/editing/state.rs— Editing-layerDocumentState(document, layout, cursor, page count) — separated from GPU rendering stateRefactored in
loki-renderersrc/page_paint_source.rs— Now importsPageCache,CacheTier,PageIndexfromappthere-canvassrc/renderer_state.rs— Simplified to hold render cache, scroll signal, and shared Vello renderersrc/document_view.rs— ReplacesWgpuSurface; usesappthere-canvas::PageCachesrc/doc_page_source.rs— Usesappthere_canvas::texture::GpuTextureandPageSourcetraitRefactored in
loki-render-cachekey.rs—CacheKeymarker trait (re-exported fromappthere-canvas)page_cache.rs— Now generic overK: CacheKeyinstead of hardcodedPageIndextier_policy.rs— Updated to useCacheKeytraitretier.rs— Updated to useCacheKeytraitRefactored in
loki-vellosrc/font_cache.rs— Now re-exportsappthere_canvas::FontDataCachefor API stabilityUpdated imports across workspace
loki-text/src/routes/editor/— ImportDocumentStatefromcrate::editing::stateinstead ofcomponents::document_sourceloki-renderer/— Import cache types fromappthere-canvasinstead ofloki-render-cacheloki-renderer/Cargo.toml— Addedappthere-canvasdependency withgpuanddioxusfeaturesImplementation Details
loki-text::editing::state; GPU rendering state is inloki-renderer::RendererStatePageCache<K: CacheKey>allows different key types (currentlyPageIndexin Loki, extensible for other apps)appthere-canvas::dioxus::scroll_driveruses watch channels instead of polling, eliminating the 16ms poll loophttps://claude.ai/code/session_017aWU3s4V3bdXh9vjBonxvk