Skip to content

DRAFT: (FEAT) implement progressive tile loading to reduce white-screen time Open#4253

Draft
grab-liyanjin wants to merge 2 commits into
maplibre:mainfrom
grab-liyanjin:feat/progressive-tile-loading-upstream
Draft

DRAFT: (FEAT) implement progressive tile loading to reduce white-screen time Open#4253
grab-liyanjin wants to merge 2 commits into
maplibre:mainfrom
grab-liyanjin:feat/progressive-tile-loading-upstream

Conversation

@grab-liyanjin

Copy link
Copy Markdown
Collaborator

Summary

This PR implements two-phase (progressive) tile loading to reduce white-screen
time when panning or zooming to unloaded areas.

Previously, a tile remained invisible (renderable=false) until all layout
work completed — including network fetches for glyphs/images required by symbol
layers (text labels, icons). On slow networks or cold cache this could leave
large blank areas for several seconds, even though fill/line/circle geometry was
already parsed and ready to draw.

After this change, geometry buckets are emitted to the render thread
immediately after parse() completes. Symbol buckets follow in a second
onLayout call once all glyph/image dependencies are resolved.

Demo

before after
normal_loading.mp4
progressive_loading.mp4

How it works

GeometryTileWorker

  • Adds a comingFromParse boolean flag (reset after each finalizeLayout()
    call).
  • parse() sets comingFromParse = true before calling finalizeLayout().
  • finalizeLayout() gains an early-exit path: when dependencies are still
    pending and firstLoad && comingFromParse && !renderData.empty(), it
    copies the already-populated renderData (geometry-only buckets, cheap
    shared_ptr copy) and invokes GeometryTile::onLayout with
    isPartialResult=true. This partial result is skipped when renderData is
    empty (symbol-only tilesets) to avoid marking a tile as renderable with
    nothing to show.

GeometryTile

  • onLayout() gains a bool isPartialResult = false parameter.
  • For partial results: sets loaded = true and renderable = true so the tile
    appears on screen immediately, but does not clear pending or fire the
    EndParse event — the tile stays pending until the full symbol result arrives.
  • The full result (isPartialResult=false) replaces layoutResult normally and
    clears pending as before.

Behaviour unchanged for

  • Non-first loads (reload / style change) — firstLoad is false, progressive
    path is skipped entirely.
  • Tiles with no pending symbol dependencies — hasPendingDependencies() returns
    false, existing code path taken unchanged.
  • Symbol-only tiles — renderData.empty() guard prevents an empty partial
    result from being sent.
  • Still-mode rendering — correlation ID handling is unchanged; pending is only
    cleared on the full result.

Testing

Verified on Android (arm64) with a style containing road labels and POI icons:
geometry (roads, water, buildings) appears within the first render frame after
tile data arrives; text and icons fade in once glyphs are downloaded.

… time

Emit geometry (fill/line/circle) buckets immediately after parse() completes,
before glyph/image dependencies for symbol layers are resolved. This lets tiles
appear on screen with road/polygon/water data right away; text and icons follow
in a second full onLayout call once all deps are ready.

- GeometryTileWorker: add comingFromParse flag; finalizeLayout() emits a partial
  LayoutResult (isPartialResult=true) on first load when deps are still pending
  and renderData is non-empty (geometry-only buckets available)
- GeometryTile: onLayout() accepts isPartialResult — suppresses pending=false
  and EndParse event for partial results so the tile stays pending until symbols
  arrive

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added the core Changes that affect the C++ core of MapLibre Native label Apr 23, 2026
@github-actions

github-actions Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Bloaty Results 🐋

Compared to main

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.9% +1.59Mi  +1.3%  +442Ki    TOTAL

Full report: https://maplibre-native.s3.eu-central-1.amazonaws.com/bloaty-results/pr-4253-compared-to-main.txt

Compared to d387090 (legacy)

    FILE SIZE        VM SIZE    
 --------------  -------------- 
   +51% +59.1Mi  +467% +27.9Mi    TOTAL

Full report: https://maplibre-native.s3.eu-central-1.amazonaws.com/bloaty-results/pr-4253-compared-to-legacy.txt

@github-actions

github-actions Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Bloaty Results (iOS) 🐋

Compared to main

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0% +2.68Ki  [ = ]       0    TOTAL

Full report: https://maplibre-native.s3.eu-central-1.amazonaws.com/bloaty-results-ios/pr-4253-compared-to-main.txt

@louwers louwers marked this pull request as draft April 23, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Changes that affect the C++ core of MapLibre Native

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant