summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmark/api/query.benchmark.cpp10
-rw-r--r--benchmark/src/mbgl/benchmark/util.cpp8
-rw-r--r--benchmark/src/mbgl/benchmark/util.hpp3
-rw-r--r--bin/glfw.cpp5
-rw-r--r--bin/render.cpp11
-rw-r--r--cmake/core-files.cmake8
-rw-r--r--cmake/core.cmake2
-rw-r--r--include/mbgl/map/backend.hpp15
-rw-r--r--include/mbgl/map/map.hpp17
-rw-r--r--include/mbgl/map/view.hpp28
-rw-r--r--include/mbgl/platform/default/glfw_view.hpp12
-rw-r--r--include/mbgl/platform/default/headless_view.hpp41
-rw-r--r--include/mbgl/platform/default/offscreen_view.hpp33
-rw-r--r--platform/android/scripts/generate-style-code.js2
-rwxr-xr-xplatform/android/src/jni.cpp2
-rwxr-xr-xplatform/android/src/native_map_view.cpp34
-rwxr-xr-xplatform/android/src/native_map_view.hpp4
-rw-r--r--platform/android/src/style/layers/custom_layer.cpp2
-rw-r--r--platform/android/src/style/sources/source.hpp2
-rw-r--r--platform/darwin/src/headless_view_cgl.cpp74
-rw-r--r--platform/darwin/src/headless_view_eagl.cpp72
-rw-r--r--platform/default/glfw_view.cpp31
-rw-r--r--platform/default/headless_view.cpp70
-rw-r--r--platform/default/headless_view_glx.cpp74
-rw-r--r--platform/default/offscreen_view.cpp47
-rw-r--r--platform/ios/config.cmake3
-rw-r--r--platform/ios/src/MGLMapView.mm45
-rw-r--r--platform/linux/config.cmake3
-rw-r--r--platform/macos/config.cmake3
-rw-r--r--platform/macos/src/MGLMapView.mm57
-rw-r--r--platform/node/src/node_map.cpp41
-rw-r--r--platform/node/src/node_map.hpp5
-rw-r--r--platform/qt/app/mapwindow.cpp26
-rw-r--r--platform/qt/app/mapwindow.hpp2
-rw-r--r--platform/qt/config.cmake3
-rw-r--r--platform/qt/include/qmapboxgl.hpp13
-rw-r--r--platform/qt/src/qmapboxgl.cpp84
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp13
-rw-r--r--platform/qt/src/qquickmapboxglrenderer.cpp12
-rw-r--r--platform/qt/src/qquickmapboxglrenderer.hpp1
-rw-r--r--platform/qt/test/headless_view_qt.cpp82
-rw-r--r--platform/qt/test/qmapboxgl.cpp2
-rw-r--r--scripts/generate-style-code.js2
-rw-r--r--src/mbgl/gl/context.cpp183
-rw-r--r--src/mbgl/gl/context.hpp27
-rw-r--r--src/mbgl/gl/framebuffer.hpp17
-rw-r--r--src/mbgl/gl/gl.cpp1
-rw-r--r--src/mbgl/gl/object.cpp5
-rw-r--r--src/mbgl/gl/object.hpp6
-rw-r--r--src/mbgl/gl/renderbuffer.hpp19
-rw-r--r--src/mbgl/gl/state.hpp36
-rw-r--r--src/mbgl/gl/types.hpp5
-rw-r--r--src/mbgl/gl/value.cpp16
-rw-r--r--src/mbgl/gl/value.hpp9
-rw-r--r--src/mbgl/map/backend.cpp10
-rw-r--r--src/mbgl/map/map.cpp147
-rw-r--r--src/mbgl/map/update.hpp (renamed from include/mbgl/map/update.hpp)1
-rw-r--r--src/mbgl/map/view.cpp17
-rw-r--r--src/mbgl/renderer/painter.cpp35
-rw-r--r--src/mbgl/renderer/painter.hpp7
-rw-r--r--src/mbgl/renderer/painter_debug.cpp44
-rw-r--r--src/mbgl/renderer/painter_fill.cpp4
-rw-r--r--src/mbgl/util/offscreen_texture.cpp70
-rw-r--r--src/mbgl/util/offscreen_texture.hpp17
-rw-r--r--test/api/annotations.test.cpp40
-rw-r--r--test/api/api_misuse.test.cpp18
-rw-r--r--test/api/custom_layer.test.cpp8
-rw-r--r--test/api/query.test.cpp8
-rw-r--r--test/api/render_missing.test.cpp8
-rw-r--r--test/api/repeated_render.test.cpp14
-rw-r--r--test/gl/object.test.cpp14
-rw-r--r--test/map/map.test.cpp70
-rw-r--r--test/src/mbgl/test/util.cpp7
-rw-r--r--test/src/mbgl/test/util.hpp3
-rw-r--r--test/util/memory.test.cpp42
-rw-r--r--test/util/offscreen_texture.test.cpp20
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 &center = 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();