[core] separate Backend from View for headless rendering

This commit is contained in:
Konstantin Käfer
2016-10-06 13:23:50 +02:00
parent 45f4dc0166
commit 5cc390d694
54 changed files with 779 additions and 525 deletions

View File

@@ -2,7 +2,7 @@
#include <mbgl/benchmark/util.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/sprite/sprite_image.hpp>
@@ -35,11 +35,11 @@ public:
}
util::RunLoop loop;
std::shared_ptr<HeadlessDisplay> display{ std::make_shared<HeadlessDisplay>() };
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 }};
};

View File

@@ -16,7 +16,7 @@
namespace {
std::unique_ptr<GLFWView> 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<GLFWView>(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;
}

View File

@@ -3,7 +3,7 @@
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -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);

View File

@@ -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

View File

@@ -0,0 +1,14 @@
#pragma once
#if defined(__QT__)
#define MBGL_USE_QT 1
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IOS
#define MBGL_USE_EAGL 1
#else
#define MBGL_USE_CGL 1
#endif
#else
#define MBGL_USE_GLX 1
#endif

View File

@@ -0,0 +1,34 @@
#pragma once
#include <mbgl/map/change.hpp>
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

View File

@@ -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,

View File

@@ -1,24 +1,23 @@
#pragma once
#include <mbgl/map/change.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/image.hpp>
#include <array>
#include <functional>
#include <memory>
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<uint16_t, 2> 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<uint16_t, 2> 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

View File

@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/mbgl.hpp>
#include <mbgl/map/backend.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/timer.hpp>
#include <mbgl/util/geometry.hpp>
@@ -11,26 +12,12 @@
#define GL_GLEXT_PROTOTYPES
#include <GLFW/glfw3.h>
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<uint16_t, 2> getSize() const override;
std::array<uint16_t, 2> 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<uint16_t, 2> getSize() const override;
std::array<uint16_t, 2> 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<void(mbgl::MapChange)> callback);
void notifyMapChange(mbgl::MapChange change) override;
private:
mbgl::Color makeRandomColor() const;
mbgl::Point<double> makeRandomPoint() const;
static std::shared_ptr<const mbgl::SpriteImage>

View File

