You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Aug 8, 2023. It is now read-only.
We want to be able to offload rendering onto a separate thread to free up the main thread on some platforms (most notably Android and Qt). The main thread should, as much as possible, be used only for user input and drawing so that a high frame-rate can be maintained.
A lot of work is already delegated to dedicate workers, but in order to offer a smooth experience on somewhat more lightweight hardware we need to eliminate some of the processing currently done on the main thread. Most notably:
Rendering (OpenGL)
DDS (evaluation of source functions)
Not all platforms need/should use a separate thread for rendering, so we want to enable a model where either is possible. Also, as an additional hurdle, on some platforms we want to be able to integrate with existing platform specific solutions such as the GLSurfaceView which come with a dedicated render thread that prohibits the use of a run loop as it blocks on a mutex.
In order to support this, we probably need a model where we utilise three threads:
Main thread for integration with the UI framework / exposes our public API
Supporting thread for orchestration of the render data preparation and render thread wake-up
The render thread
Eg. Something like (very high-level):
Preparation
Currently a couple of classes contain data that is needed on both the main thread (to offer our public, synchronous api) and on the would-be render thread. In preparation of supporting a render thread these classes need to be split up accordingly. In the case of Layers and Sources, we've decided to aim for a model where we have a parallel tree of Render items which have a reference to their counterpart's immutable implementation class.
Make Layer::Impl and Source::Impl subclasses immutable so they may be shared between style and render layers/sources [core] Immutable style Impls #8929
Promote add/remove/getImage and the style::Image map itself to Style, and make Style responsible for loading the sprite PNG and JSON, parceling out the individual images, and filling up the map.
GlyphAtlas will belong to RenderStyle, which won't be able to propagate onGlyphsLoaded/Error messages to a parent observer. Investigate whether this is necessary/how to eliminate. (Likely do the same for onSpriteLoaded/Error for consistency, even though it could be preserved.)
What's lastError used for? Should it belong to Style or RenderStyle exclusively, or does it need to be split?
TileParameters will have a RenderStyle reference rather than a Style reference. GeometryTile will need to iterate over RenderLayers rather than Layers, obtain an immutable Impl for each, and pass them to the worker rather than a copy of the Layer.
SpriteAtlas will need access to images (by immutable reference).
Replace in-place updates of renderSources and renderLayers (in setJSON and {add,remove}{Source,Layer}) with update-time updates based on diffing immutables.
Both Style and RenderStyle need isLoaded and dumpDebugLogs methods. In the places that call Style::isLoaded, add a call to RenderStyle::isLoaded(); likewise for dumpDebugLogs. ([core] Omnibus: Split RenderStyle from Style #9119)
update will move to RenderStyle. Replace direct access to sources, layers, images, transition options, classes, and light with immutable references passed via UpdateParameters. ([core] Omnibus: Split RenderStyle from Style #9119)
Probably merge Painter and RenderStyle into Renderer::Impl
Define interfaces to be implemented by SDKs to provide background rendering (or not). Methods:
setRenderer(std::unique_ptr<Renderer>) (might be null to "reset" renderer during shutdown)
update(std::unique_ptr<UpdateParameters>)
render(std::unique_ptr<RenderParameters>)
Latter two are expected to forward to Renderer either immediately (synchronously) or via sending message to rendering thread (asynchronously)
Also need some way for Renderer to trigger a re-render when needed (animation)
Also need some way to forward "Low Memory" notifications
Also need to deal with RenderStyleObserver/MapObserver notifications
Also need query APIs, w/ potential cross-thread synchronization
Next steps
Actually implement asynchronicity in the RenderFrontend for Android
Handle context loss on Android -- I (@jfirebaugh) vote for just recreating a totally fresh Renderer if we lose the context. The alternative is to keep a copy of all bucket data in main memory, so that we can recreate the GL buffers from it. This would ~2x the memory usage of maps, just to get somewhat faster reloads when the context comes back -- not worth it IMO.
TODO: can we refactor still rendering logic as an implementation of this new interface?
We want to be able to offload rendering onto a separate thread to free up the main thread on some platforms (most notably Android and Qt). The main thread should, as much as possible, be used only for user input and drawing so that a high frame-rate can be maintained.
A lot of work is already delegated to dedicate workers, but in order to offer a smooth experience on somewhat more lightweight hardware we need to eliminate some of the processing currently done on the main thread. Most notably:
Not all platforms need/should use a separate thread for rendering, so we want to enable a model where either is possible. Also, as an additional hurdle, on some platforms we want to be able to integrate with existing platform specific solutions such as the GLSurfaceView which come with a dedicated render thread that prohibits the use of a run loop as it blocks on a mutex.
In order to support this, we probably need a model where we utilise three threads:
Eg. Something like (very high-level):

Preparation
Currently a couple of classes contain data that is needed on both the main thread (to offer our public, synchronous api) and on the would-be render thread. In preparation of supporting a render thread these classes need to be split up accordingly. In the case of Layers and Sources, we've decided to aim for a model where we have a parallel tree of Render items which have a reference to their counterpart's immutable implementation class.
Related:
Currently underway
GlyphAtlascallbacksLayer::ImplandSource::Implsubclasses immutable so they may be shared between style and render layers/sources [core] Immutable style Impls #8929add/remove/getImageand thestyle::Imagemap itself toStyle, and makeStyleresponsible for loading the sprite PNG and JSON, parceling out the individual images, and filling up the map.GlyphAtlaswill belong toRenderStyle, which won't be able to propagateonGlyphsLoaded/Errormessages to a parent observer. Investigate whether this is necessary/how to eliminate. (Likely do the same foronSpriteLoaded/Errorfor consistency, even though it could be preserved.)lastErrorused for? Should it belong toStyleorRenderStyleexclusively, or does it need to be split?TileParameterswill have aRenderStylereference rather than aStylereference.GeometryTilewill need to iterate overRenderLayersrather thanLayers, obtain an immutableImplfor each, and pass them to the worker rather than a copy of theLayer.SpriteAtlaswill need access to images (by immutable reference).renderSourcesandrenderLayers(insetJSONand{add,remove}{Source,Layer}) withupdate-time updates based on diffing immutables.updateBatchmechanism with immutable layer diffing ([core] Omnibus: Split RenderStyle from Style #9119)StyleandRenderStyleneedisLoadedanddumpDebugLogsmethods. In the places that callStyle::isLoaded, add a call toRenderStyle::isLoaded(); likewise fordumpDebugLogs. ([core] Omnibus: Split RenderStyle from Style #9119)GlyphAtlaswill need access to a glyph URL (by immutable reference). ([core] Omnibus: Split RenderStyle from Style #9119)updatewill move toRenderStyle. Replace direct access to sources, layers, images, transition options, classes, and light with immutable references passed viaUpdateParameters. ([core] Omnibus: Split RenderStyle from Style #9119)FileSourceusing actors (Convert DefaultFileSource and dependents to actor model #7678), or otherwise makeDefaultFileSourcethreadsafeAnnotationManagerto use runtime styling APIs (Reimplement MGLAnnotation atop MGLSource and MGLStyleLayer #6181), or otherwise make it threadsafe Mutex guard annotation manager for cross thread usage #9220GeoJSONSource's use of GeoJSONVT and Supercluster is threadsafeRenderer/Renderer::Implpair:std::unique_ptr<Renderer>provided by coreRenderer::Implto hide details from public headerRenderermethods:update(const UpdateParameters&)(UpdateParametersforward declared)render(const RenderParameters&)(RenderParametersforward declared)PainterandRenderStyleintoRenderer::ImplsetRenderer(std::unique_ptr<Renderer>)(might be null to "reset" renderer during shutdown)update(std::unique_ptr<UpdateParameters>)render(std::unique_ptr<RenderParameters>)Renderereither immediately (synchronously) or via sending message to rendering thread (asynchronously)Rendererto trigger a re-render when needed (animation)RenderStyleObserver/MapObservernotificationsNext steps
RenderFrontendfor AndroidRendererif we lose the context. The alternative is to keep a copy of all bucket data in main memory, so that we can recreate the GL buffers from it. This would ~2x the memory usage of maps, just to get somewhat faster reloads when the context comes back -- not worth it IMO.cc @mapbox/gl