[core] Render from the main thread

Do not create a thread for the MapContext anymore.
This commit is contained in:
John Firebaugh
2015-09-01 15:07:17 +03:00
parent 18d8e80f52
commit 204c7fee03
35 changed files with 268 additions and 525 deletions

View File

@@ -32,10 +32,6 @@ class ShapeAnnotation;
struct CameraOptions;
struct AnimationOptions;
namespace util {
template <class T> class Thread;
} // namespace util
class Map : private util::noncopyable {
friend class View;
@@ -46,22 +42,15 @@ public:
ConstrainMode constrainMode = ConstrainMode::HeightOnly);
~Map();
// Pauses the render thread. The render thread will stop running but will not be terminated and will not lose state until resumed.
void pause();
bool isPaused();
// Resumes a paused render thread
void resume();
// Register a callback that will get called (on the render thread) when all resources have
// been loaded and a complete render occurs.
using StillImageCallback = std::function<void (std::exception_ptr, PremultipliedImage&&)>;
void renderStill(StillImageCallback callback);
// Triggers a synchronous render.
void renderSync();
// Main render function.
void render();
// Notifies the Map thread that the state has changed and an update might be necessary.
// Notifies the Map that the state has changed and an update might be necessary.
void update(Update update);
// Styling
@@ -189,7 +178,7 @@ public:
private:
View& view;
const std::unique_ptr<Transform> transform;
const std::unique_ptr<util::Thread<MapContext>> context;
const std::unique_ptr<MapContext> context;
MapData* data;
enum class RenderState {

View File

@@ -30,44 +30,42 @@ enum MapChange : uint8_t {
class View {
public:
// Called from the main thread directly after initialization. Must always return the same value,
// i.e. it may not change over time.
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 from the main thread when the View signaled a dimension change. Must return the
// logical dimension of this map in pixels.
// Called when the View signaled a dimension change. Must return the logical dimension
// of this map in pixels.
virtual std::array<uint16_t, 2> getSize() const = 0;
// Called from the main thread for every frame that is being rendered. Must return the absolute
// dimensions of the current framebuffer. Typically, this is the logical width scaled by the
// pixel ratio, but in case the view was moved to display with a different pixel ratio, it can
// also be different from that rule.
// Called for every frame that is being rendered. Must return the absolute dimensions of
// the current framebuffer. Typically, this is the logical width scaled by the pixel ratio,
// but in case the view was moved to display with a different pixel ratio, it can also be
// different from that rule.
virtual std::array<uint16_t, 2> getFramebufferSize() const = 0;
// Called from the main thread when this View is associated with a Map object.
virtual void initialize(Map *map_);
// Called when this View is associated with a Map object.
virtual void initialize(Map*);
// Called from the render thread. Makes the GL context active in the current
// thread. This is typically just called once at the beginning of the
// renderer setup since the render thread doesn't switch the contexts.
// 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::addCustomLayer
// 3. When calling a CustomLayerDeinitializeFunction, during Map::removeCustomLayer
// 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;
// Called from the render thread. Makes the GL context inactive in the current
// thread. This is called once just before the rendering thread terminates.
virtual void deactivate() = 0;
virtual void notify() = 0;
// Called from the render thread. The implementation must trigger a rerender.
// (map->renderSync() from the main thread must be called as a result of this)
// 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;
// Called from the render thread before the render begins.
virtual void beforeRender() = 0;
// Called from the render thread after the render is complete.
virtual void afterRender() = 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();

View File

@@ -22,13 +22,10 @@ public:
std::array<uint16_t, 2> getSize() const override;
std::array<uint16_t, 2> getFramebufferSize() const override;
void initialize(mbgl::Map *map) override;
void initialize(mbgl::Map*) override;
void activate() override;
void deactivate() override;
void notify() override;
void invalidate() override;
void beforeRender() override;
void afterRender() override;
static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods);
static void onScroll(GLFWwindow *window, double xoffset, double yoffset);

View File

@@ -38,34 +38,31 @@ public:
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 notify() override;
void invalidate() override;
void beforeRender() override;
void afterRender() override;
PremultipliedImage readStillImage() override;
void resizeFramebuffer();
void resize(uint16_t width, uint16_t height);
private:
void loadExtensions();
bool isActive() const;
// Implementation specific functions
static gl::glProc initializeExtension(const char*);
void createContext();
void destroyContext();
void clearBuffers();
void resizeFramebuffer();
void activateContext();
void deactivateContext();
private:
std::shared_ptr<HeadlessDisplay> display;
const float pixelRatio;
std::array<uint16_t, 2> dimensions;
bool needsResize = false;
bool extensionsLoaded = false;
bool active = false;
#if MBGL_USE_CGL
CGLContextObj glContext = nullptr;
@@ -82,13 +79,9 @@ private:
GLXPbuffer glxPbuffer = 0;
#endif
bool extensionsLoaded = false;
GLuint fbo = 0;
GLuint fboDepthStencil = 0;
GLuint fboColor = 0;
std::thread::id thread;
};
} // namespace mbgl

View File

