Repository navigation
Refactor/perf memory display - #1
Merged
Merged
Conversation
RenderPage now copies the bitmap out of WASM memory into a pooled Go buffer and destroys the PDFium bitmap while still holding the document lock. Previously the cleanup ran on the main thread without the lock while the prefetcher or thumbnail loader could be rendering on the same module, racing on PDFium's allocator. It also bounds WASM memory to one live bitmap and avoids views that detach when the module's memory grows. go-pdfium rejects Windows drive-letter paths inside WebAssembly, so the PDF's directory is now mounted read-only at /doc and opened by a POSIX path. This also stops PDFium from mounting the whole filesystem. Add internal/pdf/pdftest, a tiny PDF writer, so render tests run in CI on every OS (colour/size, unicode path, concurrent vs serial renders), and generate examples/sample.pdf from it with a golden test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- pdf: cache the compiled PDFium module on disk (wazero compilation cache in the user cache dir); Open drops from ~1.1s to ~40ms warm. Page sizes are read once at Open, so PageSize never waits on a render. - pdf: new priority Scheduler (urgent > visible > prefetch > background) replaces the FIFO prefetcher and the ad-hoc thumbnail goroutines. It coalesces urgent requests, cancels replaced queues and never starts a job that is in flight or awaiting upload. - app: shared pipeline (slides.go) for both windows; navigation never renders on the Ebiten goroutine. Stand-ins (same page, other size) are shown while exact renders land, and transitions wait for the incoming slide. The presenter now prefetches what the next advance needs. - app: single overview implementation for audience and presenter, with a debounced preview-size render and per-tile grid-layer updates. - ipc: Hello (static data) once on connect, then state only on change; the presenter extrapolates clock/progress. Socket in a private 0700 dir, single connection. - Skip unchanged frames (SetScreenClearedEveryFrame(false) + signature), true crossfade, rune-aware notes wrapping with typographic fallbacks, --no-fullscreen honoured by the presenter, hidden --debug-stats. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Cache entries are charged at real texture size (Ebitengine rounds textures to powers of two), so --cache-mb and the auto budget cap actual GPU memory. The auto budget keeps at least three slides, and prefetch depth is derived from it so prefetched slides aren't evicted before use. The presenter budget follows its pane sizes. - Slides are uploaded as unmanaged images: dedicated textures that are freed on eviction instead of atlas pages that linger until empty. - Cache.Put keeps an existing (possibly pinned) image; the inverted PurgeNotMatching is replaced by PurgeExcept, and a resize drops every unpinned render. - Thumbnail store is budgeted from the grid and purges stale sizes; the disk cache moves to <user cache>/boozle/thumbs (0700) with age/size pruning; memory is returned to the OS when the overview closes. - The overview preview prefers an exact preview-size render and draws without mipmaps. 300-page deck, 1280x720: steady RSS ~155 MB (was ~355 MB on main), GPU 48 MB (was ~76-88 MB). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Heap profiling full-screen on a Retina display showed most of the Go heap in render results waiting for upload and in Ebitengine's staging buffers, which only return to its reuse pool after a GC: - The scheduler caps pixels of finished-but-undrained renders at 32 MB, so at most one large slide waits while thumbnails still stream. - The pipeline nudges a background GC after every 32 MB uploaded, so staging buffers are recycled instead of piling up. 300-page deck full-screen at 3600x2338: peak RSS 369 MB (main: 1086 MB), steady ~340 MB (main: ~850 MB). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ebitengine goes fullscreen on the monitor it believes the window is on. On Linux that is derived from the window position, cached for ~60 ticks and lags behind asynchronous X11 moves (and window managers may ignore pre-map positions), so SetMonitor + SetFullscreen in one tick often landed on the primary display. - window.go: a placer state machine waits for the window to map, moves it if needed, waits until the target monitor is reported, goes fullscreen, verifies after the cache refresh and retries once; F uses the same path. --debug-stats traces each step. - display: resolve monitors by index, exact name or unique substring; --list-monitors prints name/size/scale/primary; the initial window is sized to fit inside the target so it opens centred there. - The presenter process is spawned once the audience window is placed (2 s fallback) and receives the resolved index; both windows set WM_CLASS boozle/audience|presenter. - config: explicit flags win over the sidecar via cobra's Changed set (fixes --monitor 0, -P -1, --start 1, --bg '#000000', --loop=false); monitor specs are strings (sidecar accepts ints or names); invalid --transition and absurd --pages ranges are rejected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Navigation goes through one goTo primitive: Home/End/page jumps/l record the previous position, and jumping to the current slide is a no-op (no fade into itself). Numpad digits work in the audience window. - One SidecarPath helper in config replaces the copy in pptxnotes. - Version test checks main.version against the newest CHANGELOG release instead of a hard-coded string; CLI help and the no-args hint describe monitor names, overview keys and flag precedence, without the duplicated sidecar section. - CI: gofmt, go mod tidy -diff and staticcheck; gofmt the remaining test files; socket-permission test skipped on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- cache: a Put never evicts its own entry. With a budget smaller than the pinned on-screen slide plus one more (small --cache-mb, 4K), the fresh render was evicted on insert and never requested again, so the screen stuck on the old slide (and the presenter re-rendered its next pane forever). Put now returns the image it keeps. - scheduler: clear a job's outstanding mark only after its result is in the cache, so it can't be rendered twice; drop the test-only Outstanding accessor. Thumbnail delivery passes on the image the store kept. - ipc: serialise the connect-time replay with Send so a newer state can't be overwritten by the replayed one; the presenter waits for its first state, not just the Hello. - Opening the overview finishes a running slide transition; a resize during the overview's exit no longer re-queues every thumbnail. - Stale comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README: monitor selection by name, a Linux multi-monitor section (XWayland, XRandR/Xinerama runtime libs, placement log, WM_CLASS, global X11 scale), a "Performance and memory" section describing the real-GPU-memory budget, cache directories and --debug-stats, overview keys, flag precedence, --autoquit semantics, platform list, and tests that no longer need BOOZLE_TEST_PDF. - CLAUDE.md: architecture covers every package and the new pipeline; gotchas updated (render scheduler, Go-owned pixels, /doc mount, texture-size budgeting, redraw signatures, fullscreen placer, IPC hello/state, Changed-based precedence); stale items removed. - release.yml: add darwin-amd64 (macos-15-intel) and linux-arm64 (ubuntu-24.04-arm) builds and a --version smoke test per target. - NOTICE: attribute all linked third-party modules. - examples/sample.boozle.toml matches the sample deck. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cut the 1.3.0 changelog section and bump the default version string. Document that the overview opens in the presenter window when presenter view is on. install.sh no longer prints a quarantine workaround that never applies to curl downloads (and lacked sudo for /usr/local/bin); it clears the attribute itself instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a cache budget below a few slides (e.g. --cache-mb 1, or a small budget on a 4K display), a slow machine could finish the urgent render and a prefetch between two frames. drain then uploaded both, and the prefetch's Put evicted the urgent slide before the window pinned it. refreshDisplay requests each slide only once, so that page never showed at exact size. TestTinyCacheStillShowsEveryPage failed on the amd64 CI runners this way. drain now pins every slide it uploads until it returns. TestDrainKeepsEverySlideItUploads reproduces the batch deterministically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
examples/sample.pdf is generated as plain ASCII, so git treated it as text and the Windows runner (core.autocrlf=true) checked it out with CRLF line endings. That fails TestSamplePDFUpToDate and corrupts the PDF's xref byte offsets for anyone cloning on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
No description provided.