Merge pull request #1065 from mapbox/1065-separate-ui-from-map-thread

Better separation between UI thread and Map thread
This commit is contained in:
John Firebaugh
2015-04-30 10:22:06 -04:00
85 changed files with 1373 additions and 2012 deletions

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@
/test/build
/test/node_modules
/test/fixtures/*/*_actual.*
/test/fixtures/database/*.db
/include/mbgl/shader/shaders.hpp
/src/shader/shaders_gl.cpp
/src/shader/shaders_gles2.cpp

View File

@@ -1,5 +1,6 @@
#include <cstdint>
#include <cinttypes>
#include <cassert>
#include <string>
#include <locale>
@@ -255,20 +256,6 @@ void JNICALL nativeDestroySurface(JNIEnv *env, jobject obj, jlong nativeMapViewP
nativeMapView->destroySurface();
}
void JNICALL nativeStart(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeStart");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->start();
}
void JNICALL nativeStop(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeStop");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->stop();
}
void JNICALL nativePause(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativePause");
assert(nativeMapViewPtr != 0);
@@ -287,14 +274,7 @@ void JNICALL nativeUpdate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->getMap().triggerUpdate();
}
void JNICALL nativeTerminate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminate");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->getMap().terminate();
nativeMapView->getMap().update();
}
void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint width, jint height,
@@ -310,7 +290,7 @@ void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint
assert(fbWidth <= UINT16_MAX);
assert(fbHeight <= UINT16_MAX);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->resize(width, height, ratio, fbWidth, fbHeight);
nativeMapView->getMap().resize(width, height, ratio);
}
void JNICALL nativeRemoveClass(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring clazz) {
@@ -938,12 +918,9 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
{"nativeCreateSurface", "(JLandroid/view/Surface;)V",
reinterpret_cast<void *>(&nativeCreateSurface)},
{"nativeDestroySurface", "(J)V", reinterpret_cast<void *>(&nativeDestroySurface)},
{"nativeStart", "(J)V", reinterpret_cast<void *>(&nativeStart)},
{"nativeStop", "(J)V", reinterpret_cast<void *>(&nativeStop)},
{"nativePause", "(J)V", reinterpret_cast<void *>(&nativePause)},
{"nativeResume", "(J)V", reinterpret_cast<void *>(&nativeResume)},
{"nativeUpdate", "(J)V", reinterpret_cast<void *>(&nativeUpdate)},
{"nativeTerminate", "(J)V", reinterpret_cast<void *>(&nativeTerminate)},
{"nativeResize", "(JIIFII)V",
reinterpret_cast<void *>(static_cast<void JNICALL (
*)(JNIEnv *, jobject, jlong, jint, jint, jfloat, jint, jint)>(&nativeResize))},

View File

@@ -1,5 +1,6 @@
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <memory>
#include <list>
#include <tuple>
@@ -16,6 +17,7 @@
#include <mbgl/platform/gl.hpp>
#include <mbgl/util/std.hpp>
#include <pthread.h>
pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT;
@@ -59,7 +61,7 @@ NativeMapView::NativeMapView(JNIEnv *env, jobject obj_)
: mbgl::View(*this),
fileCache(mbgl::android::cachePath + "/mbgl-cache.db"),
fileSource(&fileCache),
map(*this, fileSource) {
map(*this, fileSource, MapMode::Continuous, true) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView");
assert(env != nullptr);
@@ -121,11 +123,11 @@ void NativeMapView::deactivate() {
}
}
void NativeMapView::invalidate() {
void NativeMapView::invalidate(std::function<void()> render) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate");
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
map.render();
render();
if (!eglSwapBuffers(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
@@ -576,23 +578,6 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfi
return configId;
}
void NativeMapView::start() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::start");
if (display == EGL_NO_DISPLAY) {
initializeDisplay();
}
if (context == EGL_NO_CONTEXT) {
initializeContext();
}
assert(display != EGL_NO_DISPLAY);
assert(context != EGL_NO_CONTEXT);
map.start(true);
}
void loadExtensions() {
const GLubyte *str = glGetString(GL_EXTENSIONS);
if (str == nullptr) {
@@ -689,14 +674,6 @@ void loadExtensions() {
}
}
void NativeMapView::stop() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::stop");
if ((display != EGL_NO_DISPLAY) && (context != EGL_NO_CONTEXT)) {
map.stop();
}
}
void NativeMapView::pause(bool waitForPause) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause %s",
(waitForPause) ? "true" : "false");
@@ -824,9 +801,5 @@ void NativeMapView::updateFps() {
env = nullptr;
}
void NativeMapView::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
View::resize(width, height, ratio, fbWidth, fbHeight);
}
}
}

View File

@@ -419,7 +419,6 @@ public class MapView extends SurfaceView {
mNativeMapView.initializeDisplay();
mNativeMapView.initializeContext();
mNativeMapView.start();
}
// Called when we need to save instance state
@@ -441,7 +440,6 @@ public class MapView extends SurfaceView {
// Called when we need to clean up
// Must be called from Activity onDestroy
public void onDestroy() {
mNativeMapView.stop();
mNativeMapView.terminateContext();
mNativeMapView.terminateDisplay();
}

View File

@@ -73,14 +73,6 @@ class NativeMapView {
nativeDestroySurface(mNativeMapViewPtr);
}
public void start() {
nativeStart(mNativeMapViewPtr);
}
public void stop() {
nativeStop(mNativeMapViewPtr);
}
public void pause() {
nativePause(mNativeMapViewPtr);
}
@@ -386,18 +378,12 @@ class NativeMapView {
private native void nativeDestroySurface(long nativeMapViewPtr);
private native void nativeStart(long nativeMapViewPtr);
private native void nativeStop(long nativeMapViewPtr);
private native void nativePause(long nativeMapViewPtr);
private native void nativeResume(long nativeMapViewPtr);
private native void nativeUpdate(long nativeMapViewPtr);
private native void nativeTerminate(long nativeMapViewPtr);
private native void nativeResize(long nativeMapViewPtr, int width,
int height, float ratio, int fbWidth, int fbHeight);

View File

@@ -23,7 +23,7 @@ public:
void activate() override;
void deactivate() override;
void notify() override;
void invalidate() override;
void invalidate(std::function<void()> render) override;
void notifyMapChange(mbgl::MapChange change, Duration delay = Duration::zero()) override;
@@ -39,17 +39,12 @@ public:
void createSurface(ANativeWindow *window);
void destroySurface();
void start();
void stop();
void resume();
void pause(bool waitForPause = false);
void enableFps(bool enable);
void updateFps();
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight);
private:
EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);

View File

@@ -62,9 +62,6 @@ private:
std::vector<uint32_t> abandonedVAOs;
std::vector<uint32_t> abandonedBuffers;
std::vector<uint32_t> abandonedTextures;
public:
uv_loop_t* const loop;
};
class EnvironmentScope final {

View File

@@ -1,96 +1,57 @@
#ifndef MBGL_MAP_MAP
#define MBGL_MAP_MAP
#include <mbgl/map/transform.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/map/update.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/projection.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/vec.hpp>
#include <cstdint>
#include <atomic>
#include <thread>
#include <iosfwd>
#include <set>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <string>
#include <functional>
namespace uv { class async; }
#include <vector>
#include <memory>
namespace mbgl {
class Painter;
class GlyphStore;
class LayerDescription;
class Sprite;
class Style;
class StyleLayer;
class TexturePool;
class FileSource;
class View;
class GlyphAtlas;
class SpriteAtlas;
class LineAtlas;
class Environment;
class EnvironmentScope;
class AnnotationManager;
class MapData;
class Worker;
class MapContext;
class StillImage;
namespace util {
template <class T> class Thread;
}
class Map : private util::noncopyable {
friend class View;
public:
enum class Mode : uint8_t {
None, // we're not doing any processing
Continuous, // continually updating map
Still, // a once-off still image.
};
explicit Map(View&, FileSource&);
explicit Map(View&, FileSource&,
MapMode mode = MapMode::Continuous,
bool startPaused = false);
~Map();
// Start the map render thread. It is asynchronous.
void start(bool startPaused = false, Mode mode = Mode::Continuous);
inline void start(Mode renderMode) { start(false, renderMode); }
// Stop the map render thread. This call will block until the map rendering thread stopped.
// The optional callback function will be invoked repeatedly until the map thread is stopped.
// The callback function should wait until it is woken up again by view.notify(), otherwise
// this will be a busy waiting loop.
void stop(std::function<void ()> callback = std::function<void ()>());
// 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 waitForPause = false);
// 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::unique_ptr<const StillImage>)>;
void renderStill(StillImageCallback callback);
// Triggers a synchronous or asynchronous render.
void renderSync();
// Unconditionally performs a render with the current map state. May only be called from the Map
// thread.
void render();
void renderAsync();
// Notifies the Map thread that the state has changed and an update might be necessary.
void triggerUpdate(Update = Update::Nothing);
// Triggers a render. Can be called from any thread.
void triggerRender();
// Releases resources immediately
void terminate();
void update(Update update = Update::Nothing);
// Styling
void addClass(const std::string&);
@@ -134,18 +95,23 @@ public:
double getBearing() const;
void resetNorth();
// Size
void resize(uint16_t width, uint16_t height, float ratio = 1);
uint16_t getWidth() const;
uint16_t getHeight() const;
// API
void setAccessToken(const std::string &token);
std::string getAccessToken() const;
// Projection
inline void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) const { Projection::getWorldBoundsMeters(sw, ne); }
inline void getWorldBoundsLatLng(LatLng &sw, LatLng &ne) const { Projection::getWorldBoundsLatLng(sw, ne); }
inline double getMetersPerPixelAtLatitude(const double lat, const double zoom) const { return Projection::getMetersPerPixelAtLatitude(lat, zoom); }
inline const ProjectedMeters projectedMetersForLatLng(const LatLng latLng) const { return Projection::projectedMetersForLatLng(latLng); }
inline const LatLng latLngForProjectedMeters(const ProjectedMeters projectedMeters) const { return Projection::latLngForProjectedMeters(projectedMeters); }
inline const vec2<double> pixelForLatLng(const LatLng latLng) const { return state.pixelForLatLng(latLng); }
inline const LatLng latLngForPixel(const vec2<double> pixel) const { return state.latLngForPixel(pixel); }
void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) const;
void getWorldBoundsLatLng(LatLng &sw, LatLng &ne) const;
double getMetersPerPixelAtLatitude(const double lat, const double zoom) const;
const ProjectedMeters projectedMetersForLatLng(const LatLng latLng) const;
const LatLng latLngForProjectedMeters(const ProjectedMeters projectedMeters) const;
const vec2<double> pixelForLatLng(const LatLng latLng) const;
const LatLng latLngForPixel(const vec2<double> pixel) const;
// Annotations
void setDefaultPointAnnotationSymbol(const std::string&);
@@ -160,7 +126,6 @@ public:
// Memory
void setSourceTileCacheSize(size_t);
size_t getSourceTileCacheSize() const { return sourceCacheSize; }
void onLowMemory();
// Debug
@@ -168,104 +133,9 @@ public:
void toggleDebug();
bool getDebug() const;
inline const TransformState &getState() const { return state; }
TimePoint getTime() const;
inline AnnotationManager& getAnnotationManager() const { return *annotationManager; }
private:
// Runs the map event loop. ONLY run this function when you want to get render a single frame
// with this map object. It will *not* spawn a separate thread and instead block until the
// frame is completely rendered.
void run();
// This may only be called by the View object.
void resize(uint16_t width, uint16_t height, float ratio = 1);
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight);
util::ptr<Sprite> getSprite();
Worker& getWorker();
// Checks if render thread needs to pause
void checkForPause();
// Setup
void setup();
void updateTiles();
// Triggered by triggerUpdate();
void update();
// Loads the style set in the data object. Called by Update::StyleInfo
void reloadStyle();
void loadStyleJSON(const std::string& json, const std::string& base);
// Prepares a map render by updating the tiles we need for the current view, as well as updating
// the stylesheet.
void prepare();
// Runs the function in the map thread.
void invokeTask(std::function<void()>&&);
template <typename Fn> auto invokeSyncTask(const Fn& fn) -> decltype(fn());
void processTasks();
void updateAnnotationTiles(const std::vector<TileID>&);
size_t sourceCacheSize;
Mode mode = Mode::None;
const std::unique_ptr<Environment> env;
std::unique_ptr<EnvironmentScope> scope;
View &view;
private:
std::unique_ptr<Worker> workers;
std::thread thread;
std::unique_ptr<uv::async> asyncTerminate;
std::unique_ptr<uv::async> asyncUpdate;
std::unique_ptr<uv::async> asyncInvoke;
std::unique_ptr<uv::async> asyncRender;
bool terminating = false;
bool pausing = false;
bool isPaused = false;
std::mutex mutexRun;
std::condition_variable condRun;
std::mutex mutexPause;
std::condition_variable condPause;
// Used to signal that rendering completed.
bool rendered = false;
std::condition_variable condRendered;
std::mutex mutexRendered;
// Stores whether the map thread has been stopped already.
std::atomic_bool isStopped;
Transform transform;
TransformState state;
FileSource& fileSource;
util::ptr<Style> style;
std::unique_ptr<GlyphAtlas> glyphAtlas;
util::ptr<GlyphStore> glyphStore;
std::unique_ptr<SpriteAtlas> spriteAtlas;
util::ptr<Sprite> sprite;
std::unique_ptr<LineAtlas> lineAtlas;
util::ptr<TexturePool> texturePool;
std::unique_ptr<Painter> painter;
std::unique_ptr<AnnotationManager> annotationManager;
const std::unique_ptr<MapData> data;
std::atomic<UpdateType> updated;
std::mutex mutexTask;
std::queue<std::function<void()>> tasks;
StillImageCallback callback;
const std::unique_ptr<util::Thread<MapContext>> context;
};
}

15
include/mbgl/map/mode.hpp Normal file
View File

@@ -0,0 +1,15 @@
#ifndef MBGL_MAP_MODE
#define MBGL_MAP_MODE
#include <cstdint>
namespace mbgl {
enum class MapMode : uint8_t {
Continuous, // continually updating map
Still, // a once-off still image
};
}
#endif

View File

@@ -9,7 +9,6 @@ using UpdateType = uint32_t;
enum class Update : UpdateType {
Nothing = 0,
StyleInfo = 1 << 0,
Debug = 1 << 1,
DefaultTransitionDuration = 1 << 2,
Classes = 1 << 3,

View File

@@ -2,6 +2,7 @@
#define MBGL_MAP_VIEW
#include <mbgl/util/chrono.hpp>
#include <functional>
#include <memory>
@@ -39,13 +40,13 @@ public:
virtual void notify() = 0;
// Called from the render thread. The implementation must trigger a rerender.
// (i.e. map->renderSync() or map->renderAsync() must be called as a result of this)
virtual void invalidate() = 0;
// Called from the render thread. The implementation should resize the framebuffer.
virtual void resize(uint16_t width, uint16_t height, float pixelRatio);
// Called from the render (=GL) thread. Signals that the contents of the contents
// may be discarded. The default is a no-op.
virtual void discard();
// Called from the render thread. The implementation must trigger a rerender.
// (i.e. either the passed render() function for rendering immediately on the map thread,
// or map->renderSync() from the main thread must be called as a result of this)
virtual void invalidate(std::function<void()> render) = 0;
// Reads the pixel data from the current framebuffer. If your View implementation
// doesn't support reading from the framebuffer, return a null pointer.
@@ -58,10 +59,6 @@ public:
MapChange change,
Duration delay = Duration::zero());
protected:
// Resizes the view
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight);
protected:
mbgl::Map *map = nullptr;
};

View File

@@ -17,7 +17,7 @@ public:
void activate() override;
void deactivate() override;
void notify() override;
void invalidate() override;
void invalidate(std::function<void()> render) override;
static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods);
static void onScroll(GLFWwindow *window, double xoffset, double yoffset);
@@ -25,7 +25,7 @@ public:
static void onMouseClick(GLFWwindow *window, int button, int action, int modifiers);
static void onMouseMove(GLFWwindow *window, double x, double y);
int run();
void run();
void fps();
public:

View File

@@ -17,6 +17,7 @@ typedef XID GLXPbuffer;
#include <mbgl/platform/gl.hpp>
#include <memory>
#include <thread>
namespace mbgl {
@@ -28,13 +29,11 @@ public:
HeadlessView(std::shared_ptr<HeadlessDisplay> display, uint16_t width = 256, uint16_t height = 256, float pixelRatio = 1);
~HeadlessView();
void resize(uint16_t width, uint16_t height, float pixelRatio);
void activate() override;
void deactivate() override;
void notify() override;
void invalidate() override;
void discard() override;
void resize(uint16_t width, uint16_t height, float pixelRatio) override;
void invalidate(std::function<void()> render) override;
std::unique_ptr<StillImage> readStillImage() override;
private:
@@ -56,12 +55,7 @@ private:
float pixelRatio = 0;
};
// These are the values that represent the state of the current framebuffer.
Dimensions current;
// These are the values that will be used after the next discard() event.
std::mutex prospectiveMutex;
Dimensions prospective;
Dimensions dimensions;
#if MBGL_USE_CGL
CGLContextObj glContext = nullptr;

View File

@@ -1,95 +0,0 @@
#ifndef MBGL_UTIL_ASYNC_QUEUE
#define MBGL_UTIL_ASYNC_QUEUE
#include "std.hpp"
#include <uv.h>
#include <thread>
#include <mutex>
#include <functional>
#include <queue>
#include <string>
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
#define UV_ASYNC_PARAMS(handle) uv_async_t *handle, int
#else
#define UV_ASYNC_PARAMS(handle) uv_async_t *handle
#endif
namespace mbgl {
namespace util {
template <typename T>
class AsyncQueue {
public:
AsyncQueue(uv_loop_t *loop, std::function<void(T &)> fn) :
callback(fn) {
async.data = this;
uv_async_init(loop, &async, [](UV_ASYNC_PARAMS(handle)) {
auto q = reinterpret_cast<AsyncQueue *>(handle->data);
q->process();
});
}
void send(T &&data) {
{
std::lock_guard<std::mutex> lock(mutex);
queue.push(util::make_unique<T>(std::move(data)));
}
uv_async_send(&async);
}
void send(std::unique_ptr<T> data) {
{
std::lock_guard<std::mutex> lock(mutex);
queue.push(std::move(data));
}
uv_async_send(&async);
}
void stop() {
uv_close((uv_handle_t *)&async, [](uv_handle_t *handle) {
delete reinterpret_cast<AsyncQueue *>(handle->data);
});
}
void ref() {
uv_ref((uv_handle_t *)&async);
}
void unref() {
uv_unref((uv_handle_t *)&async);
}
private:
~AsyncQueue() {
}
void process() {
std::unique_ptr<T> item;
while (true) {
mutex.lock();
if (queue.empty()) {
mutex.unlock();
break;
}
item = std::move(queue.front());
queue.pop();
mutex.unlock();
callback(*item);
}
}
private:
std::mutex mutex;
uv_async_t async;
std::queue<std::unique_ptr<T>> queue;
std::function<void(T &)> callback;
};
}
}
#endif

View File

@@ -90,7 +90,7 @@ int main(int argc, char *argv[]) {
map.setStyleURL(style);
int ret = view->run();
view->run();
// Save settings
mbgl::LatLng latLng = map.getLatLng();
@@ -101,5 +101,5 @@ int main(int argc, char *argv[]) {
settings.debug = map.getDebug();
settings.save();
return ret;
return 0;
}

View File

@@ -133,7 +133,7 @@ int main() {
// Load style
map.setStyleURL("asset://styles/bright-v7.json");
int ret = view.run();
view.run();
[reachability stopNotifier];
@@ -146,5 +146,5 @@ int main() {
settings.debug = map.getDebug();
settings.save();
return ret;
return 0;
}

View File

@@ -1,9 +1,8 @@
#include <mbgl/storage/http_request.hpp>
#include <mbgl/storage/http_context.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/parsedate.h>
@@ -46,25 +45,29 @@ enum class ResponseStatus : uint8_t {
class HTTPNSURLContext;
class HTTPRequestImpl {
class HTTPRequest : public RequestBase {
public:
HTTPRequestImpl(HTTPRequest *request, uv_loop_t *loop, std::shared_ptr<const Response> response);
~HTTPRequestImpl();
HTTPRequest(HTTPNSURLContext*,
const Resource&,
Callback,
uv_loop_t*,
std::shared_ptr<const Response>);
~HTTPRequest();
void cancel();
void cancel() override;
void retry() override;
private:
void cancelTimer();
void start();
void handleResult(NSData *data, NSURLResponse *res, NSError *error);
void handleResponse();
void retry(uint64_t timeout);
void retryImmediately();
static void restart(uv_timer_t *timer, int);
private:
HTTPNSURLContext *context = nullptr;
HTTPRequest *request = nullptr;
bool cancelled = false;
NSURLSessionDataTask *task = nullptr;
std::unique_ptr<Response> response;
const std::shared_ptr<const Response> existingResponse;
@@ -79,18 +82,20 @@ private:
// -------------------------------------------------------------------------------------------------
class HTTPNSURLContext : public HTTPContext<HTTPNSURLContext> {
class HTTPNSURLContext : public HTTPContext {
public:
HTTPNSURLContext(uv_loop_t *loop);
~HTTPNSURLContext();
RequestBase* createRequest(const Resource&,
RequestBase::Callback,
uv_loop_t*,
std::shared_ptr<const Response>) override;
NSURLSession *session = nil;
NSString *userAgent = nil;
};
template<> pthread_key_t ThreadContext<HTTPNSURLContext>::key{};
template<> pthread_once_t ThreadContext<HTTPNSURLContext>::once = PTHREAD_ONCE_INIT;
HTTPNSURLContext::HTTPNSURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
@autoreleasepool {
NSURLSessionConfiguration *sessionConfig =
@@ -116,34 +121,51 @@ HTTPNSURLContext::~HTTPNSURLContext() {
userAgent = nullptr;
}
RequestBase* HTTPNSURLContext::createRequest(const Resource& resource,
RequestBase::Callback callback,
uv_loop_t* loop,
std::shared_ptr<const Response> response) {
return new HTTPRequest(this, resource, callback, loop, response);
}
// -------------------------------------------------------------------------------------------------
HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop,
std::shared_ptr<const Response> existingResponse_)
: context(HTTPNSURLContext::Get(loop)),
request(request_),
HTTPRequest::HTTPRequest(HTTPNSURLContext* context_, const Resource& resource_, Callback callback_, uv_loop_t *loop, std::shared_ptr<const Response> existingResponse_)
: RequestBase(resource_, callback_),
context(context_),
existingResponse(existingResponse_),
async(new uv_async_t) {
assert(request);
context->addRequest(request);
context->addRequest(this);
async->data = this;
uv_async_init(loop, async, [](uv_async_t *as, int) {
auto impl = reinterpret_cast<HTTPRequestImpl *>(as->data);
auto impl = reinterpret_cast<HTTPRequest *>(as->data);
impl->handleResponse();
});
start();
}
void HTTPRequestImpl::start() {
HTTPRequest::~HTTPRequest() {
assert(!task);
assert(async);
// Stop the backoff timer to avoid re-triggering this request.
cancelTimer();
uv::close(async);
context->removeRequest(this);
}
void HTTPRequest::start() {
assert(!task);
attempts++;
@autoreleasepool {
NSMutableString *url = [[NSMutableString alloc] initWithString:@(request->resource.url.c_str())];
NSMutableString *url = [[NSMutableString alloc] initWithString:@(resource.url.c_str())];
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"mapbox_metrics_disabled"] == nil) {
if ([url rangeOfString:@"?"].location == NSNotFound) {
[url appendString:@"?"];
@@ -175,13 +197,13 @@ void HTTPRequestImpl::start() {
}
}
void HTTPRequestImpl::handleResponse() {
void HTTPRequest::handleResponse() {
if (task) {
[task release];
task = nullptr;
}
if (request) {
if (!cancelled) {
if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) {
strategy = ExponentialBackoff;
return retry((1 << (attempts - 1)) * 1000);
@@ -194,23 +216,18 @@ void HTTPRequestImpl::handleResponse() {
// Actually return the response.
if (status == ResponseStatus::NotModified) {
request->notify(std::move(response), FileCache::Hint::Refresh);
notify(std::move(response), FileCache::Hint::Refresh);
} else {
request->notify(std::move(response), FileCache::Hint::Full);
notify(std::move(response), FileCache::Hint::Full);
}
context->removeRequest(request);
request->impl = nullptr;
delete request;
request = nullptr;
}
delete this;
}
void HTTPRequestImpl::cancel() {
context->removeRequest(request);
request = nullptr;
void HTTPRequest::cancel() {
context->removeRequest(this);
cancelled = true;
// Stop the backoff timer to avoid re-triggering this request.
cancelTimer();
@@ -224,7 +241,7 @@ void HTTPRequestImpl::cancel() {
}
}
void HTTPRequestImpl::cancelTimer() {
void HTTPRequest::cancelTimer() {
if (timer) {
uv_timer_stop(timer);
uv::close(timer);
@@ -232,21 +249,6 @@ void HTTPRequestImpl::cancelTimer() {
}
}
HTTPRequestImpl::~HTTPRequestImpl() {
assert(!task);
assert(async);
// Stop the backoff timer to avoid re-triggering this request.
cancelTimer();
uv::close(async);
if (request) {
context->removeRequest(request);
request->impl = nullptr;
}
}
int64_t parseCacheControl(const char *value) {
if (value) {
unsigned long long seconds = 0;
@@ -262,7 +264,7 @@ int64_t parseCacheControl(const char *value) {
return 0;
}
void HTTPRequestImpl::handleResult(NSData *data, NSURLResponse *res, NSError *error) {
void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error) {
if (error) {
if ([error code] == NSURLErrorCancelled) {
status = ResponseStatus::Canceled;
@@ -365,7 +367,7 @@ void HTTPRequestImpl::handleResult(NSData *data, NSURLResponse *res, NSError *er
uv_async_send(async);
}
void HTTPRequestImpl::retry(uint64_t timeout) {
void HTTPRequest::retry(uint64_t timeout) {
response.reset();
assert(!timer);
@@ -375,7 +377,7 @@ void HTTPRequestImpl::retry(uint64_t timeout) {
uv_timer_start(timer, restart, timeout, 0);
}
void HTTPRequestImpl::retryImmediately() {
void HTTPRequest::retry() {
// All batons get notified when the network status changed, but some of them
// might not actually wait for the network to become available again.
if (timer && strategy == PreemptImmediately) {
@@ -385,9 +387,9 @@ void HTTPRequestImpl::retryImmediately() {
}
}
void HTTPRequestImpl::restart(uv_timer_t *timer, int) {
void HTTPRequest::restart(uv_timer_t *timer, int) {
// Restart the request.
auto impl = reinterpret_cast<HTTPRequestImpl *>(timer->data);
auto impl = reinterpret_cast<HTTPRequest *>(timer->data);
// Get rid of the timer.
impl->timer = nullptr;
@@ -396,28 +398,8 @@ void HTTPRequestImpl::restart(uv_timer_t *timer, int) {
impl->start();
}
// -------------------------------------------------------------------------------------------------
HTTPRequest::HTTPRequest(const Resource& resource, Callback callback,
uv_loop_t* loop, std::shared_ptr<const Response> response)
: RequestBase(resource, callback)
, impl(new HTTPRequestImpl(this, loop, response)) {
}
HTTPRequest::~HTTPRequest() {
if (impl) {
impl->cancel();
}
}
void HTTPRequest::retryImmediately() {
if (impl) {
impl->retryImmediately();
}
}
void HTTPRequest::cancel() {
delete this;
std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) {
return util::make_unique<HTTPNSURLContext>(loop);
}
}

View File

@@ -1,4 +1,4 @@
#include <mbgl/storage/asset_request.hpp>
#include <mbgl/storage/asset_context.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/std.hpp>
@@ -7,27 +7,19 @@
#include <uv.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#ifndef __clang__
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop
#include <cassert>
namespace algo = boost::algorithm;
#include <limits>
namespace mbgl {
class AssetRequestImpl {
class AssetRequest : public RequestBase {
MBGL_STORE_THREAD(tid)
public:
AssetRequestImpl(AssetRequest*, uv_loop_t*, const std::string& assetRoot);
~AssetRequestImpl();
AssetRequest(const Resource&, Callback, uv_loop_t*, const std::string& assetRoot);
~AssetRequest();
void cancel() override;
static void fileOpened(uv_fs_t *req);
static void fileStated(uv_fs_t *req);
@@ -36,7 +28,6 @@ public:
static void notifyError(uv_fs_t *req);
static void cleanup(uv_fs_t *req);
AssetRequest *request = nullptr;
std::string assetRoot;
bool canceled = false;
uv_fs_t req;
@@ -45,20 +36,25 @@ public:
std::unique_ptr<Response> response;
};
AssetRequestImpl::~AssetRequestImpl() {
MBGL_VERIFY_THREAD(tid);
if (request) {
request->impl = nullptr;
class AssetFSContext : public AssetContext {
RequestBase* createRequest(const Resource& resource,
RequestBase::Callback callback,
uv_loop_t* loop,
const std::string& assetRoot) override {
return new AssetRequest(resource, callback, loop, assetRoot);
}
};
AssetRequest::~AssetRequest() {
MBGL_VERIFY_THREAD(tid);
}
AssetRequestImpl::AssetRequestImpl(AssetRequest* request_, uv_loop_t* loop, const std::string& assetRoot_)
: request(request_)
, assetRoot(assetRoot_) {
AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, uv_loop_t* loop, const std::string& assetRoot_)
: RequestBase(resource_, callback_),
assetRoot(assetRoot_) {
req.data = this;
const auto &url = request->resource.url;
const auto &url = resource.url;
std::string path;
if (url.size() <= 8 || url[8] == '/') {
// This is an empty or absolute path.
@@ -71,9 +67,9 @@ AssetRequestImpl::AssetRequestImpl(AssetRequest* request_, uv_loop_t* loop, cons
uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, fileOpened);
}
void AssetRequestImpl::fileOpened(uv_fs_t *req) {
void AssetRequest::fileOpened(uv_fs_t *req) {
assert(req->data);
auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
if (req->result < 0) {
@@ -96,9 +92,9 @@ void AssetRequestImpl::fileOpened(uv_fs_t *req) {
}
}
void AssetRequestImpl::fileStated(uv_fs_t *req) {
void AssetRequest::fileStated(uv_fs_t *req) {
assert(req->data);
auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
if (req->result != 0 || self->canceled) {
@@ -124,9 +120,7 @@ void AssetRequestImpl::fileStated(uv_fs_t *req) {
#else
response->message = uv_strerror(UV_EFBIG);
#endif
assert(self->request);
self->request->notify(std::move(response), FileCache::Hint::No);
delete self->request;
self->notify(std::move(response), FileCache::Hint::No);
uv_fs_req_cleanup(req);
uv_fs_close(req->loop, req, self->fd, fileClosed);
@@ -151,9 +145,9 @@ void AssetRequestImpl::fileStated(uv_fs_t *req) {
}
}
void AssetRequestImpl::fileRead(uv_fs_t *req) {
void AssetRequest::fileRead(uv_fs_t *req) {
assert(req->data);
auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
if (req->result < 0 || self->canceled) {
@@ -163,18 +157,16 @@ void AssetRequestImpl::fileRead(uv_fs_t *req) {
} else {
// File was successfully read.
self->response->status = Response::Successful;
assert(self->request);
self->request->notify(std::move(self->response), FileCache::Hint::No);
delete self->request;
self->notify(std::move(self->response), FileCache::Hint::No);
}
uv_fs_req_cleanup(req);
uv_fs_close(req->loop, req, self->fd, fileClosed);
}
void AssetRequestImpl::fileClosed(uv_fs_t *req) {
void AssetRequest::fileClosed(uv_fs_t *req) {
assert(req->data);
auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
(void(self)); // Silence unused variable error in Release mode
@@ -185,51 +177,37 @@ void AssetRequestImpl::fileClosed(uv_fs_t *req) {
cleanup(req);
}
void AssetRequestImpl::notifyError(uv_fs_t *req) {
void AssetRequest::notifyError(uv_fs_t *req) {
assert(req->data);
auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
if (req->result < 0 && !self->canceled && req->result != UV_ECANCELED) {
auto response = util::make_unique<Response>();
response->status = Response::Error;
response->message = uv::getFileRequestError(req);
assert(self->request);
self->request->notify(std::move(response), FileCache::Hint::No);
delete self->request;
self->notify(std::move(response), FileCache::Hint::No);
}
}
void AssetRequestImpl::cleanup(uv_fs_t *req) {
void AssetRequest::cleanup(uv_fs_t *req) {
assert(req->data);
auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
uv_fs_req_cleanup(req);
delete self;
}
// -------------------------------------------------------------------------------------------------
AssetRequest::AssetRequest(const Resource& resource_, Callback callback_,
uv_loop_t* loop, const std::string& assetRoot)
: RequestBase(resource_, callback_)
, impl(new AssetRequestImpl(this, loop, assetRoot)) {
assert(algo::starts_with(resource.url, "asset://"));
// Note: the AssetRequestImpl deletes itself.
}
AssetRequest::~AssetRequest() {
if (impl) {
impl->request = nullptr;
}
}
void AssetRequest::cancel() {
impl->canceled = true;
canceled = true;
// uv_cancel fails frequently when the request has already been started.
// In that case, we have to let it complete and check the canceled bool
// instead. The cancelation callback will delete the AssetRequest object.
uv_cancel((uv_req_t *)&impl->req);
uv_cancel((uv_req_t *)&req);
}
std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t*) {
return util::make_unique<AssetFSContext>();
}
}

View File

@@ -1,46 +1,40 @@
#include <mbgl/storage/asset_request.hpp>
#include <mbgl/storage/thread_context.hpp>
#include <mbgl/storage/asset_context.hpp>
#include <mbgl/android/jni.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/uv.hpp>
#include <uv.h>
#include "uv_zip.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#ifndef __clang__
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop
#include <map>
#include <cassert>
#include <forward_list>
namespace algo = boost::algorithm;
namespace mbgl {
class AssetZipContext : public ThreadContext<AssetZipContext> {
class AssetZipContext : public AssetContext {
public:
AssetZipContext(uv_loop_t *loop);
~AssetZipContext();
RequestBase* createRequest(const Resource& resource,
RequestBase::Callback callback,
uv_loop_t* loop,
const std::string& assetRoot) override;
uv_zip_t *getHandle(const std::string &path);
void returnHandle(const std::string &path, uv_zip_t *zip);
private:
// A list of resuable uv_zip handles to avoid creating and destroying them all the time.
std::map<std::string, std::forward_list<uv_zip_t *>> handles;
uv_loop_t *loop;
};
// -------------------------------------------------------------------------------------------------
template<> pthread_key_t ThreadContext<AssetZipContext>::key{};
template<> pthread_once_t ThreadContext<AssetZipContext>::once = PTHREAD_ONCE_INIT;
AssetZipContext::AssetZipContext(uv_loop_t *loop_) : ThreadContext(loop_) {
AssetZipContext::AssetZipContext(uv_loop_t *loop_) : loop(loop_) {
}
uv_zip_t *AssetZipContext::getHandle(const std::string &path) {
@@ -71,20 +65,18 @@ AssetZipContext::~AssetZipContext() {
handles.clear();
}
// -------------------------------------------------------------------------------------------------
class AssetRequestImpl {
class AssetRequest : public RequestBase {
MBGL_STORE_THREAD(tid)
public:
AssetRequestImpl(AssetRequest*, uv_loop_t*, const std::string& assetRoot);
~AssetRequestImpl();
AssetRequest(AssetZipContext&, const Resource&, Callback, const std::string& assetRoot);
~AssetRequest();
void cancel();
void cancel() override;
private:
AssetZipContext &context;
AssetRequest *request = nullptr;
bool cancelled = false;
const std::string root;
const std::string path;
std::unique_ptr<Response> response;
@@ -102,21 +94,18 @@ private:
void notifyError(const char *message);
};
// -------------------------------------------------------------------------------------------------
AssetRequestImpl::~AssetRequestImpl() {
MBGL_VERIFY_THREAD(tid);
if (request) {
request->impl = nullptr;
}
RequestBase* AssetZipContext::createRequest(const Resource& resource,
RequestBase::Callback callback,
uv_loop_t*,
const std::string& assetRoot) {
return new AssetRequest(*this, resource, callback, assetRoot);
}
AssetRequestImpl::AssetRequestImpl(AssetRequest* request_, uv_loop_t* loop, const std::string& assetRoot_)
: context(*AssetZipContext::Get(loop)),
request(request_),
AssetRequest::AssetRequest(AssetZipContext& context_, const Resource& resource_, Callback callback_, const std::string& assetRoot_)
: RequestBase(resource_, callback_),
context(context_),
root(assetRoot_),
path(std::string { "assets/" } + request->resource.url.substr(8)) {
path(std::string { "assets/" } + resource.url.substr(8)) {
auto zip = context.getHandle(root);
if (zip) {
archiveOpened(zip);
@@ -125,17 +114,19 @@ AssetRequestImpl::AssetRequestImpl(AssetRequest* request_, uv_loop_t* loop, cons
}
}
void AssetRequestImpl::openZipArchive() {
AssetRequest::~AssetRequest() {
MBGL_VERIFY_THREAD(tid);
}
void AssetRequest::openZipArchive() {
uv_fs_t *req = new uv_fs_t();
req->data = this;
assert(request);
// We're using uv_fs_open first to obtain a file descriptor. Then, uv_zip_fdopen will operate
// on a read-only file.
uv_fs_open(context.loop, req, root.c_str(), O_RDONLY, S_IRUSR, [](uv_fs_t *fsReq) {
if (fsReq->result < 0) {
auto impl = reinterpret_cast<AssetRequestImpl *>(fsReq->data);
auto impl = reinterpret_cast<AssetRequest *>(fsReq->data);
impl->notifyError(uv::getFileRequestError(fsReq));
delete impl;
} else {
@@ -143,7 +134,7 @@ void AssetRequestImpl::openZipArchive() {
uv_zip_init(zip);
zip->data = fsReq->data;
uv_zip_fdopen(fsReq->loop, zip, uv_file(fsReq->result), 0, [](uv_zip_t *openZip) {
auto impl = reinterpret_cast<AssetRequestImpl *>(openZip->data);
auto impl = reinterpret_cast<AssetRequest *>(openZip->data);
if (openZip->result < 0) {
impl->notifyError(openZip->message);
delete openZip;
@@ -163,20 +154,20 @@ void AssetRequestImpl::openZipArchive() {
#define INVOKE_MEMBER(name) \
[](uv_zip_t *zip_) { \
assert(zip_->data); \
reinterpret_cast<AssetRequestImpl *>(zip_->data)->name(zip_); \
reinterpret_cast<AssetRequest *>(zip_->data)->name(zip_); \
}
void AssetRequestImpl::archiveOpened(uv_zip_t *zip) {
void AssetRequest::archiveOpened(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
zip->data = this;
uv_zip_stat(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileStated));
}
void AssetRequestImpl::fileStated(uv_zip_t *zip) {
void AssetRequest::fileStated(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
if (!request || zip->result < 0) {
if (cancelled || zip->result < 0) {
// Stat failed, probably because the file doesn't exist.
if (zip->result < 0) {
notifyError(zip->message);
@@ -206,14 +197,14 @@ void AssetRequestImpl::fileStated(uv_zip_t *zip) {
}
}
void AssetRequestImpl::fileOpened(uv_zip_t *zip) {
void AssetRequest::fileOpened(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
if (zip->result < 0) {
// Opening failed.
notifyError(zip->message);
cleanup(zip);
} else if (!request) {
} else if (cancelled) {
// The request was canceled. Close the file again.
uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed));
} else {
@@ -221,23 +212,21 @@ void AssetRequestImpl::fileOpened(uv_zip_t *zip) {
}
}
void AssetRequestImpl::fileRead(uv_zip_t *zip) {
void AssetRequest::fileRead(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
if (zip->result < 0) {
// Reading failed. We still have an open file handle though.
notifyError(zip->message);
} else if (request) {
} else if (!cancelled) {
response->status = Response::Successful;
request->notify(std::move(response), FileCache::Hint::No);
delete request;
assert(request == nullptr);
notify(std::move(response), FileCache::Hint::No);
}
uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed));
}
void AssetRequestImpl::fileClosed(uv_zip_t *zip) {
void AssetRequest::fileClosed(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
if (zip->result < 0) {
@@ -247,53 +236,32 @@ void AssetRequestImpl::fileClosed(uv_zip_t *zip) {
cleanup(zip);
}
void AssetRequestImpl::cleanup(uv_zip_t *zip) {
void AssetRequest::cleanup(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
context.returnHandle(root, zip);
delete this;
}
void AssetRequestImpl::notifyError(const char *message) {
void AssetRequest::notifyError(const char *message) {
MBGL_VERIFY_THREAD(tid);
if (request) {
if (!cancelled) {
response = util::make_unique<Response>();
response->status = Response::Error;
response->message = message;
request->notify(std::move(response), FileCache::Hint::No);
delete request;
assert(request == nullptr);
notify(std::move(response), FileCache::Hint::No);
} else {
// The request was already canceled and deleted.
}
}
void AssetRequestImpl::cancel() {
request = nullptr;
}
// -------------------------------------------------------------------------------------------------
AssetRequest::AssetRequest(const Resource& resource_, Callback callback_,
uv_loop_t* loop, const std::string& assetRoot)
: RequestBase(resource_, callback_)
, impl(new AssetRequestImpl(this, loop, assetRoot)) {
assert(algo::starts_with(resource.url, "asset://"));
}
AssetRequest::~AssetRequest() {
if (impl) {
impl->cancel();
}
}
void AssetRequest::cancel() {
if (impl) {
impl->cancel();
}
cancelled = true;
}
delete this;
std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t* loop) {
return util::make_unique<AssetZipContext>(loop);
}
}

View File

@@ -2,26 +2,20 @@
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
#include <cassert>
#include <pthread.h>
pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT;
GLFWView::GLFWView(bool fullscreen_) : fullscreen(fullscreen_) {
#ifdef NVIDIA
glDiscardFramebufferEXT = reinterpret_cast<PFNGLDISCARDFRAMEBUFFEREXTPROC>(glfwGetProcAddress("glDiscardFramebufferEXT"));
#endif
}
GLFWView::~GLFWView() {
glfwDestroyWindow(window);
glfwTerminate();
}
void glfwError(int error, const char *description) {
mbgl::Log::Error(mbgl::Event::OpenGL, "GLFW error (%i): %s", error, description);
assert(false);
}
void GLFWView::initialize(mbgl::Map *map_) {
View::initialize(map_);
GLFWView::GLFWView(bool fullscreen_) : fullscreen(fullscreen_) {
#ifdef NVIDIA
glDiscardFramebufferEXT = reinterpret_cast<PFNGLDISCARDFRAMEBUFFEREXTPROC>(glfwGetProcAddress("glDiscardFramebufferEXT"));
#endif
glfwSetErrorCallback(glfwError);
@@ -63,10 +57,6 @@ void GLFWView::initialize(mbgl::Map *map_) {
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
int width, height;
glfwGetWindowSize(window, &width, &height);
onResize(window, width, height);
glfwSetCursorPosCallback(window, onMouseMove);
glfwSetMouseButtonCallback(window, onMouseClick);
glfwSetWindowSizeCallback(window, onResize);
@@ -160,6 +150,19 @@ void GLFWView::initialize(mbgl::Map *map_) {
glfwMakeContextCurrent(nullptr);
}
GLFWView::~GLFWView() {
glfwDestroyWindow(window);
glfwTerminate();
}
void GLFWView::initialize(mbgl::Map *map_) {
View::initialize(map_);
int width, height;
glfwGetWindowSize(window, &width, &height);
onResize(window, width, height);
}
void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) {
GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
@@ -221,7 +224,7 @@ void GLFWView::onResize(GLFWwindow *window, int width, int height ) {
int fbWidth, fbHeight;
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
view->resize(width, height, static_cast<float>(fbWidth) / static_cast<float>(width), fbWidth, fbHeight);
view->map->resize(width, height, static_cast<float>(fbWidth) / static_cast<float>(width));
}
void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modifiers) {
@@ -264,21 +267,10 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) {
view->lastY = y;
}
int GLFWView::run() {
map->start();
void GLFWView::run() {
while (!glfwWindowShouldClose(window)) {
glfwWaitEvents();
}
map->stop([]() {
glfwWaitEvents();
});
// Terminate here to save binary shaders
map->terminate();
return 0;
}
void GLFWView::activate() {
@@ -293,9 +285,8 @@ void GLFWView::notify() {
glfwPostEmptyEvent();
}
void GLFWView::invalidate() {
assert(map);
map->render();
void GLFWView::invalidate(std::function<void()> render) {
render();
glfwSwapBuffers(window);
fps();
}

View File

@@ -41,7 +41,9 @@ namespace mbgl {
HeadlessView::HeadlessView(uint16_t width, uint16_t height, float pixelRatio)
: display(std::make_shared<HeadlessDisplay>()) {
activate();
resize(width, height, pixelRatio);
deactivate();
}
HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display_,
@@ -49,7 +51,9 @@ HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display_,
uint16_t height,
float pixelRatio)
: display(display_) {
activate();
resize(width, height, pixelRatio);
deactivate();
}
void HeadlessView::loadExtensions() {
@@ -150,30 +154,17 @@ bool HeadlessView::isActive() {
return std::this_thread::get_id() == thread;
}
void HeadlessView::resize(const uint16_t width, const uint16_t height, const float pixelRatio) {
std::lock_guard<std::mutex> lock(prospectiveMutex);
prospective = { width, height, pixelRatio };
}
HeadlessView::Dimensions::Dimensions(uint16_t width_, uint16_t height_, float pixelRatio_)
: width(width_), height(height_), pixelRatio(pixelRatio_) {
}
void HeadlessView::discard() {
assert(isActive());
{ // Obtain the new values.
std::lock_guard<std::mutex> lock(prospectiveMutex);
if (current.pixelWidth() == prospective.pixelWidth() && current.pixelHeight() == prospective.pixelHeight()) {
return;
}
current = prospective;
}
void HeadlessView::resize(const uint16_t width, const uint16_t height, const float pixelRatio) {
dimensions = { width, height, pixelRatio };
clearBuffers();
const unsigned int w = current.width * current.pixelRatio;
const unsigned int h = current.height * current.pixelRatio;
const unsigned int w = dimensions.width * dimensions.pixelRatio;
const unsigned int h = dimensions.height * dimensions.pixelRatio;
// Create depth/stencil buffer
MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
@@ -208,15 +199,13 @@ void HeadlessView::discard() {
}
throw std::runtime_error(error.str());
}
View::resize(current.width, current.height, current.pixelRatio, w, h);
}
std::unique_ptr<StillImage> HeadlessView::readStillImage() {
assert(isActive());
const unsigned int w = current.pixelWidth();
const unsigned int h = current.pixelHeight();
const unsigned int w = dimensions.pixelWidth();
const unsigned int h = dimensions.pixelHeight();
auto image = util::make_unique<StillImage>();
image->width = w;
@@ -305,7 +294,6 @@ void HeadlessView::activate() {
#endif
loadExtensions();
discard();
}
void HeadlessView::deactivate() {
@@ -328,9 +316,8 @@ void HeadlessView::deactivate() {
#endif
}
void HeadlessView::invalidate() {
assert(map);
map->render();
void HeadlessView::invalidate(std::function<void()> render) {
render();
}
}

View File

@@ -1,4 +1,3 @@
#include <mbgl/storage/http_request.hpp>
#include <mbgl/storage/http_context.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
@@ -6,6 +5,7 @@
#include <mbgl/platform/log.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/util.hpp>
#include <curl/curl.h>
@@ -54,14 +54,20 @@ enum class ResponseStatus : int8_t {
NotModified,
};
class HTTPRequestImpl;
class HTTPRequest;
class HTTPCURLContext : public HTTPContext<HTTPCURLContext> {
class HTTPCURLContext : public HTTPContext {
MBGL_STORE_THREAD(tid)
public:
HTTPCURLContext(uv_loop_t *loop);
~HTTPCURLContext();
RequestBase* createRequest(const Resource&,
RequestBase::Callback,
uv_loop_t*,
std::shared_ptr<const Response>) override;
static int handleSocket(CURL *handle, curl_socket_t s, int action, void *userp, void *socketp);
static void perform(uv_poll_t *req, int status, int events);
static int startTimeout(CURLM *multi, long timeout_ms, void *userp);
@@ -75,7 +81,8 @@ public:
void returnHandle(CURL *handle);
void checkMultiInfo();
public:
uv_loop_t *loop = nullptr;
// Used as the CURL timer function to periodically check for socket updates.
uv_timer_t *timeout = nullptr;
@@ -91,17 +98,21 @@ public:
std::queue<CURL *> handles;
};
class HTTPRequestImpl {
class HTTPRequest : public RequestBase {
MBGL_STORE_THREAD(tid)
public:
HTTPRequestImpl(HTTPRequest *request, uv_loop_t *loop, std::shared_ptr<const Response> response);
~HTTPRequestImpl();
HTTPRequest(HTTPCURLContext*,
const Resource&,
Callback,
uv_loop_t*,
std::shared_ptr<const Response>);
~HTTPRequest();
void cancel() override;
void retry() override;
void handleResult(CURLcode code);
void abandon();
void retryImmediately();
private:
static size_t headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp);
@@ -116,9 +127,8 @@ private:
void finish(ResponseStatus status);
void start();
private:
HTTPCURLContext *context = nullptr;
HTTPRequest *request = nullptr;
bool cancelled = false;
// Will store the current response.
std::unique_ptr<Response> response;
@@ -176,10 +186,9 @@ private:
// -------------------------------------------------------------------------------------------------
template<> pthread_key_t ThreadContext<HTTPCURLContext>::key{};
template<> pthread_once_t ThreadContext<HTTPCURLContext>::once = PTHREAD_ONCE_INIT;
HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_)
: HTTPContext(loop_),
loop(loop_) {
if (curl_global_init(CURL_GLOBAL_ALL)) {
throw std::runtime_error("Could not init cURL");
}
@@ -208,6 +217,13 @@ HTTPCURLContext::~HTTPCURLContext() {
uv::close(timeout);
}
RequestBase* HTTPCURLContext::createRequest(const Resource& resource,
RequestBase::Callback callback,
uv_loop_t* loop_,
std::shared_ptr<const Response> response) {
return new HTTPRequest(this, resource, callback, loop_, response);
}
CURL *HTTPCURLContext::getHandle() {
if (!handles.empty()) {
auto handle = handles.front();
@@ -231,7 +247,7 @@ void HTTPCURLContext::checkMultiInfo() {
while ((message = curl_multi_info_read(multi, &pending))) {
switch (message->msg) {
case CURLMSG_DONE: {
HTTPRequestImpl *baton = nullptr;
HTTPRequest *baton = nullptr;
curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton);
assert(baton);
baton->handleResult(message->data.result);
@@ -426,13 +442,12 @@ static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm
}
#endif
HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::shared_ptr<const Response> response_)
: context(HTTPCURLContext::Get(loop)),
request(request_),
HTTPRequest::HTTPRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_, uv_loop_t*, std::shared_ptr<const Response> response_)
: RequestBase(resource_, callback_),
context(context_),
existingResponse(response_),
handle(context->getHandle()) {
assert(request);
context->addRequest(request);
context->addRequest(this);
// Zero out the error buffer.
memset(error, 0, sizeof(error));
@@ -463,7 +478,7 @@ HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::sh
handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
#endif
handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
handleError(curl_easy_setopt(handle, CURLOPT_URL, request->resource.url.c_str()));
handleError(curl_easy_setopt(handle, CURLOPT_URL, resource.url.c_str()));
handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
handleError(curl_easy_setopt(handle, CURLOPT_WRITEDATA, this));
handleError(curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback));
@@ -475,28 +490,10 @@ HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::sh
start();
}
void HTTPRequestImpl::abandon() {
if (request) {
context->removeRequest(request);
request = nullptr;
}
}
void HTTPRequestImpl::start() {
// Count up the attempts.
attempts++;
// Start requesting the information.
handleError(curl_multi_add_handle(context->multi, handle));
}
HTTPRequestImpl::~HTTPRequestImpl() {
HTTPRequest::~HTTPRequest() {
MBGL_VERIFY_THREAD(tid);
if (request) {
context->removeRequest(request);
request->impl = nullptr;
}
context->removeRequest(this);
handleError(curl_multi_remove_handle(context->multi, handle));
context->returnHandle(handle);
@@ -515,11 +512,23 @@ HTTPRequestImpl::~HTTPRequestImpl() {
}
}
void HTTPRequest::cancel() {
delete this;
}
void HTTPRequest::start() {
// Count up the attempts.
attempts++;
// Start requesting the information.
handleError(curl_multi_add_handle(context->multi, handle));
}
// This function is called when we have new data for a request. We just append it to the string
// containing the previous data.
size_t HTTPRequestImpl::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
assert(userp);
auto impl = reinterpret_cast<HTTPRequestImpl *>(userp);
auto impl = reinterpret_cast<HTTPRequest *>(userp);
MBGL_VERIFY_THREAD(impl->tid);
if (!impl->response) {
@@ -561,9 +570,9 @@ int64_t parseCacheControl(const char *value) {
return 0;
}
size_t HTTPRequestImpl::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
assert(userp);
auto baton = reinterpret_cast<HTTPRequestImpl *>(userp);
auto baton = reinterpret_cast<HTTPRequest *>(userp);
MBGL_VERIFY_THREAD(baton->tid);
if (!baton->response) {
@@ -590,8 +599,7 @@ size_t HTTPRequestImpl::headerCallback(char *const buffer, const size_t size, co
return length;
}
void HTTPRequestImpl::retry(uint64_t timeout) {
void HTTPRequest::retry(uint64_t timeout) {
handleError(curl_multi_remove_handle(context->multi, handle));
response.reset();
@@ -603,7 +611,7 @@ void HTTPRequestImpl::retry(uint64_t timeout) {
uv_timer_start(timer, restart, timeout, 0);
}
void HTTPRequestImpl::retryImmediately() {
void HTTPRequest::retry() {
// All batons get notified when the network status changed, but some of them
// might not actually wait for the network to become available again.
if (timer && strategy == PreemptImmediately) {
@@ -614,12 +622,12 @@ void HTTPRequestImpl::retryImmediately() {
}
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
void HTTPRequestImpl::restart(uv_timer_t *timer, int) {
void HTTPRequest::restart(uv_timer_t *timer, int) {
#else
void HTTPRequestImpl::restart(uv_timer_t *timer) {
void HTTPRequest::restart(uv_timer_t *timer) {
#endif
// Restart the request.
auto baton = reinterpret_cast<HTTPRequestImpl *>(timer->data);
auto baton = reinterpret_cast<HTTPRequest *>(timer->data);
// Get rid of the timer.
baton->timer = nullptr;
@@ -628,7 +636,7 @@ void HTTPRequestImpl::restart(uv_timer_t *timer) {
baton->start();
}
void HTTPRequestImpl::finish(ResponseStatus status) {
void HTTPRequest::finish(ResponseStatus status) {
if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) {
strategy = ExponentialBackoff;
return retry((1 << (attempts - 1)) * 1000);
@@ -641,19 +649,18 @@ void HTTPRequestImpl::finish(ResponseStatus status) {
// Actually return the response.
if (status == ResponseStatus::NotModified) {
request->notify(std::move(response), FileCache::Hint::Refresh);
notify(std::move(response), FileCache::Hint::Refresh);
} else {
request->notify(std::move(response), FileCache::Hint::Full);
notify(std::move(response), FileCache::Hint::Full);
}
delete request;
delete this;
}
void HTTPRequestImpl::handleResult(CURLcode code) {
void HTTPRequest::handleResult(CURLcode code) {
MBGL_VERIFY_THREAD(tid);
if (!request) {
if (cancelled) {
// In this case, it doesn't make sense to even process the response even further since
// the request was canceled anyway.
delete this;
@@ -721,30 +728,8 @@ void HTTPRequestImpl::handleResult(CURLcode code) {
throw std::runtime_error("Response hasn't been handled");
}
// -------------------------------------------------------------------------------------------------
HTTPRequest::HTTPRequest(const Resource& resource_, Callback callback_,
uv_loop_t* loop, std::shared_ptr<const Response> response)
: RequestBase(resource_, callback_)
, impl(new HTTPRequestImpl(this, loop, response)) {
}
HTTPRequest::~HTTPRequest() {
if (impl) {
impl->abandon();
}
}
void HTTPRequest::retryImmediately() {
if (impl) {
impl->retryImmediately();
}
}
void HTTPRequest::cancel() {
delete impl;
impl = nullptr;
delete this;
std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) {
return util::make_unique<HTTPCURLContext>(loop);
}
}

View File

@@ -67,7 +67,7 @@ SQLiteCache::SQLiteCache(const std::string& path_)
SQLiteCache::~SQLiteCache() = default;
SQLiteCache::Impl::Impl(const std::string& path_)
SQLiteCache::Impl::Impl(uv_loop_t*, const std::string& path_)
: path(path_) {
}

View File

@@ -3,6 +3,8 @@
#include <mbgl/storage/sqlite_cache.hpp>
typedef struct uv_loop_s uv_loop_t;
namespace mapbox {
namespace sqlite {
class Database;
@@ -14,7 +16,7 @@ namespace mbgl {
class SQLiteCache::Impl {
public:
Impl(const std::string &path = ":memory:");
Impl(uv_loop_t*, const std::string &path = ":memory:");
~Impl();
std::unique_ptr<Response> get(const Resource&);

View File

@@ -14,6 +14,7 @@
#include <mbgl/storage/sqlite_cache.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/constants.hpp>
#import "MGLTypes.h"
#import "NSBundle+MGLAdditions.h"
@@ -277,8 +278,10 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
mbglView = new MBGLView(self);
mbglFileCache = new mbgl::SQLiteCache(defaultCacheDatabase());
mbglFileSource = new mbgl::DefaultFileSource(mbglFileCache);
mbglMap = new mbgl::Map(*mbglView, *mbglFileSource);
mbglView->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor, _glView.drawableWidth, _glView.drawableHeight);
// Start paused on the IB canvas
mbglMap = new mbgl::Map(*mbglView, *mbglFileSource, mbgl::MapMode::Continuous, _isTargetingInterfaceBuilder);
mbglMap->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor);
// Notify map object when network reachability status changes.
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -380,12 +383,6 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
_regionChangeDelegateQueue = [NSOperationQueue new];
_regionChangeDelegateQueue.maxConcurrentOperationCount = 1;
// start the main loop, but not on the IB canvas
if ( ! _isTargetingInterfaceBuilder)
{
mbglMap->start();
}
// metrics: map load event
const mbgl::LatLng latLng = mbglMap->getLatLng();
const double zoom = mbglMap->getZoom();
@@ -623,13 +620,13 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
if ( ! self.glSnapshotView || self.glSnapshotView.hidden)
{
mbglView->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
mbglMap->resize(rect.size.width, rect.size.height, view.contentScaleFactor);
CGFloat zoomFactor = mbglMap->getMaxZoom() - mbglMap->getMinZoom() + 1;
CGFloat cpuFactor = (CGFloat)[[NSProcessInfo processInfo] processorCount];
CGFloat memoryFactor = (CGFloat)[[NSProcessInfo processInfo] physicalMemory] / 1000 / 1000 / 1000;
CGFloat sizeFactor = ((CGFloat)mbglMap->getState().getWidth() / mbgl::util::tileSize) *
((CGFloat)mbglMap->getState().getHeight() / mbgl::util::tileSize);
CGFloat sizeFactor = ((CGFloat)mbglMap->getWidth() / mbgl::util::tileSize) *
((CGFloat)mbglMap->getHeight() / mbgl::util::tileSize);
NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
@@ -646,7 +643,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
if ( ! _isTargetingInterfaceBuilder)
{
mbglMap->triggerUpdate();
mbglMap->update();
}
}
@@ -669,7 +666,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
self.glSnapshotView.image = self.glView.snapshot;
self.glSnapshotView.hidden = NO;
mbglMap->stop();
mbglMap->pause();
[self.glView deleteDrawable];
}
@@ -680,7 +677,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[self.glView bindDrawable];
mbglMap->start();
mbglMap->resume();
}
- (void)tintColorDidChange
@@ -1320,8 +1317,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
CLLocationCoordinate2D center = CLLocationCoordinate2DMake((northEastCoordinate.latitude + southWestCoordinate.latitude) / 2, (northEastCoordinate.longitude + southWestCoordinate.longitude) / 2);
CGFloat scale = mbglMap->getScale();
CGFloat scaleX = mbglMap->getState().getWidth() / (northEastCoordinate.longitude - southWestCoordinate.longitude);
CGFloat scaleY = mbglMap->getState().getHeight() / (northEastCoordinate.latitude - southWestCoordinate.latitude);
CGFloat scaleX = mbglMap->getWidth() / (northEastCoordinate.longitude - southWestCoordinate.longitude);
CGFloat scaleY = mbglMap->getHeight() / (northEastCoordinate.latitude - southWestCoordinate.latitude);
CGFloat minZoom = mbglMap->getMinZoom();
CGFloat maxZoom = mbglMap->getMaxZoom();
CGFloat zoomLevel = MAX(MIN(log(scale * MIN(scaleX, scaleY)) / log(2), maxZoom), minZoom);
@@ -2528,12 +2525,7 @@ class MBGLView : public mbgl::View
[EAGLContext setCurrentContext:nil];
}
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight)
{
View::resize(width, height, ratio, fbWidth, fbHeight);
}
void invalidate() override
void invalidate(std::function<void()>) override
{
[nativeView performSelectorOnMainThread:@selector(invalidate)
withObject:nil

View File

@@ -2,6 +2,8 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/map/live_tile.hpp>
#include <mbgl/map/map_data.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/std.hpp>
@@ -83,8 +85,10 @@ vec2<double> AnnotationManager::projectPoint(const LatLng& point) {
return { x, y };
}
std::pair<std::vector<TileID>, AnnotationIDs> AnnotationManager::addPointAnnotations(
const std::vector<LatLng>& points, const std::vector<std::string>& symbols, const Map& map) {
std::pair<std::vector<TileID>, AnnotationIDs>
AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points,
const std::vector<std::string>& symbols,
const MapData& data) {
std::lock_guard<std::mutex> lock(mtx);
// We pre-generate tiles to contain each annotation up to the map's max zoom.
@@ -109,7 +113,7 @@ std::pair<std::vector<TileID>, AnnotationIDs> AnnotationManager::addPointAnnotat
util::make_unique<Annotation>(AnnotationType::Point,
AnnotationSegments({ { points[i] } })));
const uint8_t maxZoom = map.getMaxZoom();
const uint8_t maxZoom = data.transform.getMaxZoom();
// side length of map at this zoom
uint32_t z2 = 1 << maxZoom;
@@ -181,13 +185,14 @@ std::pair<std::vector<TileID>, AnnotationIDs> AnnotationManager::addPointAnnotat
return std::make_pair(affectedTiles, annotationIDs);
}
std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& ids, const Map& map) {
std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& ids,
const MapData& data) {
std::lock_guard<std::mutex> lock(mtx);
std::vector<TileID> affectedTiles;
std::vector<uint32_t> z2s;
uint8_t zoomCount = map.getMaxZoom() + 1;
const uint8_t zoomCount = data.transform.getMaxZoom() + 1;
z2s.reserve(zoomCount);
for (uint8_t z = 0; z < zoomCount; ++z) {
z2s.emplace_back(1<< z);
@@ -231,10 +236,10 @@ std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& id
}
std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds,
const Map& map) const {
const MapData& data) const {
std::lock_guard<std::mutex> lock(mtx);
const uint8_t z = map.getMaxZoom();
const uint8_t z = data.transform.getMaxZoom();
const uint32_t z2 = 1 << z;
const vec2<double> swPoint = projectPoint(queryBounds.sw);
const vec2<double> nePoint = projectPoint(queryBounds.ne);

View File

@@ -19,6 +19,7 @@ namespace mbgl {
class Annotation;
class Map;
class LiveTile;
class MapData;
using AnnotationIDs = std::vector<uint32_t>;
@@ -29,9 +30,9 @@ public:
void setDefaultPointAnnotationSymbol(const std::string& symbol);
std::pair<std::vector<TileID>, AnnotationIDs> addPointAnnotations(
const std::vector<LatLng>&, const std::vector<std::string>& symbols, const Map&);
std::vector<TileID> removeAnnotations(const AnnotationIDs&, const Map&);
AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const Map&) const;
const std::vector<LatLng>&, const std::vector<std::string>& symbols, const MapData&);
std::vector<TileID> removeAnnotations(const AnnotationIDs&, const MapData&);
AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&) const;
LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const;
const LiveTile* getTile(const TileID& id);

View File

@@ -1,6 +1,7 @@
#include <mbgl/map/environment.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/util/run_loop.hpp>
#include <uv.h>
@@ -88,7 +89,7 @@ EnvironmentScope::~EnvironmentScope() {
}
Environment::Environment(FileSource& fs)
: id(makeEnvironmentID()), fileSource(fs), loop(uv_loop_new()) {
: id(makeEnvironmentID()), fileSource(fs) {
}
Environment::~Environment() {
@@ -128,7 +129,7 @@ void Environment::requestAsync(const Resource& resource,
Request* Environment::request(const Resource& resource,
std::function<void(const Response&)> callback) {
assert(currentlyOn(ThreadType::Map));
return fileSource.request(resource, loop, std::move(callback));
return fileSource.request(resource, util::RunLoop::current.get()->get(), std::move(callback));
}
void Environment::cancelRequest(Request* req) {

View File

@@ -11,7 +11,7 @@ using namespace mbgl;
LiveTileData::LiveTileData(const TileID& id_,
AnnotationManager& annotationManager_,
float mapMaxZoom,
util::ptr<Style> style_,
Style& style_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_,
@@ -35,29 +35,16 @@ void LiveTileData::parse() {
if (tile) {
try {
if (!style) {
throw std::runtime_error("style isn't present in LiveTileData object anymore");
}
// Parsing creates state that is encapsulated in TileParser. While parsing,
// the TileParser object writes results into this objects. All other state
// is going to be discarded afterwards.
TileParser parser(*tile, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite);
// Clear the style so that we don't have a cycle in the shared_ptr references.
style.reset();
parser.parse();
} catch (const std::exception& ex) {
Log::Error(Event::ParseTile, "Live-parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
state = State::obsolete;
return;
}
} else {
// Clear the style so that we don't have a cycle in the shared_ptr references.
style.reset();
state = State::obsolete;
}
if (state != State::obsolete) {

View File

@@ -12,7 +12,7 @@ public:
LiveTileData(const TileID&,
AnnotationManager&,
float mapMaxZoom,
util::ptr<Style>,
Style&,
GlyphAtlas&,
GlyphStore&,
SpriteAtlas&,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,288 @@
#include <mbgl/map/map_context.hpp>
#include <mbgl/map/map_data.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/map/environment.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/map/sprite.hpp>
#include <mbgl/map/still_image.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/text/glyph_store.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/worker.hpp>
#include <mbgl/util/texture_pool.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/exception.hpp>
namespace mbgl {
MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, MapData& data_, bool startPaused)
: view(view_),
data(data_),
env(fileSource),
envScope(env, ThreadType::Map, "Map"),
updated(static_cast<UpdateType>(Update::Nothing)),
asyncUpdate(util::make_unique<uv::async>(loop, [this] { update(); })),
glyphStore(util::make_unique<GlyphStore>(env)),
glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
lineAtlas(util::make_unique<LineAtlas>(512, 512)),
texturePool(util::make_unique<TexturePool>()),
painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas))
{
assert(Environment::currentlyOn(ThreadType::Map));
asyncUpdate->unref();
view.activate();
if (startPaused) {
pause();
}
painter->setup();
}
MapContext::~MapContext() {
view.notify();
// Explicit resets currently necessary because these abandon resources that need to be
// cleaned up by env.performCleanup();
style.reset();
sprite.reset();
painter.reset();
texturePool.reset();
lineAtlas.reset();
spriteAtlas.reset();
glyphAtlas.reset();
glyphStore.reset();
env.performCleanup();
view.deactivate();
}
void MapContext::pause() {
view.deactivate();
std::unique_lock<std::mutex> lockPause(data.mutexPause);
data.condPaused.notify_all();
data.condResume.wait(lockPause);
view.activate();
}
void MapContext::resize(uint16_t width, uint16_t height, float ratio) {
view.resize(width, height, ratio);
triggerUpdate();
}
void MapContext::triggerUpdate(const Update u) {
updated |= static_cast<UpdateType>(u);
asyncUpdate->send();
}
void MapContext::setStyleURL(const std::string& url) {
styleURL = mbgl::util::mapbox::normalizeStyleURL(url, data.getAccessToken());
styleJSON.clear();
const size_t pos = styleURL.rfind('/');
std::string base = "";
if (pos != std::string::npos) {
base = styleURL.substr(0, pos + 1);
}
env.request({ Resource::Kind::JSON, styleURL }, [this, base](const Response &res) {
if (res.status == Response::Successful) {
loadStyleJSON(res.data, base);
} else {
Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
}
});
}
void MapContext::setStyleJSON(const std::string& json, const std::string& base) {
styleURL.clear();
styleJSON = json;
loadStyleJSON(json, base);
}
util::ptr<Sprite> MapContext::getSprite() {
assert(Environment::currentlyOn(ThreadType::Map));
const float pixelRatio = transformState.getPixelRatio();
const std::string &sprite_url = style->getSpriteURL();
if (!sprite || !sprite->hasPixelRatio(pixelRatio)) {
sprite = std::make_shared<Sprite>(sprite_url, pixelRatio, env, [this] {
assert(Environment::currentlyOn(ThreadType::Map));
triggerUpdate();
});
}
return sprite;
}
void MapContext::loadStyleJSON(const std::string& json, const std::string& base) {
assert(Environment::currentlyOn(ThreadType::Map));
sprite.reset();
style = util::make_unique<Style>();
style->base = base;
style->loadJSON((const uint8_t *)json.c_str());
style->cascade(data.getClasses());
style->setDefaultTransitionDuration(data.getDefaultTransitionDuration());
const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, data.getAccessToken());
glyphStore->setURL(glyphURL);
for (const auto& source : style->sources) {
source->load(data.getAccessToken(), env, [this]() {
assert(Environment::currentlyOn(ThreadType::Map));
triggerUpdate();
});
}
triggerUpdate(Update::Zoom);
}
void MapContext::updateTiles() {
assert(Environment::currentlyOn(ThreadType::Map));
if (!style) return;
for (const auto& source : style->sources) {
source->update(data, transformState, *style, *glyphAtlas, *glyphStore, *spriteAtlas,
getSprite(), *texturePool, [this]() {
assert(Environment::currentlyOn(ThreadType::Map));
triggerUpdate();
});
}
}
void MapContext::updateAnnotationTiles(const std::vector<TileID>& ids) {
assert(Environment::currentlyOn(ThreadType::Map));
if (!style) return;
for (const auto &source : style->sources) {
if (source->info.type == SourceType::Annotations) {
source->invalidateTiles(ids);
}
}
triggerUpdate();
}
void MapContext::update() {
assert(Environment::currentlyOn(ThreadType::Map));
const auto now = Clock::now();
data.setAnimationTime(now);
updated |= data.transform.updateTransitions(now);
transformState = data.transform.currentState();
if (updated & static_cast<UpdateType>(Update::Debug)) {
assert(painter);
painter->setDebug(data.getDebug());
}
if (style) {
if (updated & static_cast<UpdateType>(Update::DefaultTransitionDuration)) {
style->setDefaultTransitionDuration(data.getDefaultTransitionDuration());
}
if (updated & static_cast<UpdateType>(Update::Classes)) {
style->cascade(data.getClasses());
}
if (updated & static_cast<UpdateType>(Update::Classes) ||
updated & static_cast<UpdateType>(Update::Zoom)) {
style->recalculate(transformState.getNormalizedZoom(), now);
}
// Allow the sprite atlas to potentially pull new sprite images if needed.
spriteAtlas->resize(transformState.getPixelRatio());
spriteAtlas->setSprite(getSprite());
updateTiles();
view.invalidate([this] { render(); });
}
updated = static_cast<UpdateType>(Update::Nothing);
}
void MapContext::renderStill(StillImageCallback fn) {
if (data.mode != MapMode::Still) {
throw util::Exception("Map is not in still image render mode");
}
if (callback) {
throw util::Exception("Map is currently rendering an image");
}
callback = fn;
triggerUpdate(Update::RenderStill);
}
void MapContext::render() {
assert(Environment::currentlyOn(ThreadType::Map));
// Cleanup OpenGL objects that we abandoned since the last render call.
env.performCleanup();
assert(style);
assert(painter);
painter->render(*style, transformState, data.getAnimationTime());
if (data.mode == MapMode::Still && callback && style->isLoaded() && getSprite()->isLoaded()) {
callback(view.readStillImage());
callback = nullptr;
}
// Schedule another rerender when we definitely need a next frame.
if (data.transform.needsTransition() || style->hasTransitions()) {
triggerUpdate();
}
}
double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
assert(Environment::currentlyOn(ThreadType::Map));
assert(sprite);
const SpritePosition pos = sprite->getSpritePosition(symbol);
return -pos.height / pos.pixelRatio / 2;
}
void MapContext::setSourceTileCacheSize(size_t size) {
assert(Environment::currentlyOn(ThreadType::Map));
if (size != sourceCacheSize) {
sourceCacheSize = size;
if (!style) return;
for (const auto &source : style->sources) {
source->setCacheSize(sourceCacheSize);
}
env.performCleanup();
}
}
void MapContext::onLowMemory() {
assert(Environment::currentlyOn(ThreadType::Map));
if (!style) return;
for (const auto &source : style->sources) {
source->onLowMemory();
}
env.performCleanup();
}
}

View File

@@ -0,0 +1,100 @@
#ifndef MBGL_MAP_MAP_CONTEXT
#define MBGL_MAP_MAP_CONTEXT
#include <mbgl/map/tile_id.hpp>
#include <mbgl/map/update.hpp>
#include <mbgl/map/environment.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/util/ptr.hpp>
#include <vector>
typedef struct uv_loop_s uv_loop_t;
namespace uv {
class async;
}
namespace mbgl {
class View;
class MapData;
class GlyphStore;
class GlyphAtlas;
class SpriteAtlas;
class LineAtlas;
class TexturePool;
class Painter;
class Sprite;
class Style;
class Worker;
class StillImage;
struct LatLng;
struct LatLngBounds;
class MapContext {
public:
MapContext(uv_loop_t*, View&, FileSource&, MapData&, bool startPaused);
~MapContext();
void pause();
void render();
void resize(uint16_t width, uint16_t height, float ratio);
using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>;
void renderStill(StillImageCallback callback);
void triggerUpdate(Update = Update::Nothing);
void setStyleURL(const std::string&);
void setStyleJSON(const std::string& json, const std::string& base);
std::string getStyleURL() const { return styleURL; }
std::string getStyleJSON() const { return styleJSON; }
double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol);
void updateAnnotationTiles(const std::vector<TileID>&);
void setSourceTileCacheSize(size_t size);
void onLowMemory();
private:
util::ptr<Sprite> getSprite();
void updateTiles();
// Update the state indicated by the accumulated Update flags, then render.
void update();
// Loads the actual JSON object an creates a new Style object.
void loadStyleJSON(const std::string& json, const std::string& base);
View& view;
MapData& data;
Environment env;
EnvironmentScope envScope;
UpdateType updated { static_cast<UpdateType>(Update::Nothing) };
std::unique_ptr<uv::async> asyncUpdate;
std::unique_ptr<GlyphStore> glyphStore;
std::unique_ptr<GlyphAtlas> glyphAtlas;
std::unique_ptr<SpriteAtlas> spriteAtlas;
std::unique_ptr<LineAtlas> lineAtlas;
std::unique_ptr<TexturePool> texturePool;
std::unique_ptr<Painter> painter;
std::unique_ptr<Style> style;
util::ptr<Sprite> sprite;
std::string styleURL;
std::string styleJSON;
StillImageCallback callback;
size_t sourceCacheSize;
TransformState transformState;
};
}
#endif

View File

@@ -7,33 +7,26 @@
#include <mutex>
#include <atomic>
#include <vector>
#include <cassert>
#include <condition_variable>
#include <mbgl/map/mode.hpp>
#include <mbgl/map/environment.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/map/annotation.hpp>
namespace mbgl {
struct StyleInfo {
std::string url;
std::string base;
std::string json;
};
class MapData {
using Lock = std::lock_guard<std::mutex>;
public:
inline MapData() {
inline MapData(View& view, MapMode mode_) : transform(view), mode(mode_) {
setAnimationTime(TimePoint::min());
setDefaultTransitionDuration(Duration::zero());
}
inline StyleInfo getStyleInfo() const {
Lock lock(mtx);
return styleInfo;
}
inline void setStyleInfo(StyleInfo&& info) {
Lock lock(mtx);
styleInfo = info;
}
inline std::string getAccessToken() const {
Lock lock(mtx);
return accessToken;
@@ -87,15 +80,25 @@ public:
defaultTransitionDuration = duration;
};
public:
Transform transform;
AnnotationManager annotationManager;
const MapMode mode;
private:
mutable std::mutex mtx;
StyleInfo styleInfo;
std::string accessToken;
std::vector<std::string> classes;
std::atomic<uint8_t> debug { false };
std::atomic<Duration> animationTime;
std::atomic<Duration> defaultTransitionDuration;
// TODO: make private
public:
std::mutex mutexPause;
std::condition_variable condPaused;
std::condition_variable condResume;
};
}

View File

@@ -1,4 +1,3 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/raster_tile_data.hpp>
#include <mbgl/style/style.hpp>

View File

@@ -1,30 +1,26 @@
#include <mbgl/map/source.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/map/map_data.hpp>
#include <mbgl/map/environment.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/map/tile.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/texture_pool.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/box.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/token.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/map/vector_tile_data.hpp>
#include <mbgl/map/raster_tile_data.hpp>
#include <mbgl/map/live_tile_data.hpp>
#include <mbgl/style/style.hpp>
#include <algorithm>
@@ -123,6 +119,20 @@ Source::Source()
Source::~Source() {}
bool Source::isLoaded() const {
if (!loaded) {
return false;
}
for (const auto& tile : tiles) {
if (tile.second->data->state != TileData::State::parsed) {
return false;
}
}
return true;
}
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
@@ -147,7 +157,7 @@ void Source::load(const std::string& accessToken,
d.Parse<0>(res.data.c_str());
if (d.HasParseError()) {
Log::Warning(Event::General, "Invalid source TileJSON; Parse Error at %d: %s", d.GetErrorOffset(), d.GetParseError());
Log::Error(Event::General, "Invalid source TileJSON; Parse Error at %d: %s", d.GetErrorOffset(), d.GetParseError());
return;
}
@@ -215,11 +225,16 @@ TileData::State Source::hasTile(const TileID& id) {
return TileData::State::invalid;
}
TileData::State Source::addTile(Map &map, Worker &worker,
util::ptr<Style> style, GlyphAtlas &glyphAtlas,
GlyphStore &glyphStore, SpriteAtlas &spriteAtlas,
util::ptr<Sprite> sprite, TexturePool &texturePool,
const TileID &id, std::function<void()> callback) {
TileData::State Source::addTile(MapData& data,
const TransformState& transformState,
Style& style,
GlyphAtlas& glyphAtlas,
GlyphStore& glyphStore,
SpriteAtlas& spriteAtlas,
util::ptr<Sprite> sprite,
TexturePool& texturePool,
const TileID& id,
std::function<void()> callback) {
const TileData::State state = hasTile(id);
if (state != TileData::State::invalid) {
@@ -252,18 +267,17 @@ TileData::State Source::addTile(Map &map, Worker &worker,
// If we don't find working tile data, we're just going to load it.
if (info.type == SourceType::Vector) {
new_tile.data =
std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style, glyphAtlas,
std::make_shared<VectorTileData>(normalized_id, data.transform.getMaxZoom(), style, glyphAtlas,
glyphStore, spriteAtlas, sprite, info);
new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
new_tile.data->request(style.workers, transformState.getPixelRatio(), callback);
} else if (info.type == SourceType::Raster) {
new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info);
new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
new_tile.data->request(style.workers, transformState.getPixelRatio(), callback);
} else if (info.type == SourceType::Annotations) {
AnnotationManager& annotationManager = map.getAnnotationManager();
new_tile.data = std::make_shared<LiveTileData>(normalized_id, annotationManager,
map.getMaxZoom(), style, glyphAtlas,
new_tile.data = std::make_shared<LiveTileData>(normalized_id, data.annotationManager,
data.transform.getMaxZoom(), style, glyphAtlas,
glyphStore, spriteAtlas, sprite, info);
new_tile.data->reparse(worker, callback);
new_tile.data->reparse(style.workers, callback);
} else {
throw std::runtime_error("source type not implemented");
}
@@ -352,21 +366,21 @@ bool Source::findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::fo
return false;
}
void Source::update(Map &map,
Worker &worker,
util::ptr<Style> style,
GlyphAtlas &glyphAtlas,
GlyphStore &glyphStore,
SpriteAtlas &spriteAtlas,
void Source::update(MapData& data,
const TransformState& transformState,
Style& style,
GlyphAtlas& glyphAtlas,
GlyphStore& glyphStore,
SpriteAtlas& spriteAtlas,
util::ptr<Sprite> sprite,
TexturePool &texturePool,
TexturePool& texturePool,
std::function<void()> callback) {
if (!loaded || map.getTime() <= updated) {
if (!loaded || data.getAnimationTime() <= updated) {
return;
}
int32_t zoom = std::floor(getZoom(map.getState()));
std::forward_list<TileID> required = coveringTiles(map.getState());
int32_t zoom = std::floor(getZoom(transformState));
std::forward_list<TileID> required = coveringTiles(transformState);
// Determine the overzooming/underzooming amounts.
int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info.min_zoom, info.max_zoom);
@@ -379,7 +393,7 @@ void Source::update(Map &map,
// Add existing child/parent tiles if the actual tile is not yet loaded
for (const auto& id : required) {
const TileData::State state = addTile(map, worker, style, glyphAtlas, glyphStore,
const TileData::State state = addTile(data, transformState, style, glyphAtlas, glyphStore,
spriteAtlas, sprite, texturePool, id, callback);
if (state != TileData::State::parsed) {
@@ -399,9 +413,9 @@ void Source::update(Map &map,
}
if (info.type != SourceType::Raster && cache.getSize() == 0) {
size_t conservativeCacheSize = ((float)map.getState().getWidth() / util::tileSize) *
((float)map.getState().getHeight() / util::tileSize) *
(map.getMaxZoom() - map.getMinZoom() + 1) *
size_t conservativeCacheSize = ((float)transformState.getWidth() / util::tileSize) *
((float)transformState.getHeight() / util::tileSize) *
(data.transform.getMaxZoom() - data.transform.getMinZoom() + 1) *
0.5;
cache.setSize(conservativeCacheSize);
}
@@ -441,7 +455,7 @@ void Source::update(Map &map,
}
});
updated = map.getTime();
updated = data.getAnimationTime();
}
void Source::invalidateTiles(const std::vector<TileID>& ids) {

View File

@@ -20,9 +20,8 @@
namespace mbgl {
class Map;
class MapData;
class Environment;
class Worker;
class GlyphAtlas;
class GlyphStore;
class SpriteAtlas;
@@ -60,9 +59,18 @@ public:
void load(const std::string& accessToken,
Environment&,
std::function<void()> callback);
bool isLoaded() const;
void update(Map &, Worker &, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
SpriteAtlas &, util::ptr<Sprite>, TexturePool &, std::function<void()> callback);
void load(MapData&, Environment&, std::function<void()> callback);
void update(MapData&,
const TransformState&,
Style&,
GlyphAtlas&,
GlyphStore&,
SpriteAtlas&,
util::ptr<Sprite>,
TexturePool&,
std::function<void()> callback);
void invalidateTiles(const std::vector<TileID>&);
@@ -85,9 +93,16 @@ private:
int32_t coveringZoomLevel(const TransformState&) const;
std::forward_list<TileID> coveringTiles(const TransformState&) const;
TileData::State addTile(Map &, Worker &, util::ptr<Style>, GlyphAtlas &,
GlyphStore &, SpriteAtlas &, util::ptr<Sprite>, TexturePool &,
const TileID &, std::function<void()> callback);
TileData::State addTile(MapData&,
const TransformState&,
Style&,
GlyphAtlas&,
GlyphStore&,
SpriteAtlas&,
util::ptr<Sprite>,
TexturePool&,
const TileID&,
std::function<void()> callback);
TileData::State hasTile(const TileID& id);

View File

@@ -1,5 +1,4 @@
#include <mbgl/map/sprite.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/platform/log.hpp>
@@ -24,42 +23,16 @@ SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16
sdf(sdf_) {
}
util::ptr<Sprite> Sprite::Create(const std::string &base_url, float pixelRatio, Environment &env) {
util::ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
sprite->load(env);
return sprite;
}
Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_)
: valid(base_url.length() > 0),
pixelRatio(pixelRatio_ > 1 ? 2 : 1),
spriteURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".png"),
jsonURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".json"),
Sprite::Sprite(const std::string& baseUrl, float pixelRatio_, Environment& env_, std::function<void ()> callback_)
: pixelRatio(pixelRatio_ > 1 ? 2 : 1),
raster(),
loadedImage(false),
loadedJSON(false),
future(promise.get_future()) {
}
future(promise.get_future()),
callback(callback_),
env(env_) {
bool Sprite::hasPixelRatio(float ratio) const {
return pixelRatio == (ratio > 1 ? 2 : 1);
}
void Sprite::waitUntilLoaded() const {
future.wait();
}
Sprite::operator bool() const {
return valid && isLoaded() && !pos.empty();
}
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
void Sprite::load(Environment &env) {
if (!valid) {
if (baseUrl.empty()) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
loadedImage = true;
loadedJSON = true;
@@ -67,34 +40,48 @@ void Sprite::load(Environment &env) {
return;
}
util::ptr<Sprite> sprite = shared_from_this();
std::string spriteURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".png");
std::string jsonURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".json");
env.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
jsonRequest = env.request({ Resource::Kind::JSON, jsonURL }, [this](const Response &res) {
jsonRequest = nullptr;
if (res.status == Response::Successful) {
sprite->body = res.data;
sprite->parseJSON();
body = res.data;
parseJSON();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str());
}
sprite->loadedJSON = true;
sprite->complete();
loadedJSON = true;
complete();
});
env.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this](const Response &res) {
spriteRequest = nullptr;
if (res.status == Response::Successful) {
sprite->image = res.data;
sprite->parseImage();
image = res.data;
parseImage();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str());
}
sprite->loadedImage = true;
sprite->complete();
loadedImage = true;
complete();
});
}
Sprite::~Sprite() {
if (jsonRequest) {
env.cancelRequest(jsonRequest);
}
if (spriteRequest) {
env.cancelRequest(spriteRequest);
}
}
void Sprite::complete() {
if (loadedImage && loadedJSON) {
promise.set_value();
callback();
}
}
@@ -102,6 +89,14 @@ bool Sprite::isLoaded() const {
return loadedImage && loadedJSON;
}
void Sprite::waitUntilLoaded() const {
future.wait();
}
bool Sprite::hasPixelRatio(float ratio) const {
return pixelRatio == (ratio > 1 ? 2 : 1);
}
void Sprite::parseImage() {
raster = util::make_unique<util::Image>(image);
if (!*raster) {

View File

@@ -4,6 +4,7 @@
#include <mbgl/util/image.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/storage/request.hpp>
#include <cstdint>
#include <atomic>
@@ -15,6 +16,7 @@
namespace mbgl {
class Environment;
class Request;
class SpritePosition {
public:
@@ -31,15 +33,10 @@ public:
bool sdf = false;
};
class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable {
private:
struct Key {};
void load(Environment &env);
class Sprite : private util::noncopyable {
public:
Sprite(const Key &, const std::string& base_url, float pixelRatio);
static util::ptr<Sprite>
Create(const std::string &base_url, float pixelRatio, Environment &env);
Sprite(const std::string& baseUrl, float pixelRatio, Environment&, std::function<void()> callback);
~Sprite();
const SpritePosition &getSpritePosition(const std::string& name) const;
@@ -48,15 +45,7 @@ public:
void waitUntilLoaded() const;
bool isLoaded() const;
operator bool() const;
private:
const bool valid;
public:
const float pixelRatio;
const std::string spriteURL;
const std::string jsonURL;
std::unique_ptr<util::Image> raster;
private:
@@ -64,7 +53,6 @@ private:
void parseImage();
void complete();
private:
std::string body;
std::string image;
std::atomic<bool> loadedImage;
@@ -74,7 +62,11 @@ private:
std::promise<void> promise;
std::future<void> future;
std::function<void ()> callback;
Environment& env;
Request* jsonRequest = nullptr;
Request* spriteRequest = nullptr;
};
}

View File

@@ -60,13 +60,16 @@ void TileData::cancel() {
void TileData::reparse(Worker& worker, std::function<void()> callback) {
util::ptr<TileData> tile = shared_from_this();
worker.send(
[tile]() {
EnvironmentScope scope(tile->env, ThreadType::TileWorker, "TileWorker_" + tile->name);
tile->parse();
[this]() {
EnvironmentScope scope(env, ThreadType::TileWorker, "TileWorker_" + name);
parse();
},
[tile, callback]() {
// `tile` is bound in this lambda to ensure that if it's the last owning pointer,
// destruction happens on the map thread, not the worker thread.
// destruction happens on the map thread, not the worker thread. It is _not_ bound
// in the above lambda, because we do not want the possibility to arise that the
// after callback could execute and release the penultimate reference before the
// work callback has been destructed.
callback();
});
}

View File

@@ -1,23 +1,15 @@
#include <mbgl/map/tile_parser.hpp>
#include <mbgl/map/vector_tile_data.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/renderer/line_bucket.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/renderer/raster_bucket.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/token.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/text/glyph_store.hpp>
#include <mbgl/text/collision.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/utf.hpp>
#include <mbgl/style/style.hpp>
#include <locale>
@@ -30,7 +22,7 @@ TileParser::~TileParser() = default;
TileParser::TileParser(const GeometryTile& geometryTile_,
VectorTileData& tile_,
const util::ptr<const Style>& style_,
const Style& style_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_,
@@ -43,7 +35,6 @@ TileParser::TileParser(const GeometryTile& geometryTile_,
spriteAtlas(spriteAtlas_),
sprite(sprite_),
collision(util::make_unique<Collision>(tile.id.z, 4096, tile.source.tile_size, tile.depth)) {
assert(style);
assert(sprite);
assert(collision);
}
@@ -51,7 +42,7 @@ TileParser::TileParser(const GeometryTile& geometryTile_,
bool TileParser::obsolete() const { return tile.state == TileData::State::obsolete; }
void TileParser::parse() {
for (const auto& layer_desc : style->layers) {
for (const auto& layer_desc : style.layers) {
// Cancel early when parsing.
if (obsolete()) {
return;

View File

@@ -35,7 +35,7 @@ class TileParser : private util::noncopyable {
public:
TileParser(const GeometryTile& geometryTile,
VectorTileData& tile,
const util::ptr<const Style>& style,
const Style& style,
GlyphAtlas& glyphAtlas,
GlyphStore& glyphStore,
SpriteAtlas& spriteAtlas,
@@ -60,7 +60,7 @@ private:
VectorTileData& tile;
// Cross-thread shared data.
util::ptr<const Style> style;
const Style& style;
GlyphAtlas& glyphAtlas;
GlyphStore& glyphStore;
SpriteAtlas& spriteAtlas;

View File

@@ -12,7 +12,7 @@ using namespace mbgl;
VectorTileData::VectorTileData(const TileID& id_,
float mapMaxZoom,
util::ptr<Style> style_,
Style& style_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_,
@@ -37,20 +37,12 @@ void VectorTileData::parse() {
}
try {
if (!style) {
throw std::runtime_error("style isn't present in VectorTileData object anymore");
}
// Parsing creates state that is encapsulated in TileParser. While parsing,
// the TileParser object writes results into this objects. All other state
// is going to be discarded afterwards.
VectorTile vectorTile(pbf((const uint8_t *)data.data(), data.size()));
const VectorTile* vt = &vectorTile;
TileParser parser(*vt, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite);
// Clear the style so that we don't have a cycle in the shared_ptr references.
style.reset();
parser.parse();
} catch (const std::exception& ex) {
Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());

View File

@@ -31,7 +31,7 @@ class VectorTileData : public TileData {
public:
VectorTileData(const TileID&,
float mapMaxZoom,
util::ptr<Style>,
Style&,
GlyphAtlas&,
GlyphStore&,
SpriteAtlas&,
@@ -59,7 +59,7 @@ protected:
GlyphStore& glyphStore;
SpriteAtlas& spriteAtlas;
util::ptr<Sprite> sprite;
util::ptr<Style> style;
Style& style;
public:
const float depth;

View File

@@ -2,6 +2,8 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/still_image.hpp>
#include <cassert>
namespace mbgl {
void View::initialize(Map *map_) {
@@ -9,7 +11,7 @@ void View::initialize(Map *map_) {
map = map_;
}
void View::discard() {
void View::resize(uint16_t, uint16_t, float) {
// no-op
}
@@ -17,11 +19,6 @@ std::unique_ptr<StillImage> View::readStillImage() {
return nullptr;
}
void View::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
assert(map);
map->resize(width, height, ratio, fbWidth, fbHeight);
}
void View::notifyMapChange(MapChange, Duration) {
// no-op
}

View File

@@ -1,10 +1,15 @@
#include <mbgl/renderer/debug_bucket.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/platform/gl.hpp>
#include <cassert>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#endif
using namespace mbgl;
DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer_)

View File

@@ -7,10 +7,6 @@
#include <vector>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#endif
namespace mbgl {
class PlainShader;

View File

@@ -4,18 +4,23 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layout.hpp>
#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/shader/pattern_shader.hpp>
#include <mbgl/shader/outline_shader.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
#include <cassert>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#endif
struct geometry_too_long_exception : std::exception {};
using namespace mbgl;
void *FillBucket::alloc(void *, unsigned int size) {
return ::malloc(size);
}

View File

@@ -11,10 +11,6 @@
#include <vector>
#include <memory>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#endif
namespace mbgl {
class FillVertexBuffer;

View File

@@ -4,6 +4,9 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layout.hpp>
#include <mbgl/shader/line_shader.hpp>
#include <mbgl/shader/linesdf_shader.hpp>
#include <mbgl/shader/linepattern_shader.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/platform/gl.hpp>

View File

@@ -1,16 +1,32 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/map/tile.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/shader/pattern_shader.hpp>
#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/shader/outline_shader.hpp>
#include <mbgl/shader/line_shader.hpp>
#include <mbgl/shader/linesdf_shader.hpp>
#include <mbgl/shader/linepattern_shader.hpp>
#include <mbgl/shader/icon_shader.hpp>
#include <mbgl/shader/raster_shader.hpp>
#include <mbgl/shader/sdf_shader.hpp>
#include <mbgl/shader/dot_shader.hpp>
#include <mbgl/shader/gaussian_shader.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/clip_id.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mat3.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/map/tile.hpp>
#if defined(DEBUG)
#include <mbgl/util/stopwatch.hpp>
@@ -39,10 +55,6 @@ bool Painter::needsAnimation() const {
}
void Painter::setup() {
#if defined(DEBUG)
util::stopwatch stopwatch("painter setup");
#endif
// Enable GL debugging
if ((gl::DebugMessageControl != nullptr) && (gl::DebugMessageCallback != nullptr)) {
// This will enable all messages including performance hints
@@ -106,24 +118,6 @@ void Painter::setupShaders() {
if (!gaussianShader) gaussianShader = util::make_unique<GaussianShader>();
}
void Painter::deleteShaders() {
plainShader = nullptr;
outlineShader = nullptr;
lineShader = nullptr;
linepatternShader = nullptr;
patternShader = nullptr;
iconShader = nullptr;
rasterShader = nullptr;
sdfGlyphShader = nullptr;
sdfIconShader = nullptr;
dotShader = nullptr;
gaussianShader = nullptr;
}
void Painter::terminate() {
deleteShaders();
}
void Painter::resize() {
if (gl_viewport != state.getFramebufferDimensions()) {
gl_viewport = state.getFramebufferDimensions();

View File

@@ -1,58 +1,60 @@
#ifndef MBGL_RENDERER_PAINTER
#define MBGL_RENDERER_PAINTER
#include <mbgl/map/tile_data.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/renderer/frame_history.hpp>
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/static_vertex_buffer.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/renderer/frame_history.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/shader/outline_shader.hpp>
#include <mbgl/shader/pattern_shader.hpp>
#include <mbgl/shader/line_shader.hpp>
#include <mbgl/shader/linesdf_shader.hpp>
#include <mbgl/shader/linepattern_shader.hpp>
#include <mbgl/shader/icon_shader.hpp>
#include <mbgl/shader/raster_shader.hpp>
#include <mbgl/shader/sdf_shader.hpp>
#include <mbgl/shader/dot_shader.hpp>
#include <mbgl/shader/gaussian_shader.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/chrono.hpp>
#include <map>
#include <unordered_map>
#include <array>
#include <vector>
#include <set>
namespace mbgl {
enum class RenderPass : bool { Opaque, Translucent };
class Transform;
class Style;
class StyleLayer;
class Tile;
class Sprite;
class SpriteAtlas;
class GlyphAtlas;
class LineAtlas;
class Source;
class DebugBucket;
class FillBucket;
class LineBucket;
class SymbolBucket;
class RasterBucket;
class PrerenderedTexture;
struct FillProperties;
struct RasterProperties;
class LayerDescription;
class RasterTileData;
class SDFShader;
class PlainShader;
class OutlineShader;
class LineShader;
class LinejoinShader;
class LineSDFShader;
class LinepatternShader;
class PatternShader;
class IconShader;
class RasterShader;
class SDFGlyphShader;
class SDFIconShader;
class DotShader;
class GaussianShader;
struct ClipID;
class Painter : private util::noncopyable {
@@ -62,11 +64,6 @@ public:
void setup();
// Perform cleanup tasks that prepare shutting down the app. This doesn't mean that the
// app will be shut down. That means all operations must be automatically be reversed (e.g. through
// lazy initialization) in case rendering continues.
void terminate();
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
// tiles whatsoever.
void clear();
@@ -132,7 +129,6 @@ public:
private:
void setupShaders();
void deleteShaders();
mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor);
void prepareTile(const Tile& tile);

View File

@@ -1,10 +1,12 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/map/tile.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/util/clip_id.hpp>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#endif
using namespace mbgl;
void Painter::drawClippingMasks(const std::set<Source*>& sources) {

View File

@@ -1,9 +1,14 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/debug_bucket.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/map/tile.hpp>
#include <mbgl/map/tile_data.hpp>
#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/util/string.hpp>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#endif
using namespace mbgl;
void Painter::renderTileDebug(const Tile& tile) {

View File

@@ -3,9 +3,12 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_layout.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/map/sprite.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/shader/outline_shader.hpp>
#include <mbgl/shader/pattern_shader.hpp>
#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/mat3.hpp>

View File

@@ -4,9 +4,12 @@
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_layout.hpp>
#include <mbgl/map/sprite.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/shader/line_shader.hpp>
#include <mbgl/shader/linesdf_shader.hpp>
#include <mbgl/shader/linepattern_shader.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/map/map.hpp>
using namespace mbgl;

View File

@@ -2,8 +2,8 @@
#include <mbgl/platform/gl.hpp>
#include <mbgl/renderer/raster_bucket.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/shader/raster_shader.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/map/map.hpp>
using namespace mbgl;

View File

@@ -4,7 +4,8 @@
#include <mbgl/style/style_layout.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/shader/sdf_shader.hpp>
#include <mbgl/shader/icon_shader.hpp>
#include <mbgl/util/math.hpp>
#include <cmath>

View File

@@ -1,6 +1,11 @@
#include <mbgl/renderer/raster_bucket.hpp>
#include <mbgl/shader/raster_shader.hpp>
#include <mbgl/renderer/painter.hpp>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#endif
using namespace mbgl;
RasterBucket::RasterBucket(TexturePool& texturePool, const StyleLayoutRaster& layout_)

View File

@@ -5,8 +5,6 @@
#include <mbgl/util/raster.hpp>
#include <mbgl/style/style_bucket.hpp>
namespace mbgl {
class StyleLayoutRaster;

View File

@@ -12,6 +12,8 @@
#include <mbgl/text/placement.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/text/collision.hpp>
#include <mbgl/shader/sdf_shader.hpp>
#include <mbgl/shader/icon_shader.hpp>
#include <mbgl/map/sprite.hpp>
#include <mbgl/util/utf.hpp>
@@ -20,6 +22,10 @@
#include <mbgl/util/merge_lines.hpp>
#include <mbgl/util/std.hpp>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#endif
namespace mbgl {
SymbolBucket::SymbolBucket(Collision &collision_)

View File

@@ -0,0 +1,23 @@
#ifndef MBGL_STORAGE_DEFAULT_ASSET_CONTEXT
#define MBGL_STORAGE_DEFAULT_ASSET_CONTEXT
#include <mbgl/storage/request_base.hpp>
typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class AssetContext {
public:
static std::unique_ptr<AssetContext> createContext(uv_loop_t*);
virtual ~AssetContext() = default;
virtual RequestBase* createRequest(const Resource&,
RequestBase::Callback,
uv_loop_t*,
const std::string& assetRoot) = 0;
};
}
#endif

View File

@@ -1,27 +0,0 @@
#ifndef MBGL_STORAGE_DEFAULT_ASSET_REQUEST
#define MBGL_STORAGE_DEFAULT_ASSET_REQUEST
#include "request_base.hpp"
typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class AssetRequestImpl;
class AssetRequest : public RequestBase {
public:
AssetRequest(const Resource&, Callback, uv_loop_t*, const std::string& assetRoot);
void cancel() override;
private:
~AssetRequest();
AssetRequestImpl* impl;
friend class AssetRequestImpl;
};
}
#endif

View File

@@ -1,7 +1,7 @@
#include <mbgl/storage/default_file_source_impl.hpp>
#include <mbgl/storage/request.hpp>
#include <mbgl/storage/asset_request.hpp>
#include <mbgl/storage/http_request.hpp>
#include <mbgl/storage/asset_context.hpp>
#include <mbgl/storage/http_context.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/platform.hpp>
@@ -42,7 +42,7 @@ Request* DefaultFileSource::request(const Resource& resource,
// This function can be called from any thread. Make sure we're executing the actual call in the
// file source loop by sending it over the queue.
thread->invoke(&Impl::add, req, thread->get());
thread->invoke(&Impl::add, req);
return req;
}
@@ -61,8 +61,12 @@ void DefaultFileSource::cancel(Request *req) {
// ----- Impl -----
DefaultFileSource::Impl::Impl(FileCache* cache_, const std::string& root)
: cache(cache_), assetRoot(root.empty() ? platform::assetRoot() : root) {
DefaultFileSource::Impl::Impl(uv_loop_t* loop_, FileCache* cache_, const std::string& root)
: loop(loop_),
cache(cache_),
assetRoot(root.empty() ? platform::assetRoot() : root),
assetContext(AssetContext::createContext(loop_)),
httpContext(HTTPContext::createContext(loop_)) {
}
DefaultFileRequest* DefaultFileSource::Impl::find(const Resource& resource) {
@@ -73,7 +77,7 @@ DefaultFileRequest* DefaultFileSource::Impl::find(const Resource& resource) {
return nullptr;
}
void DefaultFileSource::Impl::add(Request* req, uv_loop_t* loop) {
void DefaultFileSource::Impl::add(Request* req) {
const Resource& resource = req->resource;
DefaultFileRequest* request = find(resource);
@@ -82,7 +86,7 @@ void DefaultFileSource::Impl::add(Request* req, uv_loop_t* loop) {
return;
}
request = &pending.emplace(resource, DefaultFileRequest(resource, loop)).first->second;
request = &pending.emplace(resource, DefaultFileRequest(resource)).first->second;
request->observers.insert(req);
if (cache) {
@@ -128,9 +132,9 @@ void DefaultFileSource::Impl::startRealRequest(const Resource& resource, std::sh
};
if (algo::starts_with(resource.url, "asset://")) {
request->request = new AssetRequest(resource, callback, request->loop, assetRoot);
request->request = assetContext->createRequest(resource, callback, loop, assetRoot);
} else {
request->request = new HTTPRequest(resource, callback, request->loop, response);
request->request = httpContext->createRequest(resource, callback, loop, response);
}
}

View File

@@ -2,29 +2,32 @@
#define MBGL_STORAGE_DEFAULT_DEFAULT_FILE_SOURCE_IMPL
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/asset_context.hpp>
#include <mbgl/storage/http_context.hpp>
#include <set>
#include <unordered_map>
typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class RequestBase;
struct DefaultFileRequest {
const Resource resource;
uv_loop_t* loop;
std::set<Request*> observers;
RequestBase* request = nullptr;
DefaultFileRequest(const Resource& resource_, uv_loop_t* loop_)
: resource(resource_), loop(loop_) {}
DefaultFileRequest(const Resource& resource_)
: resource(resource_) {}
};
class DefaultFileSource::Impl {
public:
Impl(FileCache*, const std::string& = "");
Impl(uv_loop_t*, FileCache*, const std::string& = "");
void add(Request*, uv_loop_t*);
void add(Request*);
void cancel(Request*);
private:
@@ -35,8 +38,11 @@ private:
void notify(DefaultFileRequest*, std::shared_ptr<const Response>, FileCache::Hint);
std::unordered_map<Resource, DefaultFileRequest, Resource::Hash> pending;
uv_loop_t* loop = nullptr;
FileCache* cache = nullptr;
const std::string assetRoot;
std::unique_ptr<AssetContext> assetContext;
std::unique_ptr<HTTPContext> httpContext;
};
}

View File

@@ -0,0 +1,30 @@
#include <mbgl/storage/http_context.hpp>
namespace mbgl {
HTTPContext::HTTPContext(uv_loop_t* loop_)
: reachability(loop_, [this] { retryRequests(); }) {
NetworkStatus::Subscribe(reachability.get());
reachability.unref();
}
HTTPContext::~HTTPContext() {
assert(requests.empty());
NetworkStatus::Unsubscribe(reachability.get());
}
void HTTPContext::addRequest(RequestBase* request) {
requests.insert(request);
}
void HTTPContext::removeRequest(RequestBase* request) {
requests.erase(request);
}
void HTTPContext::retryRequests() {
for (auto request : requests) {
request->retry();
}
}
}

View File

@@ -1,76 +1,40 @@
#ifndef MBGL_STORAGE_DEFAULT_HTTP_CONTEXT
#define MBGL_STORAGE_DEFAULT_HTTP_CONTEXT
#include "thread_context.hpp"
#include <mbgl/storage/request_base.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <set>
namespace mbgl {
class HTTPRequest;
// This is a template class that provides a per-thread Context object. It can be used by HTTP
// implementations to store global state. It also implements the NetworkStatus mechanism and
// triggers immediate retries on all requests waiting for network status changes.
template <typename Context>
class HTTPContext : public ThreadContext<Context> {
class HTTPContext {
public:
HTTPContext(uv_loop_t *loop);
~HTTPContext();
static std::unique_ptr<HTTPContext> createContext(uv_loop_t*);
void addRequest(HTTPRequest *request);
void removeRequest(HTTPRequest *baton);
HTTPContext(uv_loop_t*);
virtual ~HTTPContext();
virtual RequestBase* createRequest(const Resource&,
RequestBase::Callback,
uv_loop_t*,
std::shared_ptr<const Response>) = 0;
void addRequest(RequestBase*);
void removeRequest(RequestBase*);
private:
void retryRequests();
public:
// Will be fired when the network status becomes reachable.
uv_async_t *reachability = nullptr;
uv::async reachability;
// A list of all pending HTTPRequestImpls that we need to notify when the network status
// changes.
std::set<HTTPRequest *> requests;
std::set<RequestBase*> requests;
};
template <typename Context>
HTTPContext<Context>::HTTPContext(uv_loop_t *loop_)
: ThreadContext<Context>(loop_) {
reachability = new uv_async_t;
reachability->data = this;
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
uv_async_init(loop_, reachability, [](uv_async_t *async, int) {
#else
uv_async_init(loop_, reachability, [](uv_async_t *async) {
#endif
for (auto request : reinterpret_cast<Context *>(async->data)->requests) {
request->retryImmediately();
}
});
// Allow the loop to quit even though this handle is still active.
uv_unref(reinterpret_cast<uv_handle_t *>(reachability));
NetworkStatus::Subscribe(reachability);
}
template <typename Context>
HTTPContext<Context>::~HTTPContext() {
MBGL_VERIFY_THREAD(HTTPContext<Context>::tid);
assert(requests.empty());
NetworkStatus::Unsubscribe(reachability);
uv::close(reachability);
}
template <typename Context>
void HTTPContext<Context>::addRequest(HTTPRequest *request) {
requests.insert(request);
}
template <typename Context>
void HTTPContext<Context>::removeRequest(HTTPRequest *request) {
requests.erase(request);
}
}
#endif

View File

@@ -1,28 +0,0 @@
#ifndef MBGL_STORAGE_DEFAULT_HTTP_REQUEST
#define MBGL_STORAGE_DEFAULT_HTTP_REQUEST
#include "request_base.hpp"
typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class HTTPRequestImpl;
class HTTPRequest : public RequestBase {
public:
HTTPRequest(const Resource&, Callback, uv_loop_t*, std::shared_ptr<const Response> = nullptr);
void cancel() override;
void retryImmediately();
private:
~HTTPRequest();
HTTPRequestImpl* impl;
friend class HTTPRequestImpl;
};
}
#endif

View File

@@ -23,6 +23,7 @@ public:
virtual ~RequestBase() = default;
virtual void cancel() = 0;
virtual void retry() {};
protected:
const Resource& resource;

View File

@@ -1,78 +0,0 @@
#ifndef MBGL_STORAGE_DEFAULT_THREAD_CONTEXT
#define MBGL_STORAGE_DEFAULT_THREAD_CONTEXT
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/uv.hpp>
#include <uv.h>
#include <pthread.h>
#include <map>
#include <cassert>
namespace mbgl {
// This is a template class that provides a per-thread and per-loop Context object. It can be used
// by implementations to store global state.
template <typename Context>
class ThreadContext : private util::noncopyable {
protected:
MBGL_STORE_THREAD(tid)
using Map = std::map<uv_loop_t *, std::unique_ptr<Context>>;
public:
static Context *Get(uv_loop_t *loop);
private:
static pthread_key_t key;
static pthread_once_t once;
public:
ThreadContext(uv_loop_t *loop);
~ThreadContext();
public:
uv_loop_t *loop;
};
template <typename Context>
Context *ThreadContext<Context>::Get(uv_loop_t *loop) {
pthread_once(&once, []() {
pthread_key_create(&key, [](void *ptr) {
assert(ptr);
delete reinterpret_cast<Map *>(ptr);
});
});
auto contexts = reinterpret_cast<Map *>(pthread_getspecific(key));
if (!contexts) {
contexts = new Map();
pthread_setspecific(key, contexts);
}
// Now find a ThreadContext that matches the requested loop.
auto it = contexts->find(loop);
if (it == contexts->end()) {
auto result = contexts->emplace(loop, util::make_unique<Context>(loop));
assert(result.second); // Make sure it was actually inserted.
return result.first->second.get();
} else {
return it->second.get();
}
}
template <typename Context>
ThreadContext<Context>::ThreadContext(uv_loop_t *loop_) : loop(loop_) {
}
template <typename Context>
ThreadContext<Context>::~ThreadContext() {
MBGL_VERIFY_THREAD(tid);
}
}
#endif

View File

@@ -17,7 +17,8 @@
namespace mbgl {
Style::Style()
: mtx(util::make_unique<uv::rwlock>()) {
: mtx(util::make_unique<uv::rwlock>()),
workers(4) {
}
// Note: This constructor is seemingly empty, but we need to declare it anyway
@@ -86,4 +87,24 @@ void Style::loadJSON(const uint8_t *const data) {
glyph_url = parser.getGlyphURL();
}
bool Style::isLoaded() const {
// TODO: move loading into Style
// if (!loaded) {
// return false;
// }
for (const auto& source : sources) {
if (!source->isLoaded()) {
return false;
}
}
// TODO: move sprite into Style
// if (sprite && !sprite.isLoaded()) {
// return false;
// }
return true;
}
}

View File

@@ -8,6 +8,7 @@
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/worker.hpp>
#include <cstdint>
#include <string>
@@ -24,6 +25,7 @@ public:
~Style();
void loadJSON(const uint8_t *const data);
bool isLoaded() const;
void cascade(const std::vector<std::string>&);
void recalculate(float z, TimePoint now);
@@ -43,6 +45,9 @@ private:
PropertyTransition defaultTransition;
std::unique_ptr<uv::rwlock> mtx;
ZoomHistory zoomHistory;
public:
Worker workers;
};
}

View File

@@ -51,8 +51,22 @@ public:
});
}
// Invoke fn() in the runloop thread, then invoke callback() in the current thread.
template <class Fn>
void invokeWithResult(Fn&& fn, std::function<void ()> callback) {
RunLoop* outer = current.get();
assert(outer);
invoke([fn, callback, outer] {
fn();
outer->invoke(std::move(callback));
});
}
uv_loop_t* get() { return *loop; }
static uv::tls<RunLoop> current;
private:
// A movable type-erasing invokable entity wrapper. This allows to store arbitrary invokable
// things (like std::function<>, or the result of a movable-only std::bind()) in the queue.
@@ -71,8 +85,6 @@ private:
using Queue = std::queue<std::unique_ptr<Message>>;
static uv::tls<RunLoop> current;
void withMutex(std::function<void()>&&);
void process();

View File

@@ -3,6 +3,7 @@
#include <future>
#include <thread>
#include <atomic>
#include <functional>
#include <mbgl/util/run_loop.hpp>
@@ -52,7 +53,34 @@ public:
loop->invokeWithResult(std::bind(fn, object, args...), callback);
}
uv_loop_t* get() { return loop->get(); }
// Invoke object->fn(args...) in the runloop thread, then invoke callback() in the current thread.
template <typename Fn, class... Args>
void invokeWithResult(Fn fn, std::function<void ()> callback, Args&&... args) {
loop->invokeWithResult(std::bind(fn, object, args...), callback);
}
// Invoke object->fn(args...) in the runloop thread, and wait for the result.
template <class R, typename Fn, class... Args>
R invokeSync(Fn fn, Args&&... args) {
std::promise<R> promise;
auto bound = std::bind(fn, object, args...);
loop->invoke([&] { promise.set_value(bound()); } );
return promise.get_future().get();
}
// Invoke object->fn(args...) in the runloop thread, and wait for it to complete.
template <typename Fn, class... Args>
void invokeSync(Fn fn, Args&&... args) {
std::promise<void> promise;
auto bound = std::bind(fn, object, args...);
loop->invoke([&] { bound(); promise.set_value(); } );
promise.get_future().get();
}
// Join the thread, but call the given function repeatedly in the current thread
// while waiting for the join to finish. This should be immediately followed by
// destroying the Thread.
void pumpingStop(std::function<void ()>);
private:
Thread(const Thread&) = delete;
@@ -96,13 +124,20 @@ Thread<Object>::Thread(const std::string& name, Args&&... args) {
template <class Object>
template <typename P, std::size_t... I>
void Thread<Object>::run(P&& params, index_sequence<I...>) {
Object object_(std::get<I>(std::forward<P>(params))...);
object = &object_;
RunLoop loop_;
loop = &loop_;
running.set_value();
{
Object object_(loop_.get(), std::get<I>(std::forward<P>(params))...);
object = &object_;
running.set_value();
loop_.run();
object = nullptr;
}
// Run the loop again to ensure that async close callbacks have been called.
loop_.run();
joinable.get_future().get();

View File

@@ -1,8 +1,29 @@
#include <mbgl/util/uv.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/string.hpp>
#include <uv.h>
// Check libuv library version.
const static bool uvVersionCheck = []() {
const unsigned int version = uv_version();
const unsigned int major = (version >> 16) & 0xFF;
const unsigned int minor = (version >> 8) & 0xFF;
const unsigned int patch = version & 0xFF;
#ifndef UV_VERSION_PATCH
// 0.10 doesn't have UV_VERSION_PATCH defined, so we "fake" it by using the library patch level.
const unsigned int UV_VERSION_PATCH = version & 0xFF;
#endif
if (major != UV_VERSION_MAJOR || minor != UV_VERSION_MINOR || patch != UV_VERSION_PATCH) {
throw std::runtime_error(mbgl::util::sprintf<96>(
"libuv version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d", UV_VERSION_MAJOR,
UV_VERSION_MINOR, UV_VERSION_PATCH, major, minor, patch));
}
return true;
}();
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
int uv_key_create(uv_key_t* key) {

View File

@@ -104,6 +104,10 @@ public:
uv_unref(reinterpret_cast<uv_handle_t*>(a.get()));
}
inline uv_async_t* get() {
return a.get();
}
private:
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
static void async_cb(uv_async_t* a, int) {

View File

@@ -4,70 +4,26 @@
namespace mbgl {
Worker::Worker(uv_loop_t* loop, std::size_t count)
: queue(new Queue(loop, [this](Fn after) { afterWork(after); }))
{
queue->unref();
class Worker::Impl {
public:
Impl(uv_loop_t*) {}
void doWork(Fn work) {
work();
}
};
Worker::Worker(std::size_t count) {
for (std::size_t i = 0; i < count; i++) {
threads.emplace_back(&Worker::workLoop, this);
threads.emplace_back(util::make_unique<util::Thread<Impl>>("Worker"));
}
}
Worker::~Worker() {
MBGL_VERIFY_THREAD(tid);
if (active++ == 0) {
queue->ref();
}
channel.send(Work());
for (auto& thread : threads) {
thread.join();
}
queue->stop();
}
Worker::~Worker() = default;
void Worker::send(Fn work, Fn after) {
MBGL_VERIFY_THREAD(tid);
assert(work);
if (active++ == 0) {
queue->ref();
}
channel.send({work, after});
}
void Worker::workLoop() {
#ifdef __APPLE__
pthread_setname_np("Worker");
#endif
while (true) {
Work item = channel.receive();
if (!item.work)
break;
item.work();
queue->send(std::move(item.after));
}
// Make sure to close all other workers too.
channel.send(Work());
}
void Worker::afterWork(Fn after) {
if (after) {
after();
}
if (--active == 0) {
queue->unref();
}
threads[current]->invokeWithResult(&Worker::Impl::doWork, after, work);
current = (current + 1) % threads.size();
}
}

View File

@@ -2,11 +2,8 @@
#define MBGL_UTIL_WORKER
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/async_queue.hpp>
#include <mbgl/util/channel.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/thread.hpp>
#include <thread>
#include <functional>
namespace mbgl {
@@ -15,28 +12,15 @@ class Worker : public mbgl::util::noncopyable {
public:
using Fn = std::function<void ()>;
Worker(uv_loop_t* loop, std::size_t count);
Worker(std::size_t count);
~Worker();
void send(Fn work, Fn after);
private:
void workLoop();
void afterWork(Fn after);
struct Work {
Fn work;
Fn after;
};
using Queue = util::AsyncQueue<std::function<void ()>>;
std::size_t active = 0;
Queue* queue = nullptr;
Channel<Work> channel;
std::vector<std::thread> threads;
MBGL_STORE_THREAD(tid)
class Impl;
std::vector<std::unique_ptr<util::Thread<Impl>>> threads;
std::size_t current = 0;
};
}

View File

@@ -22,12 +22,10 @@ TEST(API, RepeatedRender) {
Log::setObserver(util::make_unique<FixtureLogObserver>());
Map map(view, fileSource);
map.start(Map::Mode::Still);
Map map(view, fileSource, MapMode::Still);
{
view.resize(128, 512, 1);
map.resize(128, 512, 1);
map.setStyleJSON(style, "test/suite");
std::promise<std::unique_ptr<const StillImage>> promise;
map.renderStill([&promise](std::unique_ptr<const StillImage> image) {
@@ -41,7 +39,7 @@ TEST(API, RepeatedRender) {
}
{
view.resize(512, 512, 2);
map.resize(512, 512, 2);
map.setStyleJSON(style, "TEST_DATA/suite");
std::promise<std::unique_ptr<const StillImage>> promise;
map.renderStill([&promise](std::unique_ptr<const StillImage> image) {
@@ -54,8 +52,6 @@ TEST(API, RepeatedRender) {
util::write_file("test/fixtures/api/2.png", png);
}
map.stop();
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
auto unchecked = flo->unchecked();

View File

@@ -16,13 +16,10 @@ TEST(API, SetStyle) {
Log::setObserver(util::make_unique<FixtureLogObserver>());
Map map(view, fileSource);
map.start(Map::Mode::Still);
map.setStyleJSON("invalid", "test/suite");
map.stop();
{
Map map(view, fileSource, MapMode::Still);
map.setStyleJSON("invalid", "test/suite");
}
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());

View File

@@ -86,7 +86,7 @@ TEST_P(HeadlessTest, render) {
}
}
if (source->value.HasMember("url")) {
if (source->value.HasMember("url") && source->value["url"].IsString()) {
rewriteLocalScheme(source->value["url"], styleDoc.GetAllocator());
}
}
@@ -106,12 +106,13 @@ TEST_P(HeadlessTest, render) {
Log::setObserver(util::make_unique<FixtureLogObserver>());
Log::Info(Event::General, "test fixture %s", base.c_str());
for (auto it = infoDoc.MemberBegin(), end = infoDoc.MemberEnd(); it != end; it++) {
const std::string name {
it->name.GetString(), it->name.GetStringLength()
};
Log::Info(Event::General, "test fixture %s %s", base.c_str(), name.c_str());
const rapidjson::Value& value = it->value;
ASSERT_TRUE(value.IsObject());
@@ -141,16 +142,13 @@ TEST_P(HeadlessTest, render) {
}
}
HeadlessView view(display);
HeadlessView view(display, width, height, pixelRatio);
DefaultFileSource fileSource(nullptr);
Map map(view, fileSource);
map.start(Map::Mode::Still);
Map map(view, fileSource, MapMode::Still);
map.resize(width, height, pixelRatio);
map.setClasses(classes);
map.setStyleJSON(style, "test/suite");
view.resize(width, height, pixelRatio);
map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
map.setBearing(bearing);
@@ -163,8 +161,6 @@ TEST_P(HeadlessTest, render) {
});
promise.get_future().get();
map.stop();
}
}

View File

@@ -38,8 +38,6 @@
'api/set_style.cpp',
'api/repeated_render.cpp',
'headless/headless.cpp',
'miscellaneous/clip_ids.cpp',
'miscellaneous/bilinear.cpp',
'miscellaneous/comparisons.cpp',