diff options
76 files changed, 911 insertions, 1021 deletions
diff --git a/benchmark/api/query.benchmark.cpp b/benchmark/api/query.benchmark.cpp index 5fa6a84db7..5a95cd7666 100644 --- a/benchmark/api/query.benchmark.cpp +++ b/benchmark/api/query.benchmark.cpp @@ -3,7 +3,7 @@ #include <mbgl/benchmark/util.hpp> #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/sprite/sprite_image.hpp> #include <mbgl/storage/default_file_source.hpp> @@ -29,17 +29,15 @@ public: auto image = std::make_unique<SpriteImage>(std::move(decoded), 1.0); map.addImage("test-icon", std::move(image)); - view.resize(1000, 1000); - - mbgl::benchmark::render(map); + mbgl::benchmark::render(map, view); } util::RunLoop loop; HeadlessBackend backend; - HeadlessView view; + OffscreenView view{ backend.getContext(), {{ 1000, 1000 }} }; DefaultFileSource fileSource{ "benchmark/fixtures/api/cache.db", "." }; ThreadPool threadPool{ 4 }; - Map map{ backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still }; + Map map{ backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still }; ScreenBox box{{ 0, 0 }, { 1000, 1000 }}; }; diff --git a/benchmark/src/mbgl/benchmark/util.cpp b/benchmark/src/mbgl/benchmark/util.cpp index a8f71f1206..39c5f86883 100644 --- a/benchmark/src/mbgl/benchmark/util.cpp +++ b/benchmark/src/mbgl/benchmark/util.cpp @@ -1,16 +1,18 @@ #include <mbgl/benchmark/util.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/map/map.hpp> +#include <mbgl/map/view.hpp> #include <mbgl/util/image.hpp> #include <mbgl/util/run_loop.hpp> namespace mbgl { namespace benchmark { -void render(Map& map) { +void render(Map& map, OffscreenView& view) { PremultipliedImage result; - map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) { - result = std::move(image); + map.renderStill(view, [&](std::exception_ptr) { + result = view.readStillImage(); }); while (!result.size()) { diff --git a/benchmark/src/mbgl/benchmark/util.hpp b/benchmark/src/mbgl/benchmark/util.hpp index 759015e0e3..73acfb69d5 100644 --- a/benchmark/src/mbgl/benchmark/util.hpp +++ b/benchmark/src/mbgl/benchmark/util.hpp @@ -3,10 +3,11 @@ namespace mbgl { class Map; +class OffscreenView; namespace benchmark { -void render(Map&); +void render(Map&, OffscreenView&); } // namespace benchmark } // namespace mbgl diff --git a/bin/glfw.cpp b/bin/glfw.cpp index 44713d7532..b51846b4e8 100644 --- a/bin/glfw.cpp +++ b/bin/glfw.cpp @@ -13,6 +13,7 @@ #include <sstream> #include <cstdlib> #include <cstdio> +#include <array> namespace { @@ -119,7 +120,9 @@ int main(int argc, char *argv[]) { mbgl::ThreadPool threadPool(4); - mbgl::Map map(backend, backend, backend.getPixelRatio(), fileSource, threadPool); + mbgl::Map map(backend, view->getSize(), view->getPixelRatio(), fileSource, threadPool); + + backend.setMap(&map); // Load settings mbgl::Settings_JSON settings; diff --git a/bin/render.cpp b/bin/render.cpp index f9574d0523..ad29d91993 100644 --- a/bin/render.cpp +++ b/bin/render.cpp @@ -4,7 +4,7 @@ #include <mbgl/util/run_loop.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/storage/default_file_source.hpp> @@ -29,7 +29,6 @@ int main(int argc, char *argv[]) { int width = 512; int height = 512; - double pixelRatio = 1.0; static std::string output = "out.png"; std::string cache_file = "cache.sqlite"; std::string asset_root = "."; @@ -85,9 +84,9 @@ int main(int argc, char *argv[]) { } HeadlessBackend backend; - HeadlessView view(pixelRatio, width, height); + OffscreenView view(backend.getContext(), {{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }}); ThreadPool threadPool(4); - Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); + Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); map.setStyleJSON(style); map.setClasses(classes); @@ -100,7 +99,7 @@ int main(int argc, char *argv[]) { map.setDebug(debug ? mbgl::MapDebugOptions::TileBorders | mbgl::MapDebugOptions::ParseStatus : mbgl::MapDebugOptions::NoDebug); } - map.renderStill([&](std::exception_ptr error, PremultipliedImage&& image) { + map.renderStill(view, [&](std::exception_ptr error) { try { if (error) { std::rethrow_exception(error); @@ -110,7 +109,7 @@ int main(int argc, char *argv[]) { exit(1); } - util::write_file(output, encodePNG(image)); + util::write_file(output, encodePNG(view.readStillImage())); loop.stop(); }); diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 255d40fa50..4d5cddbc49 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -62,10 +62,12 @@ set(MBGL_CORE_FILES src/mbgl/gl/debugging.hpp src/mbgl/gl/extension.cpp src/mbgl/gl/extension.hpp + src/mbgl/gl/framebuffer.hpp src/mbgl/gl/gl.cpp src/mbgl/gl/index_buffer.hpp src/mbgl/gl/object.cpp src/mbgl/gl/object.hpp + src/mbgl/gl/renderbuffer.hpp src/mbgl/gl/shader.cpp src/mbgl/gl/shader.hpp src/mbgl/gl/state.hpp @@ -97,7 +99,6 @@ set(MBGL_CORE_FILES include/mbgl/map/camera.hpp include/mbgl/map/map.hpp include/mbgl/map/mode.hpp - include/mbgl/map/update.hpp include/mbgl/map/view.hpp src/mbgl/map/backend.cpp src/mbgl/map/change.hpp @@ -106,7 +107,7 @@ set(MBGL_CORE_FILES src/mbgl/map/transform.hpp src/mbgl/map/transform_state.cpp src/mbgl/map/transform_state.hpp - src/mbgl/map/view.cpp + src/mbgl/map/update.hpp src/mbgl/map/zoom_history.hpp # math @@ -135,8 +136,9 @@ set(MBGL_CORE_FILES include/mbgl/platform/default/glfw_view.hpp include/mbgl/platform/default/headless_backend.hpp include/mbgl/platform/default/headless_display.hpp - include/mbgl/platform/default/headless_view.hpp + include/mbgl/platform/default/offscreen_view.hpp include/mbgl/platform/default/settings_json.hpp + include/mbgl/platform/default/thread_pool.hpp # renderer src/mbgl/renderer/bucket.hpp diff --git a/cmake/core.cmake b/cmake/core.cmake index ef98c4eccb..bc5e7eb718 100644 --- a/cmake/core.cmake +++ b/cmake/core.cmake @@ -36,7 +36,7 @@ target_include_directories(mbgl-core target_add_mason_package(mbgl-core PUBLIC geometry) target_add_mason_package(mbgl-core PUBLIC variant) -target_add_mason_package(mbgl-core PRIVATE unique_resource) +target_add_mason_package(mbgl-core PUBLIC unique_resource) target_add_mason_package(mbgl-core PRIVATE rapidjson) target_add_mason_package(mbgl-core PRIVATE boost) target_add_mason_package(mbgl-core PRIVATE geojson) diff --git a/include/mbgl/map/backend.hpp b/include/mbgl/map/backend.hpp index e4a5634b88..c11d094906 100644 --- a/include/mbgl/map/backend.hpp +++ b/include/mbgl/map/backend.hpp @@ -2,13 +2,21 @@ #include <mbgl/map/change.hpp> +#include <memory> + namespace mbgl { -class Map; +namespace gl { +class Context; +} // namespace gl class Backend { public: - virtual ~Backend() = default; + Backend(); + virtual ~Backend(); + + // Returns the backend's context which manages OpenGL state. + gl::Context& getContext(); // Called when the backend's GL context needs to be made active or inactive. These are called, // as a matched pair, in four situations: @@ -29,6 +37,9 @@ public: // Notifies a watcher of map x/y/scale/rotation changes. virtual void notifyMapChange(MapChange change); + +private: + const std::unique_ptr<gl::Context> context; }; } // namespace mbgl diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 4f6207fc6f..b1c840e68d 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -2,8 +2,6 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/chrono.hpp> -#include <mbgl/util/image.hpp> -#include <mbgl/map/update.hpp> #include <mbgl/map/mode.hpp> #include <mbgl/util/geo.hpp> #include <mbgl/util/feature.hpp> @@ -35,7 +33,7 @@ class Layer; class Map : private util::noncopyable { public: explicit Map(Backend&, - View&, + std::array<uint16_t, 2> size, float pixelRatio, FileSource&, Scheduler&, @@ -47,14 +45,14 @@ public: // Register a callback that will get called (on the render thread) when all resources have // been loaded and a complete render occurs. - using StillImageCallback = std::function<void (std::exception_ptr, PremultipliedImage&&)>; - void renderStill(StillImageCallback callback); + using StillImageCallback = std::function<void (std::exception_ptr)>; + void renderStill(View&, StillImageCallback callback); - // Main render function. - void render(); + // Triggers a repaint. + void triggerRepaint(); - // Notifies the Map that the state has changed and an update might be necessary. - void update(Update update); + // Main render function. + void render(View&); // Styling void addClass(const std::string&); @@ -138,6 +136,7 @@ public: ViewportMode getViewportMode() const; // Size + void setSize(const std::array<uint16_t, 2>&); uint16_t getWidth() const; uint16_t getHeight() const; diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp index 0dff4b3602..6517c6b220 100644 --- a/include/mbgl/map/view.hpp +++ b/include/mbgl/map/view.hpp @@ -1,8 +1,6 @@ #pragma once -#include <mbgl/util/image.hpp> - -#include <array> +#include <mbgl/util/noncopyable.hpp> namespace mbgl { @@ -12,29 +10,11 @@ class View : private util::noncopyable { public: virtual ~View() = default; - // Called when this View is associated with a Map object. - virtual void initialize(Map*); - // Called when this View is used for rendering. Implementations should ensure that a renderable - // object is bound and glClear/glDraw* calls can be done. + // object is bound and glClear/glDraw* calls can be done. They should also make sure that + // calling .bind() repeatedly is a no-op and that the appropriate gl::Context values are + // set to the current state. virtual void bind() = 0; - - // Called when the View signaled a dimension change. Must return the logical dimension - // of this map in pixels. - virtual std::array<uint16_t, 2> getSize() const = 0; - - // Called for every frame that is being rendered. Must return the absolute dimensions of - // the current framebuffer. Typically, this is the logical width scaled by the pixel ratio, - // but in case the view was moved to display with a different pixel ratio, it can also be - // different from that rule. - virtual std::array<uint16_t, 2> getFramebufferSize() const = 0; - - // Reads the pixel data from the current framebuffer. If your View implementation - // doesn't support reading from the framebuffer, return a null pointer. - virtual PremultipliedImage readStillImage(std::array<uint16_t, 2> size = {{ 0, 0 }}); - -protected: - mbgl::Map *map = nullptr; }; } // namespace mbgl diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp index a115d03d7f..e832cd70d1 100644 --- a/include/mbgl/platform/default/glfw_view.hpp +++ b/include/mbgl/platform/default/glfw_view.hpp @@ -19,6 +19,8 @@ public: float getPixelRatio() const; + void setMap(mbgl::Map*); + // Callback called when the user presses the key mapped to style change. // The expected action is to set a new style, different to the current one. void setChangeStyleCallback(std::function<void()> callback); @@ -29,18 +31,18 @@ public: void run(); -private: // mbgl::View implementation - void initialize(mbgl::Map*) override; + void updateViewBinding(); void bind() override; - std::array<uint16_t, 2> getSize() const override; - std::array<uint16_t, 2> getFramebufferSize() const override; + std::array<uint16_t, 2> getSize() const; + std::array<uint16_t, 2> getFramebufferSize() const; // mbgl::Backend implementation void activate() override; void deactivate() override; void invalidate() override; +private: // Window callbacks static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods); static void onScroll(GLFWwindow *window, double xoffset, double yoffset); @@ -76,6 +78,8 @@ private: std::function<void(mbgl::MapChange)> mapChangeCallback; private: + mbgl::Map* map = nullptr; + bool fullscreen = false; const bool benchmark = false; bool tracking = false; diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp deleted file mode 100644 index 27af4fc9d9..0000000000 --- a/include/mbgl/platform/default/headless_view.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include <mbgl/map/view.hpp> -#include <mbgl/gl/types.hpp> - -namespace mbgl { - -class HeadlessView : public View { -public: - HeadlessView(float pixelRatio = 1, uint16_t width = 256, uint16_t height = 256); - ~HeadlessView() override; - - void bind() override; - - std::array<uint16_t, 2> getSize() const override; - std::array<uint16_t, 2> getFramebufferSize() const override; - - - PremultipliedImage readStillImage(std::array<uint16_t, 2> size = {{ 0, 0 }}) override; - - float getPixelRatio() const; - - void resize(uint16_t width, uint16_t height); - -private: - void clearBuffers(); - void resizeFramebuffer(); - void bindFramebuffer(); - -private: - const float pixelRatio; - std::array<uint16_t, 2> dimensions; - - bool needsResize = false; - - gl::FramebufferID fbo = 0; - gl::RenderbufferID fboDepthStencil = 0; - gl::RenderbufferID fboColor = 0; -}; - -} // namespace mbgl diff --git a/include/mbgl/platform/default/offscreen_view.hpp b/include/mbgl/platform/default/offscreen_view.hpp new file mode 100644 index 0000000000..034aa3aaf3 --- /dev/null +++ b/include/mbgl/platform/default/offscreen_view.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include <mbgl/map/view.hpp> +#include <mbgl/gl/framebuffer.hpp> +#include <mbgl/gl/renderbuffer.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/image.hpp> + +namespace mbgl { + +namespace gl { +class Context; +} // namespace gl + +class OffscreenView : public View { +public: + OffscreenView(gl::Context&, std::array<uint16_t, 2> size = {{ 256, 256 }}); + + void bind() override; + + PremultipliedImage readStillImage(); + + std::array<uint16_t, 2> getSize() const; + +private: + gl::Context& context; + std::array<uint16_t, 2> size; + optional<gl::Framebuffer> framebuffer; + optional<gl::Renderbuffer<gl::RenderbufferType::RGBA>> color; + optional<gl::Renderbuffer<gl::RenderbufferType::DepthStencil>> depthStencil; +}; + +} // namespace mbgl diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 166f102f18..1216bd4cbe 100644 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -103,7 +103,7 @@ global.propertyNativeType = function (property) { if (/-translate-anchor$/.test(property.name)) { return 'TranslateAnchorType'; } - if (/-(rotation|pitch)-alignment$/.test(property.name)) { + if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) { return 'AlignmentType'; } switch (property.type) { diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index a145841b47..6dcd177cd6 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -358,7 +358,7 @@ void nativeDestroySurface(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().update(mbgl::Update::Repaint); + nativeMapView->invalidate(); } void nativeRender(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index fe201ac069..8f594f2c0e 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -17,7 +17,9 @@ #include <mbgl/platform/log.hpp> #include <mbgl/gl/extension.hpp> #include <mbgl/gl/gl.hpp> +#include <mbgl/gl/context.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/image.hpp> namespace mbgl { namespace android { @@ -80,7 +82,10 @@ NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio, int a mbgl::android::cachePath + "/mbgl-offline.db", mbgl::android::apkPath); - map = std::make_unique<mbgl::Map>(*this, *this, pixelRatio, *fileSource, threadPool, MapMode::Continuous); + map = std::make_unique<mbgl::Map>( + *this, + std::array<uint16_t, 2>{{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }}, + pixelRatio, *fileSource, threadPool, MapMode::Continuous); float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1; float cpuFactor = availableProcessors; @@ -112,16 +117,14 @@ NativeMapView::~NativeMapView() { vm = nullptr; } -void NativeMapView::bind() { - MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); -} - -std::array<uint16_t, 2> NativeMapView::getSize() const { - return {{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }}; +void NativeMapView::updateViewBinding() { + getContext().bindFramebuffer.setCurrentValue(0); + getContext().viewport.setCurrentValue({ 0, 0, static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) }); } -std::array<uint16_t, 2> NativeMapView::getFramebufferSize() const { - return {{ static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) }}; +void NativeMapView::bind() { + getContext().bindFramebuffer = 0; + getContext().viewport = { 0, 0, static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) }; } void NativeMapView::activate() { @@ -189,12 +192,8 @@ void NativeMapView::invalidate() { void NativeMapView::render() { activate(); - if(sizeChanged){ - sizeChanged = false; - glViewport(0, 0, fbWidth, fbHeight); - } - - map->render(); + updateViewBinding(); + map->render(*this); if(snapshot){ snapshot = false; @@ -719,14 +718,13 @@ void NativeMapView::updateFps() { void NativeMapView::resizeView(int w, int h) { width = w; height = h; - sizeChanged = true; - map->update(mbgl::Update::Dimensions); + map->setSize({{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }}); } void NativeMapView::resizeFramebuffer(int w, int h) { fbWidth = w; fbHeight = h; - map->update(mbgl::Update::Repaint); + invalidate(); } void NativeMapView::setInsets(mbgl::EdgeInsets insets_) { diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index 42a9a10ad4..43fb0c1ccd 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -20,10 +20,9 @@ public: NativeMapView(JNIEnv *env, jobject obj, float pixelRatio, int availableProcessors, size_t totalMemory); virtual ~NativeMapView(); + void updateViewBinding(); void bind() override; - std::array<uint16_t, 2> getSize() const override; - std::array<uint16_t, 2> getFramebufferSize() const override; void activate() override; void deactivate() override; void invalidate() override; @@ -83,7 +82,6 @@ private: bool firstTime = false; bool fpsEnabled = false; - bool sizeChanged = false; bool snapshot = false; double fps = 0.0; diff --git a/platform/android/src/style/layers/custom_layer.cpp b/platform/android/src/style/layers/custom_layer.cpp index aeabb4e19d..6568455c67 100644 --- a/platform/android/src/style/layers/custom_layer.cpp +++ b/platform/android/src/style/layers/custom_layer.cpp @@ -26,7 +26,7 @@ namespace android { void CustomLayer::update(jni::JNIEnv&) { Log::Debug(mbgl::Event::JNI, "Updating map"); if (map) { - map->update(mbgl::Update::Repaint); + map->triggerRepaint(); } else { Log::Error(mbgl::Event::JNI, "No map reference, cannot update"); } diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp index 5794cd4e62..0785e4d4e0 100644 --- a/platform/android/src/style/sources/source.hpp +++ b/platform/android/src/style/sources/source.hpp @@ -40,11 +40,9 @@ public: std::unique_ptr<mbgl::style::Source> releaseCoreSource(); protected: - std::unique_ptr<mbgl::style::Source> ownedSource; mbgl::style::Source& source; mbgl::Map* map; - }; } //android diff --git a/platform/darwin/src/headless_view_cgl.cpp b/platform/darwin/src/headless_view_cgl.cpp deleted file mode 100644 index 08f0da8751..0000000000 --- a/platform/darwin/src/headless_view_cgl.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include <mbgl/platform/default/headless_view.hpp> - -#include <mbgl/gl/gl.hpp> - -#include <cassert> - -namespace mbgl { - -void HeadlessView::bindFramebuffer() { - assert(fbo); - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); -} - -void HeadlessView::resizeFramebuffer() { - const unsigned int w = dimensions[0] * pixelRatio; - const unsigned int h = dimensions[1] * pixelRatio; - - // Create depth/stencil buffer - MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil)); - MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0)); - - MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor)); - MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0)); - - MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo)); - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); - - MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor)); - MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil)); - - GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)); - - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { - std::string error("Couldn't create framebuffer: "); - switch (status) { - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break; - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break; - case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break; - default: error += "other"; break; - } - throw std::runtime_error(error); - } - - MBGL_CHECK_ERROR(glViewport(0, 0, w, h)); -} - -void HeadlessView::clearBuffers() { - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); - - if (fbo) { - MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo)); - fbo = 0; - } - - if (fboColor) { - MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor)); - fboColor = 0; - } - - if (fboDepthStencil) { - MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil)); - fboDepthStencil = 0; - } -} - -} // namespace mbgl diff --git a/platform/darwin/src/headless_view_eagl.cpp b/platform/darwin/src/headless_view_eagl.cpp deleted file mode 100644 index cc378912f9..0000000000 --- a/platform/darwin/src/headless_view_eagl.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include <mbgl/platform/default/headless_view.hpp> - -#include <mbgl/gl/gl.hpp> - -#include <cassert> - -namespace mbgl { - -void HeadlessView::bindFramebuffer() { - assert(fbo); - MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo)); -} - -void HeadlessView::resizeFramebuffer() { - const unsigned int w = dimensions[0] * pixelRatio; - const unsigned int h = dimensions[1] * pixelRatio; - - // Create depth/stencil buffer - MBGL_CHECK_ERROR(glGenRenderbuffers(1, &fboDepthStencil)); - MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, fboDepthStencil)); - MBGL_CHECK_ERROR(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, w, h)); - MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, 0)); - - MBGL_CHECK_ERROR(glGenRenderbuffers(1, &fboColor)); - MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, fboColor)); - MBGL_CHECK_ERROR(glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, w, h)); - MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, 0)); - - MBGL_CHECK_ERROR(glGenFramebuffers(1, &fbo)); - MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo)); - - MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fboColor)); - MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboDepthStencil)); - MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fboDepthStencil)); - - GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER)); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - std::string error("Couldn't create framebuffer: "); - switch (status) { - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: (error += "incomplete attachment"); break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error += "incomplete missing attachment"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: error += "incomplete dimensions"; break; - case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break; - default: error += "other"; break; - } - throw std::runtime_error(error); - } - - MBGL_CHECK_ERROR(glViewport(0, 0, w, h)); -} - -void HeadlessView::clearBuffers() { - MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); - - if (fbo) { - MBGL_CHECK_ERROR(glDeleteFramebuffers(1, &fbo)); - fbo = 0; - } - - if (fboColor) { - MBGL_CHECK_ERROR(glDeleteRenderbuffers(1, &fboColor)); - fboColor = 0; - } - - if (fboDepthStencil) { - MBGL_CHECK_ERROR(glDeleteRenderbuffers(1, &fboDepthStencil)); - fboDepthStencil = 0; - } -} - -} // namespace mbgl diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index 044181e8c8..47551d786f 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -4,6 +4,7 @@ #include <mbgl/style/transition_options.hpp> #include <mbgl/gl/gl.hpp> #include <mbgl/gl/extension.hpp> +#include <mbgl/gl/context.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/util/string.hpp> @@ -124,13 +125,21 @@ GLFWView::~GLFWView() { glfwTerminate(); } -void GLFWView::initialize(mbgl::Map *map_) { - View::initialize(map_); +void GLFWView::setMap(mbgl::Map *map_) { + map = map_; map->addAnnotationIcon("default_marker", makeSpriteImage(22, 22, 1)); } +void GLFWView::updateViewBinding() { + getContext().bindFramebuffer.setCurrentValue(0); + getContext().viewport.setCurrentValue( + { 0, 0, static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) }); +} + void GLFWView::bind() { - MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + getContext().bindFramebuffer = 0; + getContext().viewport = { 0, 0, static_cast<uint16_t>(fbWidth), + static_cast<uint16_t>(fbHeight) }; } void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) { @@ -365,8 +374,8 @@ void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) { GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window)); view->width = width; view->height = height; - - view->map->update(mbgl::Update::Dimensions); + view->map->setSize({{ static_cast<uint16_t>(view->width), + static_cast<uint16_t>(view->height) }}); } void GLFWView::onFramebufferResize(GLFWwindow *window, int width, int height) { @@ -374,7 +383,11 @@ void GLFWView::onFramebufferResize(GLFWwindow *window, int width, int height) { view->fbWidth = width; view->fbHeight = height; - view->map->update(mbgl::Update::Repaint); + // This is only triggered when the framebuffer is resized, but not the window. It can + // happen when you move the window between screens with a different pixel ratio. + // We are forcing a repaint my invalidating the view, which triggers a rerender with the + // new framebuffer dimensions. + view->invalidate(); } void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modifiers) { @@ -440,15 +453,15 @@ void GLFWView::run() { const double started = glfwGetTime(); glfwMakeContextCurrent(window); - glViewport(0, 0, fbWidth, fbHeight); - map->render(); + updateViewBinding(); + map->render(*this); glfwSwapBuffers(window); report(1000 * (glfwGetTime() - started)); if (benchmark) { - map->update(mbgl::Update::Repaint); + invalidate(); } dirty = false; diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp deleted file mode 100644 index f96b0a7f6d..0000000000 --- a/platform/default/headless_view.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include <mbgl/platform/default/headless_view.hpp> - -#include <mbgl/gl/gl.hpp> - -#include <cstring> - -namespace mbgl { - -HeadlessView::HeadlessView(float pixelRatio_, uint16_t width, uint16_t height) - : pixelRatio(pixelRatio_), dimensions({ { width, height } }), needsResize(true) { -} - -HeadlessView::~HeadlessView() { - clearBuffers(); -} - -void HeadlessView::bind() { - if (needsResize) { - clearBuffers(); - resizeFramebuffer(); - needsResize = false; - } else { - bindFramebuffer(); - } -} - -void HeadlessView::resize(const uint16_t width, const uint16_t height) { - if(dimensions[0] == width && - dimensions[1] == height) { - return; - } - dimensions = {{ width, height }}; - needsResize = true; -} - -PremultipliedImage HeadlessView::readStillImage(std::array<uint16_t, 2> size) { - if (!size[0] || !size[1]) { - size[0] = dimensions[0] * pixelRatio; - size[1] = dimensions[1] * pixelRatio; - } - - PremultipliedImage image { size[0], size[1] }; - MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get())); - - const auto stride = image.stride(); - auto tmp = std::make_unique<uint8_t[]>(stride); - uint8_t* rgba = image.data.get(); - for (int i = 0, j = size[1] - 1; i < j; i++, j--) { - std::memcpy(tmp.get(), rgba + i * stride, stride); - std::memcpy(rgba + i * stride, rgba + j * stride, stride); - std::memcpy(rgba + j * stride, tmp.get(), stride); - } - - return image; -} - -float HeadlessView::getPixelRatio() const { - return pixelRatio; -} - -std::array<uint16_t, 2> HeadlessView::getSize() const { - return dimensions; -} - -std::array<uint16_t, 2> HeadlessView::getFramebufferSize() const { - return {{ static_cast<uint16_t>(dimensions[0] * pixelRatio), - static_cast<uint16_t>(dimensions[1] * pixelRatio) }}; -} - -} // namespace mbgl diff --git a/platform/default/headless_view_glx.cpp b/platform/default/headless_view_glx.cpp deleted file mode 100644 index 08f0da8751..0000000000 --- a/platform/default/headless_view_glx.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include <mbgl/platform/default/headless_view.hpp> - -#include <mbgl/gl/gl.hpp> - -#include <cassert> - -namespace mbgl { - -void HeadlessView::bindFramebuffer() { - assert(fbo); - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); -} - -void HeadlessView::resizeFramebuffer() { - const unsigned int w = dimensions[0] * pixelRatio; - const unsigned int h = dimensions[1] * pixelRatio; - - // Create depth/stencil buffer - MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil)); - MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0)); - - MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor)); - MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0)); - - MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo)); - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); - - MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor)); - MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil)); - - GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)); - - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { - std::string error("Couldn't create framebuffer: "); - switch (status) { - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break; - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break; - case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break; - default: error += "other"; break; - } - throw std::runtime_error(error); - } - - MBGL_CHECK_ERROR(glViewport(0, 0, w, h)); -} - -void HeadlessView::clearBuffers() { - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); - - if (fbo) { - MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo)); - fbo = 0; - } - - if (fboColor) { - MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor)); - fboColor = 0; - } - - if (fboDepthStencil) { - MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil)); - fboDepthStencil = 0; - } -} - -} // namespace mbgl diff --git a/platform/default/offscreen_view.cpp b/platform/default/offscreen_view.cpp new file mode 100644 index 0000000000..eaf87d0f87 --- /dev/null +++ b/platform/default/offscreen_view.cpp @@ -0,0 +1,47 @@ +#include <mbgl/platform/default/offscreen_view.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/gl.hpp> + +#include <cstring> +#include <cassert> + +namespace mbgl { + +OffscreenView::OffscreenView(gl::Context& context_, std::array<uint16_t, 2> size_) + : context(context_), size(std::move(size_)) { + assert(size[0] > 0 && size[1] > 0); +} + +void OffscreenView::bind() { + if (!framebuffer) { + color = context.createRenderbuffer<gl::RenderbufferType::RGBA>(size); + depthStencil = context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size); + framebuffer = context.createFramebuffer(*color, *depthStencil); + } else { + context.bindFramebuffer = framebuffer->framebuffer; + } + + context.viewport = { 0, 0, size[0], size[1] }; +} + +PremultipliedImage OffscreenView::readStillImage() { + PremultipliedImage image { size[0], size[1] }; + MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get())); + + const auto stride = image.stride(); + auto tmp = std::make_unique<uint8_t[]>(stride); + uint8_t* rgba = image.data.get(); + for (int i = 0, j = size[1] - 1; i < j; i++, j--) { + std::memcpy(tmp.get(), rgba + i * stride, stride); + std::memcpy(rgba + i * stride, rgba + j * stride, stride); + std::memcpy(rgba + j * stride, tmp.get(), stride); + } + + return image; +} + +std::array<uint16_t, 2> OffscreenView::getSize() const { + return size; +} + +} // namespace mbgl diff --git a/platform/ios/config.cmake b/platform/ios/config.cmake index db75d7a4de..0813d0338f 100644 --- a/platform/ios/config.cmake +++ b/platform/ios/config.cmake @@ -38,10 +38,9 @@ macro(mbgl_platform_core) # Headless view PRIVATE platform/darwin/src/headless_backend_eagl.mm - PRIVATE platform/darwin/src/headless_view_eagl.cpp PRIVATE platform/default/headless_backend.cpp PRIVATE platform/default/headless_display.cpp - PRIVATE platform/default/headless_view.cpp + PRIVATE platform/default/offscreen_view.cpp # Thread pool PRIVATE platform/default/thread_pool.cpp diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 3d93dc8fc9..41e049ad61 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -2,6 +2,7 @@ #include <mbgl/platform/log.hpp> #include <mbgl/gl/extension.hpp> +#include <mbgl/gl/context.hpp> #import <GLKit/GLKit.h> #import <OpenGLES/EAGL.h> @@ -26,7 +27,7 @@ #include <mbgl/util/projection.hpp> #include <mbgl/util/default_styles.hpp> #include <mbgl/util/chrono.hpp> -#import <mbgl/util/run_loop.hpp> +#include <mbgl/util/run_loop.hpp> #import "Mapbox.h" #import "MGLFeature_Private.h" @@ -398,10 +399,12 @@ public: [[NSFileManager defaultManager] removeItemAtPath:fileCachePath error:NULL]; // setup mbgl map - const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; + const std::array<uint16_t, 2> size = {{ static_cast<uint16_t>(self.bounds.size.width), + static_cast<uint16_t>(self.bounds.size.height) }}; mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; + const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; _mbglThreadPool = new mbgl::ThreadPool(4); - _mbglMap = new mbgl::Map(*_mbglView, *_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); + _mbglMap = new mbgl::Map(*_mbglView, size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); [self validateTileCacheSize]; // start paused if in IB @@ -868,7 +871,8 @@ public: { if ( ! self.dormant) { - _mbglMap->render(); + _mbglView->updateViewBinding(); + _mbglMap->render(*_mbglView); [self updateUserLocationAnnotationView]; } @@ -883,7 +887,8 @@ public: if ( ! _isTargetingInterfaceBuilder) { - _mbglMap->update(mbgl::Update::Dimensions); + _mbglMap->setSize({{ static_cast<uint16_t>(self.bounds.size.width), + static_cast<uint16_t>(self.bounds.size.height) }}); } if (self.attributionSheet.visible) @@ -4946,18 +4951,32 @@ public: : nativeView(nativeView_) { } - std::array<uint16_t, 2> getSize() const override { - return {{ static_cast<uint16_t>([nativeView bounds].size.width), - static_cast<uint16_t>([nativeView bounds].size.height) }}; + mbgl::gl::value::Viewport::Type getViewport() const { + return { 0, 0, static_cast<uint16_t>(nativeView.glView.drawableWidth), + static_cast<uint16_t>(nativeView.glView.drawableHeight) }; } - std::array<uint16_t, 2> getFramebufferSize() const override { - return {{ static_cast<uint16_t>([[nativeView glView] drawableWidth]), - static_cast<uint16_t>([[nativeView glView] drawableHeight]) }}; + /// This function is called before we start rendering, when iOS invokes our rendering method. + /// iOS already sets the correct framebuffer and viewport for us, so we need to update the + /// context state with the anticipated values. + void updateViewBinding() { + // We are using 0 as the placeholder value for the GLKView's framebuffer. + getContext().bindFramebuffer.setCurrentValue(0); + getContext().viewport.setCurrentValue(getViewport()); } void bind() override { - [nativeView.glView bindDrawable]; + if (getContext().bindFramebuffer != 0) { + // Something modified our state, and we need to bind the original drawable again. + // Doing this also sets the viewport to the full framebuffer. + // Note that in reality, iOS does not use the Framebuffer 0 (it's typically 1), and we + // only use this is a placeholder value. + [nativeView.glView bindDrawable]; + updateViewBinding(); + } else { + // Our framebuffer is still bound, but the viewport might have changed. + getContext().viewport = getViewport(); + } } void notifyMapChange(mbgl::MapChange change) override @@ -5195,7 +5214,7 @@ void MGLFinishCustomStyleLayer(void *context) - (void)setCustomStyleLayersNeedDisplay { - _mbglMap->update(mbgl::Update::Repaint); + [self setNeedsGLDisplay]; } - (mbgl::Map *)mbglMap { diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index d033f9df21..6bde3136f2 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -42,10 +42,9 @@ macro(mbgl_platform_core) # Headless view PRIVATE platform/default/headless_backend_glx.cpp - PRIVATE platform/default/headless_view_glx.cpp PRIVATE platform/default/headless_backend.cpp PRIVATE platform/default/headless_display.cpp - PRIVATE platform/default/headless_view.cpp + PRIVATE platform/default/offscreen_view.cpp # Thread pool PRIVATE platform/default/thread_pool.cpp diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake index 198dfc7083..27bf555ec6 100644 --- a/platform/macos/config.cmake +++ b/platform/macos/config.cmake @@ -36,10 +36,9 @@ macro(mbgl_platform_core) # Headless view PRIVATE platform/darwin/src/headless_backend_cgl.cpp - PRIVATE platform/darwin/src/headless_view_cgl.cpp PRIVATE platform/default/headless_backend.cpp PRIVATE platform/default/headless_display.cpp - PRIVATE platform/default/headless_view.cpp + PRIVATE platform/default/offscreen_view.cpp # Thread pool PRIVATE platform/default/thread_pool.cpp diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 475ca2d259..ebcd8e00b0 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -25,6 +25,7 @@ #import <mbgl/platform/default/thread_pool.hpp> #import <mbgl/gl/extension.hpp> #import <mbgl/gl/gl.hpp> +#import <mbgl/gl/context.hpp> #import <mbgl/map/backend.hpp> #import <mbgl/sprite/sprite_image.hpp> #import <mbgl/storage/default_file_source.hpp> @@ -263,9 +264,12 @@ public: NSURL *legacyCacheURL = [cachesDirectoryURL URLByAppendingPathComponent:@"cache.db"]; [[NSFileManager defaultManager] removeItemAtURL:legacyCacheURL error:NULL]; - mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; + mbgl::DefaultFileSource* mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; + + const std::array<uint16_t, 2> size = {{ static_cast<uint16_t>(self.bounds.size.width), + static_cast<uint16_t>(self.bounds.size.height) }}; _mbglThreadPool = new mbgl::ThreadPool(4); - _mbglMap = new mbgl::Map(*_mbglView, *_mbglView, [NSScreen mainScreen].backingScaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); + _mbglMap = new mbgl::Map(*_mbglView, size, [NSScreen mainScreen].backingScaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); [self validateTileCacheSize]; // Install the OpenGL layer. Interface Builder’s synchronous drawing means @@ -635,7 +639,8 @@ public: [self validateTileCacheSize]; } if (!_isTargetingInterfaceBuilder) { - _mbglMap->update(mbgl::Update::Dimensions); + _mbglMap->setSize({{ static_cast<uint16_t>(self.bounds.size.width), + static_cast<uint16_t>(self.bounds.size.height) }}); } } @@ -746,9 +751,8 @@ public: return reinterpret_cast<mbgl::gl::glProc>(symbol); }); - _mbglView->updateFramebufferBinding(); - - _mbglMap->render(); + _mbglView->updateViewBinding(); + _mbglMap->render(*_mbglView); if (_isPrinting) { _isPrinting = NO; @@ -2539,17 +2543,6 @@ public: MGLMapViewImpl(MGLMapView *nativeView_) : nativeView(nativeView_) {} - std::array<uint16_t, 2> getSize() const override { - return {{ static_cast<uint16_t>(nativeView.bounds.size.width), - static_cast<uint16_t>(nativeView.bounds.size.height) }}; - } - - std::array<uint16_t, 2> getFramebufferSize() const override { - NSRect bounds = [nativeView convertRectToBacking:nativeView.bounds]; - return {{ static_cast<uint16_t>(bounds.size.width), - static_cast<uint16_t>(bounds.size.height) }}; - } - void notifyMapChange(mbgl::MapChange change) override { [nativeView notifyMapChange:change]; } @@ -2567,26 +2560,34 @@ public: [NSOpenGLContext clearCurrentContext]; } - void updateFramebufferBinding() { - MBGL_CHECK_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo)); + mbgl::gl::value::Viewport::Type getViewport() const { + return { 0, 0, static_cast<uint16_t>(nativeView.bounds.size.width), + static_cast<uint16_t>(nativeView.bounds.size.height) }; } - void bind() override { - MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo)); + void updateViewBinding() { + fbo = mbgl::gl::value::BindFramebuffer::Get(); + getContext().bindFramebuffer.setCurrentValue(fbo); + getContext().viewport.setCurrentValue(getViewport()); } - mbgl::PremultipliedImage readStillImage(std::array<uint16_t, 2> size = {{ 0, 0 }}) override { - if (!size[0] || !size[1]) { - size = getFramebufferSize(); - } + void bind() override { + getContext().bindFramebuffer = fbo; + getContext().viewport = getViewport(); + } - mbgl::PremultipliedImage image { size[0], size[1] }; - MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get())); + mbgl::PremultipliedImage readStillImage() { + NSRect bounds = [nativeView convertRectToBacking:nativeView.bounds]; + const uint16_t width = bounds.size.width; + const uint16_t height = bounds.size.height; + mbgl::PremultipliedImage image{ width, height }; + MBGL_CHECK_ERROR( + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get())); const size_t stride = image.stride(); auto tmp = std::make_unique<uint8_t[]>(stride); uint8_t *rgba = image.data.get(); - for (int i = 0, j = size[1] - 1; i < j; i++, j--) { + for (int i = 0, j = height - 1; i < j; i++, j--) { std::memcpy(tmp.get(), rgba + i * stride, stride); std::memcpy(rgba + i * stride, rgba + j * stride, stride); std::memcpy(rgba + j * stride, tmp.get(), stride); diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index ebdf8d62c3..671bf3e0fd 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -356,21 +356,28 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) { } void NodeMap::startRender(NodeMap::RenderOptions options) { - view.resize(options.width, options.height); - map->update(mbgl::Update::Dimensions); + map->setSize(std::array<uint16_t, 2>{{ static_cast<uint16_t>(options.width), + static_cast<uint16_t>(options.height) }}); + + const std::array<uint16_t, 2> fbSize{{ static_cast<uint16_t>(options.width * pixelRatio), + static_cast<uint16_t>(options.height * pixelRatio) }}; + if (!view || view->getSize() != fbSize) { + view.reset(); + view = std::make_unique<mbgl::OffscreenView>(backend.getContext(), fbSize); + } map->setClasses(options.classes); map->setLatLngZoom(mbgl::LatLng(options.latitude, options.longitude), options.zoom); map->setBearing(options.bearing); map->setPitch(options.pitch); map->setDebug(options.debugOptions); - map->renderStill([this](const std::exception_ptr eptr, mbgl::PremultipliedImage&& result) { + map->renderStill(*view, [this](const std::exception_ptr eptr) { if (eptr) { error = std::move(eptr); uv_async_send(async); } else { assert(!image.data); - image = std::move(result); + image = view->readStillImage(); uv_async_send(async); } }); @@ -772,15 +779,23 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>& } } -NodeMap::NodeMap(v8::Local<v8::Object> options) : - backend(sharedDisplay()), - view([&] { - Nan::HandleScope scope; - return Nan::Has(options, Nan::New("ratio").ToLocalChecked()).FromJust() ? Nan::Get(options, Nan::New("ratio").ToLocalChecked()).ToLocalChecked()->NumberValue() : 1.0; - }()), - threadpool(), - map(std::make_unique<mbgl::Map>(backend, view, view.getPixelRatio(), *this, threadpool, mbgl::MapMode::Still)), - async(new uv_async_t) { +NodeMap::NodeMap(v8::Local<v8::Object> options) + : pixelRatio([&] { + Nan::HandleScope scope; + return Nan::Has(options, Nan::New("ratio").ToLocalChecked()).FromJust() + ? Nan::Get(options, Nan::New("ratio").ToLocalChecked()) + .ToLocalChecked() + ->NumberValue() + : 1.0; + }()), + backend(sharedDisplay()), + map(std::make_unique<mbgl::Map>(backend, + std::array<uint16_t, 2>{{ 256, 256 }}, + pixelRatio, + *this, + threadpool, + mbgl::MapMode::Still)), + async(new uv_async_t) { backend.setMapChangeCallback([&](mbgl::MapChange change) { if (change == mbgl::MapChangeDidFailLoadingMap) { diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index 48ff2caab1..45de2733cc 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -5,7 +5,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -54,8 +54,9 @@ public: std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback); + const float pixelRatio; mbgl::HeadlessBackend backend; - mbgl::HeadlessView view; + std::unique_ptr<mbgl::OffscreenView> view; NodeThreadPool threadpool; std::unique_ptr<mbgl::Map> map; diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp index 0cce6caaed..e8e3a13489 100644 --- a/platform/qt/app/mapwindow.cpp +++ b/platform/qt/app/mapwindow.cpp @@ -9,10 +9,15 @@ #include <QMouseEvent> #include <QString> +#if QT_VERSION >= 0x050000 +#include <QWindow> +#endif + int kAnimationDuration = 10000; + MapWindow::MapWindow(const QMapboxGLSettings &settings) - : m_map(nullptr, settings) + : m_map(nullptr, settings, size(), pixelRatio()) , m_bearingAnimation(&m_map, "bearing") , m_zoomAnimation(&m_map, "zoom") { @@ -46,6 +51,17 @@ void MapWindow::selfTest() m_zoomAnimation.start(); } +qreal MapWindow::pixelRatio() { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + return devicePixelRatioF(); +#elif (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + return devicePixelRatio(); +#else + return 1; +#endif +} + + void MapWindow::animationFinished() { qDebug() << "Animation ticks/s: " << m_animationTicks / static_cast<float>(kAnimationDuration) * 1000.; @@ -288,16 +304,12 @@ void MapWindow::initializeGL() QMapbox::initializeGLExtensions(); } -void MapWindow::resizeGL(int w, int h) -{ - QSize size(w, h); - m_map.resize(size); -} - void MapWindow::paintGL() { m_frameDraws++; + m_map.resize(size(), size() * pixelRatio()); + #if QT_VERSION < 0x050400 && defined(__APPLE__) // XXX GL framebuffer is valid only after first attempt of painting on // older versions of Qt on macOS. diff --git a/platform/qt/app/mapwindow.hpp b/platform/qt/app/mapwindow.hpp index fa1c3f4b3b..a579a5bcc5 100644 --- a/platform/qt/app/mapwindow.hpp +++ b/platform/qt/app/mapwindow.hpp @@ -24,6 +24,7 @@ protected slots: private: void changeStyle(); + qreal pixelRatio(); // QGLWidget implementation. void keyPressEvent(QKeyEvent *ev) final; @@ -32,7 +33,6 @@ private: void wheelEvent(QWheelEvent *ev) final; void initializeGL() final; - void resizeGL(int w, int h) final; void paintGL() final; QPointF m_lastPos; diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake index 6df311f885..ff86a0dee1 100644 --- a/platform/qt/config.cmake +++ b/platform/qt/config.cmake @@ -44,11 +44,10 @@ macro(mbgl_platform_test) target_sources(mbgl-test PRIVATE test/src/main.cpp PRIVATE platform/qt/test/headless_backend_qt.cpp - PRIVATE platform/qt/test/headless_view_qt.cpp PRIVATE platform/qt/test/qmapboxgl.cpp PRIVATE platform/default/headless_backend.cpp PRIVATE platform/default/headless_display.cpp - PRIVATE platform/default/headless_view.cpp + PRIVATE platform/default/offscreen_view.cpp ) set_source_files_properties( diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index ba5631e1e4..d3937ce083 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -3,6 +3,7 @@ #include <QMapbox> #include <QObject> +#include <QSize> #include <QPointF> class QImage; @@ -10,6 +11,7 @@ class QMargins; class QSize; class QString; class QStringList; +class QOpenGLFramebufferObject; class QMapboxGLPrivate; @@ -95,7 +97,10 @@ public: NorthLeftwards, }; - QMapboxGL(QObject *parent = 0, const QMapboxGLSettings& = QMapboxGLSettings()); + QMapboxGL(QObject* parent = 0, + const QMapboxGLSettings& = QMapboxGLSettings(), + const QSize& size = QSize(), + qreal pixelRatio = 1); virtual ~QMapboxGL(); void cycleDebugOptions(); @@ -167,7 +172,7 @@ public: void scaleBy(double scale, const QPointF ¢er = QPointF()); void rotateBy(const QPointF &first, const QPointF &second); - void resize(const QSize &size); + void resize(const QSize &size, const QSize &framebufferSize); void addAnnotationIcon(const QString &name, const QImage &sprite); @@ -198,7 +203,11 @@ public: void setFilter(const QString &layer, const QVariant &filter); public slots: +#if QT_VERSION >= 0x050000 + void render(QOpenGLFramebufferObject *fbo = NULL); +#else void render(); +#endif void connectionEstablished(); signals: diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 73a1771908..edda1f9599 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -6,6 +6,7 @@ #include <mbgl/annotation/annotation.hpp> #include <mbgl/gl/gl.hpp> +#include <mbgl/gl/context.hpp> #include <mbgl/map/camera.hpp> #include <mbgl/map/map.hpp> #include <mbgl/style/conversion.hpp> @@ -23,6 +24,7 @@ #if QT_VERSION >= 0x050000 #include <QGuiApplication> #include <QWindow> +#include <QOpenGLFramebufferObject> #else #include <QCoreApplication> #endif @@ -271,7 +273,7 @@ void QMapboxGLSettings::setAccessToken(const QString &token) Constructs a QMapboxGL object with \a settings and sets \a parent as the parent object. The \a settings cannot be changed after the object is constructed. */ -QMapboxGL::QMapboxGL(QObject *parent, const QMapboxGLSettings &settings) +QMapboxGL::QMapboxGL(QObject *parent, const QMapboxGLSettings &settings, const QSize& size, qreal pixelRatio) : QObject(parent) { // Multiple QMapboxGL running on the same thread @@ -280,7 +282,7 @@ QMapboxGL::QMapboxGL(QObject *parent, const QMapboxGLSettings &settings) loop.setLocalData(std::make_shared<mbgl::util::RunLoop>()); } - d_ptr = new QMapboxGLPrivate(this, settings); + d_ptr = new QMapboxGLPrivate(this, settings, size, pixelRatio); } QMapboxGL::~QMapboxGL() @@ -602,15 +604,14 @@ void QMapboxGL::rotateBy(const QPointF &first, const QPointF &second) mbgl::ScreenCoordinate { second.x(), second.y() }); } -void QMapboxGL::resize(const QSize& size) +void QMapboxGL::resize(const QSize& size, const QSize& framebufferSize) { - QSize converted = size / d_ptr->getPixelRatio(); - if (d_ptr->size == converted) return; + if (d_ptr->size == size && d_ptr->fbSize == framebufferSize) return; - glViewport(0, 0, converted.width(), converted.height()); + d_ptr->size = size; + d_ptr->fbSize = framebufferSize; - d_ptr->size = converted; - d_ptr->mapObj->update(mbgl::Update::Dimensions); + d_ptr->mapObj->setSize({{ static_cast<uint16_t>(size.width()), static_cast<uint16_t>(size.height()) }}); } void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &sprite) @@ -793,19 +794,29 @@ void QMapboxGL::setFilter(const QString& layer_, const QVariant& filter_) qWarning() << "Layer doesn't support filters"; } +#if QT_VERSION >= 0x050000 +void QMapboxGL::render(QOpenGLFramebufferObject *fbo) +{ + d_ptr->dirty = false; + d_ptr->updateFramebufferBinding(fbo); + d_ptr->mapObj->render(*d_ptr); +} +#else void QMapboxGL::render() { d_ptr->dirty = false; - d_ptr->mapObj->render(); + d_ptr->mapObj->render(*d_ptr); } +#endif void QMapboxGL::connectionEstablished() { d_ptr->connectionEstablished(); } -QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings) +QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size_, qreal pixelRatio) : QObject(q) + , size(size_) , q_ptr(q) , fileSourceObj(std::make_unique<mbgl::DefaultFileSource>( settings.cacheDatabasePath().toStdString(), @@ -813,7 +824,8 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin settings.cacheDatabaseMaximumSize())) , threadPool(4) , mapObj(std::make_unique<mbgl::Map>( - *this, *this, getPixelRatio(), *fileSourceObj, threadPool, + *this, std::array<uint16_t, 2>{{ static_cast<uint16_t>(size.width()), static_cast<uint16_t>(size.height()) }}, + pixelRatio, *fileSourceObj, threadPool, static_cast<mbgl::MapMode>(settings.mapMode()), static_cast<mbgl::GLContextMode>(settings.contextMode()), static_cast<mbgl::ConstrainMode>(settings.constrainMode()), @@ -830,36 +842,42 @@ QMapboxGLPrivate::~QMapboxGLPrivate() { } -float QMapboxGLPrivate::getPixelRatio() const -{ #if QT_VERSION >= 0x050000 - // QWindow is the most reliable pixel ratio because QGuiApplication returns - // the maximum pixel ratio of all available QScreen objects - this is not - // valid for cases e.g. where two or more QScreen objects with different - // pixel ratios are present and the window shows on the screen with lower - // pixel ratio. - static const float pixelRatio = QGuiApplication::allWindows().first()->devicePixelRatio(); -#else - static const float pixelRatio = 1.0; -#endif - return pixelRatio; +void QMapboxGLPrivate::updateFramebufferBinding(QOpenGLFramebufferObject *fbo_) +{ + fbo = fbo_; + if (fbo) { + getContext().bindFramebuffer.setDirty(); + getContext().viewport.setCurrentValue( + { 0, 0, static_cast<uint16_t>(fbo->width()), static_cast<uint16_t>(fbo->height()) }); + } else { + getContext().bindFramebuffer.setCurrentValue(0); + getContext().viewport.setCurrentValue({ 0, 0, static_cast<uint16_t>(fbSize.width()), + static_cast<uint16_t>(fbSize.height()) }); + } } void QMapboxGLPrivate::bind() { - MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); -} - -std::array<uint16_t, 2> QMapboxGLPrivate::getSize() const -{ - return {{ static_cast<uint16_t>(size.width()), static_cast<uint16_t>(size.height()) }}; + if (fbo) { + fbo->bind(); + getContext().bindFramebuffer.setDirty(); + getContext().viewport = { 0, 0, static_cast<uint16_t>(fbo->width()), + static_cast<uint16_t>(fbo->height()) }; + } else { + getContext().bindFramebuffer = 0; + getContext().viewport = { 0, 0, static_cast<uint16_t>(fbSize.width()), + static_cast<uint16_t>(fbSize.height()) }; + } } - -std::array<uint16_t, 2> QMapboxGLPrivate::getFramebufferSize() const +#else +void QMapboxGLPrivate::bind() { - return {{ static_cast<uint16_t>(size.width() * getPixelRatio()), - static_cast<uint16_t>(size.height() * getPixelRatio()) }}; + getContext().bindFramebuffer = 0; + getContext().viewport = { 0, 0, static_cast<uint16_t>(fbSize.width()), + static_cast<uint16_t>(fbSize.height()) }; } +#endif void QMapboxGLPrivate::invalidate() { diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index 5a228896dc..e7a14601c1 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -17,22 +17,27 @@ class QMapboxGLPrivate : public QObject, public mbgl::View, public mbgl::Backend Q_OBJECT public: - explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &); + explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio); virtual ~QMapboxGLPrivate(); // mbgl::View implementation. float getPixelRatio() const; void bind() final; - std::array<uint16_t, 2> getSize() const final; - std::array<uint16_t, 2> getFramebufferSize() const final; + std::array<uint16_t, 2> getSize() const; + std::array<uint16_t, 2> getFramebufferSize() const; void activate() final {} void deactivate() final {} void invalidate() final; void notifyMapChange(mbgl::MapChange) final; +#if QT_VERSION >= 0x050000 + void updateFramebufferBinding(QOpenGLFramebufferObject *); +#endif + mbgl::EdgeInsets margins; QSize size { 0, 0 }; + QSize fbSize { 0, 0 }; QMapboxGL *q_ptr { nullptr }; @@ -42,6 +47,8 @@ public: bool dirty { false }; + QOpenGLFramebufferObject *fbo { nullptr }; + public slots: void connectionEstablished(); diff --git a/platform/qt/src/qquickmapboxglrenderer.cpp b/platform/qt/src/qquickmapboxglrenderer.cpp index d550794d64..903e1c0b05 100644 --- a/platform/qt/src/qquickmapboxglrenderer.cpp +++ b/platform/qt/src/qquickmapboxglrenderer.cpp @@ -19,7 +19,7 @@ QQuickMapboxGLRenderer::QQuickMapboxGLRenderer() settings.setCacheDatabaseMaximumSize(20 * 1024 * 1024); settings.setViewportMode(QMapboxGLSettings::FlippedYViewport); - m_map.reset(new QMapboxGL(nullptr, settings)); + m_map.reset(new QMapboxGL(nullptr, settings, QSize(256, 256), 1)); } QQuickMapboxGLRenderer::~QQuickMapboxGLRenderer() @@ -28,7 +28,7 @@ QQuickMapboxGLRenderer::~QQuickMapboxGLRenderer() QOpenGLFramebufferObject* QQuickMapboxGLRenderer::createFramebufferObject(const QSize &size) { - m_map->resize(size); + m_map->resize(size / m_pixelRatio, size); QOpenGLFramebufferObjectFormat format; format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); @@ -38,7 +38,7 @@ QOpenGLFramebufferObject* QQuickMapboxGLRenderer::createFramebufferObject(const void QQuickMapboxGLRenderer::render() { - m_map->render(); + m_map->render(framebufferObject()); } void QQuickMapboxGLRenderer::synchronize(QQuickFramebufferObject *item) @@ -51,6 +51,12 @@ void QQuickMapboxGLRenderer::synchronize(QQuickFramebufferObject *item) m_initialized = true; } + if (auto window = quickMap->window()) { + m_pixelRatio = window->devicePixelRatio(); + } else { + m_pixelRatio = 1; + } + auto syncStatus = quickMap->m_syncState; quickMap->m_syncState = QQuickMapboxGL::NothingNeedsSync; diff --git a/platform/qt/src/qquickmapboxglrenderer.hpp b/platform/qt/src/qquickmapboxglrenderer.hpp index e0fc767d58..7adeea0421 100644 --- a/platform/qt/src/qquickmapboxglrenderer.hpp +++ b/platform/qt/src/qquickmapboxglrenderer.hpp @@ -29,6 +29,7 @@ signals: private: bool m_initialized = false; + qreal m_pixelRatio = 1; QScopedPointer<QMapboxGL> m_map; }; diff --git a/platform/qt/test/headless_view_qt.cpp b/platform/qt/test/headless_view_qt.cpp deleted file mode 100644 index 133b4a2371..0000000000 --- a/platform/qt/test/headless_view_qt.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include <mbgl/platform/default/headless_view.hpp> - -#include <mbgl/gl/gl.hpp> - -#include <QApplication> -#include <QGLContext> -#include <QGLWidget> - -#if QT_VERSION >= 0x050000 -#include <QOpenGLContext> -#endif - -#include <cassert> - -namespace mbgl { - -void HeadlessView::bindFramebuffer() { - assert(fbo); - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); -} - -void HeadlessView::resizeFramebuffer() { - const unsigned int w = dimensions[0] * pixelRatio; - const unsigned int h = dimensions[1] * pixelRatio; - - // Create depth/stencil buffer - MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil)); - MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0)); - - MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor)); - MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h)); - MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0)); - - MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo)); - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); - - MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor)); - MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil)); - - GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)); - - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { - std::string error("Couldn't create framebuffer: "); - switch (status) { - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break; - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break; - case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break; - default: error += "other"; break; - } - throw std::runtime_error(error); - } - - MBGL_CHECK_ERROR(glViewport(0, 0, w, h)); -} - -void HeadlessView::clearBuffers() { - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); - - if (fbo) { - MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo)); - fbo = 0; - } - - if (fboColor) { - MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor)); - fboColor = 0; - } - - if (fboDepthStencil) { - MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil)); - fboDepthStencil = 0; - } -} - -} // namespace mbgl diff --git a/platform/qt/test/qmapboxgl.cpp b/platform/qt/test/qmapboxgl.cpp index 8bcc485d89..597aef5df3 100644 --- a/platform/qt/test/qmapboxgl.cpp +++ b/platform/qt/test/qmapboxgl.cpp @@ -19,7 +19,7 @@ public: widget.makeCurrent(); QMapbox::initializeGLExtensions(); - map.resize(QSize(512, 512)); + map.resize(QSize(512, 512), QSize(512, 512)); map.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14); } diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index 77aecbe18c..dcb527e8c9 100644 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -28,7 +28,7 @@ global.propertyType = function (property) { if (/-translate-anchor$/.test(property.name)) { return 'TranslateAnchorType'; } - if (/-(rotation|pitch)-alignment$/.test(property.name)) { + if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) { return 'AlignmentType'; } switch (property.type) { diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 2a04fcc18e..23b28a15df 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -1,3 +1,4 @@ +#include <mbgl/map/view.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/gl/gl.hpp> #include <mbgl/gl/vertex_array.hpp> @@ -142,6 +143,113 @@ UniqueFramebuffer Context::createFramebuffer() { return UniqueFramebuffer{ std::move(id), { this } }; } +UniqueRenderbuffer Context::createRenderbuffer(const RenderbufferType type, + const uint16_t width, + const uint16_t height) { + RenderbufferID id = 0; + MBGL_CHECK_ERROR(glGenRenderbuffers(1, &id)); + UniqueRenderbuffer renderbuffer{ std::move(id), { this } }; + + bindRenderbuffer = renderbuffer; + MBGL_CHECK_ERROR( + glRenderbufferStorage(GL_RENDERBUFFER, static_cast<GLenum>(type), width, height)); + return renderbuffer; +} + +namespace { + +void checkFramebuffer() { + GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER)); + if (status != GL_FRAMEBUFFER_COMPLETE) { + switch (status) { + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + throw std::runtime_error("Couldn't create framebuffer: incomplete attachment"); + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + throw std::runtime_error("Couldn't create framebuffer: incomplete missing attachment"); +#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + throw std::runtime_error("Couldn't create framebuffer: incomplete draw buffer"); +#endif +#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + throw std::runtime_error("Couldn't create framebuffer: incomplete read buffer"); +#endif +#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + throw std::runtime_error("Couldn't create framebuffer: incomplete dimensions"); +#endif + + case GL_FRAMEBUFFER_UNSUPPORTED: + throw std::runtime_error("Couldn't create framebuffer: unsupported"); + default: + throw std::runtime_error("Couldn't create framebuffer: other"); + } + } +} + +void bindDepthStencilRenderbuffer( + const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) { +#ifdef GL_DEPTH_STENCIL_ATTACHMENT + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, depthStencil.renderbuffer)); +#else + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, + depthStencil.renderbuffer)); + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, depthStencil.renderbuffer)); +#endif +} + +} // namespace + +Framebuffer +Context::createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>& color, + const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) { + if (color.size != depthStencil.size) { + throw new std::runtime_error("Renderbuffer size mismatch"); + } + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, color.renderbuffer)); + bindDepthStencilRenderbuffer(depthStencil); + checkFramebuffer(); + return { color.size, std::move(fbo) }; +} + +Framebuffer Context::createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>& color) { + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, color.renderbuffer)); + checkFramebuffer(); + return { color.size, std::move(fbo) }; +} + +Framebuffer +Context::createFramebuffer(const Texture& color, + const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) { + if (color.size != depthStencil.size) { + throw new std::runtime_error("Renderbuffer size mismatch"); + } + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + color.texture, 0)); + bindDepthStencilRenderbuffer(depthStencil); + checkFramebuffer(); + return { color.size, std::move(fbo) }; +} + +Framebuffer Context::createFramebuffer(const Texture& color) { + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + color.texture, 0)); + checkFramebuffer(); + return { color.size, std::move(fbo) }; +} + UniqueTexture Context::createTexture(uint16_t width, uint16_t height, const void* data, TextureUnit unit) { auto obj = createTexture(); @@ -186,50 +294,37 @@ void Context::reset() { performCleanup(); } -namespace { - -template <typename Fn> -void applyStateFunction(Context& context, Fn&& fn) { - fn(context.stencilFunc); - fn(context.stencilMask); - fn(context.stencilTest); - fn(context.stencilOp); - fn(context.depthRange); - fn(context.depthMask); - fn(context.depthTest); - fn(context.depthFunc); - fn(context.blend); - fn(context.blendFunc); - fn(context.blendColor); - fn(context.colorMask); - fn(context.clearDepth); - fn(context.clearColor); - fn(context.clearStencil); - fn(context.program); - fn(context.lineWidth); - fn(context.activeTexture); - fn(context.bindFramebuffer); - fn(context.viewport); +void Context::setDirtyState() { + // Note: does not set viewport/bindFramebuffer to dirty since they are handled separately in + // the view object. + stencilFunc.setDirty(); + stencilMask.setDirty(); + stencilTest.setDirty(); + stencilOp.setDirty(); + depthRange.setDirty(); + depthMask.setDirty(); + depthTest.setDirty(); + depthFunc.setDirty(); + blend.setDirty(); + blendFunc.setDirty(); + blendColor.setDirty(); + colorMask.setDirty(); + clearDepth.setDirty(); + clearColor.setDirty(); + clearStencil.setDirty(); + program.setDirty(); + lineWidth.setDirty(); + activeTexture.setDirty(); #if not MBGL_USE_GLES2 - fn(context.pixelZoom); - fn(context.rasterPos); + pixelZoom.setDirty(); + rasterPos.setDirty(); #endif // MBGL_USE_GLES2 - for (auto& tex : context.texture) { - fn(tex); + for (auto& tex : texture) { + tex.setDirty(); } - fn(context.vertexBuffer); - fn(context.elementBuffer); - fn(context.vertexArrayObject); -} - -} // namespace - -void Context::resetState() { - applyStateFunction(*this, [](auto& state) { state.reset(); }); -} - -void Context::setDirtyState() { - applyStateFunction(*this, [](auto& state) { state.setDirty(); }); + vertexBuffer.setDirty(); + elementBuffer.setDirty(); + vertexArrayObject.setDirty(); } void Context::performCleanup() { @@ -289,6 +384,12 @@ void Context::performCleanup() { glDeleteFramebuffers(int(abandonedFramebuffers.size()), abandonedFramebuffers.data())); abandonedFramebuffers.clear(); } + + if (!abandonedRenderbuffers.empty()) { + MBGL_CHECK_ERROR(glDeleteRenderbuffers(int(abandonedRenderbuffers.size()), + abandonedRenderbuffers.data())); + abandonedRenderbuffers.clear(); + } } } // namespace gl diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 6a5d44793a..cf8bb2658b 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -4,6 +4,8 @@ #include <mbgl/gl/state.hpp> #include <mbgl/gl/value.hpp> #include <mbgl/gl/texture.hpp> +#include <mbgl/gl/renderbuffer.hpp> +#include <mbgl/gl/framebuffer.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> #include <mbgl/gl/attribute.hpp> @@ -14,6 +16,9 @@ #include <array> namespace mbgl { + +class View; + namespace gl { constexpr size_t TextureMax = 64; @@ -27,7 +32,6 @@ public: UniqueShader createFragmentShader(); UniqueTexture createTexture(); UniqueVertexArray createVertexArray(); - UniqueFramebuffer createFramebuffer(); template <class V> VertexBuffer<V> createVertexBuffer(std::vector<V>&& v) { @@ -44,6 +48,20 @@ public: }; } + template <RenderbufferType type> + Renderbuffer<type> createRenderbuffer(const std::array<uint16_t, 2>& size) { + static_assert(type == RenderbufferType::RGBA || type == RenderbufferType::DepthStencil, + "invalid renderbuffer type"); + return { size, createRenderbuffer(type, size[0], size[1]) }; + } + + Framebuffer createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>&, + const Renderbuffer<RenderbufferType::DepthStencil>&); + Framebuffer createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>&); + Framebuffer createFramebuffer(const Texture&, + const Renderbuffer<RenderbufferType::DepthStencil>&); + Framebuffer createFramebuffer(const Texture&); + // Create a texture from an image with data. template <typename Image> Texture createTexture(const Image& image, TextureUnit unit = 0) { @@ -87,8 +105,6 @@ public: && abandonedFramebuffers.empty(); } - void resetState(); - void setDirtyState(); State<value::StencilFunc> stencilFunc; @@ -111,6 +127,7 @@ public: State<value::ActiveTexture> activeTexture; State<value::BindFramebuffer> bindFramebuffer; State<value::Viewport> viewport; + State<value::BindRenderbuffer> bindRenderbuffer; #if not MBGL_USE_GLES2 State<value::PixelZoom> pixelZoom; State<value::RasterPos> rasterPos; @@ -124,6 +141,8 @@ private: UniqueBuffer createVertexBuffer(const void* data, std::size_t size); UniqueBuffer createIndexBuffer(const void* data, std::size_t size); UniqueTexture createTexture(uint16_t width, uint16_t height, const void* data, TextureUnit); + UniqueFramebuffer createFramebuffer(); + UniqueRenderbuffer createRenderbuffer(RenderbufferType, uint16_t width, uint16_t height); void bindAttribute(const AttributeBinding&, std::size_t stride, const int8_t* offset); friend detail::ProgramDeleter; @@ -132,6 +151,7 @@ private: friend detail::TextureDeleter; friend detail::VertexArrayDeleter; friend detail::FramebufferDeleter; + friend detail::RenderbufferDeleter; std::vector<TextureID> pooledTextures; @@ -141,6 +161,7 @@ private: std::vector<TextureID> abandonedTextures; std::vector<VertexArrayID> abandonedVertexArrays; std::vector<FramebufferID> abandonedFramebuffers; + std::vector<RenderbufferID> abandonedRenderbuffers; }; } // namespace gl diff --git a/src/mbgl/gl/framebuffer.hpp b/src/mbgl/gl/framebuffer.hpp new file mode 100644 index 0000000000..880fed159e --- /dev/null +++ b/src/mbgl/gl/framebuffer.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include <mbgl/gl/object.hpp> + +#include <array> + +namespace mbgl { +namespace gl { + +class Framebuffer { +public: + std::array<uint16_t, 2> size; + gl::UniqueFramebuffer framebuffer; +}; + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/gl.cpp b/src/mbgl/gl/gl.cpp index 5cef254160..8999468dbd 100644 --- a/src/mbgl/gl/gl.cpp +++ b/src/mbgl/gl/gl.cpp @@ -51,6 +51,7 @@ constexpr const char* stringFromError(GLenum err) { } // namespace void checkError(const char* cmd, const char* file, int line) { +// fprintf(stderr, "cmd: %s\n", cmd); GLenum err = GL_NO_ERROR; if ((err = glGetError()) != GL_NO_ERROR) { std::string message = std::string(cmd) + ": Error " + stringFromError(err); diff --git a/src/mbgl/gl/object.cpp b/src/mbgl/gl/object.cpp index aee87f8836..e2d476e0c0 100644 --- a/src/mbgl/gl/object.cpp +++ b/src/mbgl/gl/object.cpp @@ -41,6 +41,11 @@ void FramebufferDeleter::operator()(FramebufferID id) const { context->abandonedFramebuffers.push_back(id); } +void RenderbufferDeleter::operator()(RenderbufferID id) const { + assert(context); + context->abandonedRenderbuffers.push_back(id); +} + } // namespace detail } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/object.hpp b/src/mbgl/gl/object.hpp index 1cc1f04a97..1408add65a 100644 --- a/src/mbgl/gl/object.hpp +++ b/src/mbgl/gl/object.hpp @@ -41,6 +41,11 @@ struct FramebufferDeleter { void operator()(FramebufferID) const; }; +struct RenderbufferDeleter { + Context* context; + void operator()(RenderbufferID) const; +}; + } // namespace detail using UniqueProgram = std_experimental::unique_resource<ProgramID, detail::ProgramDeleter>; @@ -49,6 +54,7 @@ using UniqueBuffer = std_experimental::unique_resource<BufferID, detail::BufferD using UniqueTexture = std_experimental::unique_resource<TextureID, detail::TextureDeleter>; using UniqueVertexArray = std_experimental::unique_resource<VertexArrayID, detail::VertexArrayDeleter>; using UniqueFramebuffer = std_experimental::unique_resource<FramebufferID, detail::FramebufferDeleter>; +using UniqueRenderbuffer = std_experimental::unique_resource<RenderbufferID, detail::RenderbufferDeleter>; } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/renderbuffer.hpp b/src/mbgl/gl/renderbuffer.hpp new file mode 100644 index 0000000000..9e8993bb77 --- /dev/null +++ b/src/mbgl/gl/renderbuffer.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include <mbgl/gl/object.hpp> + +#include <array> + +namespace mbgl { +namespace gl { + +template <RenderbufferType renderbufferType> +class Renderbuffer { +public: + using type = std::integral_constant<RenderbufferType, renderbufferType>; + std::array<uint16_t, 2> size; + gl::UniqueRenderbuffer renderbuffer; +}; + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/state.hpp b/src/mbgl/gl/state.hpp index dbb005e77d..efe869e5b9 100644 --- a/src/mbgl/gl/state.hpp +++ b/src/mbgl/gl/state.hpp @@ -3,20 +3,6 @@ namespace mbgl { namespace gl { -// Helper struct that allows obtaining the default value of a Value class -template <typename T, typename = void> -struct DefaultValue { - static typename T::Type Get() { - return T::Get(); - } -}; -template <typename T> -struct DefaultValue<T, decltype((void)T::Default, void())> { - static typename T::Type Get() { - return T::Default; - } -}; - // Wraps a piece of OpenGL state and remember its value to avoid redundant state calls. // Wrapped types need to implement to the Value class interface: // @@ -26,17 +12,12 @@ struct DefaultValue<T, decltype((void)T::Default, void())> { // static void Set(const Type& value); // static Type Get(); // }; -// -// The Get() function is optional, but if it is omitted, you must provide a Default. -// Default is also optional, but if it is omitted, you must provide a Get() function. -// If both are present, DefaultValue<T>::Get() will use the Default member. template <typename T> class State { public: void operator=(const typename T::Type& value) { if (*this != value) { - dirty = false; - currentValue = value; + setCurrentValue(value); T::Set(currentValue); } } @@ -49,9 +30,9 @@ public: return dirty || currentValue != value; } - // Explicitly resets the piece of OpenGL state to its default value. - void reset() { - *this = defaultValue; + void setCurrentValue(const typename T::Type& value) { + dirty = false; + currentValue = value; } // Mark the state as dirty. This means that the next time we are assigning a value to this @@ -68,14 +49,9 @@ public: return dirty; } - void setDefaultValue(const typename T::Type& value) { - defaultValue = value; - } - private: - typename T::Type defaultValue = DefaultValue<T>::Get(); - typename T::Type currentValue = defaultValue; - bool dirty = false; + typename T::Type currentValue = T::Default; + bool dirty = true; }; // Helper struct that stores the current state and restores it upon destruction. You should not use diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index f24674457a..dccc61b03a 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -48,6 +48,11 @@ enum class BufferType : uint32_t { Element = 0x8893 }; +enum class RenderbufferType : uint32_t { + RGBA = 0x8058, + DepthStencil = 0x88F0, +}; + enum class TextureMipMap : bool { No = false, Yes = true }; enum class TextureFilter : bool { Nearest = false, Linear = true }; diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp index e78b97ef2e..14cd03efc4 100644 --- a/src/mbgl/gl/value.cpp +++ b/src/mbgl/gl/value.cpp @@ -241,6 +241,8 @@ ActiveTexture::Type ActiveTexture::Get() { return static_cast<Type>(activeTexture - GL_TEXTURE0); } +const constexpr Viewport::Type Viewport::Default; + void Viewport::Set(const Type& value) { MBGL_CHECK_ERROR(glViewport(value.x, value.y, value.width, value.height)); } @@ -252,6 +254,8 @@ Viewport::Type Viewport::Get() { static_cast<uint16_t>(viewport[2]), static_cast<uint16_t>(viewport[3]) }; } +const constexpr BindFramebuffer::Type BindFramebuffer::Default; + void BindFramebuffer::Set(const Type& value) { MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, value)); } @@ -262,6 +266,18 @@ BindFramebuffer::Type BindFramebuffer::Get() { return binding; } +const constexpr BindRenderbuffer::Type BindRenderbuffer::Default; + +void BindRenderbuffer::Set(const Type& value) { + MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, value)); +} + +BindRenderbuffer::Type BindRenderbuffer::Get() { + GLint binding; + MBGL_CHECK_ERROR(glGetIntegerv(GL_RENDERBUFFER_BINDING, &binding)); + return binding; +} + const constexpr BindTexture::Type BindTexture::Default; void BindTexture::Set(const Type& value) { diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp index 9110b33a16..866ce389a4 100644 --- a/src/mbgl/gl/value.hpp +++ b/src/mbgl/gl/value.hpp @@ -180,6 +180,7 @@ struct Viewport { uint16_t width; uint16_t height; }; + static const constexpr Type Default = { 0, 0, 0, 0 }; static void Set(const Type&); static Type Get(); }; @@ -190,6 +191,14 @@ constexpr bool operator!=(const Viewport::Type& a, const Viewport::Type& b) { struct BindFramebuffer { using Type = FramebufferID; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct BindRenderbuffer { + using Type = RenderbufferID; + static const constexpr Type Default = 0; static void Set(const Type&); static Type Get(); }; diff --git a/src/mbgl/map/backend.cpp b/src/mbgl/map/backend.cpp index fea4b70ce9..8a06fe2d91 100644 --- a/src/mbgl/map/backend.cpp +++ b/src/mbgl/map/backend.cpp @@ -1,9 +1,19 @@ #include <mbgl/map/backend.hpp> +#include <mbgl/gl/context.hpp> #include <cassert> namespace mbgl { +Backend::Backend() : context(std::make_unique<gl::Context>()) { +} + +gl::Context& Backend::getContext() { + return *context; +} + +Backend::~Backend() = default; + void Backend::notifyMapChange(MapChange) { // no-op } diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index ab85187c98..7b58026386 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -35,10 +35,18 @@ enum class RenderState : uint8_t { Fully, }; +struct StillImageRequest { + StillImageRequest(View& view_, Map::StillImageCallback&& callback_) + : view(view_), callback(std::move(callback_)) { + } + + View& view; + Map::StillImageCallback callback; +}; + class Map::Impl : public style::Observer { public: Impl(Backend&, - View&, float pixelRatio, FileSource&, Scheduler&, @@ -54,12 +62,11 @@ public: void onResourceError(std::exception_ptr) override; void update(); - void render(); + void render(View&); void loadStyleJSON(const std::string&); Backend& backend; - View& view; FileSource& fileSource; Scheduler& scheduler; @@ -85,14 +92,14 @@ public: std::unique_ptr<AsyncRequest> styleRequest; - Map::StillImageCallback callback; + std::unique_ptr<StillImageRequest> stillImageRequest; size_t sourceCacheSize; TimePoint timePoint; bool loading = false; }; Map::Map(Backend& backend, - View& view, + const std::array<uint16_t, 2> size, const float pixelRatio, FileSource& fileSource, Scheduler& scheduler, @@ -101,7 +108,6 @@ Map::Map(Backend& backend, ConstrainMode constrainMode, ViewportMode viewportMode) : impl(std::make_unique<Impl>(backend, - view, pixelRatio, fileSource, scheduler, @@ -109,12 +115,10 @@ Map::Map(Backend& backend, contextMode, constrainMode, viewportMode)) { - view.initialize(this); - update(Update::Dimensions); + impl->transform.resize(size); } Map::Impl::Impl(Backend& backend_, - View& view_, float pixelRatio_, FileSource& fileSource_, Scheduler& scheduler_, @@ -123,7 +127,6 @@ Map::Impl::Impl(Backend& backend_, ConstrainMode constrainMode_, ViewportMode viewportMode_) : backend(backend_), - view(view_), fileSource(fileSource_), scheduler(scheduler_), transform([this](MapChange change) { backend.notifyMapChange(change); }, @@ -150,42 +153,38 @@ Map::~Map() { impl->backend.deactivate(); } -void Map::renderStill(StillImageCallback callback) { +void Map::renderStill(View& view, StillImageCallback callback) { if (!callback) { Log::Error(Event::General, "StillImageCallback not set"); return; } if (impl->mode != MapMode::Still) { - callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")), {}); + callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode"))); return; } - if (impl->callback) { - callback(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")), {}); + if (impl->stillImageRequest) { + callback(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image"))); return; } if (!impl->style) { - callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")), {}); + callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style"))); return; } if (impl->style->getLastError()) { - callback(impl->style->getLastError(), {}); + callback(impl->style->getLastError()); return; } - impl->callback = callback; + impl->stillImageRequest = std::make_unique<StillImageRequest>(view, std::move(callback)); impl->updateFlags |= Update::RenderStill; impl->asyncUpdate.send(); } -void Map::update(Update flags) { - impl->onUpdate(flags); -} - -void Map::render() { +void Map::render(View& view) { if (!impl->style) { return; } @@ -198,7 +197,7 @@ void Map::render() { const Update flags = impl->transform.updateTransitions(Clock::now()); - impl->render(); + impl->render(view); impl->backend.notifyMapChange(isFullyLoaded() ? MapChangeDidFinishRenderingFrameFullyRendered : @@ -218,16 +217,20 @@ void Map::render() { // Triggers an asynchronous update, that eventually triggers a view // invalidation, causing renderSync to be called again if in transition. if (flags != Update::Nothing) { - update(flags); + impl->onUpdate(flags); } } +void Map::triggerRepaint() { + impl->backend.invalidate(); +} + void Map::Impl::update() { if (!style) { updateFlags = Update::Nothing; } - if (updateFlags == Update::Nothing || (mode == MapMode::Still && !callback)) { + if (updateFlags == Update::Nothing || (mode == MapMode::Still && !stillImageRequest)) { return; } @@ -270,18 +273,19 @@ void Map::Impl::update() { if (mode == MapMode::Continuous) { backend.invalidate(); - } else if (callback && style->isLoaded()) { + } else if (stillImageRequest && style->isLoaded()) { + // TODO: determine whether we need activate/deactivate backend.activate(); - render(); + render(stillImageRequest->view); backend.deactivate(); } updateFlags = Update::Nothing; } -void Map::Impl::render() { +void Map::Impl::render(View& view) { if (!painter) { - painter = std::make_unique<Painter>(transform.getState()); + painter = std::make_unique<Painter>(backend.getContext(), transform.getState()); } FrameData frameData { timePoint, @@ -296,8 +300,8 @@ void Map::Impl::render() { annotationManager->getSpriteAtlas()); if (mode == MapMode::Still) { - callback(nullptr, view.readStillImage()); - callback = nullptr; + auto request = std::move(stillImageRequest); + request->callback(nullptr); } painter->cleanup(); @@ -399,12 +403,12 @@ std::string Map::getStyleJSON() const { void Map::cancelTransitions() { impl->transform.cancelTransitions(); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setGestureInProgress(bool inProgress) { impl->transform.setGestureInProgress(inProgress); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } bool Map::isGestureInProgress() const { @@ -431,24 +435,24 @@ CameraOptions Map::getCameraOptions(optional<EdgeInsets> padding) const { void Map::jumpTo(const CameraOptions& camera) { impl->transform.jumpTo(camera); - update(camera.zoom ? Update::RecalculateStyle : Update::Repaint); + impl->onUpdate(camera.zoom ? Update::RecalculateStyle : Update::Repaint); } void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) { impl->transform.easeTo(camera, animation); - update(camera.zoom ? Update::RecalculateStyle : Update::Repaint); + impl->onUpdate(camera.zoom ? Update::RecalculateStyle : Update::Repaint); } void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) { impl->transform.flyTo(camera, animation); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } #pragma mark - Position void Map::moveBy(const ScreenCoordinate& point, const Duration& duration) { impl->transform.moveBy(point, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setLatLng(const LatLng& latLng, const Duration& duration) { @@ -457,12 +461,12 @@ void Map::setLatLng(const LatLng& latLng, const Duration& duration) { void Map::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const Duration& duration) { impl->transform.setLatLng(latLng, padding, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.setLatLng(latLng, anchor, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } LatLng Map::getLatLng(optional<EdgeInsets> padding) const { @@ -477,7 +481,7 @@ void Map::resetPosition(optional<EdgeInsets> padding) { camera.padding = padding; camera.zoom = 0; impl->transform.jumpTo(camera); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } @@ -485,12 +489,12 @@ void Map::resetPosition(optional<EdgeInsets> padding) { void Map::scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.scaleBy(ds, anchor, duration); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } void Map::setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.setScale(scale, anchor, duration); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } double Map::getScale() const { @@ -503,7 +507,7 @@ void Map::setZoom(double zoom, const Duration& duration) { void Map::setZoom(double zoom, optional<EdgeInsets> padding, const Duration& duration) { impl->transform.setZoom(zoom, padding, duration); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } double Map::getZoom() const { @@ -516,7 +520,7 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& durat void Map::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const Duration& duration) { impl->transform.setLatLngZoom(latLng, zoom, padding, duration); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, optional<EdgeInsets> padding) const { @@ -613,6 +617,11 @@ double Map::getMaxZoom() const { #pragma mark - Size +void Map::setSize(const std::array<uint16_t, 2>& size) { + impl->transform.resize(size); + impl->onUpdate(Update::Repaint); +} + uint16_t Map::getWidth() const { return impl->transform.getState().getWidth(); } @@ -625,7 +634,7 @@ uint16_t Map::getHeight() const { void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& duration) { impl->transform.rotateBy(first, second, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setBearing(double degrees, const Duration& duration) { @@ -634,12 +643,12 @@ void Map::setBearing(double degrees, const Duration& duration) { void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setBearing(double degrees, optional<EdgeInsets> padding, const Duration& duration) { impl->transform.setAngle(-degrees * util::DEG2RAD, padding, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } double Map::getBearing() const { @@ -648,7 +657,7 @@ double Map::getBearing() const { void Map::resetNorth(const Duration& duration) { impl->transform.setAngle(0, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } #pragma mark - Pitch @@ -659,7 +668,7 @@ void Map::setPitch(double pitch, const Duration& duration) { void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.setPitch(pitch * util::DEG2RAD, anchor, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } double Map::getPitch() const { @@ -670,7 +679,7 @@ double Map::getPitch() const { void Map::setNorthOrientation(NorthOrientation orientation) { impl->transform.setNorthOrientation(orientation); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } NorthOrientation Map::getNorthOrientation() const { @@ -681,7 +690,7 @@ NorthOrientation Map::getNorthOrientation() const { void Map::setConstrainMode(mbgl::ConstrainMode mode) { impl->transform.setConstrainMode(mode); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } ConstrainMode Map::getConstrainMode() const { @@ -692,7 +701,7 @@ ConstrainMode Map::getConstrainMode() const { void Map::setViewportMode(mbgl::ViewportMode mode) { impl->transform.setViewportMode(mode); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } ViewportMode Map::getViewportMode() const { @@ -737,17 +746,17 @@ double Map::getTopOffsetPixelsForAnnotationIcon(const std::string& name) { AnnotationID Map::addAnnotation(const Annotation& annotation) { auto result = impl->annotationManager->addAnnotation(annotation, getMaxZoom()); - update(Update::AnnotationStyle | Update::AnnotationData); + impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData); return result; } void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) { - update(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom())); + impl->onUpdate(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom())); } void Map::removeAnnotation(AnnotationID annotation) { impl->annotationManager->removeAnnotation(annotation); - update(Update::AnnotationStyle | Update::AnnotationData); + impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData); } #pragma mark - Feature query api @@ -833,7 +842,7 @@ void Map::addLayer(std::unique_ptr<Layer> layer, const optional<std::string>& be impl->backend.activate(); impl->style->addLayer(std::move(layer), before); - update(Update::Classes); + impl->onUpdate(Update::Classes); impl->backend.deactivate(); } @@ -847,7 +856,7 @@ void Map::removeLayer(const std::string& id) { impl->backend.activate(); impl->style->removeLayer(id); - update(Update::Classes); + impl->onUpdate(Update::Classes); impl->backend.deactivate(); } @@ -861,7 +870,7 @@ void Map::addImage(const std::string& name, std::unique_ptr<const SpriteImage> i impl->style->spriteAtlas->setSprite(name, std::move(image)); impl->style->spriteAtlas->updateDirty(); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::removeImage(const std::string& name) { @@ -873,7 +882,7 @@ void Map::removeImage(const std::string& name) { impl->style->spriteAtlas->removeSprite(name); impl->style->spriteAtlas->updateDirty(); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } #pragma mark - Defaults @@ -917,7 +926,7 @@ double Map::getDefaultPitch() const { void Map::setDebug(MapDebugOptions debugOptions) { impl->debugOptions = debugOptions; - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::cycleDebugOptions() { @@ -941,7 +950,7 @@ void Map::cycleDebugOptions() { else impl->debugOptions = MapDebugOptions::TileBorders; - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } MapDebugOptions Map::getDebug() const { @@ -954,20 +963,20 @@ bool Map::isFullyLoaded() const { void Map::addClass(const std::string& className) { if (impl->style && impl->style->addClass(className)) { - update(Update::Classes); + impl->onUpdate(Update::Classes); } } void Map::removeClass(const std::string& className) { if (impl->style && impl->style->removeClass(className)) { - update(Update::Classes); + impl->onUpdate(Update::Classes); } } void Map::setClasses(const std::vector<std::string>& classNames) { if (impl->style) { impl->style->setClasses(classNames); - update(Update::Classes); + impl->onUpdate(Update::Classes); } } @@ -1019,10 +1028,6 @@ void Map::Impl::onSourceAttributionChanged(style::Source&, const std::string&) { } void Map::Impl::onUpdate(Update flags) { - if (flags & Update::Dimensions) { - transform.resize(view.getSize()); - } - updateFlags |= flags; asyncUpdate.send(); } @@ -1036,9 +1041,9 @@ void Map::Impl::onStyleError() { } void Map::Impl::onResourceError(std::exception_ptr error) { - if (mode == MapMode::Still && callback) { - callback(error, {}); - callback = nullptr; + if (mode == MapMode::Still && stillImageRequest) { + auto request = std::move(stillImageRequest); + request->callback(error); } } diff --git a/include/mbgl/map/update.hpp b/src/mbgl/map/update.hpp index 1da7e3ac92..dc383b819e 100644 --- a/include/mbgl/map/update.hpp +++ b/src/mbgl/map/update.hpp @@ -6,7 +6,6 @@ namespace mbgl { enum class Update { Nothing = 0, - Dimensions = 1 << 1, Classes = 1 << 2, RecalculateStyle = 1 << 3, RenderStill = 1 << 4, diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp deleted file mode 100644 index 23ce70a3cf..0000000000 --- a/src/mbgl/map/view.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include <mbgl/map/view.hpp> -#include <mbgl/map/map.hpp> - -#include <cassert> - -namespace mbgl { - -PremultipliedImage View::readStillImage(std::array<uint16_t, 2>) { - return {}; -} - -void View::initialize(Map *map_) { - assert(map_); - map = map_; -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index ee7ef6d212..fc61d6e0a0 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -31,6 +31,8 @@ #include <mbgl/util/mat3.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/offscreen_texture.hpp> + #include <cassert> #include <algorithm> #include <iostream> @@ -40,8 +42,9 @@ namespace mbgl { using namespace style; -Painter::Painter(const TransformState& state_) - : state(state_), +Painter::Painter(gl::Context& context_, const TransformState& state_) + : context(context_), + state(state_), tileTriangleVertexBuffer(context.createVertexBuffer(std::vector<FillVertex> {{ { 0, 0 }, { util::EXTENT, 0 }, @@ -71,9 +74,6 @@ Painter::Painter(const TransformState& state_) #ifndef NDEBUG overdrawShaders = std::make_unique<Shaders>(context, gl::Shader::Overdraw); #endif - - // Reset GL values - context.setDirtyState(); } Painter::~Painter() = default; @@ -93,9 +93,10 @@ void Painter::cleanup() { } void Painter::render(const Style& style, const FrameData& frame_, View& view, SpriteAtlas& annotationSpriteAtlas) { - context.viewport.setDefaultValue( - { 0, 0, view.getFramebufferSize()[0], view.getFramebufferSize()[1] }); frame = frame_; + if (frame.contextMode == GLContextMode::Shared) { + context.setDirtyState(); + } PaintParameters parameters { #ifndef NDEBUG @@ -126,12 +127,14 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp frameHistory.record(frame.timePoint, state.getZoom(), frame.mapMode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Milliseconds(0)); + // - UPLOAD PASS ------------------------------------------------------------------------------- // Uploads all required buffers and images before we do any actual rendering. { MBGL_DEBUG_GROUP("upload"); spriteAtlas->upload(context, 0); + lineAtlas->upload(context, 0); glyphAtlas->upload(context, 0); frameHistory.upload(context, 0); @@ -149,10 +152,8 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp // tiles whatsoever. { MBGL_DEBUG_GROUP("clear"); - context.bindFramebuffer.setDirty(); view.bind(); - context.viewport.reset(); - context.stencilFunc.reset(); + context.stencilFunc = { gl::StencilTestFunction::Always, 0, ~0u }; context.stencilTest = true; context.stencilMask = 0xFF; context.depthTest = false; @@ -249,10 +250,6 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp context.vertexArrayObject = 0; } - - if (frame.contextMode == GLContextMode::Shared) { - context.setDirtyState(); - } } template <class Iterator> @@ -296,17 +293,21 @@ void Painter::renderPass(PaintParameters& parameters, renderBackground(parameters, *layer.as<BackgroundLayer>()); } else if (layer.is<CustomLayer>()) { MBGL_DEBUG_GROUP(layer.baseImpl->id + " - custom"); + + // Reset GL state to a known state so the CustomLayer always has a clean slate. context.vertexArrayObject = 0; context.depthFunc = gl::DepthTestFunction::LessEqual; context.depthTest = true; context.depthMask = false; context.stencilTest = false; setDepthSublayer(0); + layer.as<CustomLayer>()->impl->render(state); - context.setDirtyState(); - context.bindFramebuffer.setDirty(); + + // Reset the view back to our original one, just in case the CustomLayer changed + // the viewport or Framebuffer. parameters.view.bind(); - context.viewport.reset(); + context.setDirtyState(); } else { MBGL_DEBUG_GROUP(layer.baseImpl->id + " - " + util::toString(item.tile->id)); if (item.bucket->needsClipping()) { diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index d2c89c04a9..f339ed1aed 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -68,7 +68,7 @@ struct FrameData { class Painter : private util::noncopyable { public: - Painter(const TransformState&); + Painter(gl::Context&, const TransformState&); ~Painter(); void render(const style::Style&, @@ -153,6 +153,9 @@ private: } #endif +private: + gl::Context& context; + mat4 projMatrix; std::array<float, 2> pixelsToGLUnits; @@ -169,8 +172,6 @@ private: int indent = 0; - gl::Context context; - RenderPass pass = RenderPass::Opaque; int numSublayers = 3; diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp index ca961f84e7..e57bb2205e 100644 --- a/src/mbgl/renderer/painter_debug.cpp +++ b/src/mbgl/renderer/painter_debug.cpp @@ -89,7 +89,7 @@ void Painter::renderDebugFrame(const mat4 &matrix) { } #ifndef NDEBUG -void Painter::renderClipMasks(PaintParameters& parameters) { +void Painter::renderClipMasks(PaintParameters&) { context.stencilTest = false; context.depthTest = false; context.program = 0; @@ -100,13 +100,13 @@ void Painter::renderClipMasks(PaintParameters& parameters) { context.rasterPos = { -1, -1, 0, 0 }; // Read the stencil buffer - const auto& fbSize = parameters.view.getFramebufferSize(); - auto pixels = std::make_unique<uint8_t[]>(fbSize[0] * fbSize[1]); + const auto viewport = context.viewport.getCurrentValue(); + auto pixels = std::make_unique<uint8_t[]>(viewport.width * viewport.height); MBGL_CHECK_ERROR(glReadPixels( - 0, // GLint x - 0, // GLint y - fbSize[0], // GLsizei width - fbSize[1], // GLsizei height + viewport.x, // GLint x + viewport.y, // GLint y + viewport.width, // GLsizei width + viewport.height, // GLsizei height GL_STENCIL_INDEX, // GLenum format GL_UNSIGNED_BYTE, // GLenum type pixels.get() // GLvoid * data @@ -114,22 +114,21 @@ void Painter::renderClipMasks(PaintParameters& parameters) { // Scale the Stencil buffer to cover the entire color space. auto it = pixels.get(); - auto end = it + fbSize[0] * fbSize[1]; + auto end = it + viewport.width * viewport.height; const auto factor = 255.0f / *std::max_element(it, end); for (; it != end; ++it) { *it *= factor; } - MBGL_CHECK_ERROR(glWindowPos2i(0, 0)); - MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get())); -#else - (void)parameters; + MBGL_CHECK_ERROR(glWindowPos2i(viewport.x, viewport.y)); + MBGL_CHECK_ERROR(glDrawPixels(viewport.width, viewport.height, GL_LUMINANCE, GL_UNSIGNED_BYTE, + pixels.get())); #endif // MBGL_USE_GLES2 } #endif // NDEBUG #ifndef NDEBUG -void Painter::renderDepthBuffer(PaintParameters& parameters) { +void Painter::renderDepthBuffer(PaintParameters&) { context.stencilTest = false; context.depthTest = false; context.program = 0; @@ -140,27 +139,26 @@ void Painter::renderDepthBuffer(PaintParameters& parameters) { context.rasterPos = { -1, -1, 0, 0 }; // Read the stencil buffer - const auto& fbSize = parameters.view.getFramebufferSize(); - auto pixels = std::make_unique<GLubyte[]>(fbSize[0] * fbSize[1]); + const auto viewport = context.viewport.getCurrentValue(); + auto pixels = std::make_unique<uint8_t[]>(viewport.width * viewport.height); const double base = 1.0 / (1.0 - depthRangeSize); glPixelTransferf(GL_DEPTH_SCALE, base); glPixelTransferf(GL_DEPTH_BIAS, 1.0 - base); MBGL_CHECK_ERROR(glReadPixels( - 0, // GLint x - 0, // GLint y - fbSize[0], // GLsizei width - fbSize[1], // GLsizei height + viewport.x, // GLint x + viewport.y, // GLint y + viewport.width, // GLsizei width + viewport.height, // GLsizei height GL_DEPTH_COMPONENT, // GLenum format GL_UNSIGNED_BYTE, // GLenum type pixels.get() // GLvoid * data )); - MBGL_CHECK_ERROR(glWindowPos2i(0, 0)); - MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get())); -#else - (void)parameters; + MBGL_CHECK_ERROR(glWindowPos2i(viewport.x, viewport.y)); + MBGL_CHECK_ERROR(glDrawPixels(viewport.width, viewport.height, GL_LUMINANCE, GL_UNSIGNED_BYTE, + pixels.get())); #endif // MBGL_USE_GLES2 } #endif // NDEBUG diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 1fc5f11079..b6606ca40b 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -30,7 +30,9 @@ void Painter::renderFill(PaintParameters& parameters, const bool isOutlineColorDefined = !properties.fillOutlineColor.isUndefined(); Color strokeColor = isOutlineColorDefined? properties.fillOutlineColor : fillColor; - auto worldSize = util::convert<GLfloat>(parameters.view.getFramebufferSize()); + const auto viewport = context.viewport.getCurrentValue(); + const std::array<GLfloat, 2> worldSize{ { static_cast<GLfloat>(viewport.width), + static_cast<GLfloat>(viewport.height) } }; bool pattern = !properties.fillPattern.value.from.empty(); bool outline = properties.fillAntialias && !pattern && isOutlineColorDefined; diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp index 51049f8282..40bb70b70e 100644 --- a/src/mbgl/util/offscreen_texture.cpp +++ b/src/mbgl/util/offscreen_texture.cpp @@ -1,70 +1,48 @@ +#include <mbgl/util/offscreen_texture.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/gl/gl.hpp> -#include <mbgl/util/offscreen_texture.hpp> +#include <cstring> #include <cassert> namespace mbgl { -void OffscreenTexture::bind(gl::Context& context, - std::array<uint16_t, 2> size) { +OffscreenTexture::OffscreenTexture(gl::Context& context_, std::array<uint16_t, 2> size_) + : context(context_), size(std::move(size_)) { assert(size[0] > 0 && size[1] > 0); +} - if (!texture || texture->size != size) { - texture = context.createTexture(size); - } - +void OffscreenTexture::bind() { if (!framebuffer) { - framebuffer = context.createFramebuffer(); - context.bindFramebuffer = *framebuffer; - MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - texture->texture, 0)); - - GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER)); - if (status != GL_FRAMEBUFFER_COMPLETE) { - switch (status) { - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - throw std::runtime_error("Couldn't create framebuffer: incomplete attachment"); - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - throw std::runtime_error( - "Couldn't create framebuffer: incomplete missing attachment"); -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - throw std::runtime_error("Couldn't create framebuffer: incomplete draw buffer"); -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - throw std::runtime_error("Couldn't create framebuffer: incomplete read buffer"); -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - throw std::runtime_error("Couldn't create framebuffer: incomplete dimensions"); -#endif - - case GL_FRAMEBUFFER_UNSUPPORTED: - throw std::runtime_error("Couldn't create framebuffer: unsupported"); - default: - throw std::runtime_error("Couldn't create framebuffer: other"); - } - } + texture = context.createTexture(size); + framebuffer = context.createFramebuffer(*texture); } else { - context.bindFramebuffer = *framebuffer; + context.bindFramebuffer = framebuffer->framebuffer; } context.viewport = { 0, 0, size[0], size[1] }; } gl::Texture& OffscreenTexture::getTexture() { + assert(texture); return *texture; } -std::array<uint16_t, 2> OffscreenTexture::getSize() const { - if (texture) { - // Use explicit dereference instead of -> due to clang 3.5 bug - return (*texture).size; - } else { - return {{ 0, 0 }}; +PremultipliedImage OffscreenTexture::readStillImage() { + PremultipliedImage image { size[0], size[1] }; + MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get())); + + const auto stride = image.stride(); + auto tmp = std::make_unique<uint8_t[]>(stride); + uint8_t* rgba = image.data.get(); + for (int i = 0, j = size[1] - 1; i < j; i++, j--) { + std::memcpy(tmp.get(), rgba + i * stride, stride); + std::memcpy(rgba + i * stride, rgba + j * stride, stride); + std::memcpy(rgba + j * stride, tmp.get(), stride); } + + return image; } + } // namespace mbgl diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp index c71c0e51d9..8928bc2434 100644 --- a/src/mbgl/util/offscreen_texture.hpp +++ b/src/mbgl/util/offscreen_texture.hpp @@ -1,6 +1,10 @@ #pragma once +#include <mbgl/map/view.hpp> +#include <mbgl/gl/framebuffer.hpp> #include <mbgl/gl/texture.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/image.hpp> namespace mbgl { @@ -8,15 +12,20 @@ namespace gl { class Context; } // namespace gl -class OffscreenTexture { +class OffscreenTexture : public View { public: - void bind(gl::Context&, std::array<uint16_t, 2> size); + OffscreenTexture(gl::Context&, std::array<uint16_t, 2> size = {{ 256, 256 }}); + + void bind() override; + + PremultipliedImage readStillImage(); gl::Texture& getTexture(); - std::array<uint16_t, 2> getSize() const; private: - optional<gl::UniqueFramebuffer> framebuffer; + gl::Context& context; + std::array<uint16_t, 2> size; + optional<gl::Framebuffer> framebuffer; optional<gl::Texture> texture; }; diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp index 53c0182ee9..2c875796bd 100644 --- a/test/api/annotations.test.cpp +++ b/test/api/annotations.test.cpp @@ -6,7 +6,7 @@ #include <mbgl/sprite/sprite_image.hpp> #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/color.hpp> @@ -24,14 +24,14 @@ class AnnotationTest { public: util::RunLoop loop; HeadlessBackend backend; - HeadlessView view; + OffscreenView view{ backend.getContext() }; StubFileSource fileSource; ThreadPool threadPool { 4 }; - Map map { backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still }; + Map map { backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still }; void checkRendering(const char * name) { test::checkImage(std::string("test/fixtures/annotations/") + name, - test::render(map), 0.0002, 0.1); + test::render(map, view), 0.0002, 0.1); } }; @@ -162,7 +162,7 @@ TEST(Annotations, AddMultiple) { test.map.addAnnotationIcon("default_marker", namedMarker("default_marker.png")); test.map.addAnnotation(SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" }); - test::render(test.map); + test::render(test.map, test.view); test.map.addAnnotation(SymbolAnnotation { Point<double> { 10, 0 }, "default_marker" }); test.checkRendering("add_multiple"); @@ -172,7 +172,7 @@ TEST(Annotations, NonImmediateAdd) { AnnotationTest test; test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test::render(test.map); + test::render(test.map, test.view); Polygon<double> polygon = {{ {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }}; FillAnnotation annotation { polygon }; @@ -190,7 +190,7 @@ TEST(Annotations, UpdateSymbolAnnotationGeometry) { test.map.addAnnotationIcon("flipped_marker", namedMarker("flipped_marker.png")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); - test::render(test.map); + test::render(test.map, test.view); test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" }); test.checkRendering("update_point"); @@ -204,7 +204,7 @@ TEST(Annotations, UpdateSymbolAnnotationIcon) { test.map.addAnnotationIcon("flipped_marker", namedMarker("flipped_marker.png")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); - test::render(test.map); + test::render(test.map, test.view); test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { 0, 0 }, "flipped_marker" }); test.checkRendering("update_icon"); @@ -220,7 +220,7 @@ TEST(Annotations, UpdateLineAnnotationGeometry) { test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID line = test.map.addAnnotation(annotation); - test::render(test.map); + test::render(test.map, test.view); annotation.geometry = LineString<double> {{ { 0, 0 }, { -45, -45 } }}; test.map.updateAnnotation(line, annotation); @@ -237,7 +237,7 @@ TEST(Annotations, UpdateLineAnnotationStyle) { test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID line = test.map.addAnnotation(annotation); - test::render(test.map); + test::render(test.map, test.view); annotation.color = { { 0, 255, 0, 1 } }; annotation.width = { 2 }; @@ -254,7 +254,7 @@ TEST(Annotations, UpdateFillAnnotationGeometry) { test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID fill = test.map.addAnnotation(annotation); - test::render(test.map); + test::render(test.map, test.view); annotation.geometry = Polygon<double> {{ {{ { 0, 0 }, { 0, 45 }, { 45, 0 } }} }}; test.map.updateAnnotation(fill, annotation); @@ -271,7 +271,7 @@ TEST(Annotations, UpdateFillAnnotationStyle) { test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID fill = test.map.addAnnotation(annotation); - test::render(test.map); + test::render(test.map, test.view); annotation.color = { { 0, 255, 0, 1 } }; test.map.updateAnnotation(fill, annotation); @@ -285,7 +285,7 @@ TEST(Annotations, RemovePoint) { test.map.addAnnotationIcon("default_marker", namedMarker("default_marker.png")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); - test::render(test.map); + test::render(test.map, test.view); test.map.removeAnnotation(point); test.checkRendering("remove_point"); @@ -302,7 +302,7 @@ TEST(Annotations, RemoveShape) { test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID shape = test.map.addAnnotation(annotation); - test::render(test.map); + test::render(test.map, test.view); test.map.removeAnnotation(shape); test.checkRendering("remove_shape"); @@ -314,7 +314,7 @@ TEST(Annotations, ImmediateRemoveShape) { test.map.removeAnnotation(test.map.addAnnotation(LineAnnotation { LineString<double>() })); test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test::render(test.map); + test::render(test.map, test.view); } TEST(Annotations, SwitchStyle) { @@ -324,7 +324,7 @@ TEST(Annotations, SwitchStyle) { test.map.addAnnotationIcon("default_marker", namedMarker("default_marker.png")); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); - test::render(test.map); + test::render(test.map, test.view); test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); test.checkRendering("switch_style"); @@ -338,7 +338,7 @@ TEST(Annotations, QueryRenderedFeatures) { test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 50 }, "default_marker" }); - test::render(test.map); + test::render(test.map, test.view); auto features = test.map.queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 })); EXPECT_EQ(features.size(), 1u); @@ -370,7 +370,7 @@ TEST(Annotations, QueryFractionalZoomLevels) { test.map.setLatLngZoom({ 5, 5 }, 0); for (uint16_t zoomSteps = 10; zoomSteps <= 20; ++zoomSteps) { test.map.setZoom(zoomSteps / 10.0); - test::render(test.map); + test::render(test.map, test.view); auto features = test.map.queryRenderedFeatures(box); // Filter out repeated features. @@ -402,7 +402,7 @@ TEST(Annotations, VisibleFeatures) { // Change bearing *after* adding annotations causes them to be reordered. test.map.setBearing(45); - test::render(test.map); + test::render(test.map, test.view); auto features = test.map.queryRenderedFeatures(box); auto sortID = [](const Feature& lhs, const Feature& rhs) { return lhs.id < rhs.id; }; @@ -413,7 +413,7 @@ TEST(Annotations, VisibleFeatures) { test.map.setBearing(0); test.map.setZoom(4); - test::render(test.map); + test::render(test.map, test.view); features = test.map.queryRenderedFeatures(box); std::sort(features.begin(), features.end(), sortID); features.erase(std::unique(features.begin(), features.end(), sameID), features.end()); diff --git a/test/api/api_misuse.test.cpp b/test/api/api_misuse.test.cpp index f11363fa3c..eb271b0258 100644 --- a/test/api/api_misuse.test.cpp +++ b/test/api/api_misuse.test.cpp @@ -4,7 +4,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/storage/online_file_source.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/util/exception.hpp> @@ -21,14 +21,13 @@ TEST(API, RenderWithoutCallback) { util::RunLoop loop; HeadlessBackend backend; - HeadlessView view; - view.resize(128, 512); + OffscreenView view(backend.getContext(), {{ 128, 512 }}); StubFileSource fileSource; ThreadPool threadPool(4); - std::unique_ptr<Map> map = std::make_unique<Map>(backend, view, view.getPixelRatio(), - fileSource, threadPool, MapMode::Still); - map->renderStill(nullptr); + std::unique_ptr<Map> map = + std::make_unique<Map>(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); + map->renderStill(view, nullptr); // Force Map thread to join. map.reset(); @@ -47,15 +46,14 @@ TEST(API, RenderWithoutStyle) { util::RunLoop loop; HeadlessBackend backend; - HeadlessView view; - view.resize(128, 512); + OffscreenView view(backend.getContext(), {{ 128, 512 }}); StubFileSource fileSource; ThreadPool threadPool(4); - Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); + Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); std::exception_ptr error; - map.renderStill([&](std::exception_ptr error_, PremultipliedImage&&) { + map.renderStill(view, [&](std::exception_ptr error_) { error = error_; loop.stop(); }); diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp index 84ae780174..70de102b80 100644 --- a/test/api/custom_layer.test.cpp +++ b/test/api/custom_layer.test.cpp @@ -3,7 +3,7 @@ #include <mbgl/gl/gl.hpp> #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/style/layers/custom_layer.hpp> @@ -86,7 +86,7 @@ TEST(CustomLayer, Basic) { util::RunLoop loop; HeadlessBackend backend; - HeadlessView view; + OffscreenView view(backend.getContext()); #ifdef MBGL_ASSET_ZIP // Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/` @@ -97,7 +97,7 @@ TEST(CustomLayer, Basic) { ThreadPool threadPool(4); - Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); + Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/water.json")); map.setLatLngZoom({ 37.8, -122.5 }, 10); map.addLayer(std::make_unique<CustomLayer>( @@ -117,5 +117,5 @@ TEST(CustomLayer, Basic) { layer->setFillColor(Color{ 1.0, 1.0, 0.0, 1.0 }); map.addLayer(std::move(layer)); - test::checkImage("test/fixtures/custom_layer/basic", test::render(map), 0.0006, 0.1); + test::checkImage("test/fixtures/custom_layer/basic", test::render(map, view), 0.0006, 0.1); } diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index d989d222c5..8e59c19afc 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -1,6 +1,6 @@ #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/sprite/sprite_image.hpp> #include <mbgl/test/stub_file_source.hpp> @@ -22,15 +22,15 @@ public: map.setStyleJSON(util::read_file("test/fixtures/api/query_style.json")); map.addImage("test-icon", std::move(image)); - test::render(map); + test::render(map, view); } util::RunLoop loop; HeadlessBackend backend; - HeadlessView view; + OffscreenView view { backend.getContext() }; StubFileSource fileSource; ThreadPool threadPool { 4 }; - Map map { backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still }; + Map map { backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still }; }; } // end namespace diff --git a/test/api/render_missing.test.cpp b/test/api/render_missing.test.cpp index 8123070282..366d71e67d 100644 --- a/test/api/render_missing.test.cpp +++ b/test/api/render_missing.test.cpp @@ -3,7 +3,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/util/image.hpp> @@ -26,7 +26,7 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) { const auto style = util::read_file("test/fixtures/api/water_missing_tiles.json"); HeadlessBackend backend; - HeadlessView view(1, 256, 512); + OffscreenView view(backend.getContext(), {{ 256, 512 }}); #ifdef MBGL_ASSET_ZIP // Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/` DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip"); @@ -38,14 +38,14 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) { Log::setObserver(std::make_unique<FixtureLogObserver>()); - Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); + Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); std::string message; // This host does not respond (== connection error). // Are you seeing this test fail? Make sure you don't have a server running on port 3001! map.setStyleJSON(style); - map.renderStill([&](std::exception_ptr err, PremultipliedImage&&) { + map.renderStill(view, [&](std::exception_ptr err) { ASSERT_TRUE(err.operator bool()); try { std::rethrow_exception(err); diff --git a/test/api/repeated_render.test.cpp b/test/api/repeated_render.test.cpp index 5bc57198bd..715b49b289 100644 --- a/test/api/repeated_render.test.cpp +++ b/test/api/repeated_render.test.cpp @@ -3,7 +3,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/util/image.hpp> @@ -20,7 +20,7 @@ TEST(API, RepeatedRender) { const auto style = util::read_file("test/fixtures/api/water.json"); HeadlessBackend backend; - HeadlessView view(1, 256, 512); + OffscreenView view(backend.getContext(), {{ 256, 512 }}); #ifdef MBGL_ASSET_ZIP // Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/` DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip"); @@ -32,13 +32,13 @@ TEST(API, RepeatedRender) { Log::setObserver(std::make_unique<FixtureLogObserver>()); - Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); + Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); { map.setStyleJSON(style); PremultipliedImage result; - map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) { - result = std::move(image); + map.renderStill(view, [&](std::exception_ptr) { + result = view.readStillImage(); }); while (!result.size()) { @@ -55,8 +55,8 @@ TEST(API, RepeatedRender) { { map.setStyleJSON(style); PremultipliedImage result; - map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) { - result = std::move(image); + map.renderStill(view, [&](std::exception_ptr) { + result = view.readStillImage(); }); while (!result.size()) { diff --git a/test/gl/object.test.cpp b/test/gl/object.test.cpp index 1f9d48cff7..a3457d28c6 100644 --- a/test/gl/object.test.cpp +++ b/test/gl/object.test.cpp @@ -1,7 +1,7 @@ #include <mbgl/test/util.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/gl/gl.hpp> #include <mbgl/gl/context.hpp> @@ -43,11 +43,8 @@ TEST(GLObject, Value) { auto object = std::make_unique<mbgl::gl::State<MockGLObject>>(); EXPECT_EQ(object->getCurrentValue(), false); - EXPECT_FALSE(object->isDirty()); - EXPECT_FALSE(setFlag); - - object->setDirty(); EXPECT_TRUE(object->isDirty()); + EXPECT_FALSE(setFlag); *object = false; EXPECT_EQ(object->getCurrentValue(), false); @@ -59,16 +56,11 @@ TEST(GLObject, Value) { EXPECT_EQ(object->getCurrentValue(), true); EXPECT_FALSE(object->isDirty()); EXPECT_TRUE(setFlag); - - object->reset(); - EXPECT_EQ(object->getCurrentValue(), false); - EXPECT_FALSE(object->isDirty()); - EXPECT_TRUE(setFlag); } TEST(GLObject, Store) { mbgl::HeadlessBackend backend; - mbgl::HeadlessView view; + mbgl::OffscreenView view(backend.getContext()); mbgl::gl::Context context; EXPECT_TRUE(context.empty()); diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index a5d634e77a..97a09e94a0 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -5,7 +5,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/sprite/sprite_image.hpp> #include <mbgl/storage/network_status.hpp> @@ -23,15 +23,14 @@ using namespace std::literals::string_literals; struct MapTest { util::RunLoop runLoop; HeadlessBackend backend; - HeadlessView view; + OffscreenView view { backend.getContext() }; StubFileSource fileSource; ThreadPool threadPool { 4 }; }; TEST(Map, LatLngBehavior) { MapTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); @@ -65,12 +64,11 @@ TEST(Map, Offline) { fileSource.put(Resource::glyphs(prefix + "{fontstack}/{range}.pbf", {{"Helvetica"}}, {0, 255}), expiredItem("glyph.pbf")); NetworkStatus::Set(NetworkStatus::Status::Offline); - Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); map.setStyleURL(prefix + "style.json"); test::checkImage("test/fixtures/map/offline", - test::render(map), + test::render(map, test.view), 0.0015, 0.1); @@ -90,8 +88,8 @@ TEST(Map, SetStyleInvalidJSON) { }); { - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, - test.threadPool, MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, + MapMode::Still); map.setStyleJSON("invalid"); } @@ -122,8 +120,7 @@ TEST(Map, SetStyleInvalidURL) { } }); - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); map.setStyleURL("mapbox://bar"); test.runLoop.run(); @@ -132,8 +129,7 @@ TEST(Map, SetStyleInvalidURL) { TEST(Map, DoubleStyleLoad) { MapTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); map.setStyleJSON(""); map.setStyleJSON(""); } @@ -144,8 +140,7 @@ TEST(Map, StyleFresh) { MapTest test; FakeFileSource fileSource; - Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); map.setStyleURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); @@ -165,8 +160,7 @@ TEST(Map, StyleExpired) { MapTest test; FakeFileSource fileSource; - Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); map.setStyleURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); @@ -193,8 +187,7 @@ TEST(Map, StyleExpiredWithAnnotations) { MapTest test; FakeFileSource fileSource; - Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); map.setStyleURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); @@ -218,8 +211,7 @@ TEST(Map, StyleEarlyMutation) { MapTest test; FakeFileSource fileSource; - Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); map.setStyleURL("mapbox://styles/test"); map.addLayer(std::make_unique<style::BackgroundLayer>("bg")); @@ -233,8 +225,7 @@ TEST(Map, StyleEarlyMutation) { TEST(Map, StyleLoadedSignal) { MapTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); // The map should emit a signal on style loaded bool emitted = false; @@ -255,22 +246,20 @@ TEST(Map, StyleLoadedSignal) { TEST(Map, AddLayer) { MapTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); auto layer = std::make_unique<BackgroundLayer>("background"); - layer->setBackgroundColor({{ 1, 0, 0, 1 }}); + layer->setBackgroundColor({ { 1, 0, 0, 1 } }); map.addLayer(std::move(layer)); - test::checkImage("test/fixtures/map/add_layer", test::render(map)); + test::checkImage("test/fixtures/map/add_layer", test::render(map, test.view)); } TEST(Map, RemoveLayer) { MapTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); auto layer = std::make_unique<BackgroundLayer>("background"); @@ -278,7 +267,7 @@ TEST(Map, RemoveLayer) { map.addLayer(std::move(layer)); map.removeLayer("background"); - test::checkImage("test/fixtures/map/remove_layer", test::render(map)); + test::checkImage("test/fixtures/map/remove_layer", test::render(map, test.view)); } TEST(Map, DisabledSources) { @@ -295,8 +284,7 @@ TEST(Map, DisabledSources) { return {}; }; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); map.setZoom(1); // This stylesheet has two raster layers, one that starts at zoom 1, the other at zoom 0. @@ -338,16 +326,15 @@ TEST(Map, DisabledSources) { } )STYLE"); - test::checkImage("test/fixtures/map/disabled_layers/first", test::render(map)); + test::checkImage("test/fixtures/map/disabled_layers/first", test::render(map, test.view)); map.setZoom(0.5); - test::checkImage("test/fixtures/map/disabled_layers/second", test::render(map)); + test::checkImage("test/fixtures/map/disabled_layers/second", test::render(map, test.view)); } TEST(Map, Classes) { MapTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); EXPECT_FALSE(map.getTransitionOptions().duration); @@ -381,8 +368,7 @@ TEST(Map, Classes) { TEST(Map, AddImage) { MapTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); auto decoded1 = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); auto decoded2 = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); auto image1 = std::make_unique<SpriteImage>(std::move(decoded1), 1.0); @@ -393,20 +379,18 @@ TEST(Map, AddImage) { map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json")); map.addImage("test-icon", std::move(image2)); - test::checkImage("test/fixtures/map/add_icon", test::render(map)); + test::checkImage("test/fixtures/map/add_icon", test::render(map, test.view)); } TEST(Map, RemoveImage) { MapTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); auto decoded = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); auto image = std::make_unique<SpriteImage>(std::move(decoded), 1.0); map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json")); map.addImage("test-icon", std::move(image)); map.removeImage("test-icon"); - test::checkImage("test/fixtures/map/remove_icon", test::render(map)); + test::checkImage("test/fixtures/map/remove_icon", test::render(map, test.view)); } - diff --git a/test/src/mbgl/test/util.cpp b/test/src/mbgl/test/util.cpp index a674eafeb4..7f98c43dc9 100644 --- a/test/src/mbgl/test/util.cpp +++ b/test/src/mbgl/test/util.cpp @@ -1,6 +1,7 @@ #include <mbgl/test/util.hpp> #include <mbgl/map/map.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/util/image.hpp> #include <mbgl/util/io.hpp> @@ -97,10 +98,10 @@ Server::~Server() { } } -PremultipliedImage render(Map& map) { +PremultipliedImage render(Map& map, OffscreenView& view) { PremultipliedImage result; - map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) { - result = std::move(image); + map.renderStill(view, [&](std::exception_ptr) { + result = view.readStillImage(); }); while (!result.size()) { diff --git a/test/src/mbgl/test/util.hpp b/test/src/mbgl/test/util.hpp index ce0069131c..b8ecf175aa 100644 --- a/test/src/mbgl/test/util.hpp +++ b/test/src/mbgl/test/util.hpp @@ -52,6 +52,7 @@ namespace mbgl { class Map; +class OffscreenView; namespace test { @@ -64,7 +65,7 @@ private: int fd = -1; }; -PremultipliedImage render(Map&); +PremultipliedImage render(Map&, OffscreenView&); void checkImage(const std::string& base, const PremultipliedImage& actual, diff --git a/test/util/memory.test.cpp b/test/util/memory.test.cpp index 2ab3a03799..a1e47d6c2b 100644 --- a/test/util/memory.test.cpp +++ b/test/util/memory.test.cpp @@ -3,7 +3,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/platform/default/thread_pool.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> @@ -57,7 +57,7 @@ public: util::RunLoop runLoop; HeadlessBackend backend; - HeadlessView view{ 2 }; + OffscreenView view{ backend.getContext(), {{ 512, 512 }} }; StubFileSource fileSource; ThreadPool threadPool { 4 }; @@ -93,22 +93,20 @@ private: TEST(Memory, Vector) { MemoryTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, { { 256, 256 } }, 2, test.fileSource, test.threadPool, MapMode::Still); map.setZoom(16); // more map features map.setStyleURL("mapbox://streets"); - test::render(map); + test::render(map, test.view); } TEST(Memory, Raster) { MemoryTest test; - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, - MapMode::Still); + Map map(test.backend, { { 256, 256 } }, 2, test.fileSource, test.threadPool, MapMode::Still); map.setStyleURL("mapbox://satellite"); - test::render(map); + test::render(map, test.view); } // This test will measure the size of a Map object @@ -123,19 +121,17 @@ TEST(Memory, Footprint) { MemoryTest test; - auto renderMap = [](Map* map, const char* style){ - map->setZoom(16); - - map->setStyleURL(style); - test::render(*map); + auto renderMap = [&](Map& map, const char* style){ + map.setZoom(16); + map.setStyleURL(style); + test::render(map, test.view); }; // Warm up buffers and cache. for (unsigned i = 0; i < 10; ++i) { - Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, - test.threadPool, MapMode::Still); - renderMap(&map, "mapbox://streets"); - renderMap(&map, "mapbox://satellite"); + Map map(test.backend, {{ 256, 256 }}, 2, test.fileSource, test.threadPool, MapMode::Still); + renderMap(map, "mapbox://streets"); + renderMap(map, "mapbox://satellite"); }; // Process close callbacks, mostly needed by @@ -147,9 +143,9 @@ TEST(Memory, Footprint) { long vectorInitialRSS = getRSS(); for (unsigned i = 0; i < runs; ++i) { - auto vector = std::make_unique<Map>(test.backend, test.view, test.view.getPixelRatio(), - test.fileSource, test.threadPool, MapMode::Still); - renderMap(vector.get(), "mapbox://streets"); + auto vector = std::make_unique<Map>(test.backend, std::array<uint16_t, 2>{ { 256, 256 } }, + 2, test.fileSource, test.threadPool, MapMode::Still); + renderMap(*vector, "mapbox://streets"); maps.push_back(std::move(vector)); }; @@ -157,9 +153,9 @@ TEST(Memory, Footprint) { long rasterInitialRSS = getRSS(); for (unsigned i = 0; i < runs; ++i) { - auto raster = std::make_unique<Map>(test.backend, test.view, test.view.getPixelRatio(), - test.fileSource, test.threadPool, MapMode::Still); - renderMap(raster.get(), "mapbox://satellite"); + auto raster = std::make_unique<Map>(test.backend, std::array<uint16_t, 2>{ { 256, 256 } }, + 2, test.fileSource, test.threadPool, MapMode::Still); + renderMap(*raster, "mapbox://satellite"); maps.push_back(std::move(raster)); }; diff --git a/test/util/offscreen_texture.test.cpp b/test/util/offscreen_texture.test.cpp index e281a3d65b..bd4eab69a8 100644 --- a/test/util/offscreen_texture.test.cpp +++ b/test/util/offscreen_texture.test.cpp @@ -2,7 +2,7 @@ #include <mbgl/gl/context.hpp> #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/offscreen_view.hpp> #include <mbgl/gl/gl.hpp> #include <mbgl/util/offscreen_texture.hpp> @@ -11,7 +11,7 @@ using namespace mbgl; TEST(OffscreenTexture, EmptyRed) { HeadlessBackend backend; - HeadlessView view(1.0f, 512, 256); + OffscreenView view(backend.getContext(), {{ 512, 256 }}); view.bind(); MBGL_CHECK_ERROR(glClearColor(1.0f, 0.0f, 0.0f, 1.0f)); @@ -68,10 +68,7 @@ struct Buffer { TEST(OffscreenTexture, RenderToTexture) { HeadlessBackend backend; - HeadlessView view(1.0f, 512, 256); - view.bind(); - gl::Context context; - context.viewport.setDefaultValue(gl::value::Viewport::Get()); + auto& context = backend.getContext(); MBGL_CHECK_ERROR(glEnable(GL_BLEND)); MBGL_CHECK_ERROR(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -109,14 +106,17 @@ void main() { // Make sure the texture gets destructed before we call context.reset(); { + OffscreenView view(context, {{ 512, 256 }}); + // First, draw red to the bound FBO. context.clearColor = { 1, 0, 0, 1 }; + view.bind(); MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT)); // Then, create a texture, bind it, and render yellow to that texture. This should not // affect the originally bound FBO. - OffscreenTexture texture; - texture.bind(context, {{ 128, 128 }}); + OffscreenTexture texture(context, {{ 128, 128 }}); + texture.bind(); context.clearColor = { 0, 0, 0, 0 }; MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT)); @@ -128,12 +128,10 @@ void main() { glVertexAttribPointer(paintShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr)); MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3)); - auto image = view.readStillImage(texture.getSize()); + auto image = texture.readStillImage(); test::checkImage("test/fixtures/offscreen_texture/render-to-texture", image, 0, 0); // Now reset the FBO back to normal and retrieve the original (restored) framebuffer. - context.resetState(); - context.bindFramebuffer.setDirty(); view.bind(); image = view.readStillImage(); |