diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index 1d25442a5..845d10ea6 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -124,12 +124,16 @@ void NativeMapView::deactivate() { } } -void NativeMapView::invalidate(std::function render) { +void NativeMapView::invalidate() { + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate"); + + // TODO: inform the main thread that it must call map->renderSync(); +} + +void NativeMapView::swap() { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate"); if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) { - render(); - if (!eglSwapBuffers(display, surface)) { mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d", eglGetError()); diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp index fae239e07..8c632fb1f 100644 --- a/include/mbgl/android/native_map_view.hpp +++ b/include/mbgl/android/native_map_view.hpp @@ -23,7 +23,8 @@ public: void activate() override; void deactivate() override; void notify() override; - void invalidate(std::function render) override; + void invalidate() override; + void swap() override; void notifyMapChange(mbgl::MapChange change, Duration delay = Duration::zero()) override; diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp index 41d57bdc9..84ccc8967 100644 --- a/include/mbgl/map/view.hpp +++ b/include/mbgl/map/view.hpp @@ -44,9 +44,11 @@ public: virtual void resize(uint16_t width, uint16_t height, float pixelRatio); // Called from the render thread. The implementation must trigger a rerender. - // (i.e. either the passed render() function for rendering immediately on the map thread, - // or map->renderSync() from the main thread must be called as a result of this) - virtual void invalidate(std::function render) = 0; + // (map->renderSync() from the main thread must be called as a result of this) + virtual void invalidate() = 0; + + // Called from the render thread after the render is complete. + virtual void swap() = 0; // Reads the pixel data from the current framebuffer. If your View implementation // doesn't support reading from the framebuffer, return a null pointer. diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp index 9a7e29fe2..6e03953cb 100644 --- a/include/mbgl/platform/default/glfw_view.hpp +++ b/include/mbgl/platform/default/glfw_view.hpp @@ -8,6 +8,8 @@ #endif #include +#include + class GLFWView : public mbgl::View { public: GLFWView(bool fullscreen = false); @@ -17,7 +19,8 @@ public: void activate() override; void deactivate() override; void notify() override; - void invalidate(std::function render) override; + void invalidate() override; + void swap() override; static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods); static void onScroll(GLFWwindow *window, double xoffset, double yoffset); @@ -62,6 +65,7 @@ private: std::function changeStyleCallback; GLFWwindow *window = nullptr; + std::atomic_flag clean = ATOMIC_FLAG_INIT; }; #endif diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp index 66f13cf5b..61e60ebaf 100644 --- a/include/mbgl/platform/default/headless_view.hpp +++ b/include/mbgl/platform/default/headless_view.hpp @@ -33,7 +33,8 @@ public: void deactivate() override; void notify() override; void resize(uint16_t width, uint16_t height, float pixelRatio) override; - void invalidate(std::function render) override; + void invalidate() override; + void swap() override; std::unique_ptr readStillImage() override; private: diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index 61d553ae3..756b84269 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -282,6 +282,10 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) { void GLFWView::run() { while (!glfwWindowShouldClose(window)) { glfwWaitEvents(); + const bool dirty = !clean.test_and_set(); + if (dirty) { + map->renderSync(); + } } } @@ -297,8 +301,12 @@ void GLFWView::notify() { glfwPostEmptyEvent(); } -void GLFWView::invalidate(std::function render) { - render(); +void GLFWView::invalidate() { + clean.clear(); + glfwPostEmptyEvent(); +} + +void GLFWView::swap() { glfwSwapBuffers(window); fps(); } diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index 0f00c7563..f4411bb2e 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -280,8 +280,12 @@ void HeadlessView::deactivate() { #endif } -void HeadlessView::invalidate(std::function render) { - render(); +void HeadlessView::invalidate() { + // no-op +} + +void HeadlessView::swap() { + // no-op } } diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 50f5a1f86..e70f6c7c6 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -2755,13 +2755,18 @@ class MBGLView : public mbgl::View [EAGLContext setCurrentContext:nil]; } - void invalidate(std::function) override + void invalidate() override { [nativeView performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:NO]; } + void swap() override + { + // no-op + } + private: __weak MGLMapView *nativeView = nullptr; }; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 1b8cd2b0e..5360e294c 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -43,7 +43,7 @@ void Map::renderStill(StillImageCallback callback) { } void Map::renderSync() { - context->invokeSync(&MapContext::render); + context->invokeSync(&MapContext::renderSync); } void Map::update(Update update_) { diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index bc4dc8c72..3ac05c8e4 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -269,7 +269,11 @@ void MapContext::update() { } } - view.invalidate([this] { render(); }); + if (mayRender) { + render(); + } else { + view.invalidate(); + } } updated = static_cast(Update::Nothing); @@ -302,9 +306,16 @@ void MapContext::renderStill(StillImageCallback fn) { } callback = fn; + mayRender = true; triggerUpdate(Update::RenderStill); } +void MapContext::renderSync() { + mayRender = true; + render(); + mayRender = false; +} + void MapContext::render() { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); @@ -326,11 +337,15 @@ void MapContext::render() { painter->setDebug(data.getDebug()); painter->render(*style, transformState, data.getAnimationTime()); + mayRender = false; + if (data.mode == MapMode::Still) { callback(nullptr, view.readStillImage()); callback = nullptr; } + view.swap(); + // Schedule another rerender when we definitely need a next frame. if (data.transform.needsTransition() || style->hasTransitions()) { triggerUpdate(); @@ -351,7 +366,7 @@ void MapContext::setSourceTileCacheSize(size_t size) { for (const auto &source : style->sources) { source->setCacheSize(sourceCacheSize); } - view.invalidate([this] { render(); }); + view.invalidate(); } } @@ -361,7 +376,7 @@ void MapContext::onLowMemory() { for (const auto &source : style->sources) { source->onLowMemory(); } - view.invalidate([this] { render(); }); + view.invalidate(); } void MapContext::onTileDataChanged() { diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index 6185db4a5..89b1b28a9 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -34,12 +34,12 @@ public: ~MapContext(); void pause(); - void render(); void resize(uint16_t width, uint16_t height, float ratio); using StillImageCallback = std::function)>; void renderStill(StillImageCallback callback); + void renderSync(); void triggerUpdate(Update = Update::Nothing); @@ -62,6 +62,7 @@ public: private: void updateTiles(); + void render(); // Style-related updates. void cascadeClasses(); @@ -88,6 +89,7 @@ private: std::string styleURL; std::string styleJSON; + bool mayRender = false; StillImageCallback callback; size_t sourceCacheSize; TransformState transformState; diff --git a/test/style/mock_view.hpp b/test/fixtures/mock_view.hpp similarity index 67% rename from test/style/mock_view.hpp rename to test/fixtures/mock_view.hpp index 865cd2da5..dcbe3fef8 100644 --- a/test/style/mock_view.hpp +++ b/test/fixtures/mock_view.hpp @@ -1,5 +1,5 @@ -#ifndef TEST_RESOURCES_MOCK_VIEW -#define TEST_RESOURCES_MOCK_VIEW +#ifndef TEST_FIXTURES_MOCK_VIEW +#define TEST_FIXTURES_MOCK_VIEW #include @@ -13,7 +13,8 @@ public: void activate() override {}; void deactivate() override {}; void notify() override {}; - void invalidate(std::function) override {}; + void invalidate() override {} + void swap() override {} }; } diff --git a/test/miscellaneous/transform.cpp b/test/miscellaneous/transform.cpp index 6025cbce4..3c25c2705 100644 --- a/test/miscellaneous/transform.cpp +++ b/test/miscellaneous/transform.cpp @@ -1,4 +1,5 @@ #include "../fixtures/util.hpp" +#include "../fixtures/mock_view.hpp" #include #include @@ -11,18 +12,6 @@ using namespace mbgl; -class MockView : public View { -public: - void activate() override { - } - void deactivate() override { - } - void notify() override { - } - void invalidate(std::function) override { - } -}; - TEST(Transform, InvalidScale) { MockView view; Transform transform(view); diff --git a/test/style/resource_loading.cpp b/test/style/resource_loading.cpp index 6c5eb51bd..82784f673 100644 --- a/test/style/resource_loading.cpp +++ b/test/style/resource_loading.cpp @@ -1,7 +1,7 @@ #include "../fixtures/fixture_log_observer.hpp" #include "../fixtures/util.hpp" +#include "../fixtures/mock_view.hpp" #include "mock_file_source.hpp" -#include "mock_view.hpp" #include #include