Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Asynchronous rendering #8820

@ivovandongen

Description

@ivovandongen

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):
staged_rendering 1

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.

data_structures 2

Related:

Currently underway

  • Define Renderer/Renderer::Impl pair:
    • SDK code implementing renderer thread will own std::unique_ptr<Renderer> provided by core
    • Renderer::Impl to hide details from public header
    • Public Renderer methods:
      • update(const UpdateParameters&) (UpdateParameters forward declared)
      • render(const RenderParameters&) (RenderParameters forward declared)
    • 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?

cc @mapbox/gl

Metadata

Metadata

Labels

CoreThe cross-platform C++ core, aka mbgl

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions