Skip to content

Render performance improvements using ImageBitmap#490

Open
Schibum wants to merge 2 commits intoembedpdf:mainfrom
Schibum:perf_test
Open

Render performance improvements using ImageBitmap#490
Schibum wants to merge 2 commits intoembedpdf:mainfrom
Schibum:perf_test

Conversation

@Schibum
Copy link
Copy Markdown

@Schibum Schibum commented Feb 27, 2026

Experimental implementation of using createImageBitmap() / canvas for rendering instead of convertToBlob() / img.
Mainly meant as basis for discussion for now. Still needs some work/clean, depending on the direction. Idea has been discussed in #105 before.

Adds a renderMode: 'blob' | 'bitmap' option to RenderPlugin, which switches between the existing approach and the ImageData code path.

Added performance measurements for RenderLayer (RenderLayer.blob.render / RenderLayer.bitmap.render) round-trip until paint, and same for TileImg. Along with measurements for createImageData() and convertToBlob() (PdfEngine.encodeImage.encode()).
An overlay in the snippet viewer (<PerfOverlay/> ) shows the performance data for easy testing on (mobile) devices.

ImageBitmap mode (the ?bitmap query enables it):
https://embedpdf-perf-playground.pages.dev/?bitmap

Blob mode:
https://embedpdf-perf-playground.pages.dev/

Benefits of createImageBitmap() seem to depend on device, browser and pdf file. On fast devices the the bitmap benefit seems barely noticeable (unsurprisingly since image encode/decode is just some tens ms and paid for in a separate thread).

This is from scrolling down/up on the default NASA pdf:

Chrome / M1 Max:
Screenshot 2026-02-27 at 10 14 20

Firefox M1 Max:

Screenshot 2026-02-27 at 10 13 40
Screenshot 2026-02-27 at 10 13 34

I also tested on a few other devices, including two older ones. On an 7th gen iPad, the added imageEncode delay is ~200+ms , similar on an ancient Samsung Chromebook Plus

7th gen iPad:
7th blob
7th

M5 iPad:
m5
m5 blob

iPhone 14 / Pixel 10 Pro are somewhere in-between (40-60ms image encode).
There's probably also additional GC cost for the blob path which is harder to measure.

For Thumbnails the downside of ImageBitmaps is that they are much larger, so caching (at least in an unbound way like done for Blobs right now) would probably make less sense. One could either just not cache at all, use an LRU cache limited to a few pages, or just continue using imgs for thumbnails. Demo just re-renders on sidebar re-opening with no caching.

Not sure if there's a use-case for keeping both methods - at least for RenderLayer/TileLayer. Branch currently contains a few duplications and missing vue/svelte components, happy to clean it up if this seems like a way forward.

RenderPlugin and Image encode worker could probably be simplified accordingly too if thumbnails are switched to bitmap as well - but that'd be a more breaking change.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 27, 2026

@Schibum is attempting to deploy a commit to the OpenBook Team on Vercel.

A member of the Team first needs to authorize it.

@JoackimPennerup
Copy link
Copy Markdown

This looks really promising. I'm looking forward to the continuation of this.

@Schibum
Copy link
Copy Markdown
Author

Schibum commented Mar 6, 2026

Cleaned up now, removed renderMode and just made bitmap the new behaviour. Added other framework adapters. Tested them manually, but I'm not familiar enough with the codebase to be really confident about all edge cases (e.g. vue tile-img.vue has clearing behavior noted as critical which does not seem mirrored for other frameworks).

Preview of snippet of current version is at https://45ea77f1.embedpdf-perf-playground.pages.dev

Essentially adds bitmap based render methods to render-plugin. Kept blob-based methods to keep scope of changes somewhat limited (e.g. capture-plugin is unchanged). The bitmap rendering just uses the existing raw rendering worker methods, but wires though the critical priority flag for full-page renders. Added collectTransferables to wire worker data back without additional copying.
An additional copy (WASM heap -> JS Heap) here could be avoided in a future change by moving createImageBitmap there to make it even easier on GC (the createImageBitmap GPU upload is needed anyway - but otherwise that'd avoid copies of image data entirely it seems). Did not do that here to keep scope more manageable.

Then using bitmaps in RenderLayer(s), plugin-tiling and plugin-thumbnail, while adding a small LRU cache for thumbnail to avoid re-renders when closing/opening the sidebar.

Added Task.map() to reduce task mapping boilerplate - could be reverted if desired, not needed for the scope of this.

@Schibum Schibum changed the title Render performance improvements using ImageBitmap (WIP) Render performance improvements using ImageBitmap Mar 6, 2026
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.

2 participants