-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Eliminate separate "MapContext" thread #2909
Conversation
I'm not sure the "arbitrary thread" or thread division described above is feasible. Firstly, the styles API will expose virtually all state to the main thread in ways that will make it very difficult to safely or productively share that state with other threads. Once a full read-write API for the style is exposed, every method must essentially be an Therefore I'd like to reduce the scope of this issue to something simpler: move style state and rendering to the main thread. Though simpler, this will still be a somewhat difficult transition from our current model. Because the main thread already has a native event loop, we cannot continue to
Option 1 The first option is to replace our use of a libuv event loop for the map state and rendering with the use of the native event loop of the embedding application. This approach means we would need to replace the following usages of libuv:
The use of uv_async looks feasible to replace:
I don't know how we'd replace Note that this approach does not necessarily mean we'd remove libuv entirely. We'd still continue to use a libuv event loop on the FileSource and Worker threads. Option 2 The "embed a libuv event loop" approach. The essence of this approach is that on each platform, you insert into the native event loop something that calls Since even with Option 2, we still have to implement most of the "wakeup" machinery we would need for Option 1, I lean toward Option 1. Thoughts @kkaefer @tmpsantos @brunoabinader? |
I like the option 1 and we already have that working for the Qt port. The Qt port doesn't depend on libuv at all (not entirely true, because we still use Pretty much what we need from a platform port is to implement Timer, AsyncTask and the RunLoop which for Qt was just a few lines of code and pretty easy to implement. IMO that change will make Mapbox GL more "embedded friendly" as we can reduce the binary footprint on systems that have something else already doing what libuv does. As for the GLFW port, IMO we can use libuv as its main loop on the main thread. Finally, I pushed a branch for your appreciation. It is not fully functional, not all the tests are passing ATM, but if we reach a consensus here I can move forward with it. https://github.com/mapbox/mapbox-gl-native/tree/tmpsantos-libuv_abstracted |
How would you do that? AFAICT, with GLFW you have to implement the event loop with |
I wouldn't call the blocking Bad side is the app will never sleep completely anymore, but I think it will work. |
Ah, good call. I pushed a proof of concept of that and converted this to a PR. It also includes stuff from |
Next step here is to peel off the parts from The model for |
b1a3215
to
8e7c599
Compare
Scratch that, it doesn't actually work, it'd require conflicting |
...except that that doesn't work because the |
Yeah, we are sort of having something like that already with |
I'm working on this. The changes needed for adding a new platform For rendering in the main thread, I'm also experimenting an alternative and less intrusive approach that would be a main loop integration (glib-style), but that would also depend on #3139. |
I managed to create a main loop integration mechanism that is extremely simple and worked well. @jfirebaugh please have a look on the 5 last patches of this branch here (the rest of the delta with master is basically #3139): https://github.com/tmpsantos/mapbox-gl-native/commits/render-main |
7948ce8
to
652a6ce
Compare
So this is the "Option 2" approach. How will |
652a6ce
to
4383fdd
Compare
They won't work. I documented it here: 9a11287 We don't use timers on the main thread anyway and the commit adds an assertion for that. But this is not final, I'm still experimenting with this approach. |
👍 |
4383fdd
to
4be128a
Compare
Made progress today on #4682. On that branch, all tests are passing on Linux. |
Thanks @tmpsantos. I integrated your changes into this PR. However, when running the Android test app, I only see a white screen, no map. |
@@ -88,6 +89,7 @@ class NativeMapView : public mbgl::View, private mbgl::util::noncopyable { | |||
JNIEnv *renderEnv = nullptr; | |||
|
|||
// Ensure these are initialised last | |||
std::unique_ptr<mbgl::util::RunLoop> loop; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tmpsantos Is it ok to create multiple RunLoops
representing the main thread, if multiple map views are created? Or does this need to be a singleton somewhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, it should be a singleton. I will fix that.
This turned out to be caused by #3883. |
It works on Nexus 5x just fine but throws an exception on Nexus 5 when trying to swap buffers. I don't think it has to do with the |
Do not create a thread for the MapContext anymore.
This is needed for plugging into an existing Looper because we won't call ::run() on the main thread (this is called on the Java side of the application automatically by Android). But we still need to wake up and process the events. For that we create a pipe and write to the pipe to wake up the main loop and we process events on the callback of the fd handler. Fixes #4682
Not needed as everything is now running on the same thread.
Not needed, same thread already.
Android rendering issues were due to missing |
Apparently some versions of Android, notable 4.4.4 running on my Nexus 5, creates a GLContext on the Android UIThread which is the one we are now using for rendering after #2909. If the context is not restored, nothing gets rendered (or sometimes partially, or artifacts) because Android will try to do GL stuff on the context used by Mapbox GL and mess things up.
Apparently some versions of Android, notable 4.4.4 running on my Nexus 5, creates a GLContext on the Android UIThread which is the one we are now using for rendering after #2909. If the context is not restored, nothing gets rendered (or sometimes partially, or artifacts) because Android will try to do GL stuff on the context used by Mapbox GL and mess things up.
We're currently rendering on the MapContext thread. However, when triggering a render call from the main thread, we're also blocking the main thread, so there's no advantage from parallelization here, but we have the overhead of managing the OpenGL context on the MapContext thread. Instead, we should split up Mapbox GL into two parts: the MapContext thread manages the state of the objects, coordinates tile loading and style updates, the Painter thread (which can run either on the MapContext thread, the main thread, or an entirely different thread) that just takes the state the MapContext thread produces, and renders a frame.
TODO list:
ThreadContext
for obtaining FileSource / GLObjectStorembgl::gl::InitializeExtensions
(fixes "Not using Vertex Array Objects" warning)View
implementationRunLoop
/Timer
/AsyncTask
implementation