Skip to content

raster-tile-source: add per-tile fade on new tileset loaded to avoid flicker for raster timeseries #13535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

jo-chemla
Copy link

@jo-chemla jo-chemla commented Aug 13, 2025

Launch Checklist

  • Make sure the PR title is descriptive and preferably reflects the change from the user's perspective.
  • Add additional detail and context in the PR description (with screenshots/videos if there are visual changes).
  • Manually test the debug page.
  • Write tests for all new functionality and make sure the CI checks pass.
  • Document any changes to public APIs.
  • Post benchmark scores if the change could affect performance.
  • Tag @mapbox/map-design-team @mapbox/static-apis if this PR includes style spec API or visual changes.
  • Tag @mapbox/gl-native if this PR includes shader changes or needs a native port.
  • Tag @mapbox/gl-native if this PR disables any test because it also needs to be disabled on their side.
  • Create a ticket for gl-native to groom in the MAPSNAT JIRA queue if this PR includes shader changes or features not present in the native side or if it disables a test that's not disabled there.

This is a draft PR trying to fix issue when switching raster-tile-source urls from eg one date to another, to avoid flicker as described in issue thread #13044 (comment)
The above thread also includes screencast of the issue occuring I also include right below.

2025-08-12.-.15-24-06.mp4

Made a new debug example to test the implementatino, available after npm i && npm test && npm run dev here http://localhost:9966/debug/test-rastertile-fade.html

This PR includes shader changes, so @mapbox/gl-native

The idea is to keep the previous tile texture in memory and shader uniform for a given fade duration amount, before displaying the new tile that has been loaded. I've hacked my way and think I've almost nailed it and wanted to get some feedback before wrapping it up. Inspiration is taken from how fading occurs between tile and parent lower-res tile during loading - but code could have been closer after a second pass.

The PR was tested on windows, hence had to add stylistic eslint rule '@stylistic/linebreak-style': ["error", "windows"],

EDIT: just signed the CLA to proceed, wanted to make sure I will also be in position to contribute the same code I've designed to the maplibre repo. Any insights about that?

@CLAassistant
Copy link

CLAassistant commented Aug 13, 2025

CLA assistant check
All committers have signed the CLA.

Reworked the implementation to look more like the existing parent/child fading procedure, so passing `u_per_tile_fade_mix / perTileFadeMix` as a uniform computing and storing per-tile the `perTileFadeEndTime` instead of start
replace perTileFadeEndTime by perTileFadeStartTime to test
Some wip to try debugging why still tiles flicker

Showing tile bounds, it appears tiles are actually marked for expiration
@jo-chemla
Copy link
Author

jo-chemla commented Aug 13, 2025

After further testing, it looks like the source cache aggressively discards all loaded tiles when source.setTiles([new_tms_url]) gets called - also visible with tile boundaries drawn as debug. The workaround is probably to temporarily put the loaded tiles into the retain arraylist, then retrieve previousTile from sourceCache ~the same way parentTile gets retrieved, and retain the logic to discard or remove from sourceCache after the fadeDuration has elapsed.

This also means tile fading logic could be kept as-is with no modification, at least if tiles get rendered in the order of the cache (or any other deterministic ordering), with older tiles being still drawn as background via fragShader while still in cache, at least before fade finishes, while newer ones gets loaded.

Edit: After further digging, I realized rasterTileSource.setUrl calls reload which calls this.load(() => this.map.style.clearSource(fqid)); so probably the sourceCache gets pruned and there is no way to retain the previousTiles unless they are kept somewhere else - or I remove this callback from the load call for the time being.

@jo-chemla
Copy link
Author

One final feedback after digging through the repo commits and prs: it seems that raster-tile-source smooth fading between (not only parent but also) older/sibling tiles was present in earlier mapbox versions. This smooth per-tile transition from old to new raster tile source on setTiles seem to have been lost at some point probably because of aggressive source-cache pruning - not entirely sure when this was the case. If so, this appear to be a regression introduced rather than a new feature implementation - and I'm not exactly sure how best to handle this.

Just pushed some final experiments as to how to retrieve oldTile, keep them in retain array, and set a duration before discarding. The idea was to keep them in retain array of the cache for the fadingDuration before clearing each oldTile. Not sure how to differentiate between old and new tile though with same xyz - maybe prefixing tile ids with fqid could be helpful. There might actually be no need at all to do the oldTexture to newTexture fading inside the raster fragment shader, as older tiles would probably be drawn first/below before being overwritten by the new tiles fading-in.

Would love some pointer from the mapbox team to fix this regression!

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