From 5cc390d694fc7510d445310d8eb9e32429a5e67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 6 Oct 2016 13:23:50 +0200 Subject: [PATCH] [core] separate Backend from View for headless rendering --- benchmark/api/query.benchmark.cpp | 8 +- bin/glfw.cpp | 8 +- bin/render.cpp | 5 +- cmake/core-files.cmake | 4 + include/mbgl/gl/implementation.hpp | 14 +++ include/mbgl/map/backend.hpp | 34 +++++++ include/mbgl/map/map.hpp | 7 +- include/mbgl/map/view.hpp | 39 ++------ include/mbgl/platform/default/glfw_view.hpp | 42 +++++---- .../platform/default/headless_backend.hpp | 78 ++++++++++++++++ .../platform/default/headless_display.hpp | 9 +- .../mbgl/platform/default/headless_view.hpp | 74 ++------------- platform/android/src/native_map_view.cpp | 12 ++- platform/android/src/native_map_view.hpp | 7 +- platform/darwin/src/headless_backend_cgl.cpp | 58 ++++++++++++ platform/darwin/src/headless_backend_eagl.mm | 46 ++++++++++ platform/darwin/src/headless_view_cgl.cpp | 51 ++--------- ...ss_view_eagl.mm => headless_view_eagl.cpp} | 43 ++------- platform/default/glfw_view.cpp | 4 + platform/default/headless_backend.cpp | 56 ++++++++++++ platform/default/headless_backend_glx.cpp | 67 ++++++++++++++ platform/default/headless_display.cpp | 7 +- platform/default/headless_view.cpp | 72 +++------------ platform/default/headless_view_glx.cpp | 64 ++----------- platform/ios/config.cmake | 6 +- platform/ios/src/MGLMapView.mm | 22 ++--- platform/linux/config.cmake | 4 +- platform/macos/config.cmake | 2 + platform/macos/src/MGLMapView.mm | 29 +++--- platform/node/src/node_map.cpp | 7 +- platform/node/src/node_map.hpp | 2 + platform/qt/config.cmake | 2 + platform/qt/src/qmapboxgl.cpp | 7 +- platform/qt/src/qmapboxgl_p.hpp | 6 +- platform/qt/test/headless_backend_qt.cpp | 46 ++++++++++ platform/qt/test/headless_view_qt.cpp | 38 ++------ src/mbgl/map/backend.cpp | 12 +++ src/mbgl/map/map.cpp | 89 ++++++++++++------- src/mbgl/map/view.cpp | 13 +-- src/mbgl/renderer/paint_parameters.hpp | 2 + src/mbgl/renderer/painter.cpp | 25 +++--- src/mbgl/renderer/painter.hpp | 7 +- src/mbgl/renderer/painter_debug.cpp | 14 ++- src/mbgl/renderer/painter_fill.cpp | 3 +- test/api/annotations.test.cpp | 8 +- test/api/api_misuse.test.cpp | 16 ++-- test/api/custom_layer.test.cpp | 8 +- test/api/query.test.cpp | 8 +- test/api/render_missing.test.cpp | 8 +- test/api/repeated_render.test.cpp | 9 +- test/gl/object.test.cpp | 8 +- test/map/map.test.cpp | 62 ++++++++----- test/util/memory.test.cpp | 21 +++-- test/util/offscreen_texture.test.cpp | 11 ++- 54 files changed, 779 insertions(+), 525 deletions(-) create mode 100644 include/mbgl/gl/implementation.hpp create mode 100644 include/mbgl/map/backend.hpp create mode 100644 include/mbgl/platform/default/headless_backend.hpp create mode 100644 platform/darwin/src/headless_backend_cgl.cpp create mode 100644 platform/darwin/src/headless_backend_eagl.mm rename platform/darwin/src/{headless_view_eagl.mm => headless_view_eagl.cpp} (66%) create mode 100644 platform/default/headless_backend.cpp create mode 100644 platform/default/headless_backend_glx.cpp create mode 100644 platform/qt/test/headless_backend_qt.cpp create mode 100644 src/mbgl/map/backend.cpp diff --git a/benchmark/api/query.benchmark.cpp b/benchmark/api/query.benchmark.cpp index a9c4ade2d..5fa6a84db 100644 --- a/benchmark/api/query.benchmark.cpp +++ b/benchmark/api/query.benchmark.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -35,11 +35,11 @@ public: } util::RunLoop loop; - std::shared_ptr display{ std::make_shared() }; - HeadlessView view{ display, 1 }; + HeadlessBackend backend; + HeadlessView view; DefaultFileSource fileSource{ "benchmark/fixtures/api/cache.db", "." }; ThreadPool threadPool{ 4 }; - Map map{ view, fileSource, threadPool, MapMode::Still }; + Map map{ backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still }; ScreenBox box{{ 0, 0 }, { 1000, 1000 }}; }; diff --git a/bin/glfw.cpp b/bin/glfw.cpp index c0d61820f..44713d753 100644 --- a/bin/glfw.cpp +++ b/bin/glfw.cpp @@ -16,7 +16,7 @@ namespace { -std::unique_ptr view; +GLFWView* view = nullptr; } @@ -104,7 +104,8 @@ int main(int argc, char *argv[]) { mbgl::Log::Info(mbgl::Event::General, "BENCHMARK MODE: Some optimizations are disabled."); } - view = std::make_unique(fullscreen, benchmark); + GLFWView backend(fullscreen, benchmark); + view = &backend; mbgl::DefaultFileSource fileSource("/tmp/mbgl-cache.db", "."); @@ -118,7 +119,7 @@ int main(int argc, char *argv[]) { mbgl::ThreadPool threadPool(4); - mbgl::Map map(*view, fileSource, threadPool); + mbgl::Map map(backend, backend, backend.getPixelRatio(), fileSource, threadPool); // Load settings mbgl::Settings_JSON settings; @@ -181,5 +182,6 @@ int main(int argc, char *argv[]) { "Exit location: --lat=\"%f\" --lon=\"%f\" --zoom=\"%f\" --bearing \"%f\"", settings.latitude, settings.longitude, settings.zoom, settings.bearing); + view = nullptr; return 0; } diff --git a/bin/render.cpp b/bin/render.cpp index e4d9951c0..f9574d052 100644 --- a/bin/render.cpp +++ b/bin/render.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -84,9 +84,10 @@ int main(int argc, char *argv[]) { fileSource.setAccessToken(std::string(token)); } + HeadlessBackend backend; HeadlessView view(pixelRatio, width, height); ThreadPool threadPool(4); - Map map(view, fileSource, threadPool, MapMode::Still); + Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); map.setStyleJSON(style); map.setClasses(classes); diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index fac5490bb..255d40fa5 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -54,6 +54,7 @@ set(MBGL_CORE_FILES # gl include/mbgl/gl/gl.hpp + include/mbgl/gl/implementation.hpp src/mbgl/gl/attribute.hpp src/mbgl/gl/context.cpp src/mbgl/gl/context.hpp @@ -92,11 +93,13 @@ set(MBGL_CORE_FILES src/mbgl/layout/symbol_layout.hpp # map + include/mbgl/map/backend.hpp 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 src/mbgl/map/map.cpp src/mbgl/map/transform.cpp @@ -130,6 +133,7 @@ set(MBGL_CORE_FILES # platform/default 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/settings_json.hpp diff --git a/include/mbgl/gl/implementation.hpp b/include/mbgl/gl/implementation.hpp new file mode 100644 index 000000000..4e3a3e51c --- /dev/null +++ b/include/mbgl/gl/implementation.hpp @@ -0,0 +1,14 @@ +#pragma once + +#if defined(__QT__) + #define MBGL_USE_QT 1 +#elif defined(__APPLE__) + #include + #if TARGET_OS_IOS + #define MBGL_USE_EAGL 1 + #else + #define MBGL_USE_CGL 1 + #endif +#else + #define MBGL_USE_GLX 1 +#endif diff --git a/include/mbgl/map/backend.hpp b/include/mbgl/map/backend.hpp new file mode 100644 index 000000000..e4a5634b8 --- /dev/null +++ b/include/mbgl/map/backend.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace mbgl { + +class Map; + +class Backend { +public: + virtual ~Backend() = default; + + // Called when the backend's GL context needs to be made active or inactive. These are called, + // as a matched pair, in four situations: + // + // 1. When releasing GL resources during Map destruction + // 2. When calling a CustomLayerInitializeFunction, during Map::addLayer + // 3. When calling a CustomLayerDeinitializeFunction, during Map::removeLayer + // 4. When rendering for Map::renderStill + // + // They are *not* called for Map::render; it is assumed that the correct context is already + // activated prior to calling Map::render. + virtual void activate() = 0; + virtual void deactivate() = 0; + + // Called when the map needs to be rendered; the backend should call Map::render() at some point + // in the near future. (Not called for Map::renderStill() mode.) + virtual void invalidate() = 0; + + // Notifies a watcher of map x/y/scale/rotation changes. + virtual void notifyMapChange(MapChange change); +}; + +} // namespace mbgl diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 2831206d5..4f6207fc6 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -19,6 +19,7 @@ namespace mbgl { +class Backend; class View; class FileSource; class Scheduler; @@ -33,7 +34,11 @@ class Layer; class Map : private util::noncopyable { public: - explicit Map(View&, FileSource&, Scheduler&, + explicit Map(Backend&, + View&, + float pixelRatio, + FileSource&, + Scheduler&, MapMode mapMode = MapMode::Continuous, GLContextMode contextMode = GLContextMode::Unique, ConstrainMode constrainMode = ConstrainMode::HeightOnly, diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp index 590eef723..0dff4b360 100644 --- a/include/mbgl/map/view.hpp +++ b/include/mbgl/map/view.hpp @@ -1,24 +1,23 @@ #pragma once -#include -#include #include #include -#include -#include namespace mbgl { class Map; -class View { +class View : private util::noncopyable { public: virtual ~View() = default; - // Called directly after initialization. Must always return the same value, i.e. it may - // not change over time. - virtual float getPixelRatio() const = 0; + // 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. + virtual void bind() = 0; // Called when the View signaled a dimension change. Must return the logical dimension // of this map in pixels. @@ -30,34 +29,12 @@ public: // different from that rule. virtual std::array getFramebufferSize() const = 0; - // Called when this View is associated with a Map object. - virtual void initialize(Map*); - - // Called when the view's GL context needs to be made active or inactive. These are called, - // as a matched pair, in four situations: - // - // 1. When releasing GL resources during Map destruction - // 2. When calling a CustomLayerInitializeFunction, during Map::addLayer - // 3. When calling a CustomLayerDeinitializeFunction, during Map::removeLayer - // 4. When rendering for Map::renderStill - // - // They are *not* called for Map::render; it is assumed that the correct context is already - // activated prior to calling Map::render. - virtual void activate() = 0; - virtual void deactivate() = 0; - - // Called when the map needs to be rendered; the view should call Map::render() at some point - // in the near future. (Not called for Map::renderStill() mode.) - virtual void invalidate() = 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 size = {{ 0, 0 }}); - // Notifies a watcher of map x/y/scale/rotation changes. - virtual void notifyMapChange(MapChange change); - 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 8662a90bf..a115d03d7 100644 --- a/include/mbgl/platform/default/glfw_view.hpp +++ b/include/mbgl/platform/default/glfw_view.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -11,26 +12,12 @@ #define GL_GLEXT_PROTOTYPES #include -class GLFWView : public mbgl::View { +class GLFWView : public mbgl::View, public mbgl::Backend { public: GLFWView(bool fullscreen = false, bool benchmark = false); ~GLFWView() override; - float getPixelRatio() const override; - std::array getSize() const override; - std::array getFramebufferSize() const override; - - void initialize(mbgl::Map*) override; - void activate() override; - void deactivate() override; - void invalidate() override; - - static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods); - static void onScroll(GLFWwindow *window, double xoffset, double yoffset); - static void onWindowResize(GLFWwindow *window, int width, int height); - static void onFramebufferResize(GLFWwindow *window, int width, int height); - static void onMouseClick(GLFWwindow *window, int button, int action, int modifiers); - static void onMouseMove(GLFWwindow *window, double x, double y); + float getPixelRatio() const; // 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. @@ -41,12 +28,33 @@ public: void setWindowTitle(const std::string&); void run(); + +private: + // mbgl::View implementation + void initialize(mbgl::Map*) override; + void bind() override; + std::array getSize() const override; + std::array getFramebufferSize() const override; + + // mbgl::Backend implementation + void activate() override; + void deactivate() override; + void invalidate() override; + + // Window callbacks + static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods); + static void onScroll(GLFWwindow *window, double xoffset, double yoffset); + static void onWindowResize(GLFWwindow *window, int width, int height); + static void onFramebufferResize(GLFWwindow *window, int width, int height); + static void onMouseClick(GLFWwindow *window, int button, int action, int modifiers); + static void onMouseMove(GLFWwindow *window, double x, double y); + + // Internal void report(float duration); void setMapChangeCallback(std::function callback); void notifyMapChange(mbgl::MapChange change) override; -private: mbgl::Color makeRandomColor() const; mbgl::Point makeRandomPoint() const; static std::shared_ptr diff --git a/include/mbgl/platform/default/headless_backend.hpp b/include/mbgl/platform/default/headless_backend.hpp new file mode 100644 index 000000000..2f4886a36 --- /dev/null +++ b/include/mbgl/platform/default/headless_backend.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include + +#if MBGL_USE_QT +class QGLWidget; +#elif MBGL_USE_CGL +#include +#elif MBGL_USE_GLX +typedef struct _XDisplay Display; +typedef struct __GLXcontextRec* GLXContext; +typedef struct __GLXFBConfigRec* GLXFBConfig; +typedef long unsigned int XID; +typedef XID GLXPbuffer; +#endif + +#include +#include + +#include +#include + +namespace mbgl { + +class HeadlessDisplay; + +class HeadlessBackend : public Backend { +public: + HeadlessBackend(); + HeadlessBackend(std::shared_ptr); + ~HeadlessBackend() override; + + void invalidate() override; + void activate() override; + void deactivate() override; + void notifyMapChange(MapChange) override; + + void setMapChangeCallback(std::function&& cb) { mapChangeCallback = std::move(cb); } + +private: + void activateContext(); + void deactivateContext(); + +private: + // Implementation specific functions + static gl::glProc initializeExtension(const char*); + void createContext(); + void destroyContext(); + + std::shared_ptr display; + + bool extensionsLoaded = false; + bool active = false; + +#if MBGL_USE_QT + QGLWidget* glContext = nullptr; +#endif + +#if MBGL_USE_CGL + CGLContextObj glContext = nullptr; +#endif + +#if MBGL_USE_EAGL + void *glContext = nullptr; +#endif + +#if MBGL_USE_GLX + Display *xDisplay = nullptr; + GLXFBConfig *fbConfigs = nullptr; + GLXContext glContext = nullptr; + GLXPbuffer glxPbuffer = 0; +#endif + + std::function mapChangeCallback; + +}; + +} // namespace mbgl diff --git a/include/mbgl/platform/default/headless_display.hpp b/include/mbgl/platform/default/headless_display.hpp index edcc90522..f43e61340 100644 --- a/include/mbgl/platform/default/headless_display.hpp +++ b/include/mbgl/platform/default/headless_display.hpp @@ -1,6 +1,13 @@ #pragma once -#include +#include + +#if MBGL_USE_CGL +#include +#elif MBGL_USE_GLX +typedef struct _XDisplay Display; +typedef struct __GLXFBConfigRec* GLXFBConfig; +#endif namespace mbgl { diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp index 23f1e8251..27af4fc9d 100644 --- a/include/mbgl/platform/default/headless_view.hpp +++ b/include/mbgl/platform/default/headless_view.hpp @@ -1,95 +1,37 @@ #pragma once -#if defined(__QT__) -#define MBGL_USE_QT 1 -class QGLWidget; -#elif defined(__APPLE__) -#include -#if TARGET_OS_IOS -#define MBGL_USE_EAGL 1 -#else -#define MBGL_USE_CGL 1 -#endif -#else -#define GL_GLEXT_PROTOTYPES -#define MBGL_USE_GLX 1 -typedef struct _XDisplay Display; -typedef struct __GLXcontextRec* GLXContext; -typedef struct __GLXFBConfigRec* GLXFBConfig; -typedef long unsigned int XID; -typedef XID GLXPbuffer; -#endif - -#include -#include +#include #include -#include - -#include -#include namespace mbgl { -class HeadlessDisplay; - class HeadlessView : public View { public: - HeadlessView(float pixelRatio, uint16_t width = 256, uint16_t height = 256); - HeadlessView(std::shared_ptr display, float pixelRatio, uint16_t width = 256, uint16_t height = 256); + HeadlessView(float pixelRatio = 1, uint16_t width = 256, uint16_t height = 256); ~HeadlessView() override; - float getPixelRatio() const override; + void bind() override; + std::array getSize() const override; std::array getFramebufferSize() const override; - void invalidate() override; - void activate() override; - void deactivate() override; - void notifyMapChange(MapChange) override; PremultipliedImage readStillImage(std::array size = {{ 0, 0 }}) override; + float getPixelRatio() const; + void resize(uint16_t width, uint16_t height); - void setMapChangeCallback(std::function&& cb) { mapChangeCallback = std::move(cb); } private: - // Implementation specific functions - static gl::glProc initializeExtension(const char*); - void createContext(); - void destroyContext(); void clearBuffers(); void resizeFramebuffer(); - void activateContext(); - void deactivateContext(); + void bindFramebuffer(); - std::shared_ptr display; +private: const float pixelRatio; std::array dimensions; bool needsResize = false; - bool extensionsLoaded = false; - bool active = false; - -#if MBGL_USE_QT - QGLWidget* glContext = nullptr; -#endif - -#if MBGL_USE_CGL - CGLContextObj glContext = nullptr; -#endif - -#if MBGL_USE_EAGL - void *glContext = nullptr; -#endif - -#if MBGL_USE_GLX - Display *xDisplay = nullptr; - GLXFBConfig *fbConfigs = nullptr; - GLXContext glContext = nullptr; - GLXPbuffer glxPbuffer = 0; -#endif - - std::function mapChangeCallback; gl::FramebufferID fbo = 0; gl::RenderbufferID fboDepthStencil = 0; diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 5bd269493..fe201ac06 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -55,10 +55,8 @@ void log_gl_string(GLenum name, const char *label) { } } -NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio_, int availableProcessors_, size_t totalMemory_) - : mbgl::View(*this), - env(env_), - pixelRatio(pixelRatio_), +NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio, int availableProcessors_, size_t totalMemory_) + : env(env_), availableProcessors(availableProcessors_), totalMemory(totalMemory_), threadPool(4) { @@ -82,7 +80,7 @@ NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio_, int mbgl::android::cachePath + "/mbgl-offline.db", mbgl::android::apkPath); - map = std::make_unique(*this, *fileSource, threadPool, MapMode::Continuous); + map = std::make_unique(*this, *this, pixelRatio, *fileSource, threadPool, MapMode::Continuous); float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1; float cpuFactor = availableProcessors; @@ -114,8 +112,8 @@ NativeMapView::~NativeMapView() { vm = nullptr; } -float NativeMapView::getPixelRatio() const { - return pixelRatio; +void NativeMapView::bind() { + MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); } std::array NativeMapView::getSize() const { diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index d42890dae..42a9a10ad 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -14,12 +15,13 @@ namespace mbgl { namespace android { -class NativeMapView : public mbgl::View, private mbgl::util::noncopyable { +class NativeMapView : public mbgl::View, public mbgl::Backend { public: NativeMapView(JNIEnv *env, jobject obj, float pixelRatio, int availableProcessors, size_t totalMemory); virtual ~NativeMapView(); - float getPixelRatio() const override; + void bind() override; + std::array getSize() const override; std::array getFramebufferSize() const override; void activate() override; @@ -89,7 +91,6 @@ private: int height = 0; int fbWidth = 0; int fbHeight = 0; - const float pixelRatio; int availableProcessors = 0; size_t totalMemory = 0; diff --git a/platform/darwin/src/headless_backend_cgl.cpp b/platform/darwin/src/headless_backend_cgl.cpp new file mode 100644 index 000000000..4ca567f55 --- /dev/null +++ b/platform/darwin/src/headless_backend_cgl.cpp @@ -0,0 +1,58 @@ +#include +#include + +#include + +#include +#include + +namespace mbgl { + +gl::glProc HeadlessBackend::initializeExtension(const char* name) { + static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + if (!framework) { + throw std::runtime_error("Failed to load OpenGL framework."); + } + + CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); + void* symbol = CFBundleGetFunctionPointerForName(framework, str); + CFRelease(str); + + return reinterpret_cast(symbol); +} + +void HeadlessBackend::createContext() { + CGLError error = CGLCreateContext(display->pixelFormat, nullptr, &glContext); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Error creating GL context object:") + + CGLErrorString(error) + "\n"); + } + + error = CGLEnable(glContext, kCGLCEMPEngine); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") + + CGLErrorString(error) + "\n"); + } +} + +void HeadlessBackend::destroyContext() { + CGLDestroyContext(glContext); +} + +void HeadlessBackend::activateContext() { + CGLError error = CGLSetCurrentContext(glContext); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Switching OpenGL context failed:") + + CGLErrorString(error) + "\n"); + } +} + +void HeadlessBackend::deactivateContext() { + CGLError error = CGLSetCurrentContext(nullptr); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Removing OpenGL context failed:") + + CGLErrorString(error) + "\n"); + } +} + +} // namespace mbgl diff --git a/platform/darwin/src/headless_backend_eagl.mm b/platform/darwin/src/headless_backend_eagl.mm new file mode 100644 index 000000000..0a1ae706b --- /dev/null +++ b/platform/darwin/src/headless_backend_eagl.mm @@ -0,0 +1,46 @@ +#include + +#include + +#include + +#include + +namespace mbgl { + +gl::glProc HeadlessBackend::initializeExtension(const char* name) { + static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles")); + if (!framework) { + throw std::runtime_error("Failed to load OpenGL framework."); + } + + CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); + void* symbol = CFBundleGetFunctionPointerForName(framework, str); + CFRelease(str); + + return reinterpret_cast(symbol); +} + +void HeadlessBackend::createContext() { + glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + if (glContext == nil) { + throw std::runtime_error("Error creating GL context object"); + } + [reinterpret_cast(glContext) retain]; + reinterpret_cast(glContext).multiThreaded = YES; +} + +void HeadlessBackend::destroyContext() { + [reinterpret_cast(glContext) release]; + glContext = nil; +} + +void HeadlessBackend::activateContext() { + [EAGLContext setCurrentContext:reinterpret_cast(glContext)]; +} + +void HeadlessBackend::deactivateContext() { + [EAGLContext setCurrentContext:nil]; +} + +} // namespace mbgl diff --git a/platform/darwin/src/headless_view_cgl.cpp b/platform/darwin/src/headless_view_cgl.cpp index dc58463b5..08f0da875 100644 --- a/platform/darwin/src/headless_view_cgl.cpp +++ b/platform/darwin/src/headless_view_cgl.cpp @@ -1,37 +1,14 @@ #include -#include -#include +#include + +#include namespace mbgl { -gl::glProc HeadlessView::initializeExtension(const char* name) { - static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); - if (!framework) { - throw std::runtime_error("Failed to load OpenGL framework."); - } - - CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); - void* symbol = CFBundleGetFunctionPointerForName(framework, str); - CFRelease(str); - - return reinterpret_cast(symbol); -} - -void HeadlessView::createContext() { - CGLError error = CGLCreateContext(display->pixelFormat, nullptr, &glContext); - if (error != kCGLNoError) { - throw std::runtime_error(std::string("Error creating GL context object:") + CGLErrorString(error) + "\n"); - } - - error = CGLEnable(glContext, kCGLCEMPEngine); - if (error != kCGLNoError) { - throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") + CGLErrorString(error) + "\n"); - } -} - -void HeadlessView::destroyContext() { - CGLDestroyContext(glContext); +void HeadlessView::bindFramebuffer() { + assert(fbo); + MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); } void HeadlessView::resizeFramebuffer() { @@ -76,8 +53,6 @@ void HeadlessView::resizeFramebuffer() { } void HeadlessView::clearBuffers() { - assert(active); - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); if (fbo) { @@ -96,18 +71,4 @@ void HeadlessView::clearBuffers() { } } -void HeadlessView::activateContext() { - CGLError error = CGLSetCurrentContext(glContext); - if (error != kCGLNoError) { - throw std::runtime_error(std::string("Switching OpenGL context failed:") + CGLErrorString(error) + "\n"); - } -} - -void HeadlessView::deactivateContext() { - CGLError error = CGLSetCurrentContext(nullptr); - if (error != kCGLNoError) { - throw std::runtime_error(std::string("Removing OpenGL context failed:") + CGLErrorString(error) + "\n"); - } -} - } // namespace mbgl diff --git a/platform/darwin/src/headless_view_eagl.mm b/platform/darwin/src/headless_view_eagl.cpp similarity index 66% rename from platform/darwin/src/headless_view_eagl.mm rename to platform/darwin/src/headless_view_eagl.cpp index a1f335fc6..cc378912f 100644 --- a/platform/darwin/src/headless_view_eagl.mm +++ b/platform/darwin/src/headless_view_eagl.cpp @@ -1,35 +1,14 @@ #include -#include -#include +#include + +#include namespace mbgl { -gl::glProc HeadlessView::initializeExtension(const char* name) { - static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles")); - if (!framework) { - throw std::runtime_error("Failed to load OpenGL framework."); - } - - CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); - void* symbol = CFBundleGetFunctionPointerForName(framework, str); - CFRelease(str); - - return reinterpret_cast(symbol); -} - -void HeadlessView::createContext() { - glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - if (glContext == nil) { - throw std::runtime_error("Error creating GL context object"); - } - [reinterpret_cast(glContext) retain]; - reinterpret_cast(glContext).multiThreaded = YES; -} - -void HeadlessView::destroyContext() { - [reinterpret_cast(glContext) release]; - glContext = nil; +void HeadlessView::bindFramebuffer() { + assert(fbo); + MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo)); } void HeadlessView::resizeFramebuffer() { @@ -72,8 +51,6 @@ void HeadlessView::resizeFramebuffer() { } void HeadlessView::clearBuffers() { - assert(active); - MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); if (fbo) { @@ -92,12 +69,4 @@ void HeadlessView::clearBuffers() { } } -void HeadlessView::activateContext() { - [EAGLContext setCurrentContext:reinterpret_cast(glContext)]; -} - -void HeadlessView::deactivateContext() { - [EAGLContext setCurrentContext:nil]; -} - } // namespace mbgl diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index 9c54fc4eb..044181e8c 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -129,6 +129,10 @@ void GLFWView::initialize(mbgl::Map *map_) { map->addAnnotationIcon("default_marker", makeSpriteImage(22, 22, 1)); } +void GLFWView::bind() { + MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); +} + void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) { GLFWView *view = reinterpret_cast(glfwGetWindowUserPointer(window)); diff --git a/platform/default/headless_backend.cpp b/platform/default/headless_backend.cpp new file mode 100644 index 000000000..279a7973c --- /dev/null +++ b/platform/default/headless_backend.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include +#include + +namespace mbgl { + +HeadlessBackend::HeadlessBackend() : display(std::make_shared()) { + activate(); +} + +HeadlessBackend::HeadlessBackend(std::shared_ptr display_) + : display(std::move(display_)) { + activate(); +} + +HeadlessBackend::~HeadlessBackend() { + deactivate(); + destroyContext(); +} + +void HeadlessBackend::activate() { + active = true; + + if (!glContext) { + if (!display) { + throw std::runtime_error("Display is not set"); + } + createContext(); + } + + activateContext(); + + if (!extensionsLoaded) { + gl::InitializeExtensions(initializeExtension); + extensionsLoaded = true; + } +} + +void HeadlessBackend::deactivate() { + deactivateContext(); + active = false; +} + +void HeadlessBackend::invalidate() { + assert(false); +} + +void HeadlessBackend::notifyMapChange(MapChange change) { + if (mapChangeCallback) { + mapChangeCallback(change); + } +} + +} // namespace mbgl diff --git a/platform/default/headless_backend_glx.cpp b/platform/default/headless_backend_glx.cpp new file mode 100644 index 000000000..bbfd19345 --- /dev/null +++ b/platform/default/headless_backend_glx.cpp @@ -0,0 +1,67 @@ +#include +#include + +#include + +// #include + +#include + +namespace mbgl { + +gl::glProc HeadlessBackend::initializeExtension(const char* name) { + return glXGetProcAddress(reinterpret_cast(name)); +} + +void HeadlessBackend::createContext() { + xDisplay = display->xDisplay; + fbConfigs = display->fbConfigs; + + if (!glContext) { + // Try to create a legacy context + glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True); + if (glContext) { + if (!glXIsDirect(xDisplay, glContext)) { + Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context"); + glXDestroyContext(xDisplay, glContext); + glContext = nullptr; + } + } + } + + if (glContext == nullptr) { + throw std::runtime_error("Error creating GL context object."); + } + + // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to + // activate the context. + int pbufferAttributes[] = { + GLX_PBUFFER_WIDTH, 8, + GLX_PBUFFER_HEIGHT, 8, + None + }; + glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes); +} + +void HeadlessBackend::destroyContext() { + if (glxPbuffer) { + glXDestroyPbuffer(xDisplay, glxPbuffer); + glxPbuffer = 0; + } + + glXDestroyContext(xDisplay, glContext); +} + +void HeadlessBackend::activateContext() { + if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) { + throw std::runtime_error("Switching OpenGL context failed.\n"); + } +} + +void HeadlessBackend::deactivateContext() { + if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) { + throw std::runtime_error("Removing OpenGL context failed.\n"); + } +} + +} // namespace mbgl diff --git a/platform/default/headless_display.cpp b/platform/default/headless_display.cpp index 8b9f3fe04..b98aef790 100644 --- a/platform/default/headless_display.cpp +++ b/platform/default/headless_display.cpp @@ -1,12 +1,13 @@ #include -#include -#include - #if MBGL_USE_GLX #include #endif +#include +#include +#include + namespace mbgl { HeadlessDisplay::HeadlessDisplay() { diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index 95b196b74..f96b0a7f6 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -1,34 +1,27 @@ #include -#include -#include +#include + #include namespace mbgl { HeadlessView::HeadlessView(float pixelRatio_, uint16_t width, uint16_t height) - : display(std::make_shared()) - , pixelRatio(pixelRatio_) - , dimensions({{ width, height }}) - , needsResize(true) { -} - -HeadlessView::HeadlessView(std::shared_ptr display_, - float pixelRatio_, - uint16_t width, - uint16_t height) - : display(std::move(display_)) - , pixelRatio(pixelRatio_) - , dimensions({{ width, height }}) - , needsResize(true) { + : pixelRatio(pixelRatio_), dimensions({ { width, height } }), needsResize(true) { } HeadlessView::~HeadlessView() { - activate(); clearBuffers(); - deactivate(); +} - destroyContext(); +void HeadlessView::bind() { + if (needsResize) { + clearBuffers(); + resizeFramebuffer(); + needsResize = false; + } else { + bindFramebuffer(); + } } void HeadlessView::resize(const uint16_t width, const uint16_t height) { @@ -41,8 +34,6 @@ void HeadlessView::resize(const uint16_t width, const uint16_t height) { } PremultipliedImage HeadlessView::readStillImage(std::array size) { - assert(active); - if (!size[0] || !size[1]) { size[0] = dimensions[0] * pixelRatio; size[1] = dimensions[1] * pixelRatio; @@ -76,43 +67,4 @@ std::array HeadlessView::getFramebufferSize() const { static_cast(dimensions[1] * pixelRatio) }}; } -void HeadlessView::activate() { - active = true; - - if (!glContext) { - if (!display) { - throw std::runtime_error("Display is not set"); - } - createContext(); - } - - activateContext(); - - if (!extensionsLoaded) { - gl::InitializeExtensions(initializeExtension); - extensionsLoaded = true; - } - - if (needsResize) { - clearBuffers(); - resizeFramebuffer(); - needsResize = false; - } -} - -void HeadlessView::deactivate() { - deactivateContext(); - active = false; -} - -void HeadlessView::invalidate() { - assert(false); -} - -void HeadlessView::notifyMapChange(MapChange change) { - if (mapChangeCallback) { - mapChangeCallback(change); - } -} - } // namespace mbgl diff --git a/platform/default/headless_view_glx.cpp b/platform/default/headless_view_glx.cpp index 55d9313f9..08f0da875 100644 --- a/platform/default/headless_view_glx.cpp +++ b/platform/default/headless_view_glx.cpp @@ -1,54 +1,14 @@ #include -#include -#include + +#include #include -#include - namespace mbgl { -gl::glProc HeadlessView::initializeExtension(const char* name) { - return glXGetProcAddress(reinterpret_cast(name)); -} - -void HeadlessView::createContext() { - xDisplay = display->xDisplay; - fbConfigs = display->fbConfigs; - - if (!glContext) { - // Try to create a legacy context - glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True); - if (glContext) { - if (!glXIsDirect(xDisplay, glContext)) { - Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context"); - glXDestroyContext(xDisplay, glContext); - glContext = nullptr; - } - } - } - - if (glContext == nullptr) { - throw std::runtime_error("Error creating GL context object."); - } - - // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to - // activate the context. - int pbufferAttributes[] = { - GLX_PBUFFER_WIDTH, 8, - GLX_PBUFFER_HEIGHT, 8, - None - }; - glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes); -} - -void HeadlessView::destroyContext() { - if (glxPbuffer) { - glXDestroyPbuffer(xDisplay, glxPbuffer); - glxPbuffer = 0; - } - - glXDestroyContext(xDisplay, glContext); +void HeadlessView::bindFramebuffer() { + assert(fbo); + MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); } void HeadlessView::resizeFramebuffer() { @@ -93,8 +53,6 @@ void HeadlessView::resizeFramebuffer() { } void HeadlessView::clearBuffers() { - assert(active); - MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); if (fbo) { @@ -113,16 +71,4 @@ void HeadlessView::clearBuffers() { } } -void HeadlessView::activateContext() { - if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) { - throw std::runtime_error("Switching OpenGL context failed.\n"); - } -} - -void HeadlessView::deactivateContext() { - if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) { - throw std::runtime_error("Removing OpenGL context failed.\n"); - } -} - } // namespace mbgl diff --git a/platform/ios/config.cmake b/platform/ios/config.cmake index e5480e37d..db75d7a4d 100644 --- a/platform/ios/config.cmake +++ b/platform/ios/config.cmake @@ -37,7 +37,9 @@ macro(mbgl_platform_core) PRIVATE platform/darwin/src/image.mm # Headless view - PRIVATE platform/darwin/src/headless_view_eagl.mm + 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 @@ -53,7 +55,7 @@ macro(mbgl_platform_core) # TODO: Remove this by converting to ARC set_source_files_properties( - platform/darwin/src/headless_view_eagl.mm + platform/darwin/src/headless_backend_eagl.mm PROPERTIES COMPILE_FLAGS -fno-objc-arc ) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 194f1a88d..3d93dc8fc 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -389,8 +390,7 @@ public: self.clipsToBounds = YES; // setup mbgl view - const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; - _mbglView = new MBGLView(self, scaleFactor); + _mbglView = new MBGLView(self); // Delete the pre-offline ambient cache at ~/Library/Caches/cache.db. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); @@ -398,9 +398,10 @@ public: [[NSFileManager defaultManager] removeItemAtPath:fileCachePath error:NULL]; // setup mbgl map + const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; _mbglThreadPool = new mbgl::ThreadPool(4); - _mbglMap = new mbgl::Map(*_mbglView, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); + _mbglMap = new mbgl::Map(*_mbglView, *_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); [self validateTileCacheSize]; // start paused if in IB @@ -4938,15 +4939,11 @@ public: return _annotationViewReuseQueueByIdentifier[identifier]; } -class MBGLView : public mbgl::View +class MBGLView : public mbgl::View, public mbgl::Backend { public: - MBGLView(MGLMapView* nativeView_, const float scaleFactor_) - : nativeView(nativeView_), scaleFactor(scaleFactor_) { - } - - float getPixelRatio() const override { - return scaleFactor; + MBGLView(MGLMapView* nativeView_) + : nativeView(nativeView_) { } std::array getSize() const override { @@ -4959,6 +4956,10 @@ public: static_cast([[nativeView glView] drawableHeight]) }}; } + void bind() override { + [nativeView.glView bindDrawable]; + } + void notifyMapChange(mbgl::MapChange change) override { [nativeView notifyMapChange:change]; @@ -4981,7 +4982,6 @@ public: private: __weak MGLMapView *nativeView = nullptr; - const float scaleFactor; }; @end diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index 86e8697f8..d033f9df2 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -41,9 +41,11 @@ macro(mbgl_platform_core) PRIVATE platform/default/webp_reader.cpp # 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/headless_view_glx.cpp # Thread pool PRIVATE platform/default/thread_pool.cpp diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake index 645feb90c..198dfc708 100644 --- a/platform/macos/config.cmake +++ b/platform/macos/config.cmake @@ -35,7 +35,9 @@ macro(mbgl_platform_core) PRIVATE platform/darwin/src/image.mm # 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 diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 46766b573..475ca2d25 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -25,6 +25,7 @@ #import #import #import +#import #import #import #import @@ -248,7 +249,7 @@ public: _isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent; // Set up cross-platform controllers and resources. - _mbglView = new MGLMapViewImpl(self, [NSScreen mainScreen].backingScaleFactor); + _mbglView = new MGLMapViewImpl(self); // Delete the pre-offline ambient cache at // ~/Library/Caches/com.mapbox.sdk.ios/cache.db. @@ -264,7 +265,7 @@ public: mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; _mbglThreadPool = new mbgl::ThreadPool(4); - _mbglMap = new mbgl::Map(*_mbglView, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); + _mbglMap = new mbgl::Map(*_mbglView, *_mbglView, [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 @@ -745,6 +746,8 @@ public: return reinterpret_cast(symbol); }); + _mbglView->updateFramebufferBinding(); + _mbglMap->render(); if (_isPrinting) { @@ -2531,14 +2534,10 @@ public: } /// Adapter responsible for bridging calls from mbgl to MGLMapView and Cocoa. -class MGLMapViewImpl : public mbgl::View { +class MGLMapViewImpl : public mbgl::View, public mbgl::Backend { public: - MGLMapViewImpl(MGLMapView *nativeView_, const float scaleFactor_) - : nativeView(nativeView_), scaleFactor(scaleFactor_) {} - - float getPixelRatio() const override { - return scaleFactor; - } + MGLMapViewImpl(MGLMapView *nativeView_) + : nativeView(nativeView_) {} std::array getSize() const override { return {{ static_cast(nativeView.bounds.size.width), @@ -2568,6 +2567,14 @@ public: [NSOpenGLContext clearCurrentContext]; } + void updateFramebufferBinding() { + MBGL_CHECK_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo)); + } + + void bind() override { + MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo)); + } + mbgl::PremultipliedImage readStillImage(std::array size = {{ 0, 0 }}) override { if (!size[0] || !size[1]) { size = getFramebufferSize(); @@ -2592,8 +2599,8 @@ private: /// Cocoa map view that this adapter bridges to. __weak MGLMapView *nativeView = nullptr; - /// Backing scale factor of the view. - const float scaleFactor; + /// The current framebuffer of the NSOpenGLLayer we are painting to. + GLint fbo = 0; }; @end diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index a13adbc41..ebdf8d62c 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -773,15 +773,16 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo& } NodeMap::NodeMap(v8::Local options) : - view(sharedDisplay(), [&] { + 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(view, *this, threadpool, mbgl::MapMode::Still)), + map(std::make_unique(backend, view, view.getPixelRatio(), *this, threadpool, mbgl::MapMode::Still)), async(new uv_async_t) { - view.setMapChangeCallback([&](mbgl::MapChange change) { + backend.setMapChangeCallback([&](mbgl::MapChange change) { if (change == mbgl::MapChangeDidFailLoadingMap) { throw std::runtime_error("Requires a map style to be a valid style JSON"); } diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index 588ece2c7..48ff2caab 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #pragma GCC diagnostic push @@ -53,6 +54,7 @@ public: std::unique_ptr request(const mbgl::Resource&, mbgl::FileSource::Callback); + mbgl::HeadlessBackend backend; mbgl::HeadlessView view; NodeThreadPool threadpool; std::unique_ptr map; diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake index d8ede6b45..6df311f88 100644 --- a/platform/qt/config.cmake +++ b/platform/qt/config.cmake @@ -43,8 +43,10 @@ endmacro() 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 ) diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index d0a05301f..73a177190 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -813,7 +813,7 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin settings.cacheDatabaseMaximumSize())) , threadPool(4) , mapObj(std::make_unique( - *this, *fileSourceObj, threadPool, + *this, *this, getPixelRatio(), *fileSourceObj, threadPool, static_cast(settings.mapMode()), static_cast(settings.contextMode()), static_cast(settings.constrainMode()), @@ -845,6 +845,11 @@ float QMapboxGLPrivate::getPixelRatio() const return pixelRatio; } +void QMapboxGLPrivate::bind() +{ + MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); +} + std::array QMapboxGLPrivate::getSize() const { return {{ static_cast(size.width()), static_cast(size.height()) }}; diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index c35ac4e7b..5a228896d 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -3,6 +3,7 @@ #include "qmapboxgl.hpp" #include +#include #include #include #include @@ -11,7 +12,7 @@ #include #include -class QMapboxGLPrivate : public QObject, public mbgl::View +class QMapboxGLPrivate : public QObject, public mbgl::View, public mbgl::Backend { Q_OBJECT @@ -20,7 +21,8 @@ public: virtual ~QMapboxGLPrivate(); // mbgl::View implementation. - float getPixelRatio() const final; + float getPixelRatio() const; + void bind() final; std::array getSize() const final; std::array getFramebufferSize() const final; diff --git a/platform/qt/test/headless_backend_qt.cpp b/platform/qt/test/headless_backend_qt.cpp new file mode 100644 index 000000000..3f287ae57 --- /dev/null +++ b/platform/qt/test/headless_backend_qt.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#endif + +namespace mbgl { + +gl::glProc HeadlessBackend::initializeExtension(const char* name) { +#if QT_VERSION >= 0x050000 + QOpenGLContext* thisContext = QOpenGLContext::currentContext(); + return thisContext->getProcAddress(name); +#else + const QGLContext* thisContext = QGLContext::currentContext(); + return reinterpret_cast(thisContext->getProcAddress(name)); +#endif +} + +void HeadlessBackend::createContext() { + static const char* argv[] = { "mbgl" }; + static int argc = 1; + static auto* app = new QApplication(argc, const_cast(argv)); + + Q_UNUSED(app); + + glContext = new QGLWidget; +} + +void HeadlessBackend::destroyContext() { + delete glContext; +} + +void HeadlessBackend::activateContext() { + glContext->makeCurrent(); +} + +void HeadlessBackend::deactivateContext() { + glContext->doneCurrent(); +} + +} // namespace mbgl diff --git a/platform/qt/test/headless_view_qt.cpp b/platform/qt/test/headless_view_qt.cpp index 03ecb741a..133b4a237 100644 --- a/platform/qt/test/headless_view_qt.cpp +++ b/platform/qt/test/headless_view_qt.cpp @@ -1,6 +1,7 @@ -#include #include +#include + #include #include #include @@ -9,30 +10,13 @@ #include #endif +#include + namespace mbgl { -gl::glProc HeadlessView::initializeExtension(const char* name) { -#if QT_VERSION >= 0x050000 - QOpenGLContext* thisContext = QOpenGLContext::currentContext(); - return thisContext->getProcAddress(name); -#else - const QGLContext* thisContext = QGLContext::currentContext(); - return reinterpret_cast(thisContext->getProcAddress(name)); -#endif -} - -void HeadlessView::createContext() { - static const char* argv[] = { "mbgl" }; - static int argc = 1; - static auto* app = new QApplication(argc, const_cast(argv)); - - Q_UNUSED(app); - - glContext = new QGLWidget; -} - -void HeadlessView::destroyContext() { - delete glContext; +void HeadlessView::bindFramebuffer() { + assert(fbo); + MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); } void HeadlessView::resizeFramebuffer() { @@ -95,12 +79,4 @@ void HeadlessView::clearBuffers() { } } -void HeadlessView::activateContext() { - glContext->makeCurrent(); -} - -void HeadlessView::deactivateContext() { - glContext->doneCurrent(); -} - } // namespace mbgl diff --git a/src/mbgl/map/backend.cpp b/src/mbgl/map/backend.cpp new file mode 100644 index 000000000..fea4b70ce --- /dev/null +++ b/src/mbgl/map/backend.cpp @@ -0,0 +1,12 @@ +#include + +#include + +namespace mbgl { + +void Backend::notifyMapChange(MapChange) { + // no-op +} + + +} // namespace mbgl diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index d948225d8..ab85187c9 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,15 @@ enum class RenderState : uint8_t { class Map::Impl : public style::Observer { public: - Impl(View&, FileSource&, Scheduler&, MapMode, GLContextMode, ConstrainMode, ViewportMode); + Impl(Backend&, + View&, + float pixelRatio, + FileSource&, + Scheduler&, + MapMode, + GLContextMode, + ConstrainMode, + ViewportMode); void onSourceAttributionChanged(style::Source&, const std::string&) override; void onUpdate(Update) override; @@ -49,6 +58,7 @@ public: void loadStyleJSON(const std::string&); + Backend& backend; View& view; FileSource& fileSource; Scheduler& scheduler; @@ -81,34 +91,53 @@ public: bool loading = false; }; -Map::Map(View& view, FileSource& fileSource, Scheduler& scheduler, MapMode mapMode, GLContextMode contextMode, ConstrainMode constrainMode, ViewportMode viewportMode) - : impl(std::make_unique(view, fileSource, scheduler, mapMode, contextMode, constrainMode, viewportMode)) { +Map::Map(Backend& backend, + View& view, + const float pixelRatio, + FileSource& fileSource, + Scheduler& scheduler, + MapMode mapMode, + GLContextMode contextMode, + ConstrainMode constrainMode, + ViewportMode viewportMode) + : impl(std::make_unique(backend, + view, + pixelRatio, + fileSource, + scheduler, + mapMode, + contextMode, + constrainMode, + viewportMode)) { view.initialize(this); update(Update::Dimensions); } -Map::Impl::Impl(View& view_, +Map::Impl::Impl(Backend& backend_, + View& view_, + float pixelRatio_, FileSource& fileSource_, Scheduler& scheduler_, MapMode mode_, GLContextMode contextMode_, ConstrainMode constrainMode_, ViewportMode viewportMode_) - : view(view_), + : backend(backend_), + view(view_), fileSource(fileSource_), scheduler(scheduler_), - transform([this](MapChange change) { view.notifyMapChange(change); }, + transform([this](MapChange change) { backend.notifyMapChange(change); }, constrainMode_, viewportMode_), mode(mode_), contextMode(contextMode_), - pixelRatio(view.getPixelRatio()), + pixelRatio(pixelRatio_), asyncUpdate([this] { update(); }), annotationManager(std::make_unique(pixelRatio)) { } Map::~Map() { - impl->view.activate(); + impl->backend.activate(); impl->styleRequest = nullptr; @@ -118,7 +147,7 @@ Map::~Map() { impl->annotationManager.reset(); impl->painter.reset(); - impl->view.deactivate(); + impl->backend.deactivate(); } void Map::renderStill(StillImageCallback callback) { @@ -162,16 +191,16 @@ void Map::render() { } if (impl->renderState == RenderState::Never) { - impl->view.notifyMapChange(MapChangeWillStartRenderingMap); + impl->backend.notifyMapChange(MapChangeWillStartRenderingMap); } - impl->view.notifyMapChange(MapChangeWillStartRenderingFrame); + impl->backend.notifyMapChange(MapChangeWillStartRenderingFrame); const Update flags = impl->transform.updateTransitions(Clock::now()); impl->render(); - impl->view.notifyMapChange(isFullyLoaded() ? + impl->backend.notifyMapChange(isFullyLoaded() ? MapChangeDidFinishRenderingFrameFullyRendered : MapChangeDidFinishRenderingFrame); @@ -179,10 +208,10 @@ void Map::render() { impl->renderState = RenderState::Partial; } else if (impl->renderState != RenderState::Fully) { impl->renderState = RenderState::Fully; - impl->view.notifyMapChange(MapChangeDidFinishRenderingMapFullyRendered); + impl->backend.notifyMapChange(MapChangeDidFinishRenderingMapFullyRendered); if (impl->loading) { impl->loading = false; - impl->view.notifyMapChange(MapChangeDidFinishLoadingMap); + impl->backend.notifyMapChange(MapChangeDidFinishLoadingMap); } } @@ -240,11 +269,11 @@ void Map::Impl::update() { style->updateTiles(parameters); if (mode == MapMode::Continuous) { - view.invalidate(); + backend.invalidate(); } else if (callback && style->isLoaded()) { - view.activate(); + backend.activate(); render(); - view.deactivate(); + backend.deactivate(); } updateFlags = Update::Nothing; @@ -255,8 +284,7 @@ void Map::Impl::render() { painter = std::make_unique(transform.getState()); } - FrameData frameData { view.getFramebufferSize(), - timePoint, + FrameData frameData { timePoint, pixelRatio, mode, contextMode, @@ -264,6 +292,7 @@ void Map::Impl::render() { painter->render(*style, frameData, + view, annotationManager->getSpriteAtlas()); if (mode == MapMode::Still) { @@ -291,7 +320,7 @@ void Map::setStyleURL(const std::string& url) { impl->loading = true; - impl->view.notifyMapChange(MapChangeWillStartLoadingMap); + impl->backend.notifyMapChange(MapChangeWillStartLoadingMap); impl->styleRequest = nullptr; impl->styleURL = url; @@ -335,7 +364,7 @@ void Map::setStyleJSON(const std::string& json) { impl->loading = true; - impl->view.notifyMapChange(MapChangeWillStartLoadingMap); + impl->backend.notifyMapChange(MapChangeWillStartLoadingMap); impl->styleURL.clear(); impl->styleJSON.clear(); @@ -801,12 +830,12 @@ void Map::addLayer(std::unique_ptr layer, const optional& be } impl->styleMutated = true; - impl->view.activate(); + impl->backend.activate(); impl->style->addLayer(std::move(layer), before); update(Update::Classes); - impl->view.deactivate(); + impl->backend.deactivate(); } void Map::removeLayer(const std::string& id) { @@ -815,12 +844,12 @@ void Map::removeLayer(const std::string& id) { } impl->styleMutated = true; - impl->view.activate(); + impl->backend.activate(); impl->style->removeLayer(id); update(Update::Classes); - impl->view.deactivate(); + impl->backend.deactivate(); } void Map::addImage(const std::string& name, std::unique_ptr image) { @@ -971,7 +1000,7 @@ void Map::setSourceTileCacheSize(size_t size) { impl->sourceCacheSize = size; if (!impl->style) return; impl->style->setSourceTileCacheSize(size); - impl->view.invalidate(); + impl->backend.invalidate(); } } @@ -981,12 +1010,12 @@ void Map::onLowMemory() { } if (impl->style) { impl->style->onLowMemory(); - impl->view.invalidate(); + impl->backend.invalidate(); } } void Map::Impl::onSourceAttributionChanged(style::Source&, const std::string&) { - view.notifyMapChange(MapChangeSourceDidChange); + backend.notifyMapChange(MapChangeSourceDidChange); } void Map::Impl::onUpdate(Update flags) { @@ -999,11 +1028,11 @@ void Map::Impl::onUpdate(Update flags) { } void Map::Impl::onStyleLoaded() { - view.notifyMapChange(MapChangeDidFinishLoadingStyle); + backend.notifyMapChange(MapChangeDidFinishLoadingStyle); } void Map::Impl::onStyleError() { - view.notifyMapChange(MapChangeDidFailLoadingMap); + backend.notifyMapChange(MapChangeDidFailLoadingMap); } void Map::Impl::onResourceError(std::exception_ptr error) { diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp index 8541753c7..23ce70a3c 100644 --- a/src/mbgl/map/view.cpp +++ b/src/mbgl/map/view.cpp @@ -5,18 +5,13 @@ namespace mbgl { +PremultipliedImage View::readStillImage(std::array) { + return {}; +} + void View::initialize(Map *map_) { assert(map_); map = map_; } -PremultipliedImage View::readStillImage(std::array) { - return {}; -} - -void View::notifyMapChange(MapChange) { - // no-op -} - - } // namespace mbgl diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index 13bf21080..bd67dc9cf 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -3,10 +3,12 @@ namespace mbgl { class Shaders; +class View; class PaintParameters { public: Shaders& shaders; + View& view; }; } // namespace mbgl diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index ad0e75cd9..ee7ef6d21 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include #include @@ -90,19 +92,18 @@ void Painter::cleanup() { context.performCleanup(); } -void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& annotationSpriteAtlas) { - if (frame.framebufferSize != frame_.framebufferSize) { - context.viewport.setDefaultValue( - { 0, 0, frame_.framebufferSize[0], frame_.framebufferSize[1] }); - } +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_; PaintParameters parameters { #ifndef NDEBUG - paintMode() == PaintMode::Overdraw ? *overdrawShaders : *shaders + paintMode() == PaintMode::Overdraw ? *overdrawShaders : *shaders, #else - *shaders + *shaders, #endif + view }; glyphAtlas = style.glyphAtlas.get(); @@ -148,7 +149,8 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a // tiles whatsoever. { MBGL_DEBUG_GROUP("clear"); - context.bindFramebuffer.reset(); + context.bindFramebuffer.setDirty(); + view.bind(); context.viewport.reset(); context.stencilFunc.reset(); context.stencilTest = true; @@ -188,7 +190,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a #if not MBGL_USE_GLES2 and not defined(NDEBUG) if (frame.debugOptions & MapDebugOptions::StencilClip) { - renderClipMasks(); + renderClipMasks(parameters); return; } #endif @@ -231,7 +233,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a #if not MBGL_USE_GLES2 and not defined(NDEBUG) if (frame.debugOptions & MapDebugOptions::DepthBuffer) { - renderDepthBuffer(); + renderDepthBuffer(parameters); } #endif @@ -302,7 +304,8 @@ void Painter::renderPass(PaintParameters& parameters, setDepthSublayer(0); layer.as()->impl->render(state); context.setDirtyState(); - context.bindFramebuffer.reset(); + context.bindFramebuffer.setDirty(); + parameters.view.bind(); context.viewport.reset(); } else { MBGL_DEBUG_GROUP(layer.baseImpl->id + " - " + util::toString(item.tile->id)); diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 28aa5aab4..d2c89c04a 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -28,6 +28,7 @@ namespace mbgl { class RenderTile; class SpriteAtlas; +class View; class GlyphAtlas; class LineAtlas; struct FrameData; @@ -58,7 +59,6 @@ class BackgroundLayer; } // namespace style struct FrameData { - std::array framebufferSize = {{ 0, 0 }}; TimePoint timePoint; float pixelRatio; MapMode mapMode; @@ -73,6 +73,7 @@ public: void render(const style::Style&, const FrameData&, + View&, SpriteAtlas& annotationSpriteAtlas); void cleanup(); @@ -85,9 +86,9 @@ public: #ifndef NDEBUG // Renders tile clip boundaries, using stencil buffer to calculate fill color. - void renderClipMasks(); + void renderClipMasks(PaintParameters&); // Renders the depth buffer. - void renderDepthBuffer(); + void renderDepthBuffer(PaintParameters&); #endif void renderDebugText(Tile&, const mat4&); diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp index 23a2a8e57..ca961f84e 100644 --- a/src/mbgl/renderer/painter_debug.cpp +++ b/src/mbgl/renderer/painter_debug.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -87,7 +89,7 @@ void Painter::renderDebugFrame(const mat4 &matrix) { } #ifndef NDEBUG -void Painter::renderClipMasks() { +void Painter::renderClipMasks(PaintParameters& parameters) { context.stencilTest = false; context.depthTest = false; context.program = 0; @@ -98,7 +100,7 @@ void Painter::renderClipMasks() { context.rasterPos = { -1, -1, 0, 0 }; // Read the stencil buffer - const auto& fbSize = frame.framebufferSize; + const auto& fbSize = parameters.view.getFramebufferSize(); auto pixels = std::make_unique(fbSize[0] * fbSize[1]); MBGL_CHECK_ERROR(glReadPixels( 0, // GLint x @@ -120,12 +122,14 @@ void Painter::renderClipMasks() { MBGL_CHECK_ERROR(glWindowPos2i(0, 0)); MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get())); +#else + (void)parameters; #endif // MBGL_USE_GLES2 } #endif // NDEBUG #ifndef NDEBUG -void Painter::renderDepthBuffer() { +void Painter::renderDepthBuffer(PaintParameters& parameters) { context.stencilTest = false; context.depthTest = false; context.program = 0; @@ -136,7 +140,7 @@ void Painter::renderDepthBuffer() { context.rasterPos = { -1, -1, 0, 0 }; // Read the stencil buffer - const auto& fbSize = frame.framebufferSize; + const auto& fbSize = parameters.view.getFramebufferSize(); auto pixels = std::make_unique(fbSize[0] * fbSize[1]); const double base = 1.0 / (1.0 - depthRangeSize); @@ -155,6 +159,8 @@ void Painter::renderDepthBuffer() { MBGL_CHECK_ERROR(glWindowPos2i(0, 0)); MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get())); +#else + (void)parameters; #endif // MBGL_USE_GLES2 } #endif // NDEBUG diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 50ead900f..1fc5f1107 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,7 @@ void Painter::renderFill(PaintParameters& parameters, const bool isOutlineColorDefined = !properties.fillOutlineColor.isUndefined(); Color strokeColor = isOutlineColorDefined? properties.fillOutlineColor : fillColor; - auto worldSize = util::convert(frame.framebufferSize); + auto worldSize = util::convert(parameters.view.getFramebufferSize()); bool pattern = !properties.fillPattern.value.from.empty(); bool outline = properties.fillAntialias && !pattern && isOutlineColorDefined; diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp index c1716a522..53c0182ee 100644 --- a/test/api/annotations.test.cpp +++ b/test/api/annotations.test.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include @@ -23,11 +23,11 @@ std::shared_ptr namedMarker(const std::string &name) { class AnnotationTest { public: util::RunLoop loop; - std::shared_ptr display { std::make_shared() }; - HeadlessView view { display, 1 }; + HeadlessBackend backend; + HeadlessView view; StubFileSource fileSource; ThreadPool threadPool { 4 }; - Map map { view, fileSource, threadPool, MapMode::Still }; + Map map { backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still }; void checkRendering(const char * name) { test::checkImage(std::string("test/fixtures/annotations/") + name, diff --git a/test/api/api_misuse.test.cpp b/test/api/api_misuse.test.cpp index 2016e278d..f11363fa3 100644 --- a/test/api/api_misuse.test.cpp +++ b/test/api/api_misuse.test.cpp @@ -3,7 +3,8 @@ #include #include -#include +#include +#include #include #include #include @@ -19,13 +20,14 @@ TEST(API, RenderWithoutCallback) { util::RunLoop loop; - auto display = std::make_shared(); - HeadlessView view(display, 1); + HeadlessBackend backend; + HeadlessView view; view.resize(128, 512); StubFileSource fileSource; ThreadPool threadPool(4); - std::unique_ptr map = std::make_unique(view, fileSource, threadPool, MapMode::Still); + std::unique_ptr map = std::make_unique(backend, view, view.getPixelRatio(), + fileSource, threadPool, MapMode::Still); map->renderStill(nullptr); // Force Map thread to join. @@ -44,13 +46,13 @@ TEST(API, RenderWithoutCallback) { TEST(API, RenderWithoutStyle) { util::RunLoop loop; - auto display = std::make_shared(); - HeadlessView view(display, 1); + HeadlessBackend backend; + HeadlessView view; view.resize(128, 512); StubFileSource fileSource; ThreadPool threadPool(4); - Map map(view, fileSource, threadPool, MapMode::Still); + Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); std::exception_ptr error; map.renderStill([&](std::exception_ptr error_, PremultipliedImage&&) { diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp index 1342dfa50..84ae78017 100644 --- a/test/api/custom_layer.test.cpp +++ b/test/api/custom_layer.test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -85,8 +85,8 @@ public: TEST(CustomLayer, Basic) { util::RunLoop loop; - auto display = std::make_shared(); - HeadlessView view(display, 1); + HeadlessBackend backend; + HeadlessView view; #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(view, fileSource, threadPool, MapMode::Still); + Map map(backend, view, view.getPixelRatio(), 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( diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index da38dd0cb..d989d222c 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -26,11 +26,11 @@ public: } util::RunLoop loop; - std::shared_ptr display { std::make_shared() }; - HeadlessView view { display, 1 }; + HeadlessBackend backend; + HeadlessView view; StubFileSource fileSource; ThreadPool threadPool { 4 }; - Map map { view, fileSource, threadPool, MapMode::Still }; + Map map { backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still }; }; } // end namespace diff --git a/test/api/render_missing.test.cpp b/test/api/render_missing.test.cpp index d57a5a98b..812307028 100644 --- a/test/api/render_missing.test.cpp +++ b/test/api/render_missing.test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -25,8 +25,8 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) { const auto style = util::read_file("test/fixtures/api/water_missing_tiles.json"); - auto display = std::make_shared(); - HeadlessView view(display, 1, 256, 512); + HeadlessBackend backend; + HeadlessView view(1, 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,7 +38,7 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) { Log::setObserver(std::make_unique()); - Map map(view, fileSource, threadPool, MapMode::Still); + Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); std::string message; diff --git a/test/api/repeated_render.test.cpp b/test/api/repeated_render.test.cpp index 4229f9d7a..5bc57198b 100644 --- a/test/api/repeated_render.test.cpp +++ b/test/api/repeated_render.test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -19,9 +19,8 @@ TEST(API, RepeatedRender) { const auto style = util::read_file("test/fixtures/api/water.json"); - auto display = std::make_shared(); - HeadlessView view(display, 1, 256, 512); - + HeadlessBackend backend; + HeadlessView view(1, 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"); @@ -33,7 +32,7 @@ TEST(API, RepeatedRender) { Log::setObserver(std::make_unique()); - Map map(view, fileSource, threadPool, MapMode::Still); + Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still); { map.setStyleJSON(style); diff --git a/test/gl/object.test.cpp b/test/gl/object.test.cpp index 1a90cec83..1f9d48cff 100644 --- a/test/gl/object.test.cpp +++ b/test/gl/object.test.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include @@ -67,8 +67,8 @@ TEST(GLObject, Value) { } TEST(GLObject, Store) { - mbgl::HeadlessView view(std::make_shared(), 1); - view.activate(); + mbgl::HeadlessBackend backend; + mbgl::HeadlessView view; mbgl::gl::Context context; EXPECT_TRUE(context.empty()); @@ -106,5 +106,5 @@ TEST(GLObject, Store) { context.reset(); EXPECT_TRUE(context.empty()); - view.deactivate(); + backend.deactivate(); } diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index c03a038dc..a5d634e77 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include @@ -22,15 +22,16 @@ using namespace std::literals::string_literals; struct MapTest { util::RunLoop runLoop; - std::shared_ptr display { std::make_shared() }; - HeadlessView view { display, 1 }; + HeadlessBackend backend; + HeadlessView view; StubFileSource fileSource; ThreadPool threadPool { 4 }; }; TEST(Map, LatLngBehavior) { MapTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); @@ -64,7 +65,8 @@ 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.view, fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, + MapMode::Still); map.setStyleURL(prefix + "style.json"); test::checkImage("test/fixtures/map/offline", @@ -81,14 +83,15 @@ TEST(Map, SetStyleInvalidJSON) { Log::setObserver(std::make_unique()); bool fail = false; - test.view.setMapChangeCallback([&](MapChange change) { + test.backend.setMapChangeCallback([&](MapChange change) { if (change == mbgl::MapChangeDidFailLoadingMap) { fail = true; } }); { - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, + test.threadPool, MapMode::Still); map.setStyleJSON("invalid"); } @@ -113,13 +116,14 @@ TEST(Map, SetStyleInvalidURL) { return response; }; - test.view.setMapChangeCallback([&](MapChange change) { + test.backend.setMapChangeCallback([&](MapChange change) { if (change == mbgl::MapChangeDidFailLoadingMap) { test.runLoop.stop(); } }); - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); map.setStyleURL("mapbox://bar"); test.runLoop.run(); @@ -128,7 +132,8 @@ TEST(Map, SetStyleInvalidURL) { TEST(Map, DoubleStyleLoad) { MapTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); map.setStyleJSON(""); map.setStyleJSON(""); } @@ -139,7 +144,8 @@ TEST(Map, StyleFresh) { MapTest test; FakeFileSource fileSource; - Map map(test.view, fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, + MapMode::Still); map.setStyleURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); @@ -159,7 +165,8 @@ TEST(Map, StyleExpired) { MapTest test; FakeFileSource fileSource; - Map map(test.view, fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, + MapMode::Still); map.setStyleURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); @@ -186,7 +193,8 @@ TEST(Map, StyleExpiredWithAnnotations) { MapTest test; FakeFileSource fileSource; - Map map(test.view, fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, + MapMode::Still); map.setStyleURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); @@ -210,7 +218,8 @@ TEST(Map, StyleEarlyMutation) { MapTest test; FakeFileSource fileSource; - Map map(test.view, fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool, + MapMode::Still); map.setStyleURL("mapbox://styles/test"); map.addLayer(std::make_unique("bg")); @@ -224,11 +233,12 @@ TEST(Map, StyleEarlyMutation) { TEST(Map, StyleLoadedSignal) { MapTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); - + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); + // The map should emit a signal on style loaded bool emitted = false; - test.view.setMapChangeCallback([&](MapChange change) { + test.backend.setMapChangeCallback([&](MapChange change) { if (change == mbgl::MapChangeDidFinishLoadingStyle) { emitted = true; } @@ -245,7 +255,8 @@ TEST(Map, StyleLoadedSignal) { TEST(Map, AddLayer) { MapTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); auto layer = std::make_unique("background"); @@ -258,7 +269,8 @@ TEST(Map, AddLayer) { TEST(Map, RemoveLayer) { MapTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); auto layer = std::make_unique("background"); @@ -283,7 +295,8 @@ TEST(Map, DisabledSources) { return {}; }; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), 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. @@ -333,7 +346,8 @@ TEST(Map, DisabledSources) { TEST(Map, Classes) { MapTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); EXPECT_FALSE(map.getTransitionOptions().duration); @@ -367,7 +381,8 @@ TEST(Map, Classes) { TEST(Map, AddImage) { MapTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), 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(std::move(decoded1), 1.0); @@ -384,7 +399,8 @@ TEST(Map, AddImage) { TEST(Map, RemoveImage) { MapTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); auto decoded = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); auto image = std::make_unique(std::move(decoded), 1.0); diff --git a/test/util/memory.test.cpp b/test/util/memory.test.cpp index e2ace99c4..2ab3a0379 100644 --- a/test/util/memory.test.cpp +++ b/test/util/memory.test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -56,8 +56,8 @@ public: } util::RunLoop runLoop; - std::shared_ptr display { std::make_shared() }; - HeadlessView view { display, 2 }; + HeadlessBackend backend; + HeadlessView view{ 2 }; StubFileSource fileSource; ThreadPool threadPool { 4 }; @@ -93,7 +93,8 @@ private: TEST(Memory, Vector) { MemoryTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); map.setZoom(16); // more map features map.setStyleURL("mapbox://streets"); @@ -103,7 +104,8 @@ TEST(Memory, Vector) { TEST(Memory, Raster) { MemoryTest test; - Map map(test.view, test.fileSource, test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool, + MapMode::Still); map.setStyleURL("mapbox://satellite"); test::render(map); @@ -130,7 +132,8 @@ TEST(Memory, Footprint) { // Warm up buffers and cache. for (unsigned i = 0; i < 10; ++i) { - Map map(test.view, test.fileSource,test.threadPool, MapMode::Still); + Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, + test.threadPool, MapMode::Still); renderMap(&map, "mapbox://streets"); renderMap(&map, "mapbox://satellite"); }; @@ -144,7 +147,8 @@ TEST(Memory, Footprint) { long vectorInitialRSS = getRSS(); for (unsigned i = 0; i < runs; ++i) { - auto vector = std::make_unique(test.view, test.fileSource, test.threadPool, MapMode::Still); + auto vector = std::make_unique(test.backend, test.view, test.view.getPixelRatio(), + test.fileSource, test.threadPool, MapMode::Still); renderMap(vector.get(), "mapbox://streets"); maps.push_back(std::move(vector)); }; @@ -153,7 +157,8 @@ TEST(Memory, Footprint) { long rasterInitialRSS = getRSS(); for (unsigned i = 0; i < runs; ++i) { - auto raster = std::make_unique(test.view, test.fileSource, test.threadPool, MapMode::Still); + auto raster = std::make_unique(test.backend, test.view, test.view.getPixelRatio(), + test.fileSource, test.threadPool, MapMode::Still); renderMap(raster.get(), "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 301fc2c25..e281a3d65 100644 --- a/test/util/offscreen_texture.test.cpp +++ b/test/util/offscreen_texture.test.cpp @@ -1,15 +1,18 @@ #include #include +#include #include +#include #include using namespace mbgl; TEST(OffscreenTexture, EmptyRed) { + HeadlessBackend backend; HeadlessView view(1.0f, 512, 256); - view.activate(); + view.bind(); MBGL_CHECK_ERROR(glClearColor(1.0f, 0.0f, 0.0f, 1.0f)); MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT)); @@ -64,11 +67,11 @@ struct Buffer { TEST(OffscreenTexture, RenderToTexture) { + HeadlessBackend backend; HeadlessView view(1.0f, 512, 256); - view.activate(); + view.bind(); gl::Context context; context.viewport.setDefaultValue(gl::value::Viewport::Get()); - context.bindFramebuffer.setDefaultValue(gl::value::BindFramebuffer::Get()); MBGL_CHECK_ERROR(glEnable(GL_BLEND)); MBGL_CHECK_ERROR(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -130,6 +133,8 @@ void main() { // Now reset the FBO back to normal and retrieve the original (restored) framebuffer. context.resetState(); + context.bindFramebuffer.setDirty(); + view.bind(); image = view.readStillImage(); test::checkImage("test/fixtures/offscreen_texture/render-to-fbo", image, 0, 0);