@@ -521,7 +521,6 @@ public class MapView extends FrameLayout {
mConnectivityReceiver = null;
mUserLocationView.onPause();
mNativeMapView.pause();
}
/**
@@ -533,7 +532,6 @@ public class MapView extends FrameLayout {
mConnectivityReceiver = new ConnectivityReceiver();
getContext().registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
mNativeMapView.resume();
mNativeMapView.update();
mUserLocationView.onResume();
@@ -1094,9 +1092,7 @@ public class MapView extends FrameLayout {
}
int getTopOffsetPixelsForIcon(Icon icon) {
// This method will dead lock if map paused. Causes a freeze if you add a marker in an
// activity's onCreate()
if (mDestroyed || mNativeMapView.isPaused()) {
if (mDestroyed) {
return 0;
}
@@ -1243,11 +1239,11 @@ public class MapView extends FrameLayout {
return;
}
if (mDestroyed || mNativeMapView.isPaused()) {
if (mDestroyed) {
return;
}
mNativeMapView.renderSync();
mNativeMapView.render();
}
@Override

View File

@@ -119,24 +119,12 @@ final class NativeMapView {
nativeDestroySurface(mNativeMapViewPtr);
}
public void pause() {
nativePause(mNativeMapViewPtr);
}
public boolean isPaused() {
return nativeIsPaused(mNativeMapViewPtr);
}
public void resume() {
nativeResume(mNativeMapViewPtr);
}
public void update() {
nativeUpdate(mNativeMapViewPtr);
}
public void renderSync() {
nativeRenderSync(mNativeMapViewPtr);
public void render() {
nativeRender(mNativeMapViewPtr);
}
public void resizeView(int width, int height) {
@@ -515,15 +503,9 @@ final class NativeMapView {
private native void nativeDestroySurface(long nativeMapViewPtr);
private native void nativePause(long nativeMapViewPtr);
private native boolean nativeIsPaused(long nativeMapViewPtr);
private native void nativeResume(long nativeMapViewPtr);
private native void nativeUpdate(long nativeMapViewPtr);
private native void nativeRenderSync(long nativeMapViewPtr);
private native void nativeRender(long nativeMapViewPtr);
private native void nativeViewResize(long nativeMapViewPtr, int width, int height);

View File

@@ -404,27 +404,6 @@ void nativeDestroySurface(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr
nativeMapView->destroySurface();
}
void nativePause(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativePause");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->pause();
}
jboolean nativeIsPaused(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeIsPaused");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
return nativeMapView->getMap().isPaused();
}
void nativeResume(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeResume");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->resume();
}
void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate");
assert(nativeMapViewPtr != 0);
@@ -432,11 +411,11 @@ void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
nativeMapView->getMap().update(mbgl::Update::Repaint);
}
void nativeRenderSync(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeRenderSync");
void nativeRender(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeRender");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->getMap().renderSync();
nativeMapView->render();
}
void nativeViewResize(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jint width, jint height) {
@@ -1815,11 +1794,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeTerminateContext, "(J)V"),
MAKE_NATIVE_METHOD(nativeCreateSurface, "(JLandroid/view/Surface;)V"),
MAKE_NATIVE_METHOD(nativeDestroySurface, "(J)V"),
MAKE_NATIVE_METHOD(nativePause, "(J)V"),
MAKE_NATIVE_METHOD(nativeIsPaused, "(J)Z"),
MAKE_NATIVE_METHOD(nativeResume, "(J)V"),
MAKE_NATIVE_METHOD(nativeUpdate, "(J)V"),
MAKE_NATIVE_METHOD(nativeRenderSync, "(J)V"),
MAKE_NATIVE_METHOD(nativeRender, "(J)V"),
MAKE_NATIVE_METHOD(nativeViewResize, "(JII)V"),
MAKE_NATIVE_METHOD(nativeFramebufferResize, "(JII)V"),
MAKE_NATIVE_METHOD(nativeAddClass, "(JLjava/lang/String;)V"),

View File

@@ -90,8 +90,6 @@ NativeMapView::NativeMapView(JNIEnv *env, jobject obj_, float pixelRatio_, int a
size_t cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f;
map->setSourceTileCacheSize(cacheSize);
map->pause();
}
NativeMapView::~NativeMapView() {
@@ -189,20 +187,15 @@ void NativeMapView::invalidate() {
detach_jni_thread(vm, &env, detach);
}
void NativeMapView::beforeRender() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::beforeRender()");
void NativeMapView::render() {
activate();
if(sizeChanged){
sizeChanged = false;
glViewport(0, 0, fbWidth, fbHeight);
}
}
void NativeMapView::afterRender() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::afterRender()");
assert(vm != nullptr);
assert(obj != nullptr);
map->render();
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
if (!eglSwapBuffers(display, surface)) {
@@ -215,11 +208,8 @@ void NativeMapView::afterRender() {
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
}
}
void NativeMapView::notify() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notify()");
// noop
deactivate();
}
mbgl::Map &NativeMapView::getMap() { return *map; }
@@ -444,15 +434,11 @@ void NativeMapView::createSurface(ANativeWindow *window_) {
throw std::runtime_error("eglMakeCurrent() failed");
}
}
resume();
}
void NativeMapView::destroySurface() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::destroySurface");
pause();
if (surface != EGL_NO_SURFACE) {
if (!eglDestroySurface(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
@@ -669,27 +655,6 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfi
return configId;
}
void NativeMapView::pause() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause");
if ((display != EGL_NO_DISPLAY) && (context != EGL_NO_CONTEXT)) {
map->pause();
}
}
void NativeMapView::resume() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::resume");
assert(display != EGL_NO_DISPLAY);
assert(context != EGL_NO_CONTEXT);
if (surface != EGL_NO_SURFACE) {
map->resume();
} else {
mbgl::Log::Debug(mbgl::Event::Android, "Not resuming because we are not ready");
}
}
void NativeMapView::notifyMapChange(mbgl::MapChange change) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notifyMapChange()");

View File

@@ -24,10 +24,7 @@ public:
std::array<uint16_t, 2> getFramebufferSize() const override;
void activate() override;
void deactivate() override;
void notify() override;
void invalidate() override;
void beforeRender() override;
void afterRender() override;
void notifyMapChange(mbgl::MapChange) override;
@@ -43,8 +40,7 @@ public:
void createSurface(ANativeWindow *window);
void destroySurface();
void resume();
void pause();
void render();
void enableFps(bool enable);
void updateFps();

View File

@@ -76,7 +76,7 @@ void HeadlessView::resizeFramebuffer() {
}
void HeadlessView::clearBuffers() {
assert(isActive());
assert(active);
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));

View File

@@ -72,7 +72,7 @@ void HeadlessView::resizeFramebuffer() {
}
void HeadlessView::clearBuffers() {
assert(isActive());
assert(active);
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0));

View File

@@ -428,7 +428,18 @@ void GLFWView::run() {
const bool dirty = !clean.test_and_set();
if (dirty) {
const double started = glfwGetTime();
map->renderSync();
glfwMakeContextCurrent(window);
glViewport(0, 0, fbWidth, fbHeight);
map->render();
if (showClipMasks) {
renderClipMasks();
}
glfwSwapBuffers(window);
report(1000 * (glfwGetTime() - started));
if (benchmark) {
map->update(mbgl::Update::Repaint);
@@ -460,31 +471,11 @@ void GLFWView::deactivate() {
glfwMakeContextCurrent(nullptr);
}
void GLFWView::notify() {
glfwPostEmptyEvent();
}
void GLFWView::invalidate() {
clean.clear();
glfwPostEmptyEvent();
}
void GLFWView::beforeRender() {
// This is called from the map thread but `width` and `height`
// can be accessed with no race because the main thread is blocked
// when we render. This will be more straightforward when we move
// rendering to the main thread.
glViewport(0, 0, fbWidth, fbHeight);
}
void GLFWView::afterRender() {
if (showClipMasks) {
renderClipMasks();
}
glfwSwapBuffers(window);
}
void GLFWView::renderClipMasks() {
// Read the stencil buffer
auto pixels = std::make_unique<uint8_t[]>(fbWidth * fbHeight);

View File

@@ -31,22 +31,6 @@ HeadlessView::~HeadlessView() {
destroyContext();
}
void HeadlessView::loadExtensions() {
if (extensionsLoaded) {
return;
}
gl::InitializeExtensions(initializeExtension);
extensionsLoaded = true;
}
bool HeadlessView::isActive() const {
return std::this_thread::get_id() == thread;
}
void HeadlessView::resize(const uint16_t width, const uint16_t height) {
if(dimensions[0] == width &&
dimensions[1] == height) {
@@ -57,7 +41,7 @@ void HeadlessView::resize(const uint16_t width, const uint16_t height) {
}
PremultipliedImage HeadlessView::readStillImage() {
assert(isActive());
assert(active);
const unsigned int w = dimensions[0] * pixelRatio;
const unsigned int h = dimensions[1] * pixelRatio;
@@ -76,9 +60,6 @@ PremultipliedImage HeadlessView::readStillImage() {
return image;
}
void HeadlessView::notify() {
// no-op
}
float HeadlessView::getPixelRatio() const {
return pixelRatio;
@@ -94,10 +75,7 @@ std::array<uint16_t, 2> HeadlessView::getFramebufferSize() const {
}
void HeadlessView::activate() {
if (thread != std::thread::id()) {
throw std::runtime_error("OpenGL context was already current");
}
thread = std::this_thread::get_id();
active = true;
if (!glContext) {
if (!display) {
@@ -107,23 +85,12 @@ void HeadlessView::activate() {
}
activateContext();
loadExtensions();
}
void HeadlessView::deactivate() {
if (thread == std::thread::id()) {
throw std::runtime_error("OpenGL context was not current");
if (!extensionsLoaded) {
gl::InitializeExtensions(initializeExtension);
extensionsLoaded = true;
}
thread = std::thread::id();
deactivateContext();
}
void HeadlessView::invalidate() {
// no-op
}
void HeadlessView::beforeRender() {
if (needsResize) {
clearBuffers();
resizeFramebuffer();
@@ -131,8 +98,13 @@ void HeadlessView::beforeRender() {
}
}
void HeadlessView::afterRender() {
// no-op
void HeadlessView::deactivate() {
deactivateContext();
active = false;
}
void HeadlessView::invalidate() {
assert(false);
}
} // namespace mbgl

View File

@@ -93,7 +93,7 @@ void HeadlessView::resizeFramebuffer() {
}
void HeadlessView::clearBuffers() {
assert(isActive());
assert(active);
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));

View File

@@ -320,7 +320,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// start paused if in IB
if (_isTargetingInterfaceBuilder || background) {
self.dormant = YES;
_mbglMap->pause();
}
// Notify map object when network reachability status changes.
@@ -751,8 +750,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
_mbglMap->setSourceTileCacheSize(cacheSize);
_mbglMap->renderSync();
_mbglMap->render();
[self updateUserLocationAnnotationView];
}
@@ -895,7 +893,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
[self validateDisplayLink];
self.dormant = YES;
_mbglMap->pause();
[self.glView deleteDrawable];
}
}
@@ -967,8 +964,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self.glSnapshotView addSubview:snapshotTint];
}
_mbglMap->pause();
[self.glView deleteDrawable];
}
}
@@ -988,8 +983,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self.glSnapshotView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
[self.glView bindDrawable];
_mbglMap->resume();
_displayLink.paused = NO;
@@ -3957,12 +3950,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
class MBGLView : public mbgl::View
{
public:
MBGLView(MGLMapView* nativeView_, const float scaleFactor_)
: nativeView(nativeView_), scaleFactor(scaleFactor_) {
}
virtual ~MBGLView() {}
public:
MBGLView(MGLMapView* nativeView_, const float scaleFactor_)
: nativeView(nativeView_), scaleFactor(scaleFactor_) {
}
float getPixelRatio() const override {
return scaleFactor;
@@ -3970,7 +3961,7 @@ class MBGLView : public mbgl::View
std::array<uint16_t, 2> getSize() const override {
return {{ static_cast<uint16_t>([nativeView bounds].size.width),
static_cast<uint16_t>([nativeView bounds].size.height) }};
static_cast<uint16_t>([nativeView bounds].size.height) }};
}
std::array<uint16_t, 2> getFramebufferSize() const override {
@@ -3978,17 +3969,16 @@ class MBGLView : public mbgl::View
static_cast<uint16_t>([[nativeView glView] drawableHeight]) }};
}
void notify() override
{
// no-op
}
void notifyMapChange(mbgl::MapChange change) override
{
assert([[NSThread currentThread] isMainThread]);
[nativeView notifyMapChange:change];
}
void invalidate() override
{
[nativeView setNeedsGLDisplay];
}
void activate() override
{
[EAGLContext setCurrentContext:nativeView.context];
@@ -3999,26 +3989,9 @@ class MBGLView : public mbgl::View
[EAGLContext setCurrentContext:nil];
}
void invalidate() override
{
[nativeView performSelectorOnMainThread:@selector(setNeedsGLDisplay)
withObject:nil
waitUntilDone:NO];
}
void beforeRender() override
{
// no-op
}
void afterRender() override
{
// no-op
}
private:
__weak MGLMapView *nativeView = nullptr;
const float scaleFactor;
private:
__weak MGLMapView *nativeView = nullptr;
const float scaleFactor;
};
@end

View File

@@ -1,10 +1,8 @@
#include "node_map.hpp"
#include "node_request.hpp"
#include "node_mapbox_gl_native.hpp"
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/work_request.hpp>
#include <unistd.h>
@@ -474,18 +472,16 @@ NodeMap::~NodeMap() {
if (valid) release();
}
std::unique_ptr<mbgl::AsyncRequest> NodeMap::request(const mbgl::Resource& resource, Callback cb1) {
// This function can be called from any thread. Make sure we're executing the
// JS implementation in the node event loop.
return NodeRunLoop().invokeWithCallback([this] (mbgl::Resource res, Callback cb2) {
Nan::HandleScope scope;
std::unique_ptr<mbgl::AsyncRequest> NodeMap::request(const mbgl::Resource& resource, Callback callback_) {
Nan::HandleScope scope;
auto requestHandle = NodeRequest::Create(res, cb2)->ToObject();
auto callbackHandle = Nan::New<v8::Function>(NodeRequest::Respond, requestHandle);
auto requestHandle = NodeRequest::Create(resource, callback_)->ToObject();
auto callbackHandle = Nan::New<v8::Function>(NodeRequest::Respond, requestHandle);
v8::Local<v8::Value> argv[] = { requestHandle, callbackHandle };
Nan::MakeCallback(handle()->GetInternalField(1)->ToObject(), "request", 2, argv);
}, cb1, resource);
v8::Local<v8::Value> argv[] = { requestHandle, callbackHandle };
Nan::MakeCallback(handle()->GetInternalField(1)->ToObject(), "request", 2, argv);
return std::make_unique<mbgl::AsyncRequest>();
}
}

View File

@@ -5,25 +5,18 @@
#include <nan.h>
#pragma GCC diagnostic pop
#include "node_mapbox_gl_native.hpp"
#include <mbgl/util/run_loop.hpp>
#include "node_map.hpp"
#include "node_log.hpp"
#include "node_request.hpp"
namespace node_mbgl {
mbgl::util::RunLoop& NodeRunLoop() {
static mbgl::util::RunLoop nodeRunLoop;
return nodeRunLoop;
}
}
void RegisterModule(v8::Local<v8::Object> target, v8::Local<v8::Object> module) {
// This has the effect of:
// a) Ensuring that the static local variable is initialized before any thread contention.
// b) unreffing an async handle, which otherwise would keep the default loop running.
node_mbgl::NodeRunLoop().stop();
static mbgl::util::RunLoop nodeRunLoop;
nodeRunLoop.stop();
node_mbgl::NodeMap::Init(target);
node_mbgl::NodeRequest::Init(target);

View File

@@ -1,9 +0,0 @@
#pragma once
#include <mbgl/util/run_loop.hpp>
namespace node_mbgl {
mbgl::util::RunLoop& NodeRunLoop();
}

View File

@@ -298,7 +298,9 @@ test('Map', function(t) {
t.test('returning an error', function(t) {
var map = new mbgl.Map({
request: function(req, callback) {
callback(new Error('request error'));
setImmediate(function () {
callback(new Error('request error'));
});
},
});
map.load(style);

View File

@@ -540,7 +540,6 @@ public:
[self deselectAnnotation:self.selectedAnnotation];
if (!self.dormant && !newWindow) {
self.dormant = YES;
_mbglMap->pause();
}
[self.window removeObserver:self forKeyPath:@"contentLayoutRect"];
@@ -550,7 +549,6 @@ public:
- (void)viewDidMoveToWindow {
NSWindow *window = self.window;
if (self.dormant && window) {
_mbglMap->resume();
self.dormant = NO;
}
@@ -686,8 +684,31 @@ public:
NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
_mbglMap->setSourceTileCacheSize(cacheSize);
_mbglMap->renderSync();
// Enable vertex buffer objects.
mbgl::gl::InitializeExtensions([](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<mbgl::gl::glProc>(symbol);
});
_mbglMap->render();
if (_isPrinting) {
_isPrinting = NO;
std::string png = encodePNG(_mbglView->readStillImage());
NSData *data = [[NSData alloc] initWithBytes:png.data() length:png.size()];
NSImage *image = [[NSImage alloc] initWithData:data];
[self printWithImage:image];
}
// [self updateUserLocationAnnotationView];
}
}
@@ -2270,85 +2291,39 @@ class MGLMapViewImpl : public mbgl::View {
public:
MGLMapViewImpl(MGLMapView *nativeView_, const float scaleFactor_)
: nativeView(nativeView_), scaleFactor(scaleFactor_) {}
virtual ~MGLMapViewImpl() {}
float getPixelRatio() const override {
return scaleFactor;
}
std::array<uint16_t, 2> getSize() const override {
return {{ static_cast<uint16_t>(nativeView.bounds.size.width),
static_cast<uint16_t>(nativeView.bounds.size.height) }};
static_cast<uint16_t>(nativeView.bounds.size.height) }};
}
std::array<uint16_t, 2> getFramebufferSize() const override {
NSRect bounds = [nativeView convertRectToBacking:nativeView.bounds];
return {{ static_cast<uint16_t>(bounds.size.width),
static_cast<uint16_t>(bounds.size.height) }};
static_cast<uint16_t>(bounds.size.height) }};
}
void notify() override {}
void notifyMapChange(mbgl::MapChange change) override {
assert([[NSThread currentThread] isMainThread]);
[nativeView notifyMapChange:change];
}
void invalidate() override {
[nativeView invalidate];
}
void activate() override {
MGLOpenGLLayer *layer = (MGLOpenGLLayer *)nativeView.layer;
if ([NSOpenGLContext currentContext] != layer.openGLContext) {
// Enable our OpenGL context on the Map thread.
[layer.openGLContext makeCurrentContext];
// Enable vertex buffer objects.
mbgl::gl::InitializeExtensions([](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<mbgl::gl::glProc>(symbol);
});
}
[layer.openGLContext makeCurrentContext];
}
void deactivate() override {
[NSOpenGLContext clearCurrentContext];
}
void invalidate() override {
[nativeView performSelectorOnMainThread:@selector(invalidate)
withObject:nil
waitUntilDone:NO];
}
void beforeRender() override {
// This normally gets called right away by mbgl::Map, but only on the
// main thread. OpenGL contexts and extensions are thread-local, so this
// has to happen on the Map thread too.
activate();
// auto size = getFramebufferSize();
// MBGL_CHECK_ERROR(glViewport(0, 0, size[0], size[1]));
}
void afterRender() override {
if (nativeView->_isPrinting) {
nativeView->_isPrinting = NO;
std::string png = encodePNG(readStillImage());
NSData *data = [[NSData alloc] initWithBytes:png.data() length:png.size()];
NSImage *image = [[NSImage alloc] initWithData:data];
[nativeView performSelectorOnMainThread:@selector(printWithImage:)
withObject:image
waitUntilDone:NO];
}
}
mbgl::PremultipliedImage readStillImage() override {
auto size = getFramebufferSize();
const unsigned int w = size[0];

View File

@@ -12,7 +12,6 @@
#include <mbgl/layer/custom_layer.hpp>
#include <mbgl/util/projection.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/math.hpp>
namespace mbgl {
@@ -20,46 +19,24 @@ namespace mbgl {
Map::Map(View& view_, FileSource& fileSource, MapMode mapMode, GLContextMode contextMode, ConstrainMode constrainMode)
: view(view_),
transform(std::make_unique<Transform>(view, constrainMode)),
context(std::make_unique<util::Thread<MapContext>>(
util::ThreadContext{"Map", util::ThreadType::Map, util::ThreadPriority::Regular},
context(std::make_unique<MapContext>(
view, fileSource, mapMode, contextMode, view.getPixelRatio())),
data(&context->invokeSync<MapData&>(&MapContext::getData))
data(&context->getData())
{
view.initialize(this);
update(Update::Dimensions);
}
Map::~Map() {
resume();
context->invokeSync(&MapContext::cleanup);
}
void Map::pause() {
assert(data->mode == MapMode::Continuous);
std::unique_lock<std::mutex> lockPause(data->mutexPause);
if (!data->paused) {
context->invoke(&MapContext::pause);
data->condPause.wait(lockPause, [&]{ return data->paused; });
}
}
bool Map::isPaused() {
return data->paused;
}
void Map::resume() {
std::unique_lock<std::mutex> lockPause(data->mutexPause);
data->paused = false;
data->condPause.notify_all();
context->cleanup();
}
void Map::renderStill(StillImageCallback callback) {
context->invoke(&MapContext::renderStill, transform->getState(),
context->renderStill(transform->getState(),
FrameData { view.getFramebufferSize(), Clock::now() }, callback);
}
void Map::renderSync() {
void Map::render() {
if (renderState == RenderState::never) {
view.notifyMapChange(MapChangeWillStartRenderingMap);
}
@@ -67,8 +44,7 @@ void Map::renderSync() {
view.notifyMapChange(MapChangeWillStartRenderingFrame);
const Update flags = transform->updateTransitions(Clock::now());
const bool fullyLoaded = context->invokeSync<bool>(
&MapContext::renderSync, transform->getState(), FrameData { view.getFramebufferSize(), Clock::now() });
const bool fullyLoaded = context->renderSync(transform->getState(), FrameData { view.getFramebufferSize(), Clock::now() });
view.notifyMapChange(fullyLoaded ?
MapChangeDidFinishRenderingFrameFullyRendered :
@@ -94,7 +70,7 @@ void Map::renderSync() {
void Map::update(Update flags) {
if (flags & Update::Dimensions) transform->resize(view.getSize());
context->invoke(&MapContext::triggerUpdate, transform->getState(), flags);
context->triggerUpdate(transform->getState(), flags);
}
#pragma mark - Style
@@ -102,21 +78,21 @@ void Map::update(Update flags) {
void Map::setStyleURL(const std::string &url) {
loading = true;
view.notifyMapChange(MapChangeWillStartLoadingMap);
context->invoke(&MapContext::setStyleURL, url);
context->setStyleURL(url);
}
void Map::setStyleJSON(const std::string& json, const std::string& base) {
loading = true;
view.notifyMapChange(MapChangeWillStartLoadingMap);
context->invoke(&MapContext::setStyleJSON, json, base);
context->setStyleJSON(json, base);
}
std::string Map::getStyleURL() const {
return context->invokeSync<std::string>(&MapContext::getStyleURL);
return context->getStyleURL();
}
std::string Map::getStyleJSON() const {
return context->invokeSync<std::string>(&MapContext::getStyleJSON);
return context->getStyleJSON();
}
#pragma mark - Transitions
@@ -436,15 +412,15 @@ LatLng Map::latLngForPixel(const ScreenCoordinate& pixel) const {
#pragma mark - Annotations
void Map::addAnnotationIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
context->invoke(&MapContext::addAnnotationIcon, name, sprite);
context->addAnnotationIcon(name, sprite);
}
void Map::removeAnnotationIcon(const std::string& name) {
context->invoke(&MapContext::removeAnnotationIcon, name);
context->removeAnnotationIcon(name);
}
double Map::getTopOffsetPixelsForAnnotationIcon(const std::string& symbol) {
return context->invokeSync<double>(&MapContext::getTopOffsetPixelsForAnnotationIcon, symbol);
return context->getTopOffsetPixelsForAnnotationIcon(symbol);
}
AnnotationID Map::addPointAnnotation(const PointAnnotation& annotation) {
@@ -489,17 +465,21 @@ AnnotationIDs Map::getPointAnnotationsInBounds(const LatLngBounds& bounds) {
void Map::addCustomLayer(const std::string& id,
CustomLayerInitializeFunction initialize,
CustomLayerRenderFunction render,
CustomLayerRenderFunction render_,
CustomLayerDeinitializeFunction deinitialize,
void* context_,
const char* before) {
context->invoke(&MapContext::addLayer,
std::make_unique<CustomLayer>(id, initialize, render, deinitialize, context_),
view.activate();
context->addLayer(
std::make_unique<CustomLayer>(id, initialize, render_, deinitialize, context_),
before ? std::string(before) : optional<std::string>());
view.deactivate();
}
void Map::removeCustomLayer(const std::string& id) {
context->invoke(&MapContext::removeLayer, id);
view.activate();
context->removeLayer(id);
view.deactivate();
}
#pragma mark - Toggles
@@ -519,39 +499,39 @@ MapDebugOptions Map::getDebug() const {
}
bool Map::isFullyLoaded() const {
return context->invokeSync<bool>(&MapContext::isLoaded);
return context->isLoaded();
}
void Map::addClass(const std::string& className, const PropertyTransition& properties) {
context->invoke(&MapContext::addClass, className, properties);
context->addClass(className, properties);
}
void Map::removeClass(const std::string& className, const PropertyTransition& properties) {
context->invoke(&MapContext::removeClass, className, properties);
context->removeClass(className, properties);
}
void Map::setClasses(const std::vector<std::string>& classNames, const PropertyTransition& properties) {
context->invoke(&MapContext::setClasses, classNames, properties);
context->setClasses(classNames, properties);
}
bool Map::hasClass(const std::string& className) const {
return context->invokeSync<bool>(&MapContext::hasClass, className);
return context->hasClass(className);
}
std::vector<std::string> Map::getClasses() const {
return context->invokeSync<std::vector<std::string>>(&MapContext::getClasses);
return context->getClasses();
}
void Map::setSourceTileCacheSize(size_t size) {
context->invoke(&MapContext::setSourceTileCacheSize, size);
context->setSourceTileCacheSize(size);
}
void Map::onLowMemory() {
context->invoke(&MapContext::onLowMemory);
context->onLowMemory();
}
void Map::dumpDebugLogs() const {
context->invokeSync(&MapContext::dumpDebugLogs);
context->dumpDebugLogs();
}
} // namespace mbgl

View File

@@ -35,11 +35,7 @@ MapContext::MapContext(View& view_, FileSource& fileSource_, MapMode mode_, GLCo
dataPtr(std::make_unique<MapData>(mode_, contextMode_, pixelRatio_)),
data(*dataPtr),
asyncUpdate([this] { update(); }),
asyncInvalidate([&view_] { view_.invalidate(); }),
texturePool(std::make_unique<gl::TexturePool>()) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
view.activate();
}
MapContext::~MapContext() {
@@ -48,7 +44,7 @@ MapContext::~MapContext() {
}
void MapContext::cleanup() {
view.notify();
view.activate();
styleRequest = nullptr;
@@ -64,21 +60,6 @@ void MapContext::cleanup() {
view.deactivate();
}
void MapContext::pause() {
MBGL_CHECK_ERROR(glFinish());
view.deactivate();
std::unique_lock<std::mutex> lockPause(data.mutexPause);
data.paused = true;
data.condPause.notify_all();
data.condPause.wait(lockPause, [&]{ return !data.paused; });
view.activate();
asyncInvalidate.send();
}
void MapContext::updateAsync(Update flags) {
updateFlags |= flags;
asyncUpdate.send();
@@ -180,9 +161,11 @@ void MapContext::update() {
style->update(transformState, frameData.timePoint, *texturePool);
if (data.mode == MapMode::Continuous) {
asyncInvalidate.send();
view.invalidate();
} else if (callback && isLoaded()) {
view.activate();
renderSync(transformState, frameData);
view.deactivate();
}
updateFlags = Update::Nothing;
@@ -229,8 +212,6 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame)
return false;
}
view.beforeRender();
transformState = state;
frameData = frame;
@@ -245,8 +226,6 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame)
// Cleanup OpenGL objects that we abandoned since the last render call.
glObjectStore.performCleanup();
view.afterRender();
if (style->hasTransitions()) {
updateAsync(Update::RecalculateStyle);
} else if (painter->needsAnimation()) {
@@ -312,7 +291,7 @@ void MapContext::setSourceTileCacheSize(size_t size) {
sourceCacheSize = size;
if (!style) return;
style->setSourceTileCacheSize(size);
asyncInvalidate.send();
view.invalidate();
}
}
@@ -320,7 +299,7 @@ void MapContext::onLowMemory() {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
if (!style) return;
style->onLowMemory();
asyncInvalidate.send();
view.invalidate();
}
void MapContext::onResourceLoaded() {

View File

@@ -37,8 +37,6 @@ public:
MapData& getData() { return data; }
void pause();
void triggerUpdate(const TransformState&, Update = Update::Nothing);
void renderStill(const TransformState&, const FrameData&, Map::StillImageCallback callback);
@@ -97,7 +95,6 @@ private:
Update updateFlags = Update::Nothing;
util::AsyncTask asyncUpdate;
util::AsyncTask asyncInvalidate;
std::unique_ptr<gl::TexturePool> texturePool;
std::unique_ptr<Painter> painter;

View File

@@ -1,11 +1,8 @@
#ifndef MBGL_MAP_MAP_DATA
#define MBGL_MAP_MAP_DATA
#include <mutex>
#include <atomic>
#include <vector>
#include <cassert>
#include <condition_variable>
#include <mbgl/map/mode.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
@@ -14,8 +11,6 @@
namespace mbgl {
class MapData {
using Lock = std::lock_guard<std::mutex>;
public:
inline MapData(MapMode mode_, GLContextMode contextMode_, const float pixelRatio_)
: mode(mode_)
@@ -46,10 +41,8 @@ public:
debugOptions = debugOptions_;
}
util::exclusive<AnnotationManager> getAnnotationManager() {
return util::exclusive<AnnotationManager>(
&annotationManager,
std::make_unique<std::lock_guard<std::mutex>>(annotationManagerMutex));
AnnotationManager* getAnnotationManager() {
return &annotationManager;
}
public:
@@ -58,16 +51,12 @@ public:
const float pixelRatio;
private:
mutable std::mutex annotationManagerMutex;
AnnotationManager annotationManager;
std::atomic<MapDebugOptions> debugOptions { MapDebugOptions::NoDebug };
MapDebugOptions debugOptions { MapDebugOptions::NoDebug };
// TODO: make private
public:
bool paused = false;
std::mutex mutexPause;
std::condition_variable condPause;
};
} // namespace mbgl

View File

@@ -15,7 +15,7 @@ enum class ThreadPriority : bool {
enum class ThreadType : uint8_t {
Main,
Map,
Map = Main,
Worker,
Unknown,
};

View File

@@ -8,6 +8,7 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
#include <future>
#include <vector>
@@ -29,6 +30,8 @@ void checkRendering(Map& map, const char * name) {
} // end namespace
TEST(Annotations, PointAnnotation) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -42,6 +45,8 @@ TEST(Annotations, PointAnnotation) {
}
TEST(Annotations, LineAnnotation) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -61,6 +66,8 @@ TEST(Annotations, LineAnnotation) {
}
TEST(Annotations, FillAnnotation) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -79,6 +86,8 @@ TEST(Annotations, FillAnnotation) {
}
TEST(Annotations, StyleSourcedShapeAnnotation) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -94,6 +103,8 @@ TEST(Annotations, StyleSourcedShapeAnnotation) {
}
TEST(Annotations, AddMultiple) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -111,6 +122,8 @@ TEST(Annotations, AddMultiple) {
}
TEST(Annotations, NonImmediateAdd) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -131,6 +144,8 @@ TEST(Annotations, NonImmediateAdd) {
}
TEST(Annotations, UpdateIcon) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -150,6 +165,8 @@ TEST(Annotations, UpdateIcon) {
}
TEST(Annotations, UpdatePoint) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -168,6 +185,8 @@ TEST(Annotations, UpdatePoint) {
}
TEST(Annotations, RemovePoint) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -185,6 +204,8 @@ TEST(Annotations, RemovePoint) {
}
TEST(Annotations, RemoveShape) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -207,6 +228,8 @@ TEST(Annotations, RemoveShape) {
}
TEST(Annotations, ImmediateRemoveShape) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
@@ -219,6 +242,8 @@ TEST(Annotations, ImmediateRemoveShape) {
}
TEST(Annotations, SwitchStyle) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;

View File

@@ -6,6 +6,7 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/run_loop.hpp>
#include <future>
@@ -15,6 +16,8 @@ TEST(API, RenderWithoutCallback) {
FixtureLogObserver* log = new FixtureLogObserver();
Log::setObserver(std::unique_ptr<Log::Observer>(log));
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
view.resize(128, 512);
@@ -37,6 +40,8 @@ TEST(API, RenderWithoutCallback) {
}
TEST(API, RenderWithoutStyle) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
view.resize(128, 512);
@@ -44,13 +49,16 @@ TEST(API, RenderWithoutStyle) {
Map map(view, fileSource, MapMode::Still);
std::promise<std::exception_ptr> promise;
map.renderStill([&promise](std::exception_ptr error, PremultipliedImage&&) {
promise.set_value(error);
std::exception_ptr error;
map.renderStill([&](std::exception_ptr error_, PremultipliedImage&&) {
error = error_;
loop.stop();
});
loop.run();
try {
std::rethrow_exception(promise.get_future().get());
std::rethrow_exception(error);
} catch (const util::MisuseException& ex) {
EXPECT_EQ(std::string(ex.what()), "Map doesn't have a style");
} catch (const std::exception&) {

View File

@@ -7,6 +7,7 @@
#include <mbgl/layer/custom_layer.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/run_loop.hpp>
using namespace mbgl;
@@ -68,6 +69,8 @@ public:
};
TEST(CustomLayer, Basic) {
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;

View File

@@ -7,6 +7,7 @@
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
#include <future>
@@ -19,6 +20,8 @@
TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) {
using namespace mbgl;
util::RunLoop loop;
const auto style = util::read_file("test/fixtures/api/water_missing_tiles.json");
auto display = std::make_shared<mbgl::HeadlessDisplay>();
@@ -38,8 +41,7 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) {
// This host does not respond (== connection error).
map.setStyleJSON(style, "");
std::promise<void> promise;
map.renderStill([&promise, &message](std::exception_ptr err, PremultipliedImage&&) {
map.renderStill([&](std::exception_ptr err, PremultipliedImage&&) {
ASSERT_TRUE(err.operator bool());
try {
std::rethrow_exception(err);
@@ -47,9 +49,10 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) {
message = ex.what();
EXPECT_TRUE(message.find("connect") != std::string::npos);
}
promise.set_value();
loop.stop();
});
promise.get_future().get();
loop.run();
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());

View File

@@ -7,12 +7,15 @@
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
#include <future>
TEST(API, RepeatedRender) {
using namespace mbgl;
util::RunLoop loop;
const auto style = util::read_file("test/fixtures/api/water.json");
auto display = std::make_shared<mbgl::HeadlessDisplay>();
@@ -30,11 +33,15 @@ TEST(API, RepeatedRender) {
{
map.setStyleJSON(style, "");
std::promise<PremultipliedImage> promise;
map.renderStill([&promise](std::exception_ptr, PremultipliedImage&& image) {
promise.set_value(std::move(image));
PremultipliedImage result;
map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
result = std::move(image);
});
auto result = promise.get_future().get();
while (!result.size()) {
loop.runOnce();
}
ASSERT_EQ(256, result.width);
ASSERT_EQ(512, result.height);
#if !TEST_READ_ONLY
@@ -44,11 +51,15 @@ TEST(API, RepeatedRender) {
{
map.setStyleJSON(style, "");
std::promise<PremultipliedImage> promise;
map.renderStill([&promise](std::exception_ptr, PremultipliedImage&& image) {
promise.set_value(std::move(image));
PremultipliedImage result;
map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
result = std::move(image);
});
auto result = promise.get_future().get();
while (!result.size()) {
loop.runOnce();
}
ASSERT_EQ(256, result.width);
ASSERT_EQ(512, result.height);
#if !TEST_READ_ONLY

View File

@@ -5,11 +5,14 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/run_loop.hpp>
TEST(API, SetStyle) {
using namespace mbgl;
util::RunLoop loop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;

View File

@@ -3,59 +3,17 @@
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/storage/offline_database.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
using namespace mbgl;
using namespace std::literals::string_literals;
TEST(Map, PauseResume) {
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
Map map(view, fileSource, MapMode::Continuous);
map.pause();
map.resume();
}
TEST(Map, DoublePause) {
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
Map map(view, fileSource, MapMode::Continuous);
map.pause();
map.pause();
map.resume();
}
TEST(Map, ResumeWithoutPause) {
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
Map map(view, fileSource, MapMode::Continuous);
map.resume();
}
TEST(Map, DestroyPaused) {
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
OnlineFileSource fileSource;
Map map(view, fileSource, MapMode::Continuous);
map.pause();
}
TEST(Map, Offline) {
util::RunLoop runLoop;
auto display = std::make_shared<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
DefaultFileSource fileSource(":memory:", ".");

View File

@@ -18,10 +18,7 @@ public:
void activate() override {};
void deactivate() override {};
void notify() override {};
void invalidate() override {}
void beforeRender() override {}
void afterRender() override {}
};
}

View File

@@ -5,6 +5,7 @@
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mapbox/pixelmatch.hpp>
@@ -86,11 +87,16 @@ Server::~Server() {
}
PremultipliedImage render(Map& map) {
std::promise<PremultipliedImage> promise;
map.renderStill([&](std::exception_ptr, PremultipliedImage&& image) {
promise.set_value(std::move(image));
PremultipliedImage result;
map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
result = std::move(image);
});
return promise.get_future().get();
while (!result.size()) {
util::RunLoop::Get()->runOnce();
}
return result;
}
void checkImage(const std::string& base,

View File

@@ -41,7 +41,6 @@
'geometry/binpack.cpp',
'map/map.cpp',
'map/map_context.cpp',
'map/tile.cpp',
'map/transform.cpp',