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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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))},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
15
include/mbgl/map/mode.hpp
Normal 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
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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&);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
288
src/mbgl/map/map_context.cpp
Normal file
288
src/mbgl/map/map_context.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
100
src/mbgl/map/map_context.hpp
Normal file
100
src/mbgl/map/map_context.hpp
Normal 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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/raster_tile_data.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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_)
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifndef BUFFER_OFFSET
|
||||
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
|
||||
#endif
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class PlainShader;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -11,10 +11,6 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#ifndef BUFFER_OFFSET
|
||||
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
|
||||
#endif
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class FillVertexBuffer;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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_)
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <mbgl/util/raster.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
|
||||
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class StyleLayoutRaster;
|
||||
|
||||
@@ -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_)
|
||||
|
||||
23
src/mbgl/storage/asset_context.hpp
Normal file
23
src/mbgl/storage/asset_context.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
30
src/mbgl/storage/http_context.cpp
Normal file
30
src/mbgl/storage/http_context.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
|
||||
virtual ~RequestBase() = default;
|
||||
virtual void cancel() = 0;
|
||||
virtual void retry() {};
|
||||
|
||||
protected:
|
||||
const Resource& resource;
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Submodule test/suite updated: 6ac1515382...fe4b1ff371
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user