@@ -0,0 +1,78 @@
#pragma once
#include <mbgl/gl/implementation.hpp>
#if MBGL_USE_QT
class QGLWidget;
#elif MBGL_USE_CGL
#include <OpenGL/OpenGL.h>
#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 <mbgl/map/backend.hpp>
#include <mbgl/gl/extension.hpp>
#include <memory>
#include <functional>
namespace mbgl {
class HeadlessDisplay;
class HeadlessBackend : public Backend {
public:
HeadlessBackend();
HeadlessBackend(std::shared_ptr<HeadlessDisplay>);
~HeadlessBackend() override;
void invalidate() override;
void activate() override;
void deactivate() override;
void notifyMapChange(MapChange) override;
void setMapChangeCallback(std::function<void(MapChange)>&& 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<HeadlessDisplay> 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<void(MapChange)> mapChangeCallback;
};
} // namespace mbgl

View File

@@ -1,6 +1,13 @@
#pragma once
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/implementation.hpp>
#if MBGL_USE_CGL
#include <OpenGL/OpenGL.h>
#elif MBGL_USE_GLX
typedef struct _XDisplay Display;
typedef struct __GLXFBConfigRec* GLXFBConfig;
#endif
namespace mbgl {

View File

@@ -1,95 +1,37 @@
#pragma once
#if defined(__QT__)
#define MBGL_USE_QT 1
class QGLWidget;
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#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 <mbgl/mbgl.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/gl/types.hpp>
#include <mbgl/gl/extension.hpp>
#include <memory>
#include <thread>
namespace mbgl {
class HeadlessDisplay;
class HeadlessView : public View {
public:
HeadlessView(float pixelRatio, uint16_t width = 256, uint16_t height = 256);
HeadlessView(std::shared_ptr<HeadlessDisplay> 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<uint16_t, 2> getSize() const override;
std::array<uint16_t, 2> getFramebufferSize() const override;
void invalidate() override;
void activate() override;
void deactivate() override;
void notifyMapChange(MapChange) override;
PremultipliedImage readStillImage(std::array<uint16_t, 2> size = {{ 0, 0 }}) override;
float getPixelRatio() const;
void resize(uint16_t width, uint16_t height);
void setMapChangeCallback(std::function<void(MapChange)>&& 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<HeadlessDisplay> display;
private:
const float pixelRatio;
std::array<uint16_t, 2> 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<void(MapChange)> mapChangeCallback;
gl::FramebufferID fbo = 0;
gl::RenderbufferID fboDepthStencil = 0;

View File

@@ -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<mbgl::Map>(*this, *fileSource, threadPool, MapMode::Continuous);
map = std::make_unique<mbgl::Map>(*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<uint16_t, 2> NativeMapView::getSize() const {

View File

@@ -2,6 +2,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/map/backend.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -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<uint16_t, 2> getSize() const override;
std::array<uint16_t, 2> 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;

View File

@@ -0,0 +1,58 @@
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <CoreFoundation/CoreFoundation.h>
#include <string>
#include <stdexcept>
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<gl::glProc>(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

View File

@@ -0,0 +1,46 @@
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/gl/extension.hpp>
#include <OpenGLES/EAGL.h>
#include <stdexcept>
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<gl::glProc>(symbol);
}
void HeadlessBackend::createContext() {
glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (glContext == nil) {
throw std::runtime_error("Error creating GL context object");
}
[reinterpret_cast<EAGLContext*>(glContext) retain];
reinterpret_cast<EAGLContext*>(glContext).multiThreaded = YES;
}
void HeadlessBackend::destroyContext() {
[reinterpret_cast<EAGLContext*>(glContext) release];
glContext = nil;
}
void HeadlessBackend::activateContext() {
[EAGLContext setCurrentContext:reinterpret_cast<EAGLContext*>(glContext)];
}
void HeadlessBackend::deactivateContext() {
[EAGLContext setCurrentContext:nil];
}
} // namespace mbgl

View File

@@ -1,37 +1,14 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <CoreFoundation/CoreFoundation.h>
#include <mbgl/gl/gl.hpp>
#include <cassert>
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<gl::glProc>(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

View File

@@ -1,35 +1,14 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <OpenGLES/EAGL.h>
#include <mbgl/gl/gl.hpp>
#include <cassert>
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<gl::glProc>(symbol);
}
void HeadlessView::createContext() {
glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (glContext == nil) {
throw std::runtime_error("Error creating GL context object");
}
[reinterpret_cast<EAGLContext*>(glContext) retain];
reinterpret_cast<EAGLContext*>(glContext).multiThreaded = YES;
}
void HeadlessView::destroyContext() {
[reinterpret_cast<EAGLContext*>(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<EAGLContext*>(glContext)];
}
void HeadlessView::deactivateContext() {
[EAGLContext setCurrentContext:nil];
}
} // namespace mbgl

View File

@@ -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<GLFWView *>(glfwGetWindowUserPointer(window));

View File

@@ -0,0 +1,56 @@
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <cassert>
#include <stdexcept>
namespace mbgl {
HeadlessBackend::HeadlessBackend() : display(std::make_shared<HeadlessDisplay>()) {
activate();
}
HeadlessBackend::HeadlessBackend(std::shared_ptr<HeadlessDisplay> 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

View File

@@ -0,0 +1,67 @@
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/log.hpp>
// #include <cassert>
#include <GL/glx.h>
namespace mbgl {
gl::glProc HeadlessBackend::initializeExtension(const char* name) {
return glXGetProcAddress(reinterpret_cast<const GLubyte*>(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

View File

@@ -1,12 +1,13 @@
#include <mbgl/platform/default/headless_display.hpp>
#include <cstring>
#include <stdexcept>
#if MBGL_USE_GLX
#include <GL/glx.h>
#endif
#include <cstring>
#include <stdexcept>
#include <string>
namespace mbgl {
HeadlessDisplay::HeadlessDisplay() {

View File

@@ -1,34 +1,27 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <cassert>
#include <mbgl/gl/gl.hpp>
#include <cstring>
namespace mbgl {
HeadlessView::HeadlessView(float pixelRatio_, uint16_t width, uint16_t height)
: display(std::make_shared<HeadlessDisplay>())
, pixelRatio(pixelRatio_)
, dimensions({{ width, height }})
, needsResize(true) {
}
HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> 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<uint16_t, 2> size) {
assert(active);
if (!size[0] || !size[1]) {
size[0] = dimensions[0] * pixelRatio;
size[1] = dimensions[1] * pixelRatio;
@@ -76,43 +67,4 @@ std::array<uint16_t, 2> HeadlessView::getFramebufferSize() const {
static_cast<uint16_t>(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

View File

@@ -1,54 +1,14 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/gl/gl.hpp>
#include <cassert>
#include <GL/glx.h>
namespace mbgl {
gl::glProc HeadlessView::initializeExtension(const char* name) {
return glXGetProcAddress(reinterpret_cast<const GLubyte*>(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

View File

@@ -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
)

View File

@@ -18,6 +18,7 @@
#include <mbgl/storage/network_status.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/map/backend.hpp>
#include <mbgl/math/wrap.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/constants.hpp>
@@ -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<uint16_t, 2> getSize() const override {
@@ -4959,6 +4956,10 @@ public:
static_cast<uint16_t>([[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

View File

@@ -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

View File

@@ -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

View File

@@ -25,6 +25,7 @@
#import <mbgl/platform/default/thread_pool.hpp>
#import <mbgl/gl/extension.hpp>
#import <mbgl/gl/gl.hpp>
#import <mbgl/map/backend.hpp>
#import <mbgl/sprite/sprite_image.hpp>
#import <mbgl/storage/default_file_source.hpp>
#import <mbgl/storage/network_status.hpp>
@@ -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<mbgl::gl::glProc>(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<uint16_t, 2> getSize() const override {
return {{ static_cast<uint16_t>(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<uint16_t, 2> 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

View File

@@ -773,15 +773,16 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
}
NodeMap::NodeMap(v8::Local<v8::Object> 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<mbgl::Map>(view, *this, threadpool, mbgl::MapMode::Still)),
map(std::make_unique<mbgl::Map>(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");
}

View File

@@ -4,6 +4,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>
#pragma GCC diagnostic push
@@ -53,6 +54,7 @@ public:
std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback);
mbgl::HeadlessBackend backend;
mbgl::HeadlessView view;
NodeThreadPool threadpool;
std::unique_ptr<mbgl::Map> map;

View File

@@ -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
)

View File

@@ -813,7 +813,7 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin
settings.cacheDatabaseMaximumSize()))
, threadPool(4)
, mapObj(std::make_unique<mbgl::Map>(
*this, *fileSourceObj, threadPool,
*this, *this, getPixelRatio(), *fileSourceObj, threadPool,
static_cast<mbgl::MapMode>(settings.mapMode()),
static_cast<mbgl::GLContextMode>(settings.contextMode()),
static_cast<mbgl::ConstrainMode>(settings.constrainMode()),
@@ -845,6 +845,11 @@ float QMapboxGLPrivate::getPixelRatio() const
return pixelRatio;
}
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()) }};

View File

@@ -3,6 +3,7 @@
#include "qmapboxgl.hpp"
#include <mbgl/map/map.hpp>
#include <mbgl/map/backend.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -11,7 +12,7 @@
#include <QObject>
#include <QSize>
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<uint16_t, 2> getSize() const final;
std::array<uint16_t, 2> getFramebufferSize() const final;

View File

@@ -0,0 +1,46 @@
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <QApplication>
#include <QGLContext>
#include <QGLWidget>
#if QT_VERSION >= 0x050000
#include <QOpenGLContext>
#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<mbgl::gl::glProc>(thisContext->getProcAddress(name));
#endif
}
void HeadlessBackend::createContext() {
static const char* argv[] = { "mbgl" };
static int argc = 1;
static auto* app = new QApplication(argc, const_cast<char**>(argv));
Q_UNUSED(app);
glContext = new QGLWidget;
}
void HeadlessBackend::destroyContext() {
delete glContext;
}
void HeadlessBackend::activateContext() {
glContext->makeCurrent();
}
void HeadlessBackend::deactivateContext() {
glContext->doneCurrent();
}
} // namespace mbgl

View File

@@ -1,6 +1,7 @@
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <QApplication>
#include <QGLContext>
#include <QGLWidget>
@@ -9,30 +10,13 @@
#include <QOpenGLContext>
#endif
#include <cassert>
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<mbgl::gl::glProc>(thisContext->getProcAddress(name));
#endif
}
void HeadlessView::createContext() {
static const char* argv[] = { "mbgl" };
static int argc = 1;
static auto* app = new QApplication(argc, const_cast<char**>(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

12
src/mbgl/map/backend.cpp Normal file
View File

@@ -0,0 +1,12 @@
#include <mbgl/map/backend.hpp>
#include <cassert>
namespace mbgl {
void Backend::notifyMapChange(MapChange) {
// no-op
}
} // namespace mbgl

View File

@@ -1,6 +1,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/map/backend.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
@@ -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<Impl>(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<Impl>(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<AnnotationManager>(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<Painter>(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> layer, const optional<std::string>& 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<const SpriteImage> 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) {

View File

@@ -5,18 +5,13 @@
namespace mbgl {
PremultipliedImage View::readStillImage(std::array<uint16_t, 2>) {
return {};
}
void View::initialize(Map *map_) {
assert(map_);
map = map_;
}
PremultipliedImage View::readStillImage(std::array<uint16_t, 2>) {
return {};
}
void View::notifyMapChange(MapChange) {
// no-op
}
} // namespace mbgl

View File

@@ -3,10 +3,12 @@
namespace mbgl {
class Shaders;
class View;
class PaintParameters {
public:
Shaders& shaders;
View& view;
};
} // namespace mbgl

View File

@@ -5,6 +5,8 @@
#include <mbgl/style/source.hpp>
#include <mbgl/style/source_impl.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/debugging.hpp>
@@ -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<CustomLayer>()->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));

View File

@@ -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<uint16_t, 2> 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&);

View File

@@ -1,6 +1,8 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/debug_bucket.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/shader/shaders.hpp>
#include <mbgl/util/string.hpp>
@@ -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<uint8_t[]>(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<GLubyte[]>(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

View File

@@ -1,6 +1,7 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/renderer/render_tile.hpp>
@@ -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<GLfloat>(frame.framebufferSize);
auto worldSize = util::convert<GLfloat>(parameters.view.getFramebufferSize());
bool pattern = !properties.fillPattern.value.from.empty();
bool outline = properties.fillAntialias && !pattern && isOutlineColorDefined;

View File

@@ -5,7 +5,7 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
@@ -23,11 +23,11 @@ std::shared_ptr<SpriteImage> namedMarker(const std::string &name) {
class AnnotationTest {
public:
util::RunLoop loop;
std::shared_ptr<HeadlessDisplay> display { std::make_shared<HeadlessDisplay>() };
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,

View File

@@ -3,7 +3,8 @@
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/util/exception.hpp>
@@ -19,13 +20,14 @@ TEST(API, RenderWithoutCallback) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
HeadlessBackend backend;
HeadlessView view;
view.resize(128, 512);
StubFileSource fileSource;
ThreadPool threadPool(4);
std::unique_ptr<Map> map = std::make_unique<Map>(view, fileSource, threadPool, MapMode::Still);
std::unique_ptr<Map> map = std::make_unique<Map>(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<mbgl::HeadlessDisplay>();
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&&) {

View File

@@ -2,7 +2,7 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -85,8 +85,8 @@ public:
TEST(CustomLayer, Basic) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
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<CustomLayer>(

View File

@@ -1,5 +1,5 @@
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/sprite/sprite_image.hpp>
@@ -26,11 +26,11 @@ public:
}
util::RunLoop loop;
std::shared_ptr<HeadlessDisplay> display { std::make_shared<HeadlessDisplay>() };
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

View File

@@ -2,7 +2,7 @@
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -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<mbgl::HeadlessDisplay>();
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<FixtureLogObserver>());
Map map(view, fileSource, threadPool, MapMode::Still);
Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still);
std::string message;

View File

@@ -2,7 +2,7 @@
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -19,9 +19,8 @@ TEST(API, RepeatedRender) {
const auto style = util::read_file("test/fixtures/api/water.json");
auto display = std::make_shared<mbgl::HeadlessDisplay>();
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<FixtureLogObserver>());
Map map(view, fileSource, threadPool, MapMode::Still);
Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still);
{
map.setStyleJSON(style);

View File

@@ -1,6 +1,6 @@
#include <mbgl/test/util.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/gl.hpp>
@@ -67,8 +67,8 @@ TEST(GLObject, Value) {
}
TEST(GLObject, Store) {
mbgl::HeadlessView view(std::make_shared<mbgl::HeadlessDisplay>(), 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();
}

View File

@@ -4,7 +4,7 @@
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/sprite/sprite_image.hpp>
@@ -22,15 +22,16 @@ using namespace std::literals::string_literals;
struct MapTest {
util::RunLoop runLoop;
std::shared_ptr<HeadlessDisplay> display { std::make_shared<mbgl::HeadlessDisplay>() };
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<FixtureLogObserver>());
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<style::BackgroundLayer>("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<BackgroundLayer>("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<BackgroundLayer>("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<SpriteImage>(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<SpriteImage>(std::move(decoded), 1.0);

View File

@@ -2,7 +2,7 @@
#include <mbgl/test/util.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/util/io.hpp>
@@ -56,8 +56,8 @@ public:
}
util::RunLoop runLoop;
std::shared_ptr<HeadlessDisplay> display { std::make_shared<mbgl::HeadlessDisplay>() };
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<Map>(test.view, test.fileSource, test.threadPool, MapMode::Still);
auto vector = std::make_unique<Map>(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<Map>(test.view, test.fileSource, test.threadPool, MapMode::Still);
auto raster = std::make_unique<Map>(test.backend, test.view, test.view.getPixelRatio(),
test.fileSource, test.threadPool, MapMode::Still);
renderMap(raster.get(), "mapbox://satellite");
maps.push_back(std::move(raster));
};

View File

@@ -1,15 +1,18 @@
#include <mbgl/test/util.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/util/offscreen_texture.hpp>
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);