Repository navigation
Let a StyleImageInterface draw itself on the GPU for more performant animated icons - #7954
Conversation
cd5c010 to
bcfe903
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7954 +/- ##
==========================================
+ Coverage 93.42% 93.59% +0.16%
==========================================
Files 291 291
Lines 24631 24655 +24
Branches 6490 6498 +8
==========================================
+ Hits 23012 23075 +63
+ Misses 1619 1580 -39 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
4f15f38 to
7e76f83
Compare
|
|
Status update: I'm heading into the backcountry and offline through Tuesday 7/28. I'll pick this back up Wednesday. |
cd5d3c1 to
06d1c5d
Compare
7735ed0 to
e9afc1c
Compare
|
This is ready for review |
Animating an icon today means a StyleImageInterface#render() callback that rewrites its pixels every frame, which MapLibre reprocesses on the CPU and re-uploads in full. Add an optional renderToTexture, so an image can write straight into its slot in the icon atlas instead. ImageAtlas#patchUpdatedImage calls it in place of the usual upload and resets the WebGL state cache once per batch afterward, so the callback is free to leave state behind. Nothing about the hook is animation-specific, and it keeps ImageDecoder out of core: animated icons can now live in a plugin. Also adds Map#getMaxTextureSize(), so an image that manages its own textures can tell what will fit.
e9afc1c to
4966e3a
Compare
|
Can you add a short html example using this new API? Add This looks very specific and concise, which is good. Can you share maybe what other alternatives you have considered and rejected to solve this? I'm trying to get the bigger picture here. |
|
Added a few commets, see if you can reduce some in method comments and move those to tsdocs, also reduce comments inside tests as much as possible. |
StyleImageInterface draw itself on the GPU for cheaper animated iconsStyleImageInterface draw itself on the GPU for more performant animated icons
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…animated-icons # Conflicts: # CHANGELOG.md
|
This is ready for another round of review |
|
Added a minor comment. |
Head branch was pushed to by a user without write access
|
Just did a final manual check on the example page and some of my prototype plugins. All looks good to me! I think this is ready to 🚢 . Thanks for the review. |
|
Thanks!! It will probably take a week or two to release, I hope that's fine. |
Reconciles the image manager rework with the WebGL style images of #7954: the atlas now walks its own positions rather than a global dictionary of updated images, and reaches a WebGL image's pending first render through the same version bump that `addImage` already makes for it.
##### [v6.4.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#640) ##### ✨ Features and improvements - Avoid a per-query `Array.sort()` in cross-tile symbol matching (`TileLayerIndex.findMatches`), claiming the lowest-index unclaimed candidate in a single pass instead; reduces main-thread symbol-placement cost on dense/coincident symbol layers ([#7797](maplibre/maplibre-gl-js#7797)) (by [@pholmstr](https://github.com/pholmstr)) - Use `texelFetch` for exact DEM and color-relief elevation stop lookups instead of normalized texture coordinate arithmetic ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Make default draggable markers keyboard-focusable and movable with the arrow keys (1 px per press, 10 px with Shift); custom marker elements stay application-owned ([#8020](maplibre/maplibre-gl-js#8020)) (by [@smmariquit](https://github.com/smmariquit)) ##### 🐞 Bug fixes - Fix a permanent frame rate degradation after switching styles: every sprite reload marked its images as updated forever, making every in-view tile re-check and re-upload them on every frame. Also stop leaking the images of a replaced sprite, which were never removed from the image manager ([#8052](maplibre/maplibre-gl-js#8052)) (by [@HarelM](https://github.com/HarelM)) - Prevent a rejected missing style image resolver from blocking successfully resolved images in the same batch ([#8146](maplibre/maplibre-gl-js#8146)) (by [@birkskyum](https://github.com/birkskyum)) - Explicitly request no browser color management when decoding raster-DEM tiles so their RGB-encoded elevation values are not changed (what would otherwise happen with `gfx.color_management.mode = 1` in Firefox) ([#8125](maplibre/maplibre-gl-js#8125)) (by [@tnikkel](https://github.com/tnikkel)) - Let an abort reach an image or raster tile load that is still awaiting its `transformRequest`, so `ImageSource.updateImage` no longer loses the image it was just handed and an aborted tile is no longer fetched ([#8071](maplibre/maplibre-gl-js#8071)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix raster tiles fading in again when they are reloaded, briefly flashing the map background, most visibly when switching projection ([#8106](maplibre/maplibre-gl-js#8106)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix globe panning inverting and stalling near and across the poles by rotating the globe with a versor, keeping the drag direction consistent at every latitude. Panning also eases off as the cursor approaches the edge of the globe and continues past it, instead of stopping. The bearing is preserved while panning, as before ([#5296](maplibre/maplibre-gl-js#5296)) (by [@jcolot](https://github.com/jcolot)) - Fix `fill-extrusion-rounded-corner-distance` producing spikes: corner arcs now land on the integer tile grid, and corners created by tile clipping are left sharp ([#8153](maplibre/maplibre-gl-js#8153)) (by [@HarelM](https://github.com/HarelM)) - Fix a gesture which was held still before being released still flinging the map ([#1303](maplibre/maplibre-gl-js#1303)) (by [@zdila](https://github.com/zdila)) ##### [v6.3.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#630) ##### ✨ Features and improvements - Make fired/listened map events typed. This means that `map.on("something", ...)` (and `once`, `listens`) will now give you an typescript error and better autocomplete. If you relied on firing/listening custom events via the map, this still works via the escape hatches `map.fire("something" as any)` -> `map.on("something" as any, ...)` ([#8072](maplibre/maplibre-gl-js#8072)) (by [@CommanderStorm](https://github.com/CommanderStorm)) - Let a `StyleImageInterface` give a `{renderWithWebGL}` callback as its `data`, an escape hatch for plugin developers and advanced users that renders a style image on the GPU instead of moving its pixels through the CPU. Nothing new is possible that pixels could not already express, but an image that changes often, such as an animated icon, gets more performant ([#7954](maplibre/maplibre-gl-js#7954)) (by [@lucaswoj](https://github.com/lucaswoj)) - Use integer vertex attributes for packed line data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Use integer vertex attributes for packed circle, heatmap, symbol, and fill-extrusion data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640), [#8143](maplibre/maplibre-gl-js#8143)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Redesign benchmarks to use vitest bench capabilities and remove custom build for benchmarks code ([#982](maplibre/maplibre-gl-js#982)) (by [@johncarmack1984](https://github.com/johncarmack1984)) ##### 🐞 Bug fixes - Fix terrain pan/zoom gestures losing the grabbed terrain point: gestures are now solved against the elevation of the terrain under the gesture instead of the frozen center elevation, so terrain under the pointer/fingers no longer slips during moving-centroid pinches and drags ([#8067](maplibre/maplibre-gl-js#8067)) (by [@StrawberryJam22](https://github.com/StrawberryJam22)) - Fix `ImageSource`, `VideoSource` and `CanvasSource` leaking a GPU texture on every image update and on removal, and a resized texture losing its wrap and filter settings ([#8094](maplibre/maplibre-gl-js#8094)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix `map.queryRenderedFeatures()` sometimes causing "Out of bounds" error due to race condition while loading tile data ([#8064](maplibre/maplibre-gl-js#8064)) (by [@smvjohansenbouvet](https://github.com/smvjohansenbouvet)) - Fix zooming the globe with the scroll wheel or a two-finger pinch drifting away from the pointer while the globe is small on screen, instead of keeping the location under the pointer as it does when zoomed in ([#8095](maplibre/maplibre-gl-js#8095)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix projective rendering for non-parallelogram image source quads ([#7887](maplibre/maplibre-gl-js#7887)) (by [@i4innovationnet](https://github.com/i4innovationnet))
##### [v6.4.1](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#641) ##### 🐞 Bug fixes - Fix `DOM.sanitize` leaving dangerous attributes behind when multiple consecutive attributes are present. Iterating the live `NamedNodeMap` from `elem.attributes` while calling `removeAttribute` skipped the attribute directly after a removed one, so a second dangerous attribute (for example an `ontoggle` on a `<details open>` element) could survive sanitisation and later execute ([#8189](maplibre/maplibre-gl-js#8189)) (by [@0xKirisame](https://github.com/0xKirisame)) - Give custom layers the live globe transition in `CustomRenderMethodInput.defaultProjectionData.projectionTransition`, which was hardcoded to 1 for the whole globe/mercator transition, so a custom layer jumped straight to the fully bent globe while every other layer eased ([#8169](maplibre/maplibre-gl-js#8169)) (by [@mondsichtung](https://github.com/mondsichtung)) ##### [v6.4.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#640) ##### ✨ Features and improvements - Avoid a per-query `Array.sort()` in cross-tile symbol matching (`TileLayerIndex.findMatches`), claiming the lowest-index unclaimed candidate in a single pass instead; reduces main-thread symbol-placement cost on dense/coincident symbol layers ([#7797](maplibre/maplibre-gl-js#7797)) (by [@pholmstr](https://github.com/pholmstr)) - Use `texelFetch` for exact DEM and color-relief elevation stop lookups instead of normalized texture coordinate arithmetic ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Make default draggable markers keyboard-focusable and movable with the arrow keys (1 px per press, 10 px with Shift); custom marker elements stay application-owned ([#8020](maplibre/maplibre-gl-js#8020)) (by [@smmariquit](https://github.com/smmariquit)) ##### 🐞 Bug fixes - Fix a permanent frame rate degradation after switching styles: every sprite reload marked its images as updated forever, making every in-view tile re-check and re-upload them on every frame. Also stop leaking the images of a replaced sprite, which were never removed from the image manager ([#8052](maplibre/maplibre-gl-js#8052)) (by [@HarelM](https://github.com/HarelM)) - Prevent a rejected missing style image resolver from blocking successfully resolved images in the same batch ([#8146](maplibre/maplibre-gl-js#8146)) (by [@birkskyum](https://github.com/birkskyum)) - Explicitly request no browser color management when decoding raster-DEM tiles so their RGB-encoded elevation values are not changed (what would otherwise happen with `gfx.color_management.mode = 1` in Firefox) ([#8125](maplibre/maplibre-gl-js#8125)) (by [@tnikkel](https://github.com/tnikkel)) - Let an abort reach an image or raster tile load that is still awaiting its `transformRequest`, so `ImageSource.updateImage` no longer loses the image it was just handed and an aborted tile is no longer fetched ([#8071](maplibre/maplibre-gl-js#8071)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix raster tiles fading in again when they are reloaded, briefly flashing the map background, most visibly when switching projection ([#8106](maplibre/maplibre-gl-js#8106)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix globe panning inverting and stalling near and across the poles by rotating the globe with a versor, keeping the drag direction consistent at every latitude. Panning also eases off as the cursor approaches the edge of the globe and continues past it, instead of stopping. The bearing is preserved while panning, as before ([#5296](maplibre/maplibre-gl-js#5296)) (by [@jcolot](https://github.com/jcolot)) - Fix `fill-extrusion-rounded-corner-distance` producing spikes: corner arcs now land on the integer tile grid, and corners created by tile clipping are left sharp ([#8153](maplibre/maplibre-gl-js#8153)) (by [@HarelM](https://github.com/HarelM)) - Fix a gesture which was held still before being released still flinging the map ([#1303](maplibre/maplibre-gl-js#1303)) (by [@zdila](https://github.com/zdila)) ##### [v6.3.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#630) ##### ✨ Features and improvements - Make fired/listened map events typed. This means that `map.on("something", ...)` (and `once`, `listens`) will now give you an typescript error and better autocomplete. If you relied on firing/listening custom events via the map, this still works via the escape hatches `map.fire("something" as any)` -> `map.on("something" as any, ...)` ([#8072](maplibre/maplibre-gl-js#8072)) (by [@CommanderStorm](https://github.com/CommanderStorm)) - Let a `StyleImageInterface` give a `{renderWithWebGL}` callback as its `data`, an escape hatch for plugin developers and advanced users that renders a style image on the GPU instead of moving its pixels through the CPU. Nothing new is possible that pixels could not already express, but an image that changes often, such as an animated icon, gets more performant ([#7954](maplibre/maplibre-gl-js#7954)) (by [@lucaswoj](https://github.com/lucaswoj)) - Use integer vertex attributes for packed line data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Use integer vertex attributes for packed circle, heatmap, symbol, and fill-extrusion data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640), [#8143](maplibre/maplibre-gl-js#8143)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Redesign benchmarks to use vitest bench capabilities and remove custom build for benchmarks code ([#982](maplibre/maplibre-gl-js#982)) (by [@johncarmack1984](https://github.com/johncarmack1984)) ##### 🐞 Bug fixes - Fix terrain pan/zoom gestures losing the grabbed terrain point: gestures are now solved against the elevation of the terrain under the gesture instead of the frozen center elevation, so terrain under the pointer/fingers no longer slips during moving-centroid pinches and drags ([#8067](maplibre/maplibre-gl-js#8067)) (by [@StrawberryJam22](https://github.com/StrawberryJam22)) - Fix `ImageSource`, `VideoSource` and `CanvasSource` leaking a GPU texture on every image update and on removal, and a resized texture losing its wrap and filter settings ([#8094](maplibre/maplibre-gl-js#8094)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix `map.queryRenderedFeatures()` sometimes causing "Out of bounds" error due to race condition while loading tile data ([#8064](maplibre/maplibre-gl-js#8064)) (by [@smvjohansenbouvet](https://github.com/smvjohansenbouvet)) - Fix zooming the globe with the scroll wheel or a two-finger pinch drifting away from the pointer while the globe is small on screen, instead of keeping the location under the pointer as it does when zoomed in ([#8095](maplibre/maplibre-gl-js#8095)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix projective rendering for non-parallelogram image source quads ([#7887](maplibre/maplibre-gl-js#7887)) (by [@i4innovationnet](https://github.com/i4innovationnet))
##### [v6.4.1](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#641) ##### 🐞 Bug fixes - Fix `DOM.sanitize` leaving dangerous attributes behind when multiple consecutive attributes are present. Iterating the live `NamedNodeMap` from `elem.attributes` while calling `removeAttribute` skipped the attribute directly after a removed one, so a second dangerous attribute (for example an `ontoggle` on a `<details open>` element) could survive sanitisation and later execute ([#8189](maplibre/maplibre-gl-js#8189)) (by [@0xKirisame](https://github.com/0xKirisame)) - Give custom layers the live globe transition in `CustomRenderMethodInput.defaultProjectionData.projectionTransition`, which was hardcoded to 1 for the whole globe/mercator transition, so a custom layer jumped straight to the fully bent globe while every other layer eased ([#8169](maplibre/maplibre-gl-js#8169)) (by [@mondsichtung](https://github.com/mondsichtung)) ##### [v6.4.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#640) ##### ✨ Features and improvements - Avoid a per-query `Array.sort()` in cross-tile symbol matching (`TileLayerIndex.findMatches`), claiming the lowest-index unclaimed candidate in a single pass instead; reduces main-thread symbol-placement cost on dense/coincident symbol layers ([#7797](maplibre/maplibre-gl-js#7797)) (by [@pholmstr](https://github.com/pholmstr)) - Use `texelFetch` for exact DEM and color-relief elevation stop lookups instead of normalized texture coordinate arithmetic ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Make default draggable markers keyboard-focusable and movable with the arrow keys (1 px per press, 10 px with Shift); custom marker elements stay application-owned ([#8020](maplibre/maplibre-gl-js#8020)) (by [@smmariquit](https://github.com/smmariquit)) ##### 🐞 Bug fixes - Fix a permanent frame rate degradation after switching styles: every sprite reload marked its images as updated forever, making every in-view tile re-check and re-upload them on every frame. Also stop leaking the images of a replaced sprite, which were never removed from the image manager ([#8052](maplibre/maplibre-gl-js#8052)) (by [@HarelM](https://github.com/HarelM)) - Prevent a rejected missing style image resolver from blocking successfully resolved images in the same batch ([#8146](maplibre/maplibre-gl-js#8146)) (by [@birkskyum](https://github.com/birkskyum)) - Explicitly request no browser color management when decoding raster-DEM tiles so their RGB-encoded elevation values are not changed (what would otherwise happen with `gfx.color_management.mode = 1` in Firefox) ([#8125](maplibre/maplibre-gl-js#8125)) (by [@tnikkel](https://github.com/tnikkel)) - Let an abort reach an image or raster tile load that is still awaiting its `transformRequest`, so `ImageSource.updateImage` no longer loses the image it was just handed and an aborted tile is no longer fetched ([#8071](maplibre/maplibre-gl-js#8071)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix raster tiles fading in again when they are reloaded, briefly flashing the map background, most visibly when switching projection ([#8106](maplibre/maplibre-gl-js#8106)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix globe panning inverting and stalling near and across the poles by rotating the globe with a versor, keeping the drag direction consistent at every latitude. Panning also eases off as the cursor approaches the edge of the globe and continues past it, instead of stopping. The bearing is preserved while panning, as before ([#5296](maplibre/maplibre-gl-js#5296)) (by [@jcolot](https://github.com/jcolot)) - Fix `fill-extrusion-rounded-corner-distance` producing spikes: corner arcs now land on the integer tile grid, and corners created by tile clipping are left sharp ([#8153](maplibre/maplibre-gl-js#8153)) (by [@HarelM](https://github.com/HarelM)) - Fix a gesture which was held still before being released still flinging the map ([#1303](maplibre/maplibre-gl-js#1303)) (by [@zdila](https://github.com/zdila)) ##### [v6.3.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#630) ##### ✨ Features and improvements - Make fired/listened map events typed. This means that `map.on("something", ...)` (and `once`, `listens`) will now give you an typescript error and better autocomplete. If you relied on firing/listening custom events via the map, this still works via the escape hatches `map.fire("something" as any)` -> `map.on("something" as any, ...)` ([#8072](maplibre/maplibre-gl-js#8072)) (by [@CommanderStorm](https://github.com/CommanderStorm)) - Let a `StyleImageInterface` give a `{renderWithWebGL}` callback as its `data`, an escape hatch for plugin developers and advanced users that renders a style image on the GPU instead of moving its pixels through the CPU. Nothing new is possible that pixels could not already express, but an image that changes often, such as an animated icon, gets more performant ([#7954](maplibre/maplibre-gl-js#7954)) (by [@lucaswoj](https://github.com/lucaswoj)) - Use integer vertex attributes for packed line data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Use integer vertex attributes for packed circle, heatmap, symbol, and fill-extrusion data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640), [#8143](maplibre/maplibre-gl-js#8143)) (by [@johncarmack1984](https://github.com/johncarmack1984)) - Redesign benchmarks to use vitest bench capabilities and remove custom build for benchmarks code ([#982](maplibre/maplibre-gl-js#982)) (by [@johncarmack1984](https://github.com/johncarmack1984)) ##### 🐞 Bug fixes - Fix terrain pan/zoom gestures losing the grabbed terrain point: gestures are now solved against the elevation of the terrain under the gesture instead of the frozen center elevation, so terrain under the pointer/fingers no longer slips during moving-centroid pinches and drags ([#8067](maplibre/maplibre-gl-js#8067)) (by [@StrawberryJam22](https://github.com/StrawberryJam22)) - Fix `ImageSource`, `VideoSource` and `CanvasSource` leaking a GPU texture on every image update and on removal, and a resized texture losing its wrap and filter settings ([#8094](maplibre/maplibre-gl-js#8094)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix `map.queryRenderedFeatures()` sometimes causing "Out of bounds" error due to race condition while loading tile data ([#8064](maplibre/maplibre-gl-js#8064)) (by [@smvjohansenbouvet](https://github.com/smvjohansenbouvet)) - Fix zooming the globe with the scroll wheel or a two-finger pinch drifting away from the pointer while the globe is small on screen, instead of keeping the location under the pointer as it does when zoomed in ([#8095](maplibre/maplibre-gl-js#8095)) (by [@mondsichtung](https://github.com/mondsichtung)) - Fix projective rendering for non-parallelogram image source quads ([#7887](maplibre/maplibre-gl-js#7887)) (by [@i4innovationnet](https://github.com/i4innovationnet)) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
fix #7857
Today the recommended way to animate an icon is a
StyleImageInterface#rendercallback that rewrites the icon's pixels every frame. MapLibre reprocesses those pixels on the CPU and re-uploads the whole icon to the GPU. That looks fine in a demo, but in a real app where many effects compete for frame time, a few animated icons cause real lag, especially on lower-powered phones.This PR lets a
StyleImageInterfacehand over a{renderWithWebGL}callback as itsdata, in place of an array of pixels. Such an image renders straight into its slot of the shared icon atlas on the GPU:An image asks to be rendered again by returning
truefromrender, the same signal the existingStyleImageInterfaceuses to commit new pixels.renderWithWebGLis the image's counterpart toCustomLayerInterface.render, and receives its GL context in the same state. It is also called before the first frame the image is used in, and again for each atlas holding a slot this image has never rendered into, so one change can mean several calls with different targets.This can be used to show icons driven by animated images, videos, an external canvas, or shaders. The new
animate-an-icon-on-the-gpuexample renders a triangle whose corner hues cycle once a second, computed in a vertex shader instead of a JS pixel loop.Performance
I'm working on a plugin that decodes a GIF, animated WebP, APNG or animated AVIF; uploads every frame to the GPU once packed into a grid; and advances a frame with a
copyTexSubImage2Dinto the icon's atlas slot. No per-frame CPU work, no upload, no atlas repack. I'll publish it separately.50 icons, 256px, 8 frames, one frame change per render, Apple M4 Max, headless Chrome:
StyleImageInterface#renderrewriting pixelsStyleImageWebGLData#renderWithWebGLAbout 28x here, and more on phones where the CPU work is relatively more expensive.
Launch Checklist
Include before/after visuals or gifs if this PR includes visual changes.CHANGELOG.mdunder the## mainsection.Assisted-By: Claude Opus 5