summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mbgl/geometry/buffer.hpp23
-rw-r--r--src/mbgl/geometry/elements_buffer.cpp5
-rw-r--r--src/mbgl/geometry/elements_buffer.hpp10
-rw-r--r--src/mbgl/geometry/glyph_atlas.cpp53
-rw-r--r--src/mbgl/geometry/glyph_atlas.hpp5
-rw-r--r--src/mbgl/geometry/line_atlas.cpp14
-rw-r--r--src/mbgl/geometry/line_atlas.hpp5
-rw-r--r--src/mbgl/geometry/sprite_atlas.cpp13
-rw-r--r--src/mbgl/geometry/sprite_atlas.hpp8
-rw-r--r--src/mbgl/map/annotation.cpp19
-rw-r--r--src/mbgl/map/annotation.hpp7
-rw-r--r--src/mbgl/map/environment.cpp13
-rw-r--r--src/mbgl/map/live_tile_data.cpp15
-rw-r--r--src/mbgl/map/live_tile_data.hpp2
-rw-r--r--src/mbgl/map/map.cpp808
-rw-r--r--src/mbgl/map/map_context.cpp290
-rw-r--r--src/mbgl/map/map_context.hpp100
-rw-r--r--src/mbgl/map/map_data.hpp37
-rw-r--r--src/mbgl/map/raster_tile_data.cpp9
-rw-r--r--src/mbgl/map/raster_tile_data.hpp3
-rw-r--r--src/mbgl/map/source.cpp107
-rw-r--r--src/mbgl/map/source.hpp36
-rw-r--r--src/mbgl/map/sprite.cpp83
-rw-r--r--src/mbgl/map/sprite.hpp26
-rw-r--r--src/mbgl/map/tile_data.cpp11
-rw-r--r--src/mbgl/map/tile_data.hpp3
-rw-r--r--src/mbgl/map/tile_parser.cpp24
-rw-r--r--src/mbgl/map/tile_parser.hpp4
-rw-r--r--src/mbgl/map/transform.cpp24
-rw-r--r--src/mbgl/map/transform.hpp101
-rw-r--r--src/mbgl/map/transform_state.cpp8
-rw-r--r--src/mbgl/map/transform_state.hpp91
-rw-r--r--src/mbgl/map/vector_tile_data.cpp34
-rw-r--r--src/mbgl/map/vector_tile_data.hpp8
-rw-r--r--src/mbgl/map/view.cpp9
-rw-r--r--src/mbgl/renderer/bucket.hpp16
-rw-r--r--src/mbgl/renderer/debug_bucket.cpp16
-rw-r--r--src/mbgl/renderer/debug_bucket.hpp9
-rw-r--r--src/mbgl/renderer/fill_bucket.cpp28
-rw-r--r--src/mbgl/renderer/fill_bucket.hpp10
-rw-r--r--src/mbgl/renderer/gl_config.cpp19
-rw-r--r--src/mbgl/renderer/gl_config.hpp149
-rw-r--r--src/mbgl/renderer/line_bucket.cpp516
-rw-r--r--src/mbgl/renderer/line_bucket.hpp35
-rw-r--r--src/mbgl/renderer/painter.cpp356
-rw-r--r--src/mbgl/renderer/painter.hpp97
-rw-r--r--src/mbgl/renderer/painter_clipping.cpp30
-rw-r--r--src/mbgl/renderer/painter_debug.cpp23
-rw-r--r--src/mbgl/renderer/painter_fill.cpp23
-rw-r--r--src/mbgl/renderer/painter_line.cpp37
-rw-r--r--src/mbgl/renderer/painter_raster.cpp7
-rw-r--r--src/mbgl/renderer/painter_symbol.cpp18
-rw-r--r--src/mbgl/renderer/raster_bucket.cpp25
-rw-r--r--src/mbgl/renderer/raster_bucket.hpp10
-rw-r--r--src/mbgl/renderer/render_pass.hpp31
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp28
-rw-r--r--src/mbgl/renderer/symbol_bucket.hpp6
-rw-r--r--src/mbgl/shader/linejoin.fragment.glsl14
-rw-r--r--src/mbgl/shader/linejoin.vertex.glsl13
-rw-r--r--src/mbgl/shader/linejoin_shader.cpp22
-rw-r--r--src/mbgl/shader/linejoin_shader.hpp27
-rw-r--r--src/mbgl/shader/uniform.hpp4
-rw-r--r--src/mbgl/storage/asset_context.hpp23
-rw-r--r--src/mbgl/storage/asset_request.hpp24
-rw-r--r--src/mbgl/storage/default_file_source.cpp229
-rw-r--r--src/mbgl/storage/default_file_source_impl.hpp42
-rw-r--r--src/mbgl/storage/http_context.cpp30
-rw-r--r--src/mbgl/storage/http_context.hpp72
-rw-r--r--src/mbgl/storage/http_request.hpp26
-rw-r--r--src/mbgl/storage/request.cpp4
-rw-r--r--src/mbgl/storage/request_base.hpp35
-rw-r--r--src/mbgl/storage/shared_request_base.hpp106
-rw-r--r--src/mbgl/storage/thread_context.hpp78
-rw-r--r--src/mbgl/style/property_fallback.cpp1
-rw-r--r--src/mbgl/style/style.cpp23
-rw-r--r--src/mbgl/style/style.hpp5
-rw-r--r--src/mbgl/style/style_layer.cpp16
-rw-r--r--src/mbgl/style/style_layer.hpp3
-rw-r--r--src/mbgl/style/style_parser.cpp5
-rw-r--r--src/mbgl/style/style_properties.hpp1
-rw-r--r--src/mbgl/style/types.hpp4
-rw-r--r--src/mbgl/text/collision.hpp5
-rw-r--r--src/mbgl/util/mat4.cpp4
-rw-r--r--src/mbgl/util/raster.cpp30
-rw-r--r--src/mbgl/util/raster.hpp4
-rw-r--r--src/mbgl/util/run_loop.cpp16
-rw-r--r--src/mbgl/util/run_loop.hpp36
-rw-r--r--src/mbgl/util/thread.hpp65
-rw-r--r--src/mbgl/util/uv.cpp21
-rw-r--r--src/mbgl/util/uv_detail.hpp92
-rw-r--r--src/mbgl/util/worker.cpp71
-rw-r--r--src/mbgl/util/worker.hpp28
92 files changed, 2424 insertions, 2252 deletions
diff --git a/src/mbgl/geometry/buffer.hpp b/src/mbgl/geometry/buffer.hpp
index 4198425ecf..7e3ced4424 100644
--- a/src/mbgl/geometry/buffer.hpp
+++ b/src/mbgl/geometry/buffer.hpp
@@ -2,6 +2,7 @@
#define MBGL_GEOMETRY_BUFFER
#include <mbgl/platform/gl.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/map/environment.hpp>
@@ -38,17 +39,16 @@ public:
}
// Transfers this buffer to the GPU and binds the buffer to the GL context.
- void bind(bool force = false) {
- if (buffer == 0) {
+ void bind() {
+ if (buffer) {
+ MBGL_CHECK_ERROR(glBindBuffer(bufferType, buffer));
+ } else {
MBGL_CHECK_ERROR(glGenBuffers(1, &buffer));
- force = true;
- }
- MBGL_CHECK_ERROR(glBindBuffer(bufferType, buffer));
- if (force) {
+ MBGL_CHECK_ERROR(glBindBuffer(bufferType, buffer));
if (array == nullptr) {
- throw std::runtime_error("Buffer was already deleted or doesn't contain elements");
+ Log::Debug(Event::OpenGL, "Buffer doesn't contain elements");
+ pos = 0;
}
-
MBGL_CHECK_ERROR(glBufferData(bufferType, pos, array, GL_STATIC_DRAW));
if (!retainAfterUpload) {
cleanup();
@@ -67,6 +67,13 @@ public:
return buffer;
}
+ // Uploads the buffer to the GPU to be available when we need it.
+ inline void upload() {
+ if (!buffer) {
+ bind();
+ }
+ }
+
protected:
// increase the buffer size by at least /required/ bytes.
inline void *addElement() {
diff --git a/src/mbgl/geometry/elements_buffer.cpp b/src/mbgl/geometry/elements_buffer.cpp
index 79af1b7e35..3e2e2794dd 100644
--- a/src/mbgl/geometry/elements_buffer.cpp
+++ b/src/mbgl/geometry/elements_buffer.cpp
@@ -14,8 +14,3 @@ void LineElementsBuffer::add(element_type a, element_type b) {
elements[0] = a;
elements[1] = b;
}
-
-void PointElementsBuffer::add(element_type a) {
- uint16_t *data = static_cast<element_type *>(addElement());
- data[0] = a;
-}
diff --git a/src/mbgl/geometry/elements_buffer.hpp b/src/mbgl/geometry/elements_buffer.hpp
index 5c1b421d35..24753ebafe 100644
--- a/src/mbgl/geometry/elements_buffer.hpp
+++ b/src/mbgl/geometry/elements_buffer.hpp
@@ -44,16 +44,6 @@ public:
void add(element_type a, element_type b);
};
-class PointElementsBuffer : public Buffer<
- 2, // bytes per point (1 unsigned short)
- GL_ELEMENT_ARRAY_BUFFER
-> {
-public:
- typedef uint16_t element_type;
-
- void add(element_type a);
-};
-
}
#endif
diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp
index f690004b52..f2978de980 100644
--- a/src/mbgl/geometry/glyph_atlas.cpp
+++ b/src/mbgl/geometry/glyph_atlas.cpp
@@ -124,8 +124,6 @@ void GlyphAtlas::removeGlyphs(uintptr_t tileUID) {
}
}
- dirty = true;
-
bin.release(rect);
// Make sure to post-increment the iterator: This will return the
@@ -140,6 +138,47 @@ void GlyphAtlas::removeGlyphs(uintptr_t tileUID) {
}
}
+void GlyphAtlas::upload() {
+ if (dirty) {
+ const bool first = !texture;
+ bind();
+
+ std::lock_guard<std::mutex> lock(mtx);
+
+ if (first) {
+ MBGL_CHECK_ERROR(glTexImage2D(
+ GL_TEXTURE_2D, // GLenum target
+ 0, // GLint level
+ GL_ALPHA, // GLint internalformat
+ width, // GLsizei width
+ height, // GLsizei height
+ 0, // GLint border
+ GL_ALPHA, // GLenum format
+ GL_UNSIGNED_BYTE, // GLenum type
+ data.get() // const GLvoid* data
+ ));
+ } else {
+ MBGL_CHECK_ERROR(glTexSubImage2D(
+ GL_TEXTURE_2D, // GLenum target
+ 0, // GLint level
+ 0, // GLint xoffset
+ 0, // GLint yoffset
+ width, // GLsizei width
+ height, // GLsizei height
+ GL_ALPHA, // GLenum format
+ GL_UNSIGNED_BYTE, // GLenum type
+ data.get() // const GLvoid* data
+ ));
+ }
+
+ dirty = false;
+
+#if defined(DEBUG)
+ // platform::showDebugImage("Glyph Atlas", data.get(), width, height);
+#endif
+ }
+}
+
void GlyphAtlas::bind() {
if (!texture) {
MBGL_CHECK_ERROR(glGenTextures(1, &texture));
@@ -154,14 +193,4 @@ void GlyphAtlas::bind() {
} else {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
}
-
- if (dirty) {
- std::lock_guard<std::mutex> lock(mtx);
- MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data.get()));
- dirty = false;
-
-#if defined(DEBUG)
- // platform::showDebugImage("Glyph Atlas", data, width, height);
-#endif
- }
};
diff --git a/src/mbgl/geometry/glyph_atlas.hpp b/src/mbgl/geometry/glyph_atlas.hpp
index a25c735a8e..dfa568f0fd 100644
--- a/src/mbgl/geometry/glyph_atlas.hpp
+++ b/src/mbgl/geometry/glyph_atlas.hpp
@@ -24,8 +24,13 @@ public:
GlyphPositions&);
void removeGlyphs(uintptr_t tileUID);
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind();
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
const uint16_t width = 0;
const uint16_t height = 0;
diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp
index f64989d661..42d2380777 100644
--- a/src/mbgl/geometry/line_atlas.cpp
+++ b/src/mbgl/geometry/line_atlas.cpp
@@ -129,6 +129,12 @@ LinePatternPos LineAtlas::addDash(const std::vector<float> &dasharray, bool roun
return position;
};
+void LineAtlas::upload() {
+ if (dirty) {
+ bind();
+ }
+}
+
void LineAtlas::bind() {
std::lock_guard<std::recursive_mutex> lock(mtx);
@@ -147,7 +153,7 @@ void LineAtlas::bind() {
if (dirty) {
if (first) {
- glTexImage2D(
+ MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
GL_ALPHA, // GLint internalformat
@@ -157,9 +163,9 @@ void LineAtlas::bind() {
GL_ALPHA, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
data // const GLvoid * data
- );
+ ));
} else {
- glTexSubImage2D(
+ MBGL_CHECK_ERROR(glTexSubImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
0, // GLint xoffset
@@ -169,7 +175,7 @@ void LineAtlas::bind() {
GL_ALPHA, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
data // const GLvoid *pixels
- );
+ ));
}
diff --git a/src/mbgl/geometry/line_atlas.hpp b/src/mbgl/geometry/line_atlas.hpp
index df60a2dec5..3683272f98 100644
--- a/src/mbgl/geometry/line_atlas.hpp
+++ b/src/mbgl/geometry/line_atlas.hpp
@@ -19,8 +19,13 @@ public:
LineAtlas(uint16_t width, uint16_t height);
~LineAtlas();
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind();
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
LinePatternPos getDashPosition(const std::vector<float>&, bool);
LinePatternPos addDash(const std::vector<float> &dasharray, bool round);
diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp
index c2686e2f34..9d0aeac8b7 100644
--- a/src/mbgl/geometry/sprite_atlas.cpp
+++ b/src/mbgl/geometry/sprite_atlas.cpp
@@ -58,6 +58,7 @@ bool SpriteAtlas::resize(const float newRatio) {
::operator delete(old_data);
dirty = true;
+ fullUploadRequired = true;
// Mark all sprite images as in need of update
for (const auto &pair : images) {
@@ -230,8 +231,13 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) {
});
}
+void SpriteAtlas::upload() {
+ if (dirty) {
+ bind();
+ }
+}
+
void SpriteAtlas::bind(bool linear) {
- bool first = false;
if (!texture) {
MBGL_CHECK_ERROR(glGenTextures(1, &texture));
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
@@ -242,7 +248,7 @@ void SpriteAtlas::bind(bool linear) {
// We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus.
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- first = true;
+ fullUploadRequired = true;
} else {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
}
@@ -258,7 +264,7 @@ void SpriteAtlas::bind(bool linear) {
std::lock_guard<std::recursive_mutex> lock(mtx);
allocate();
- if (first) {
+ if (fullUploadRequired) {
MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
@@ -270,6 +276,7 @@ void SpriteAtlas::bind(bool linear) {
GL_UNSIGNED_BYTE, // GLenum type
data // const GLvoid * data
));
+ fullUploadRequired = false;
} else {
MBGL_CHECK_ERROR(glTexSubImage2D(
GL_TEXTURE_2D, // GLenum target
diff --git a/src/mbgl/geometry/sprite_atlas.hpp b/src/mbgl/geometry/sprite_atlas.hpp
index 079c15cefd..6c4a381aa1 100644
--- a/src/mbgl/geometry/sprite_atlas.hpp
+++ b/src/mbgl/geometry/sprite_atlas.hpp
@@ -50,10 +50,13 @@ public:
SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false);
- // Binds the image buffer of this sprite atlas to the GPU, and uploads data if it is out
- // of date.
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind(bool linear = false);
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
inline float getWidth() const { return width; }
inline float getHeight() const { return height; }
inline float getTextureWidth() const { return width * pixelRatio; }
@@ -76,6 +79,7 @@ private:
std::set<std::string> uninitialized;
uint32_t *data = nullptr;
std::atomic<bool> dirty;
+ bool fullUploadRequired = true;
uint32_t texture = 0;
uint32_t filter = 0;
static const int buffer = 1;
diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp
index b921c08f74..7a7a0ed683 100644
--- a/src/mbgl/map/annotation.cpp
+++ b/src/mbgl/map/annotation.cpp
@@ -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);
diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp
index f1596dbdff..a80b03226f 100644
--- a/src/mbgl/map/annotation.hpp
+++ b/src/mbgl/map/annotation.hpp
@@ -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);
diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp
index 469790501c..dd6c1f1933 100644
--- a/src/mbgl/map/environment.cpp
+++ b/src/mbgl/map/environment.cpp
@@ -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() {
@@ -122,13 +123,13 @@ unsigned Environment::getID() const {
void Environment::requestAsync(const Resource& resource,
std::function<void(const Response&)> callback) {
- fileSource.request(resource, *this, std::move(callback));
+ fileSource.request(resource, std::move(callback));
}
Request* Environment::request(const Resource& resource,
std::function<void(const Response&)> callback) {
assert(currentlyOn(ThreadType::Map));
- return fileSource.request(resource, loop, *this, std::move(callback));
+ return fileSource.request(resource, util::RunLoop::current.get()->get(), std::move(callback));
}
void Environment::cancelRequest(Request* req) {
@@ -178,10 +179,4 @@ void Environment::performCleanup() {
}
}
-// #############################################################################################
-
-void Environment::terminate() {
- fileSource.abort(*this);
-}
-
}
diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp
index 8b4f46deb5..1582b0190e 100644
--- a/src/mbgl/map/live_tile_data.cpp
+++ b/src/mbgl/map/live_tile_data.cpp
@@ -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) {
diff --git a/src/mbgl/map/live_tile_data.hpp b/src/mbgl/map/live_tile_data.hpp
index d40cfdfd69..e56a7bf7e1 100644
--- a/src/mbgl/map/live_tile_data.hpp
+++ b/src/mbgl/map/live_tile_data.hpp
@@ -12,7 +12,7 @@ public:
LiveTileData(const TileID&,
AnnotationManager&,
float mapMaxZoom,
- util::ptr<Style>,
+ Style&,
GlyphAtlas&,
GlyphStore&,
SpriteAtlas&,
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index e931d09e0d..0464ed3f1e 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -1,541 +1,146 @@
#include <mbgl/map/map.hpp>
-#include <mbgl/map/environment.hpp>
+#include <mbgl/map/map_context.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/map/map_data.hpp>
-#include <mbgl/map/still_image.hpp>
-#include <mbgl/platform/platform.hpp>
-#include <mbgl/map/source.hpp>
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/map/annotation.hpp>
-#include <mbgl/map/sprite.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/clip_id.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/uv_detail.hpp>
+
#include <mbgl/util/std.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/text/glyph_store.hpp>
-#include <mbgl/geometry/glyph_atlas.hpp>
-#include <mbgl/style/style_layer.hpp>
-#include <mbgl/style/style_bucket.hpp>
-#include <mbgl/util/texture_pool.hpp>
-#include <mbgl/geometry/sprite_atlas.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/platform/log.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/uv.hpp>
-#include <mbgl/util/mapbox.hpp>
-#include <mbgl/util/exception.hpp>
-#include <mbgl/util/worker.hpp>
-
-#include <algorithm>
-#include <iostream>
-
-#define _USE_MATH_DEFINES
-#include <cmath>
-
-#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;
-}();
-
-using namespace mbgl;
-
-Map::Map(View& view_, FileSource& fileSource_)
- : env(util::make_unique<Environment>(fileSource_)),
- scope(util::make_unique<EnvironmentScope>(*env, ThreadType::Main, "Main")),
- view(view_),
- transform(view_),
- fileSource(fileSource_),
- glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
- glyphStore(std::make_shared<GlyphStore>(*env)),
- spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
- lineAtlas(util::make_unique<LineAtlas>(512, 512)),
- texturePool(std::make_shared<TexturePool>()),
- painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)),
- annotationManager(util::make_unique<AnnotationManager>()),
- data(util::make_unique<MapData>()),
- updated(static_cast<UpdateType>(Update::Nothing))
+#include <mbgl/util/projection.hpp>
+#include <mbgl/util/thread.hpp>
+
+namespace mbgl {
+
+Map::Map(View& view, FileSource& fileSource, MapMode mode, bool startPaused)
+ : data(util::make_unique<MapData>(view, mode)),
+ context(util::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data, startPaused))
{
view.initialize(this);
}
Map::~Map() {
- if (mode != Mode::None) {
- stop();
- }
-
- // Extend the scope to include both Main and Map thread types to ease cleanup.
- scope.reset();
- scope = util::make_unique<EnvironmentScope>(
- *env, static_cast<ThreadType>(static_cast<uint8_t>(ThreadType::Main) |
- static_cast<uint8_t>(ThreadType::Map)),
- "MapandMain");
-
- // Explicitly reset all pointers.
- sprite.reset();
- glyphStore.reset();
- style.reset();
- workers.reset();
- painter.reset();
- annotationManager.reset();
- lineAtlas.reset();
- spriteAtlas.reset();
- glyphAtlas.reset();
-
- uv_run(env->loop, UV_RUN_DEFAULT);
-
- env->performCleanup();
-}
-
-Worker& Map::getWorker() {
- assert(workers);
- return *workers;
-}
-
-void Map::start(bool startPaused, Mode renderMode) {
- assert(Environment::currentlyOn(ThreadType::Main));
- assert(mode == Mode::None);
-
- // When starting map rendering in another thread, we perform async/continuously
- // updated rendering. Only in these cases, we attach the async handlers.
- mode = renderMode;
-
- // Reset the flag.
- isStopped = false;
-
- // Setup async notifications
- asyncTerminate = util::make_unique<uv::async>(env->loop, [this]() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- // Remove all of these to make sure they are destructed in the correct thread.
- style.reset();
-
- // It's now safe to destroy/join the workers since there won't be any more callbacks that
- // could dispatch to the worker pool.
- workers.reset();
-
- terminating = true;
-
- // Closes all open handles on the loop. This means that the loop will automatically terminate.
- asyncRender.reset();
- asyncUpdate.reset();
- asyncInvoke.reset();
- asyncTerminate.reset();
- });
-
- asyncUpdate = util::make_unique<uv::async>(env->loop, [this] {
- // Whenever we call triggerUpdate(), we ref() the asyncUpdate handle to make sure that all
- // of the calls actually get triggered.
- asyncUpdate->unref();
-
- update();
- });
-
- asyncInvoke = util::make_unique<uv::async>(env->loop, [this] {
- processTasks();
- });
-
- asyncRender = util::make_unique<uv::async>(env->loop, [this] {
- // Must be called in Map thread.
- assert(Environment::currentlyOn(ThreadType::Map));
-
- render();
-
- // Finally, notify all listeners that we have finished rendering this frame.
- {
- std::lock_guard<std::mutex> lk(mutexRendered);
- rendered = true;
- }
- condRendered.notify_all();
- });
-
- // Do we need to pause first?
- if (startPaused) {
- pause();
- }
-
- thread = std::thread([this]() {
-#ifdef __APPLE__
- pthread_setname_np("Map");
-#endif
-
- run();
-
- // Make sure that the stop() function knows when to stop invoking the callback function.
- isStopped = true;
- view.notify();
- });
-
- triggerUpdate();
-}
-
-void Map::stop(std::function<void ()> cb) {
- assert(Environment::currentlyOn(ThreadType::Main));
- assert(mode != Mode::None);
-
- asyncTerminate->send();
-
resume();
-
- if (cb) {
- // Wait until the render thread stopped. We are using this construct instead of plainly
- // relying on the thread_join because the system might need to run things in the current
- // thread that is required for the render thread to terminate correctly. This is for example
- // the case with Cocoa's NSURLRequest. Otherwise, we will eventually deadlock because this
- // thread (== main thread) is blocked. The callback function should use an efficient waiting
- // function to avoid a busy waiting loop.
- while (!isStopped) {
- cb();
- }
- }
-
- // If a callback function was provided, this should return immediately because the thread has
- // already finished executing.
- thread.join();
-
- mode = Mode::None;
}
void Map::pause(bool waitForPause) {
- assert(Environment::currentlyOn(ThreadType::Main));
- assert(mode == Mode::Continuous);
- mutexRun.lock();
- pausing = true;
- mutexRun.unlock();
+ assert(data->mode == MapMode::Continuous);
- uv_stop(env->loop);
- triggerUpdate(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
+ std::unique_lock<std::mutex> lockPause(data->mutexPause);
+ context->invoke(&MapContext::pause);
if (waitForPause) {
- std::unique_lock<std::mutex> lockPause (mutexPause);
- while (!isPaused) {
- condPause.wait(lockPause);
- }
+ data->condPaused.wait(lockPause);
}
}
void Map::resume() {
- assert(Environment::currentlyOn(ThreadType::Main));
- assert(mode != Mode::None);
-
- mutexRun.lock();
- pausing = false;
- condRun.notify_all();
- mutexRun.unlock();
-}
-
-void Map::renderStill(StillImageCallback fn) {
- assert(Environment::currentlyOn(ThreadType::Main));
-
- if (mode != Mode::Still) {
- throw util::Exception("Map is not in still image render mode");
- }
-
- if (callback) {
- throw util::Exception("Map is currently rendering an image");
- }
-
- assert(mode == Mode::Still);
-
- callback = std::move(fn);
-
- triggerUpdate(Update::RenderStill);
+ data->condResume.notify_all();
}
-void Map::run() {
- EnvironmentScope mapScope(*env, ThreadType::Map, "Map");
- assert(Environment::currentlyOn(ThreadType::Map));
- assert(mode != Mode::None);
-
- if (mode == Mode::Continuous) {
- checkForPause();
- }
-
- auto styleInfo = data->getStyleInfo();
-
- view.activate();
- view.discard();
-
- workers = util::make_unique<Worker>(env->loop, 4);
-
- setup();
- prepare();
-
- if (mode == Mode::Continuous) {
- terminating = false;
- while (!terminating) {
- uv_run(env->loop, UV_RUN_DEFAULT);
- checkForPause();
- }
- } else if (mode == Mode::Still) {
- terminating = false;
- while (!terminating) {
- uv_run(env->loop, UV_RUN_DEFAULT);
-
- // After the loop terminated, these async handles may have been deleted if the terminate()
- // callback was fired. In this case, we are exiting the loop.
- if (asyncTerminate && asyncUpdate) {
- // Otherwise, loop termination means that we have acquired and parsed all resources
- // required for this map image and we can now proceed to rendering.
- render();
- auto image = view.readStillImage();
-
- // We are moving the callback out of the way and empty it in case the callback function
- // starts the next map image render.
- assert(callback);
- StillImageCallback cb;
- std::swap(cb, callback);
-
- // Now we can finally invoke the callback function with the map image we rendered.
- cb(std::move(image));
-
- // To prepare for the next event loop run, we have to make sure the async handles keep
- // the loop alive.
- asyncTerminate->ref();
- asyncUpdate->ref();
- asyncInvoke->ref();
- asyncRender->ref();
- }
- }
- } else {
- abort();
- }
-
- // Run the event loop once more to make sure our async delete handlers are called.
- uv_run(env->loop, UV_RUN_ONCE);
-
- view.deactivate();
+void Map::renderStill(StillImageCallback callback) {
+ context->invoke(&MapContext::renderStill, callback);
}
void Map::renderSync() {
- // Must be called in UI thread.
- assert(Environment::currentlyOn(ThreadType::Main));
-
- triggerRender();
-
- std::unique_lock<std::mutex> lock(mutexRendered);
- condRendered.wait(lock, [this] { return rendered; });
- rendered = false;
+ context->invokeSync(&MapContext::render);
}
-void Map::triggerUpdate(const Update u) {
- updated |= static_cast<UpdateType>(u);
-
- if (asyncUpdate) {
- asyncUpdate->ref();
- asyncUpdate->send();
- }
+void Map::renderAsync() {
+ context->invoke(&MapContext::render);
}
-void Map::triggerRender() {
- assert(mode == Mode::Continuous);
- assert(asyncRender);
- asyncRender->send();
+void Map::update(Update update_) {
+ context->invoke(&MapContext::triggerUpdate, update_);
}
-// Runs the function in the map thread.
-void Map::invokeTask(std::function<void()>&& fn) {
- {
- std::lock_guard<std::mutex> lock(mutexTask);
- tasks.emplace(::std::forward<std::function<void()>>(fn));
- }
-
- if (asyncInvoke) {
- asyncInvoke->send();
- }
-}
-
-template <typename Fn> auto Map::invokeSyncTask(const Fn& fn) -> decltype(fn()) {
- std::promise<decltype(fn())> promise;
- invokeTask([&fn, &promise] { promise.set_value(fn()); });
- return promise.get_future().get();
-}
-
-// Processes the functions that should be run in the map thread.
-void Map::processTasks() {
- std::queue<std::function<void()>> queue;
- {
- std::lock_guard<std::mutex> lock(mutexTask);
- queue.swap(tasks);
- }
-
- while (!queue.empty()) {
- queue.front()();
- queue.pop();
- }
-}
-
-void Map::checkForPause() {
- std::unique_lock<std::mutex> lockRun (mutexRun);
- while (pausing) {
- view.deactivate();
-
- mutexPause.lock();
- isPaused = true;
- condPause.notify_all();
- mutexPause.unlock();
-
- condRun.wait(lockRun);
-
- view.activate();
- }
-
- mutexPause.lock();
- isPaused = false;
- mutexPause.unlock();
-}
-
-void Map::terminate() {
- assert(painter);
- painter->terminate();
- view.deactivate();
-}
-
-#pragma mark - Setup
-
-void Map::setup() {
- assert(Environment::currentlyOn(ThreadType::Map));
- assert(painter);
- painter->setup();
-}
-
-std::string Map::getStyleURL() const {
- return data->getStyleInfo().url;
-}
+#pragma mark - Style
void Map::setStyleURL(const std::string &url) {
- assert(Environment::currentlyOn(ThreadType::Main));
-
- const std::string styleURL = mbgl::util::mapbox::normalizeStyleURL(url, getAccessToken());
-
- const size_t pos = styleURL.rfind('/');
- std::string base = "";
- if (pos != std::string::npos) {
- base = styleURL.substr(0, pos + 1);
- }
-
- data->setStyleInfo({ styleURL, base, "" });
- triggerUpdate(Update::StyleInfo);
+ context->invoke(&MapContext::setStyleURL, url);
}
void Map::setStyleJSON(const std::string& json, const std::string& base) {
- assert(Environment::currentlyOn(ThreadType::Main));
-
- data->setStyleInfo({ "", base, json });
- triggerUpdate(Update::StyleInfo);
+ context->invoke(&MapContext::setStyleJSON, json, base);
}
-std::string Map::getStyleJSON() const {
- return data->getStyleInfo().json;
+std::string Map::getStyleURL() const {
+ return context->invokeSync<std::string>(&MapContext::getStyleURL);
}
-util::ptr<Sprite> Map::getSprite() {
- const float pixelRatio = state.getPixelRatio();
- const std::string &sprite_url = style->getSpriteURL();
- if (!sprite || !sprite->hasPixelRatio(pixelRatio)) {
- sprite = Sprite::Create(sprite_url, pixelRatio, *env);
- }
-
- return sprite;
+std::string Map::getStyleJSON() const {
+ return context->invokeSync<std::string>(&MapContext::getStyleJSON);
}
-
#pragma mark - Size
void Map::resize(uint16_t width, uint16_t height, float ratio) {
- resize(width, height, ratio, width * ratio, height * ratio);
-}
-
-void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
- if (transform.resize(width, height, ratio, fbWidth, fbHeight)) {
- triggerUpdate();
+ if (data->transform.resize(width, height, ratio, width * ratio, height * ratio)) {
+ context->invoke(&MapContext::resize, width, height, ratio);
}
}
#pragma mark - Transitions
void Map::cancelTransitions() {
- transform.cancelTransitions();
- triggerUpdate();
+ data->transform.cancelTransitions();
+ update();
}
void Map::setGestureInProgress(bool inProgress) {
- transform.setGestureInProgress(inProgress);
- triggerUpdate();
+ data->transform.setGestureInProgress(inProgress);
+ update();
}
#pragma mark - Position
void Map::moveBy(double dx, double dy, Duration duration) {
- transform.moveBy(dx, dy, duration);
- triggerUpdate();
+ data->transform.moveBy(dx, dy, duration);
+ update();
}
void Map::setLatLng(LatLng latLng, Duration duration) {
- transform.setLatLng(latLng, duration);
- triggerUpdate();
+ data->transform.setLatLng(latLng, duration);
+ update();
}
LatLng Map::getLatLng() const {
- return transform.getLatLng();
+ return data->transform.getLatLng();
}
void Map::resetPosition() {
- transform.setAngle(0);
- transform.setLatLng(LatLng(0, 0));
- transform.setZoom(0);
- triggerUpdate(Update::Zoom);
+ data->transform.setAngle(0);
+ data->transform.setLatLng(LatLng(0, 0));
+ data->transform.setZoom(0);
+ update(Update::Zoom);
}
#pragma mark - Scale
void Map::scaleBy(double ds, double cx, double cy, Duration duration) {
- transform.scaleBy(ds, cx, cy, duration);
- triggerUpdate(Update::Zoom);
+ data->transform.scaleBy(ds, cx, cy, duration);
+ update(Update::Zoom);
}
void Map::setScale(double scale, double cx, double cy, Duration duration) {
- transform.setScale(scale, cx, cy, duration);
- triggerUpdate(Update::Zoom);
+ data->transform.setScale(scale, cx, cy, duration);
+ update(Update::Zoom);
}
double Map::getScale() const {
- return transform.getScale();
+ return data->transform.getScale();
}
void Map::setZoom(double zoom, Duration duration) {
- transform.setZoom(zoom, duration);
- triggerUpdate(Update::Zoom);
+ data->transform.setZoom(zoom, duration);
+ update(Update::Zoom);
}
double Map::getZoom() const {
- return transform.getZoom();
+ return data->transform.getZoom();
}
void Map::setLatLngZoom(LatLng latLng, double zoom, Duration duration) {
- transform.setLatLngZoom(latLng, zoom, duration);
- triggerUpdate(Update::Zoom);
+ data->transform.setLatLngZoom(latLng, zoom, duration);
+ update(Update::Zoom);
}
void Map::resetZoom() {
@@ -543,40 +148,52 @@ void Map::resetZoom() {
}
double Map::getMinZoom() const {
- return transform.getMinZoom();
+ return data->transform.getMinZoom();
}
double Map::getMaxZoom() const {
- return transform.getMaxZoom();
+ return data->transform.getMaxZoom();
+}
+
+
+#pragma mark - Size
+
+uint16_t Map::getWidth() const {
+ return data->transform.currentState().getWidth();
+}
+
+uint16_t Map::getHeight() const {
+ return data->transform.currentState().getHeight();
}
#pragma mark - Rotation
void Map::rotateBy(double sx, double sy, double ex, double ey, Duration duration) {
- transform.rotateBy(sx, sy, ex, ey, duration);
- triggerUpdate();
+ data->transform.rotateBy(sx, sy, ex, ey, duration);
+ update();
}
void Map::setBearing(double degrees, Duration duration) {
- transform.setAngle(-degrees * M_PI / 180, duration);
- triggerUpdate();
+ data->transform.setAngle(-degrees * M_PI / 180, duration);
+ update();
}
void Map::setBearing(double degrees, double cx, double cy) {
- transform.setAngle(-degrees * M_PI / 180, cx, cy);
- triggerUpdate();
+ data->transform.setAngle(-degrees * M_PI / 180, cx, cy);
+ update();
}
double Map::getBearing() const {
- return -transform.getAngle() / M_PI * 180;
+ return -data->transform.getAngle() / M_PI * 180;
}
void Map::resetNorth() {
- transform.setAngle(0, std::chrono::milliseconds(500));
- triggerUpdate();
+ data->transform.setAngle(0, std::chrono::milliseconds(500));
+ update();
}
+
#pragma mark - Access Token
void Map::setAccessToken(const std::string &token) {
@@ -587,22 +204,45 @@ std::string Map::getAccessToken() const {
return data->getAccessToken();
}
+
+#pragma mark - Projection
+
+void Map::getWorldBoundsMeters(ProjectedMeters& sw, ProjectedMeters& ne) const {
+ Projection::getWorldBoundsMeters(sw, ne);
+}
+
+void Map::getWorldBoundsLatLng(LatLng& sw, LatLng& ne) const {
+ Projection::getWorldBoundsLatLng(sw, ne);
+}
+
+double Map::getMetersPerPixelAtLatitude(const double lat, const double zoom) const {
+ return Projection::getMetersPerPixelAtLatitude(lat, zoom);
+}
+
+const ProjectedMeters Map::projectedMetersForLatLng(const LatLng latLng) const {
+ return Projection::projectedMetersForLatLng(latLng);
+}
+
+const LatLng Map::latLngForProjectedMeters(const ProjectedMeters projectedMeters) const {
+ return Projection::latLngForProjectedMeters(projectedMeters);
+}
+
+const vec2<double> Map::pixelForLatLng(const LatLng latLng) const {
+ return data->transform.currentState().pixelForLatLng(latLng);
+}
+
+const LatLng Map::latLngForPixel(const vec2<double> pixel) const {
+ return data->transform.currentState().latLngForPixel(pixel);
+}
+
#pragma mark - Annotations
void Map::setDefaultPointAnnotationSymbol(const std::string& symbol) {
- assert(Environment::currentlyOn(ThreadType::Main));
- invokeTask([=] {
- annotationManager->setDefaultPointAnnotationSymbol(symbol);
- });
+ data->annotationManager.setDefaultPointAnnotationSymbol(symbol);
}
double Map::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
- assert(Environment::currentlyOn(ThreadType::Main));
- return invokeSyncTask([&] {
- assert(sprite);
- const SpritePosition pos = sprite->getSpritePosition(symbol);
- return -pos.height / pos.pixelRatio / 2;
- });
+ return context->invokeSync<double>(&MapContext::getTopOffsetPixelsForAnnotationSymbol, symbol);
}
uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol) {
@@ -610,87 +250,60 @@ uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol)
}
std::vector<uint32_t> Map::addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols) {
- assert(Environment::currentlyOn(ThreadType::Main));
- return invokeSyncTask([&] {
- auto result = annotationManager->addPointAnnotations(points, symbols, *this);
- updateAnnotationTiles(result.first);
- return result.second;
- });
+ auto result = data->annotationManager.addPointAnnotations(points, symbols, *data);
+ context->invoke(&MapContext::updateAnnotationTiles, result.first);
+ return result.second;
}
void Map::removeAnnotation(uint32_t annotation) {
- assert(Environment::currentlyOn(ThreadType::Main));
removeAnnotations({ annotation });
}
void Map::removeAnnotations(const std::vector<uint32_t>& annotations) {
- assert(Environment::currentlyOn(ThreadType::Main));
- invokeTask([=] {
- auto result = annotationManager->removeAnnotations(annotations, *this);
- updateAnnotationTiles(result);
- });
+ auto result = data->annotationManager.removeAnnotations(annotations, *data);
+ context->invoke(&MapContext::updateAnnotationTiles, result);
}
std::vector<uint32_t> Map::getAnnotationsInBounds(const LatLngBounds& bounds) {
- assert(Environment::currentlyOn(ThreadType::Main));
- return invokeSyncTask([&] {
- return annotationManager->getAnnotationsInBounds(bounds, *this);
- });
+ return data->annotationManager.getAnnotationsInBounds(bounds, *data);
}
LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotations) {
- assert(Environment::currentlyOn(ThreadType::Main));
- return invokeSyncTask([&] {
- return annotationManager->getBoundsForAnnotations(annotations);
- });
-}
-
-void Map::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();
+ return data->annotationManager.getBoundsForAnnotations(annotations);
}
+
#pragma mark - Toggles
void Map::setDebug(bool value) {
data->setDebug(value);
- triggerUpdate(Update::Debug);
+ update(Update::Debug);
}
void Map::toggleDebug() {
data->toggleDebug();
- triggerUpdate(Update::Debug);
+ update(Update::Debug);
}
bool Map::getDebug() const {
return data->getDebug();
}
-TimePoint Map::getTime() const {
- return data->getAnimationTime();
-}
-
void Map::addClass(const std::string& klass) {
if (data->addClass(klass)) {
- triggerUpdate(Update::Classes);
+ update(Update::Classes);
}
}
void Map::removeClass(const std::string& klass) {
if (data->removeClass(klass)) {
- triggerUpdate(Update::Classes);
+ update(Update::Classes);
}
}
void Map::setClasses(const std::vector<std::string>& classes) {
data->setClasses(classes);
- triggerUpdate(Update::Classes);
+ update(Update::Classes);
}
bool Map::hasClass(const std::string& klass) const {
@@ -702,185 +315,20 @@ std::vector<std::string> Map::getClasses() const {
}
void Map::setDefaultTransitionDuration(Duration duration) {
- assert(Environment::currentlyOn(ThreadType::Main));
-
data->setDefaultTransitionDuration(duration);
- triggerUpdate(Update::DefaultTransitionDuration);
+ update(Update::DefaultTransitionDuration);
}
Duration Map::getDefaultTransitionDuration() {
- assert(Environment::currentlyOn(ThreadType::Main));
return data->getDefaultTransitionDuration();
}
-void Map::updateTiles() {
- assert(Environment::currentlyOn(ThreadType::Map));
- if (!style) return;
- for (const auto& source : style->sources) {
- source->update(*this, getWorker(), style, *glyphAtlas, *glyphStore,
- *spriteAtlas, getSprite(), *texturePool, [this]() {
- assert(Environment::currentlyOn(ThreadType::Map));
- triggerUpdate();
- });
- }
-}
-
-void Map::update() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- if (state.hasSize()) {
- prepare();
- }
-}
-
-void Map::reloadStyle() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- style = std::make_shared<Style>();
-
- const auto styleInfo = data->getStyleInfo();
-
- if (!styleInfo.url.empty()) {
- const auto base = styleInfo.base;
- // We have a style URL
- env->request({ Resource::Kind::JSON, styleInfo.url }, [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());
- }
- });
- } else if (!styleInfo.json.empty()) {
- // We got JSON data directly.
- loadStyleJSON(styleInfo.json, styleInfo.base);
- }
-}
-
-void Map::loadStyleJSON(const std::string& json, const std::string& base) {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- sprite.reset();
- style = std::make_shared<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, getAccessToken());
- glyphStore->setURL(glyphURL);
-
- for (const auto& source : style->sources) {
- source->load(getAccessToken(), *env, [this]() {
- assert(Environment::currentlyOn(ThreadType::Map));
- triggerUpdate();
- });
- }
-
- triggerUpdate(Update::Zoom);
-}
-
-void Map::prepare() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- const auto now = Clock::now();
- data->setAnimationTime(now);
-
- auto u = updated.exchange(static_cast<UpdateType>(Update::Nothing)) |
- transform.updateTransitions(now);
-
- if (!style) {
- u |= static_cast<UpdateType>(Update::StyleInfo);
- }
-
- state = transform.currentState();
-
- if (u & static_cast<UpdateType>(Update::StyleInfo)) {
- reloadStyle();
- }
-
- if (u & static_cast<UpdateType>(Update::Debug)) {
- assert(painter);
- painter->setDebug(data->getDebug());
- }
-
- if (u & static_cast<UpdateType>(Update::RenderStill)) {
- // Triggers a view resize.
- view.discard();
-
- // Whenever we trigger an image render, we are unrefing all async handles so the loop will
- // eventually terminate. However, it'll stay alive as long as there are pending requests
- // (like work requests or HTTP requests).
- asyncTerminate->unref();
- asyncUpdate->unref();
- asyncInvoke->unref();
- asyncRender->unref();
- }
-
- if (style) {
- if (u & static_cast<UpdateType>(Update::DefaultTransitionDuration)) {
- style->setDefaultTransitionDuration(data->getDefaultTransitionDuration());
- }
-
- if (u & static_cast<UpdateType>(Update::Classes)) {
- style->cascade(data->getClasses());
- }
-
- if (u & static_cast<UpdateType>(Update::StyleInfo) ||
- u & static_cast<UpdateType>(Update::Classes) ||
- u & static_cast<UpdateType>(Update::Zoom)) {
- style->recalculate(state.getNormalizedZoom(), now);
- }
-
- // Allow the sprite atlas to potentially pull new sprite images if needed.
- spriteAtlas->resize(state.getPixelRatio());
- spriteAtlas->setSprite(getSprite());
-
- updateTiles();
- }
-
- if (mode == Mode::Continuous) {
- view.invalidate();
- }
+void Map::setSourceTileCacheSize(size_t size) {
+ context->invoke(&MapContext::setSourceTileCacheSize, size);
}
-void Map::render() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- view.discard();
-
- // Cleanup OpenGL objects that we abandoned since the last render call.
- env->performCleanup();
-
- assert(style);
- assert(painter);
-
- painter->render(*style, state, data->getAnimationTime());
-
- // Schedule another rerender when we definitely need a next frame.
- if (transform.needsTransition() || style->hasTransitions()) {
- triggerUpdate();
- }
+void Map::onLowMemory() {
+ context->invoke(&MapContext::onLowMemory);
}
-void Map::setSourceTileCacheSize(size_t size) {
- if (size != getSourceTileCacheSize()) {
- invokeTask([=] {
- sourceCacheSize = size;
- if (!style) return;
- for (const auto &source : style->sources) {
- source->setCacheSize(sourceCacheSize);
- }
- env->performCleanup();
- });
- }
}
-
-void Map::onLowMemory() {
- invokeTask([=] {
- if (!style) return;
- for (const auto &source : style->sources) {
- source->onLowMemory();
- }
- env->performCleanup();
- });
-};
diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp
new file mode 100644
index 0000000000..e72a174801
--- /dev/null
+++ b/src/mbgl/map/map_context.cpp
@@ -0,0 +1,290 @@
+#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.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();
+}
+
+}
diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp
new file mode 100644
index 0000000000..93670fd1f2
--- /dev/null
+++ b/src/mbgl/map/map_context.hpp
@@ -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
diff --git a/src/mbgl/map/map_data.hpp b/src/mbgl/map/map_data.hpp
index 718c3eb0e0..86a22e123b 100644
--- a/src/mbgl/map/map_data.hpp
+++ b/src/mbgl/map/map_data.hpp
@@ -7,33 +7,26 @@
#include <mutex>
#include <atomic>
#include <vector>
+#include <cassert>
+#include <condition_variable>
-namespace mbgl {
+#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>
-struct StyleInfo {
- std::string url;
- std::string base;
- std::string json;
-};
+namespace mbgl {
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;
};
}
diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp
index 2531f5130b..0b849ca7cb 100644
--- a/src/mbgl/map/raster_tile_data.cpp
+++ b/src/mbgl/map/raster_tile_data.cpp
@@ -1,4 +1,3 @@
-#include <mbgl/map/map.hpp>
#include <mbgl/map/raster_tile_data.hpp>
#include <mbgl/style/style.hpp>
@@ -24,10 +23,6 @@ void RasterTileData::parse() {
}
}
-void RasterTileData::render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) {
- bucket.render(painter, layer_desc, id, matrix);
-}
-
-bool RasterTileData::hasData(StyleLayer const& /*layer_desc*/) const {
- return bucket.hasData();
+Bucket* RasterTileData::getBucket(StyleLayer const&) {
+ return &bucket;
}
diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp
index dab2b7b842..45aee8f74c 100644
--- a/src/mbgl/map/raster_tile_data.hpp
+++ b/src/mbgl/map/raster_tile_data.hpp
@@ -20,8 +20,7 @@ public:
~RasterTileData();
void parse() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) override;
- bool hasData(StyleLayer const &layer_desc) const override;
+ Bucket* getBucket(StyleLayer const &layer_desc) override;
protected:
StyleLayoutRaster layout;
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index 9e318964b0..708ff6b27a 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -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;
}
@@ -174,16 +184,6 @@ void Source::drawClippingMasks(Painter &painter) {
}
}
-void Source::render(Painter &painter, const StyleLayer &layer_desc) {
- gl::group group(std::string { "layer: " } + layer_desc.id);
- for (const auto& pair : tiles) {
- Tile &tile = *pair.second;
- if (tile.data && tile.data->state == TileData::State::parsed) {
- painter.renderTileLayer(tile, layer_desc, tile.matrix);
- }
- }
-}
-
void Source::finishRender(Painter &painter) {
for (const auto& pair : tiles) {
Tile &tile = *pair.second;
@@ -202,6 +202,9 @@ std::forward_list<Tile *> Source::getLoadedTiles() const {
return ptrs;
}
+const std::vector<Tile*>& Source::getTiles() const {
+ return tilePtrs;
+}
TileData::State Source::hasTile(const TileID& id) {
auto it = tiles.find(id);
@@ -215,11 +218,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 +260,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 +359,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 +386,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 +406,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 +448,9 @@ void Source::update(Map &map,
}
});
- updated = map.getTime();
+ updateTilePtrs();
+
+ updated = data.getAnimationTime();
}
void Source::invalidateTiles(const std::vector<TileID>& ids) {
@@ -450,6 +459,14 @@ void Source::invalidateTiles(const std::vector<TileID>& ids) {
tiles.erase(id);
tile_data.erase(id);
}
+ updateTilePtrs();
+}
+
+void Source::updateTilePtrs() {
+ tilePtrs.clear();
+ for (const auto& pair : tiles) {
+ tilePtrs.push_back(pair.second.get());
+ }
}
void Source::setCacheSize(size_t size) {
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index 035e862284..b15de0e642 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -20,9 +20,8 @@
namespace mbgl {
-class Map;
+class MapData;
class Environment;
-class Worker;
class GlyphAtlas;
class GlyphStore;
class SpriteAtlas;
@@ -30,7 +29,6 @@ class Sprite;
class TexturePool;
class Style;
class Painter;
-class StyleLayer;
class TransformState;
class Tile;
struct ClipID;
@@ -60,18 +58,27 @@ public:
void load(const std::string& accessToken,
Environment&,
std::function<void()> callback);
-
- void update(Map &, Worker &, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
- SpriteAtlas &, util::ptr<Sprite>, TexturePool &, std::function<void()> callback);
+ bool isLoaded() const;
+
+ 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>&);
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);
- void render(Painter &painter, const StyleLayer &layer_desc);
void finishRender(Painter &painter);
std::forward_list<Tile *> getLoadedTiles() const;
+ const std::vector<Tile*>& getTiles() const;
void setCacheSize(size_t);
void onLowMemory();
@@ -85,11 +92,19 @@ 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);
+ void updateTilePtrs();
double getZoom(const TransformState &state) const;
@@ -99,6 +114,7 @@ private:
TimePoint updated = TimePoint::min();
std::map<TileID, std::unique_ptr<Tile>> tiles;
+ std::vector<Tile*> tilePtrs;
std::map<TileID, std::weak_ptr<TileData>> tile_data;
TileCache cache;
};
diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp
index 8883ee092d..75e5f845c4 100644
--- a/src/mbgl/map/sprite.cpp
+++ b/src/mbgl/map/sprite.cpp
@@ -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()) {
-}
-
-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();
-}
-
+ future(promise.get_future()),
+ callback(callback_),
+ env(env_) {
-// 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) {
diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp
index 6c1b3ba8e3..bd6ae89bc6 100644
--- a/src/mbgl/map/sprite.hpp
+++ b/src/mbgl/map/sprite.hpp
@@ -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;
};
}
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index bb8d18d12f..80fd6346d3 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -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();
});
}
diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp
index 92d9778fd5..b85db7db78 100644
--- a/src/mbgl/map/tile_data.hpp
+++ b/src/mbgl/map/tile_data.hpp
@@ -47,8 +47,7 @@ public:
// Override this in the child class.
virtual void parse() = 0;
- virtual void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) = 0;
- virtual bool hasData(StyleLayer const &layer_desc) const = 0;
+ virtual Bucket* getBucket(StyleLayer const &layer_desc) = 0;
const TileID id;
const std::string name;
diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp
index 275b77be91..1438bcddaa 100644
--- a/src/mbgl/map/tile_parser.cpp
+++ b/src/mbgl/map/tile_parser.cpp
@@ -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;
@@ -166,14 +157,13 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& la
tile.triangleElementsBuffer,
tile.lineElementsBuffer);
addBucketGeometries(bucket, layer, bucket_desc.filter);
- return std::move(bucket);
+ return bucket->hasData() ? std::move(bucket) : nullptr;
}
std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
auto bucket = util::make_unique<LineBucket>(tile.lineVertexBuffer,
- tile.triangleElementsBuffer,
- tile.pointElementsBuffer);
+ tile.triangleElementsBuffer);
const float z = tile.id.z;
auto& layout = bucket->layout;
@@ -184,7 +174,7 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& la
applyLayoutProperty(PropertyKey::LineRoundLimit, bucket_desc.layout, layout.round_limit, z);
addBucketGeometries(bucket, layer, bucket_desc.filter);
- return std::move(bucket);
+ return bucket->hasData() ? std::move(bucket) : nullptr;
}
std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& layer,
@@ -234,6 +224,6 @@ std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer&
bucket->addFeatures(
layer, bucket_desc.filter, reinterpret_cast<uintptr_t>(&tile), spriteAtlas, *sprite, glyphAtlas, glyphStore);
- return std::move(bucket);
+ return bucket->hasData() ? std::move(bucket) : nullptr;
}
}
diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp
index 2c16d2a2fd..2dbb8cb17f 100644
--- a/src/mbgl/map/tile_parser.hpp
+++ b/src/mbgl/map/tile_parser.hpp
@@ -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;
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index cb73915e36..ffe7422962 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -199,14 +199,14 @@ void Transform::_setScale(double new_scale, double cx, double cy, const Duration
// Zoom in on the center if we don't have click or gesture anchor coordinates.
if (cx < 0 || cy < 0) {
- cx = current.width / 2;
- cy = current.height / 2;
+ cx = static_cast<double>(current.width) / 2.0;
+ cy = static_cast<double>(current.height) / 2.0;
}
// Account for the x/y offset from the center (= where the user clicked or pinched)
const double factor = new_scale / current.scale;
- const double dx = (cx - current.width / 2) * (1.0 - factor);
- const double dy = (cy - current.height / 2) * (1.0 - factor);
+ const double dx = (cx - static_cast<double>(current.width) / 2.0) * (1.0 - factor);
+ const double dy = (cy - static_cast<double>(current.height) / 2.0) * (1.0 - factor);
// Account for angle
const double angle_sin = std::sin(-current.angle);
@@ -238,6 +238,9 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
current.scale = final.scale;
current.x = final.x;
current.y = final.y;
+ const double s = current.scale * util::tileSize;
+ current.Bc = s / 360;
+ current.Cc = s / util::M2PI;
} else {
const double startS = current.scale;
const double startX = current.x;
@@ -250,6 +253,9 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
current.scale = util::interpolate(startS, final.scale, t);
current.x = util::interpolate(startX, final.x, t);
current.y = util::interpolate(startY, final.y, t);
+ const double s = current.scale * util::tileSize;
+ current.Bc = s / 360;
+ current.Cc = s / util::M2PI;
return Update::Zoom;
},
[=] {
@@ -258,10 +264,6 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
}, duration);
}
- const double s = final.scale * util::tileSize;
- current.Bc = s / 360;
- current.Cc = s / util::M2PI;
-
view.notifyMapChange(duration != Duration::zero() ?
MapChangeRegionDidChangeAnimated :
MapChangeRegionDidChange,
@@ -287,7 +289,7 @@ void Transform::rotateBy(const double start_x, const double start_y, const doubl
const double end_y, const Duration duration) {
std::lock_guard<std::recursive_mutex> lock(mtx);
- double center_x = current.width / 2, center_y = current.height / 2;
+ double center_x = static_cast<double>(current.width) / 2.0, center_y = static_cast<double>(current.height) / 2.0;
const double begin_center_x = start_x - center_x;
const double begin_center_y = start_y - center_y;
@@ -326,8 +328,8 @@ void Transform::setAngle(const double new_angle, const double cx, const double c
double dx = 0, dy = 0;
if (cx >= 0 && cy >= 0) {
- dx = (final.width / 2) - cx;
- dy = (final.height / 2) - cy;
+ dx = (static_cast<double>(final.width) / 2.0) - cx;
+ dy = (static_cast<double>(final.height) / 2.0) - cy;
_moveBy(dx, dy, Duration::zero());
}
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
new file mode 100644
index 0000000000..ef89a4eefa
--- /dev/null
+++ b/src/mbgl/map/transform.hpp
@@ -0,0 +1,101 @@
+#ifndef MBGL_MAP_TRANSFORM
+#define MBGL_MAP_TRANSFORM
+
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/map/update.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/vec.hpp>
+
+#include <cstdint>
+#include <cmath>
+#include <forward_list>
+#include <mutex>
+
+namespace mbgl {
+
+class View;
+namespace util { class transition; }
+
+class Transform : private util::noncopyable {
+public:
+ Transform(View &view);
+
+ // Map view
+ // Note: width * ratio does not necessarily equal fb_width
+ bool resize(uint16_t width, uint16_t height, float ratio,
+ uint16_t fb_width, uint16_t fb_height);
+
+ // Position
+ void moveBy(double dx, double dy, Duration = Duration::zero());
+ void setLatLng(LatLng latLng, Duration = Duration::zero());
+ void setLatLngZoom(LatLng latLng, double zoom, Duration = Duration::zero());
+ inline const LatLng getLatLng() const { return current.getLatLng(); }
+
+ // Zoom
+ void scaleBy(double ds, double cx = -1, double cy = -1, Duration = Duration::zero());
+ void setScale(double scale, double cx = -1, double cy = -1, Duration = Duration::zero());
+ void setZoom(double zoom, Duration = Duration::zero());
+ double getZoom() const;
+ double getScale() const;
+ double getMinZoom() const;
+ double getMaxZoom() const;
+
+ // Angle
+ void rotateBy(double sx, double sy, double ex, double ey, Duration = Duration::zero());
+ void setAngle(double angle, Duration = Duration::zero());
+ void setAngle(double angle, double cx, double cy);
+ double getAngle() const;
+
+ // Transitions
+ bool needsTransition() const;
+ UpdateType updateTransitions(TimePoint now);
+ void cancelTransitions();
+
+ // Gesture
+ void setGestureInProgress(bool);
+
+ // Transform state
+ const TransformState currentState() const;
+ const TransformState finalState() const;
+
+private:
+ // Functions prefixed with underscores will *not* perform any locks. It is the caller's
+ // responsibility to lock this object.
+ void _moveBy(double dx, double dy, Duration = Duration::zero());
+ void _setScale(double scale, double cx, double cy, Duration = Duration::zero());
+ void _setScaleXY(double new_scale, double xn, double yn, Duration = Duration::zero());
+ void _setAngle(double angle, Duration = Duration::zero());
+
+ void constrain(double& scale, double& y) const;
+
+ View &view;
+
+ mutable std::recursive_mutex mtx;
+
+ // This reflects the current state of the transform, representing the actual position of the
+ // map. After calling a transform function with a timer, this will likely remain the same until
+ // you render a new frame.
+ TransformState current;
+
+ // This reflects the final position of the transform, after all possible transition took place.
+ TransformState final;
+
+ // Limit the amount of zooming possible on the map.
+ const double min_scale = std::pow(2, 0);
+ const double max_scale = std::pow(2, 18);
+
+ void startTransition(std::function<Update(double)> frame,
+ std::function<void()> finish,
+ Duration);
+
+ TimePoint transitionStart;
+ Duration transitionDuration;
+ std::function<Update(TimePoint)> transitionFrameFn;
+ std::function<void()> transitionFinishFn;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index 507e63f67e..2a1cc4b9ea 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -31,8 +31,8 @@ box TransformState::cornersToBox(uint32_t z) const {
const double angle_sin = std::sin(-angle);
const double angle_cos = std::cos(-angle);
- const double w_2 = width / 2;
- const double h_2 = height / 2;
+ const double w_2 = static_cast<double>(width) / 2.0;
+ const double h_2 = static_cast<double>(height) / 2.0;
const double ss_0 = scale * util::tileSize;
const double ss_1 = ref_scale / ss_0;
const double ss_2 = ss_0 / 2.0;
@@ -166,8 +166,8 @@ const vec2<double> TransformState::pixelForLatLng(const LatLng latLng) const {
LatLng ll = getLatLng();
double zoom = getZoom();
- const double centerX = width / 2;
- const double centerY = height / 2;
+ const double centerX = static_cast<double>(width) / 2.0;
+ const double centerY = static_cast<double>(height) / 2.0;
const double m = Projection::getMetersPerPixelAtLatitude(0, zoom);
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
new file mode 100644
index 0000000000..f6a00a4a3d
--- /dev/null
+++ b/src/mbgl/map/transform_state.hpp
@@ -0,0 +1,91 @@
+#ifndef MBGL_MAP_TRANSFORM_STATE
+#define MBGL_MAP_TRANSFORM_STATE
+
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/vec.hpp>
+
+#include <cstdint>
+#include <array>
+#include <limits>
+
+namespace mbgl {
+
+class TileID;
+struct box;
+
+class TransformState {
+ friend class Transform;
+
+public:
+ // Matrix
+ void matrixFor(mat4& matrix, const TileID& id) const;
+ box cornersToBox(uint32_t z) const;
+
+ // Dimensions
+ bool hasSize() const;
+ uint16_t getWidth() const;
+ uint16_t getHeight() const;
+ uint16_t getFramebufferWidth() const;
+ uint16_t getFramebufferHeight() const;
+ const std::array<uint16_t, 2> getFramebufferDimensions() const;
+ float getPixelRatio() const;
+
+ float worldSize() const;
+ float lngX(float lon) const;
+ float latY(float lat) const;
+ std::array<float, 2> locationCoordinate(float lon, float lat) const;
+ void getLonLat(double &lon, double &lat) const;
+
+ // Position
+ const LatLng getLatLng() const;
+
+ // Zoom
+ float getNormalizedZoom() const;
+ double getZoom() const;
+ int32_t getIntegerZoom() const;
+ double getZoomFraction() const;
+ double getScale() const;
+
+ // Rotation
+ float getAngle() const;
+
+ // Projection
+ const vec2<double> pixelForLatLng(const LatLng latLng) const;
+ const LatLng latLngForPixel(const vec2<double> pixel) const;
+
+ // Changing
+ bool isChanging() const;
+
+private:
+ double pixel_x() const;
+ double pixel_y() const;
+
+private:
+ // logical dimensions
+ uint16_t width = 0, height = 0;
+
+ // physical (framebuffer) dimensions
+ std::array<uint16_t, 2> framebuffer = {{ 0, 0 }};
+
+ // map scale factor
+ float pixelRatio = 0;
+
+ // cache values for spherical mercator math
+ double Bc, Cc;
+
+ // animation state
+ bool rotating = false;
+ bool scaling = false;
+ bool panning = false;
+ bool gestureInProgress = false;
+
+ // map position
+ double x = 0, y = 0;
+ double angle = 0;
+ double scale = 1;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index 39c2d9fce3..4c6c1150bd 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -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());
@@ -63,23 +55,13 @@ void VectorTileData::parse() {
}
}
-void VectorTileData::render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) {
- if (state == State::parsed && layer_desc.bucket) {
- auto databucket_it = buckets.find(layer_desc.bucket->name);
- if (databucket_it != buckets.end()) {
- assert(databucket_it->second);
- databucket_it->second->render(painter, layer_desc, id, matrix);
- }
- }
-}
-
-bool VectorTileData::hasData(const StyleLayer &layer_desc) const {
- if (state == State::parsed && layer_desc.bucket) {
- auto databucket_it = buckets.find(layer_desc.bucket->name);
- if (databucket_it != buckets.end()) {
- assert(databucket_it->second);
- return databucket_it->second->hasData();
+Bucket* VectorTileData::getBucket(StyleLayer const& layer) {
+ if (state == State::parsed && layer.bucket) {
+ const auto it = buckets.find(layer.bucket->name);
+ if (it != buckets.end()) {
+ assert(it->second);
+ return it->second.get();
}
}
- return false;
+ return nullptr;
}
diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp
index 4e2f252f85..10eaf9c184 100644
--- a/src/mbgl/map/vector_tile_data.hpp
+++ b/src/mbgl/map/vector_tile_data.hpp
@@ -31,7 +31,7 @@ class VectorTileData : public TileData {
public:
VectorTileData(const TileID&,
float mapMaxZoom,
- util::ptr<Style>,
+ Style&,
GlyphAtlas&,
GlyphStore&,
SpriteAtlas&,
@@ -40,8 +40,7 @@ public:
~VectorTileData();
void parse() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) override;
- bool hasData(StyleLayer const& layer_desc) const override;
+ virtual Bucket* getBucket(StyleLayer const &layer_desc) override;
protected:
// Holds the actual geometries in this tile.
@@ -50,7 +49,6 @@ protected:
TriangleElementsBuffer triangleElementsBuffer;
LineElementsBuffer lineElementsBuffer;
- PointElementsBuffer pointElementsBuffer;
// Holds the buckets of this tile.
// They contain the location offsets in the buffers stored above
@@ -60,7 +58,7 @@ protected:
GlyphStore& glyphStore;
SpriteAtlas& spriteAtlas;
util::ptr<Sprite> sprite;
- util::ptr<Style> style;
+ Style& style;
public:
const float depth;
diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp
index fddcf3acdd..fb771a8c49 100644
--- a/src/mbgl/map/view.cpp
+++ b/src/mbgl/map/view.cpp
@@ -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
}
diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp
index a7b0f61a3b..711fc42384 100644
--- a/src/mbgl/renderer/bucket.hpp
+++ b/src/mbgl/renderer/bucket.hpp
@@ -1,6 +1,7 @@
#ifndef MBGL_RENDERER_BUCKET
#define MBGL_RENDERER_BUCKET
+#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/mat4.hpp>
@@ -12,10 +13,23 @@ class TileID;
class Bucket : private util::noncopyable {
public:
+ // As long as this bucket has a Prepare render pass, this function is getting called. Typically,
+ // this only happens once when the bucket is being rendered for the first time.
+ virtual void upload() = 0;
+
+ // Every time this bucket is getting rendered, this function is called. This happens either
+ // once or twice (for Opaque and Transparent render passes).
virtual void render(Painter&, const StyleLayer&, const TileID&, const mat4&) = 0;
- virtual bool hasData() const = 0;
+
virtual ~Bucket() {}
+ inline bool needsUpload() const {
+ return !uploaded;
+ }
+
+protected:
+ bool uploaded = false;
+
};
}
diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp
index ed03458dad..a315d089e4 100644
--- a/src/mbgl/renderer/debug_bucket.cpp
+++ b/src/mbgl/renderer/debug_bucket.cpp
@@ -1,23 +1,29 @@
#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_)
: fontBuffer(fontBuffer_) {
}
-void DebugBucket::render(Painter &painter, const StyleLayer & /*layer_desc*/,
- const TileID & /*id*/, const mat4 &matrix) {
- painter.renderDebugText(*this, matrix);
+void DebugBucket::upload() {
+ fontBuffer.upload();
+
+ uploaded = true;
}
-bool DebugBucket::hasData() const {
- return fontBuffer.index() > 0;
+void DebugBucket::render(Painter& painter, const StyleLayer&, const TileID&, const mat4& matrix) {
+ painter.renderDebugText(*this, matrix);
}
void DebugBucket::drawLines(PlainShader& shader) {
diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/debug_bucket.hpp
index 074d1d3991..e3c01bbddb 100644
--- a/src/mbgl/renderer/debug_bucket.hpp
+++ b/src/mbgl/renderer/debug_bucket.hpp
@@ -7,10 +7,6 @@
#include <vector>
-#ifndef BUFFER_OFFSET
-#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
-#endif
-
namespace mbgl {
class PlainShader;
@@ -19,9 +15,8 @@ class DebugBucket : public Bucket {
public:
DebugBucket(DebugFontBuffer& fontBuffer);
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
void drawLines(PlainShader& shader);
void drawPoints(PlainShader& shader);
diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp
index f5726690b1..c59b0970e0 100644
--- a/src/mbgl/renderer/fill_bucket.cpp
+++ b/src/mbgl/renderer/fill_bucket.cpp
@@ -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);
}
@@ -84,7 +89,7 @@ void FillBucket::tessellate() {
hasVertices = false;
std::vector<std::vector<ClipperLib::IntPoint>> polygons;
- clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftPositive);
+ clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd);
clipper.Clear();
if (polygons.size() == 0) {
@@ -132,7 +137,7 @@ void FillBucket::tessellate() {
lineGroup.elements_length += total_vertex_count;
- if (tessTesselate(tesselator, TESS_WINDING_POSITIVE, TESS_POLYGONS, vertices_per_group, vertexSize, 0)) {
+ if (tessTesselate(tesselator, TESS_WINDING_ODD, TESS_POLYGONS, vertices_per_group, vertexSize, 0)) {
const TESSreal *vertices = tessGetVertices(tesselator);
const size_t vertex_count = tessGetVertexCount(tesselator);
TESSindex *vertex_indices = const_cast<TESSindex *>(tessGetVertexIndices(tesselator));
@@ -195,8 +200,19 @@ void FillBucket::tessellate() {
lineGroup.vertex_length += total_vertex_count;
}
-void FillBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) {
+void FillBucket::upload() {
+ vertexBuffer.upload();
+ triangleElementsBuffer.upload();
+ lineElementsBuffer.upload();
+
+ // From now on, we're going to render during the opaque and translucent pass.
+ uploaded = true;
+}
+
+void FillBucket::render(Painter& painter,
+ const StyleLayer& layer_desc,
+ const TileID& id,
+ const mat4& matrix) {
painter.renderFill(*this, layer_desc, id, matrix);
}
diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/fill_bucket.hpp
index d28f849a2c..2ff86ba9af 100644
--- a/src/mbgl/renderer/fill_bucket.hpp
+++ b/src/mbgl/renderer/fill_bucket.hpp
@@ -11,10 +11,6 @@
#include <vector>
#include <memory>
-#ifndef BUFFER_OFFSET
-#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
-#endif
-
namespace mbgl {
class FillVertexBuffer;
@@ -37,9 +33,9 @@ public:
LineElementsBuffer &lineElementsBuffer);
~FillBucket() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ bool hasData() const;
void addGeometry(const GeometryCollection&);
void tessellate();
diff --git a/src/mbgl/renderer/gl_config.cpp b/src/mbgl/renderer/gl_config.cpp
new file mode 100644
index 0000000000..ae648d4d33
--- /dev/null
+++ b/src/mbgl/renderer/gl_config.cpp
@@ -0,0 +1,19 @@
+#include "gl_config.hpp"
+
+namespace mbgl {
+namespace gl {
+
+const ClearDepth::Type ClearDepth::Default = 0;
+const ClearColor::Type ClearColor::Default = { 0, 0, 0, 0 };
+const ClearStencil::Type ClearStencil::Default = 0;
+const StencilMask::Type StencilMask::Default = ~0u;
+const DepthMask::Type DepthMask::Default = GL_TRUE;
+const ColorMask::Type ColorMask::Default = { false, false, false, false };
+const StencilFunc::Type StencilFunc::Default = { GL_ALWAYS, 0, ~0u };
+const StencilTest::Type StencilTest::Default = false;
+const DepthRange::Type DepthRange::Default = { 0, 1 };
+const DepthTest::Type DepthTest::Default = false;
+const Blend::Type Blend::Default = false;
+
+}
+}
diff --git a/src/mbgl/renderer/gl_config.hpp b/src/mbgl/renderer/gl_config.hpp
new file mode 100644
index 0000000000..59507f721f
--- /dev/null
+++ b/src/mbgl/renderer/gl_config.hpp
@@ -0,0 +1,149 @@
+#ifndef MBGL_RENDERER_GL_CONFIG
+#define MBGL_RENDERER_GL_CONFIG
+
+#include <cstdint>
+#include <tuple>
+#include <array>
+
+#include <mbgl/platform/gl.hpp>
+
+namespace mbgl {
+namespace gl {
+
+template <typename T>
+class Value {
+public:
+ inline void operator=(const typename T::Type& value) {
+ if (current != value) {
+ current = value;
+ MBGL_CHECK_ERROR(T::Set(current));
+ }
+ }
+
+private:
+ typename T::Type current = T::Default;
+};
+
+struct ClearDepth {
+ using Type = GLfloat;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearDepth(value));
+ }
+};
+
+struct ClearColor {
+ struct Type { float r, g, b, a; };
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearColor(value.r, value.g, value.b, value.a));
+ }
+};
+
+inline bool operator!=(const ClearColor::Type& a, const ClearColor::Type& b) {
+ return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
+}
+
+struct ClearStencil {
+ using Type = GLint;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearStencil(value));
+ }
+};
+
+struct StencilMask {
+ using Type = GLuint;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glStencilMask(value));
+ }
+};
+
+struct DepthMask {
+ using Type = GLboolean;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glDepthMask(value));
+ }
+};
+
+struct ColorMask {
+ struct Type { bool r, g, b, a; };
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glColorMask(value.r, value.g, value.b, value.a));
+ }
+};
+
+inline bool operator!=(const ColorMask::Type& a, const ColorMask::Type& b) {
+ return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
+}
+
+struct StencilFunc {
+ struct Type { GLenum func; GLint ref; GLuint mask; };
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glStencilFunc(value.func, value.ref, value.mask));
+ }
+};
+
+inline bool operator!=(const StencilFunc::Type& a, const StencilFunc::Type& b) {
+ return a.func != b.func || a.ref != b.ref || a.mask != b.mask;
+}
+
+struct StencilTest {
+ using Type = bool;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_STENCIL_TEST) : glDisable(GL_STENCIL_TEST));
+ }
+};
+
+struct DepthRange {
+ struct Type { GLfloat near, far; };
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glDepthRange(value.near, value.far));
+ }
+};
+
+inline bool operator!=(const DepthRange::Type& a, const DepthRange::Type& b) {
+ return a.near != b.near || a.far != b.far;
+}
+
+struct DepthTest {
+ using Type = bool;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST));
+ }
+};
+
+struct Blend {
+ using Type = bool;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_BLEND) : glDisable(GL_BLEND));
+ }
+};
+
+class Config {
+public:
+ Value<StencilFunc> stencilFunc;
+ Value<StencilMask> stencilMask;
+ Value<StencilTest> stencilTest;
+ Value<DepthRange> depthRange;
+ Value<DepthMask> depthMask;
+ Value<DepthTest> depthTest;
+ Value<Blend> blend;
+ Value<ColorMask> colorMask;
+ Value<ClearDepth> clearDepth;
+ Value<ClearColor> clearColor;
+ Value<ClearStencil> clearStencil;
+};
+
+}
+}
+
+#endif
diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp
index 4e09b74640..651b4986e4 100644
--- a/src/mbgl/renderer/line_bucket.cpp
+++ b/src/mbgl/renderer/line_bucket.cpp
@@ -4,38 +4,32 @@
#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>
-#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char*)nullptr + (i))
+#endif
#include <cassert>
using namespace mbgl;
-LineBucket::LineBucket(LineVertexBuffer &vertexBuffer_,
- TriangleElementsBuffer &triangleElementsBuffer_,
- PointElementsBuffer &pointElementsBuffer_)
+LineBucket::LineBucket(LineVertexBuffer& vertexBuffer_,
+ TriangleElementsBuffer& triangleElementsBuffer_)
: vertexBuffer(vertexBuffer_),
triangleElementsBuffer(triangleElementsBuffer_),
- pointElementsBuffer(pointElementsBuffer_),
vertex_start(vertexBuffer_.index()),
- triangle_elements_start(triangleElementsBuffer_.index()),
- point_elements_start(pointElementsBuffer_.index()) {
-}
+ triangle_elements_start(triangleElementsBuffer_.index()){};
LineBucket::~LineBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
-struct TriangleElement {
- TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
- uint16_t a, b, c;
-};
-
-typedef uint16_t PointElement;
-
void LineBucket::addGeometry(const GeometryCollection& geometryCollection) {
for (auto& line : geometryCollection) {
addGeometry(line);
@@ -43,366 +37,374 @@ void LineBucket::addGeometry(const GeometryCollection& geometryCollection) {
}
void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
- // TODO: use roundLimit
- // const float roundLimit = geometry.round_limit;
+ const auto len = [&vertices] {
+ auto l = vertices.size();
+ // If the line has duplicate vertices at the end, adjust length to remove them.
+ while (l > 2 && vertices[l - 1] == vertices[l - 2]) {
+ l--;
+ }
+ return l;
+ }();
- if (vertices.size() < 2) {
+ if (len < 2) {
// fprintf(stderr, "a line must have at least two vertices\n");
return;
}
- Coordinate firstVertex = vertices.front();
- Coordinate lastVertex = vertices.back();
- bool closed = firstVertex.x == lastVertex.x && firstVertex.y == lastVertex.y;
+ const float miterLimit = layout.join == JoinType::Bevel ? 1.05f : layout.miter_limit;
+
+ const Coordinate firstVertex = vertices.front();
+ const Coordinate lastVertex = vertices[len - 1];
+ const bool closed = firstVertex == lastVertex;
- if (vertices.size() == 2 && closed) {
+ if (len == 2 && closed) {
// fprintf(stderr, "a line may not have coincident points\n");
return;
}
- CapType beginCap = layout.cap;
- CapType endCap = closed ? CapType::Butt : layout.cap;
-
- JoinType currentJoin = JoinType::Miter;
-
- Coordinate currentVertex = Coordinate::null(),
- prevVertex = Coordinate::null(),
- nextVertex = Coordinate::null();
- vec2<double> prevNormal = vec2<double>::null(),
- nextNormal = vec2<double>::null();
-
- int32_t e1 = -1, e2 = -1, e3 = -1;
+ const CapType beginCap = layout.cap;
+ const CapType endCap = closed ? CapType::Butt : layout.cap;
int8_t flip = 1;
double distance = 0;
+ bool startOfLine = true;
+ Coordinate currentVertex = Coordinate::null(), prevVertex = Coordinate::null(),
+ nextVertex = Coordinate::null();
+ vec2<double> prevNormal = vec2<double>::null(), nextNormal = vec2<double>::null();
+
+ // the last three vertices added
+ e1 = e2 = e3 = -1;
if (closed) {
- currentVertex = vertices[vertices.size() - 2];
- nextNormal = util::normal<double>(currentVertex, lastVertex);
+ currentVertex = vertices[len - 2];
+ nextNormal = util::perp(util::unit(vec2<double>(firstVertex - currentVertex)));
}
- int32_t start_vertex = (int32_t)vertexBuffer.index();
-
- std::vector<TriangleElement> triangle_store;
- std::vector<PointElement> point_store;
+ const int32_t startVertex = (int32_t)vertexBuffer.index();
+ std::vector<TriangleElement> triangleStore;
- for (size_t i = 0; i < vertices.size(); ++i) {
- if (nextNormal) prevNormal = { -nextNormal.x, -nextNormal.y };
- if (currentVertex) prevVertex = currentVertex;
-
- currentVertex = vertices[i];
- currentJoin = layout.join;
-
- if (prevVertex) distance += util::dist<double>(currentVertex, prevVertex);
-
- // Find the next vertex.
- if (i + 1 < vertices.size()) {
+ for (size_t i = 0; i < len; ++i) {
+ if (closed && i == len - 1) {
+ // if the line is closed, we treat the last vertex like the first
+ nextVertex = vertices[i];
+ } else if (i + 1 < len) {
+ // just the next vertex
nextVertex = vertices[i + 1];
} else {
+ // there is no next vertex
nextVertex = Coordinate::null();
}
- // If the line is closed, we treat the last vertex like the first vertex.
- if (!nextVertex && closed) {
- nextVertex = vertices[1];
+ // if two consecutive vertices exist, skip the current one
+ if (nextVertex && vertices[i] == nextVertex) {
+ continue;
}
- if (nextVertex) {
- // if two consecutive vertices exist, skip one
- if (currentVertex.x == nextVertex.x && currentVertex.y == nextVertex.y) continue;
+ if (nextNormal) {
+ prevNormal = nextNormal;
+ }
+ if (currentVertex) {
+ prevVertex = currentVertex;
}
+ currentVertex = vertices[i];
+
+ // Calculate how far along the line the currentVertex is
+ if (prevVertex)
+ distance += util::dist<double>(currentVertex, prevVertex);
+
// Calculate the normal towards the next vertex in this line. In case
// there is no next vertex, pretend that the line is continuing straight,
- // meaning that we are just reversing the previous normal
- if (nextVertex) {
- nextNormal = util::normal<double>(currentVertex, nextVertex);
- } else {
- nextNormal = { -prevNormal.x, -prevNormal.y };
- }
+ // meaning that we are just using the previous normal.
+ nextNormal = nextVertex ? util::perp(util::unit(vec2<double>(nextVertex - currentVertex)))
+ : prevNormal;
// If we still don't have a previous normal, this is the beginning of a
// non-closed line, so we're doing a straight "join".
if (!prevNormal) {
- prevNormal = { -nextNormal.x, -nextNormal.y };
+ prevNormal = nextNormal;
}
// Determine the normal of the join extrusion. It is the angle bisector
// of the segments between the previous line and the next line.
- vec2<double> joinNormal = {
- prevNormal.x + nextNormal.x,
- prevNormal.y + nextNormal.y
- };
-
- // Cross product yields 0..1 depending on whether they are parallel
- // or perpendicular.
- double joinAngularity = nextNormal.x * joinNormal.y - nextNormal.y * joinNormal.x;
- joinNormal.x /= joinAngularity;
- joinNormal.y /= joinAngularity;
- double roundness = std::fmax(std::abs(joinNormal.x), std::abs(joinNormal.y));
-
-
- // Switch to miter joins if the angle is very low.
- if (currentJoin != JoinType::Miter) {
- if (std::fabs(joinAngularity) < 0.5 && roundness < layout.miter_limit) {
+ vec2<double> joinNormal = util::unit(prevNormal + nextNormal);
+
+ /* joinNormal prevNormal
+ * ↖ ↑
+ * .________. prevVertex
+ * |
+ * nextNormal ← | currentVertex
+ * |
+ * nextVertex !
+ *
+ */
+
+ // Calculate the length of the miter (the ratio of the miter to the width).
+ // Find the cosine of the angle between the next and join normals
+ // using dot product. The inverse of that is the miter length.
+ const float cosHalfAngle = joinNormal.x * nextNormal.x + joinNormal.y * nextNormal.y;
+ const float miterLength = 1 / cosHalfAngle;
+
+ // The join if a middle vertex, otherwise the cap
+ const bool middleVertex = prevVertex && nextVertex;
+ JoinType currentJoin = layout.join;
+ const CapType currentCap = nextVertex ? beginCap : endCap;
+
+ if (middleVertex) {
+ if (currentJoin == JoinType::Round && miterLength < layout.round_limit) {
currentJoin = JoinType::Miter;
}
- }
-
- // Add offset square begin cap.
- if (!prevVertex && beginCap == CapType::Square) {
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * (prevNormal.x + prevNormal.y), flip * (-prevNormal.x + prevNormal.y), // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
-
- // Add second vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * (prevNormal.x - prevNormal.y), flip * (prevNormal.x + prevNormal.y), // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
- }
-
- // Add offset square end cap.
- else if (!nextVertex && endCap == CapType::Square) {
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- nextNormal.x - flip * nextNormal.y, flip * nextNormal.x + nextNormal.y, // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ if (currentJoin == JoinType::Miter && miterLength > miterLimit) {
+ currentJoin = JoinType::Bevel;
+ }
- // Add second vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- nextNormal.x + flip * nextNormal.y, -flip * nextNormal.x + nextNormal.y, // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
+ if (currentJoin == JoinType::Bevel) {
+ // The maximum extrude length is 128 / 63 = 2 times the width of the line
+ // so if miterLength >= 2 we need to draw a different type of bevel where.
+ if (miterLength > 2) {
+ currentJoin = JoinType::FlipBevel;
+ }
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ // If the miterLength is really small and the line bevel wouldn't be visible,
+ // just draw a miter join to save a triangle.
+ if (miterLength < miterLimit) {
+ currentJoin = JoinType::Miter;
+ }
+ }
}
- else if (currentJoin == JoinType::Miter) {
- // MITER JOIN
- if (std::fabs(joinAngularity) < 0.01) {
- // The two normals are almost parallel.
- joinNormal.x = -nextNormal.y;
- joinNormal.y = nextNormal.x;
- } else if (roundness > layout.miter_limit) {
- // If the miter grows too large, flip the direction to make a
- // bevel join.
- joinNormal.x = (prevNormal.x - nextNormal.x) / joinAngularity;
- joinNormal.y = (prevNormal.y - nextNormal.y) / joinAngularity;
+ if (middleVertex && currentJoin == JoinType::Miter) {
+ joinNormal = joinNormal * miterLength;
+ addCurrentVertex(currentVertex, flip, distance, joinNormal, 0, 0, false, startVertex,
+ triangleStore);
+
+ } else if (middleVertex && currentJoin == JoinType::FlipBevel) {
+ // miter is too big, flip the direction to make a beveled join
+
+ if (miterLength > 100) {
+ // Almost parallel lines
+ joinNormal = nextNormal;
+ } else {
+ const float direction = prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x > 0 ? -1 : 1;
+ const float bevelLength = miterLength * util::mag(prevNormal + nextNormal) /
+ util::mag(prevNormal - nextNormal * direction);
+ joinNormal = util::perp(joinNormal) * bevelLength;
}
- if (roundness > layout.miter_limit) {
- flip = -flip;
+ addCurrentVertex(currentVertex, flip, distance, joinNormal, 0, 0, false, startVertex,
+ triangleStore);
+ flip = -flip;
+
+ } else if (middleVertex && currentJoin == JoinType::Bevel) {
+ const float dir = prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x;
+ const float offset = -std::sqrt(miterLength * miterLength - 1);
+ float offsetA;
+ float offsetB;
+
+ if (flip * dir > 0) {
+ offsetB = 0;
+ offsetA = offset;
+ } else {
+ offsetA = 0;
+ offsetB = offset;
}
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * joinNormal.x, flip * joinNormal.y, // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ // Close previous segement with bevel
+ if (!startOfLine) {
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, offsetA, offsetB, false,
+ startVertex, triangleStore);
+ }
- // Add second vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- -flip * joinNormal.x, -flip * joinNormal.y, // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
+ // Start next segment
+ if (nextVertex) {
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, -offsetA, -offsetB,
+ false, startVertex, triangleStore);
+ }
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ } else if (!middleVertex && currentCap == CapType::Butt) {
+ if (!startOfLine) {
+ // Close previous segment with a butt
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, 0, 0, false,
+ startVertex, triangleStore);
+ }
- if ((!prevVertex && beginCap == CapType::Round) ||
- (!nextVertex && endCap == CapType::Round)) {
- point_store.emplace_back(e1);
+ // Start next segment with a butt
+ if (nextVertex) {
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, 0, 0, false,
+ startVertex, triangleStore);
}
- }
- else {
- // Close up the previous line
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * prevNormal.y, -flip * prevNormal.x, // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
+ } else if (!middleVertex && currentCap == CapType::Square) {
+ if (!startOfLine) {
+ // Close previous segment with a square cap
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, 1, 1, false,
+ startVertex, triangleStore);
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ // The segment is done. Unset vertices to disconnect segments.
+ e1 = e2 = -1;
+ flip = 1;
+ }
- // Add second vertex.
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- -flip * prevNormal.y, flip * prevNormal.x, // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
+ // Start next segment
+ if (nextVertex) {
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, -1, -1, false,
+ startVertex, triangleStore);
+ }
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ } else if (middleVertex ? currentJoin == JoinType::Round : currentCap == CapType::Round) {
+ if (!startOfLine) {
+ // Close previous segment with a butt
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, 0, 0, false,
+ startVertex, triangleStore);
- prevNormal = { -nextNormal.x, -nextNormal.y };
- flip = 1;
+ // Add round cap or linejoin at end of segment
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, 1, 1, true, startVertex,
+ triangleStore);
+ // The segment is done. Unset vertices to disconnect segments.
+ e1 = e2 = -1;
+ flip = 1;
- // begin/end caps
- if ((!prevVertex && beginCap == CapType::Round) ||
- (!nextVertex && endCap == CapType::Round)) {
- point_store.emplace_back(e1);
+ } else if (beginCap == CapType::Round) {
+ // Add round cap before first segment
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, -1, -1, true,
+ startVertex, triangleStore);
}
-
- if (currentJoin == JoinType::Round) {
- if (prevVertex && nextVertex && (!closed || i > 0)) {
- point_store.emplace_back(e1);
- }
-
- // Reset the previous vertices so that we don't accidentally create
- // any triangles.
- e1 = -1; e2 = -1; e3 = -1;
+ // Start next segment with a butt
+ if (nextVertex) {
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, 0, 0, false,
+ startVertex, triangleStore);
}
-
- // Start the new quad.
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- -flip * nextNormal.y, flip * nextNormal.x, // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
-
- // Add second vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * nextNormal.y, -flip * nextNormal.x, // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
}
+
+ startOfLine = false;
}
- size_t end_vertex = vertexBuffer.index();
- size_t vertex_count = end_vertex - start_vertex;
+ const size_t endVertex = vertexBuffer.index();
+ const size_t vertexCount = endVertex - startVertex;
// Store the triangle/line groups.
{
- if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + vertex_count > 65535)) {
+ if (!triangleGroups.size() ||
+ (triangleGroups.back()->vertex_length + vertexCount > 65535)) {
// Move to a new group because the old one can't hold the geometry.
- triangleGroups.emplace_back(util::make_unique<triangle_group_type>());
+ triangleGroups.emplace_back(util::make_unique<TriangleGroup>());
}
assert(triangleGroups.back());
- triangle_group_type& group = *triangleGroups.back();
- for (const auto& triangle : triangle_store) {
- triangleElementsBuffer.add(
- group.vertex_length + triangle.a,
- group.vertex_length + triangle.b,
- group.vertex_length + triangle.c
- );
+ auto& group = *triangleGroups.back();
+ for (const auto& triangle : triangleStore) {
+ triangleElementsBuffer.add(group.vertex_length + triangle.a,
+ group.vertex_length + triangle.b,
+ group.vertex_length + triangle.c);
}
- group.vertex_length += vertex_count;
- group.elements_length += triangle_store.size();
+ group.vertex_length += vertexCount;
+ group.elements_length += triangleStore.size();
}
+}
- // Store the line join/cap groups.
- {
- if (!pointGroups.size() || (pointGroups.back()->vertex_length + vertex_count > 65535)) {
- // Move to a new group because the old one can't hold the geometry.
- pointGroups.emplace_back(util::make_unique<point_group_type>());
- }
+void LineBucket::addCurrentVertex(const Coordinate& currentVertex,
+ float flip,
+ double distance,
+ const vec2<double>& normal,
+ float endLeft,
+ float endRight,
+ bool round,
+ int32_t startVertex,
+ std::vector<TriangleElement>& triangleStore) {
+ int8_t tx = round ? 1 : 0;
+
+ vec2<double> extrude = normal * flip;
+ if (endLeft)
+ extrude = extrude - (util::perp(normal) * endLeft);
+ e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 0,
+ distance) -
+ startVertex;
+ if (e1 >= 0 && e2 >= 0) {
+ triangleStore.emplace_back(e1, e2, e3);
+ }
+ e1 = e2;
+ e2 = e3;
+
+ extrude = normal * (-flip);
+ if (endRight)
+ extrude = extrude - (util::perp(normal) * endRight);
+ e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 1,
+ distance) -
+ startVertex;
+ if (e1 >= 0 && e2 >= 0) {
+ triangleStore.emplace_back(e1, e2, e3);
+ }
+ e1 = e2;
+ e2 = e3;
+}
- assert(pointGroups.back());
- point_group_type& group = *pointGroups.back();
- for (const auto point : point_store) {
- pointElementsBuffer.add(group.vertex_length + point);
- }
+void LineBucket::upload() {
+ vertexBuffer.upload();
+ triangleElementsBuffer.upload();
- group.vertex_length += vertex_count;
- group.elements_length += point_store.size();
- }
+ // From now on, we're only going to render during the translucent pass.
+ uploaded = true;
}
-void LineBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) {
+void LineBucket::render(Painter& painter,
+ const StyleLayer& layer_desc,
+ const TileID& id,
+ const mat4& matrix) {
painter.renderLine(*this, layer_desc, id, matrix);
}
bool LineBucket::hasData() const {
- return !triangleGroups.empty() || !pointGroups.empty();
-}
-
-bool LineBucket::hasPoints() const {
- if (!pointGroups.empty()) {
- for (const auto& group : pointGroups) {
- assert(group);
- if (group->elements_length) {
- return true;
- }
- }
- }
- return false;
+ return !triangleGroups.empty();
}
void LineBucket::drawLines(LineShader& shader) {
- char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
- char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
+ char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
+ char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
for (auto& group : triangleGroups) {
assert(group);
if (!group->elements_length) {
continue;
}
group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
- MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
+ MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
+ elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
}
}
void LineBucket::drawLineSDF(LineSDFShader& shader) {
- char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
- char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
+ char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
+ char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
for (auto& group : triangleGroups) {
assert(group);
if (!group->elements_length) {
continue;
}
group->array[2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
- MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
+ MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
+ elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
}
}
void LineBucket::drawLinePatterns(LinepatternShader& shader) {
- char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
- char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
+ char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
+ char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
for (auto& group : triangleGroups) {
assert(group);
if (!group->elements_length) {
continue;
}
group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
- MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
+ MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
+ elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
}
}
-
-void LineBucket::drawPoints(LinejoinShader& shader) {
- char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
- char *elements_index = BUFFER_OFFSET(point_elements_start * pointElementsBuffer.itemSize);
- for (auto& group : pointGroups) {
- assert(group);
- if (!group->elements_length) {
- continue;
- }
- group->array[0].bind(shader, vertexBuffer, pointElementsBuffer, vertex_index);
- MBGL_CHECK_ERROR(glDrawElements(GL_POINTS, group->elements_length, GL_UNSIGNED_SHORT, elements_index));
- vertex_index += group->vertex_length * vertexBuffer.itemSize;
- elements_index += group->elements_length * pointElementsBuffer.itemSize;
- }
-}
diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp
index d70801e0bf..6f13e3c819 100644
--- a/src/mbgl/renderer/line_bucket.hpp
+++ b/src/mbgl/renderer/line_bucket.hpp
@@ -18,33 +18,35 @@ class Style;
class LineVertexBuffer;
class TriangleElementsBuffer;
class LineShader;
-class LinejoinShader;
class LineSDFShader;
class LinepatternShader;
class LineBucket : public Bucket {
- typedef ElementGroup<3> triangle_group_type;
- typedef ElementGroup<1> point_group_type;
+ using TriangleGroup = ElementGroup<3>;
public:
- LineBucket(LineVertexBuffer &vertexBuffer,
- TriangleElementsBuffer &triangleElementsBuffer,
- PointElementsBuffer &pointElementsBuffer);
+ LineBucket(LineVertexBuffer &vertexBuffer, TriangleElementsBuffer &triangleElementsBuffer);
~LineBucket() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ bool hasData() const;
void addGeometry(const GeometryCollection&);
void addGeometry(const std::vector<Coordinate>& line);
- bool hasPoints() const;
-
void drawLines(LineShader& shader);
void drawLineSDF(LineSDFShader& shader);
void drawLinePatterns(LinepatternShader& shader);
- void drawPoints(LinejoinShader& shader);
+
+private:
+ struct TriangleElement {
+ TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
+ uint16_t a, b, c;
+ };
+ void addCurrentVertex(const Coordinate& currentVertex, float flip, double distance,
+ const vec2<double>& normal, float endLeft, float endRight, bool round,
+ int32_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore);
public:
StyleLayoutLine layout;
@@ -52,14 +54,15 @@ public:
private:
LineVertexBuffer& vertexBuffer;
TriangleElementsBuffer& triangleElementsBuffer;
- PointElementsBuffer& pointElementsBuffer;
const size_t vertex_start;
const size_t triangle_elements_start;
- const size_t point_elements_start;
- std::vector<std::unique_ptr<triangle_group_type>> triangleGroups;
- std::vector<std::unique_ptr<point_group_type>> pointGroups;
+ int32_t e1;
+ int32_t e2;
+ int32_t e3;
+
+ std::vector<std::unique_ptr<TriangleGroup>> triangleGroups;
};
}
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index cafd614244..a80fd4e8b7 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -1,16 +1,33 @@
#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/geometry/line_atlas.hpp>
+#include <mbgl/geometry/glyph_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 +56,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
@@ -62,7 +75,6 @@ void Painter::setup() {
assert(plainShader);
assert(outlineShader);
assert(lineShader);
- assert(linejoinShader);
assert(linepatternShader);
assert(patternShader);
assert(rasterShader);
@@ -76,16 +88,15 @@ void Painter::setup() {
// We are blending new pixels on top of old pixels. Since we have depth testing
// and are drawing opaque fragments first front-to-back, then translucent
// fragments back-to-front, this shades the fewest fragments possible.
- MBGL_CHECK_ERROR(glEnable(GL_BLEND));
+ config.blend = true;
MBGL_CHECK_ERROR(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
// Set clear values
- MBGL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
- MBGL_CHECK_ERROR(glClearDepth(1.0f));
- MBGL_CHECK_ERROR(glClearStencil(0x0));
+ config.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
+ config.clearDepth = 1.0f;
+ config.clearStencil = 0x0;
// Stencil test
- MBGL_CHECK_ERROR(glEnable(GL_STENCIL_TEST));
MBGL_CHECK_ERROR(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
// Depth test
@@ -96,7 +107,6 @@ void Painter::setupShaders() {
if (!plainShader) plainShader = util::make_unique<PlainShader>();
if (!outlineShader) outlineShader = util::make_unique<OutlineShader>();
if (!lineShader) lineShader = util::make_unique<LineShader>();
- if (!linejoinShader) linejoinShader = util::make_unique<LinejoinShader>();
if (!linesdfShader) linesdfShader = util::make_unique<LineSDFShader>();
if (!linepatternShader) linepatternShader = util::make_unique<LinepatternShader>();
if (!patternShader) patternShader = util::make_unique<PatternShader>();
@@ -108,25 +118,6 @@ void Painter::setupShaders() {
if (!gaussianShader) gaussianShader = util::make_unique<GaussianShader>();
}
-void Painter::deleteShaders() {
- plainShader = nullptr;
- outlineShader = nullptr;
- lineShader = nullptr;
- linejoinShader = 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();
@@ -153,21 +144,6 @@ void Painter::lineWidth(float line_width) {
}
}
-void Painter::depthMask(bool value) {
- if (gl_depthMask != value) {
- MBGL_CHECK_ERROR(glDepthMask(value ? GL_TRUE : GL_FALSE));
- gl_depthMask = value;
- }
-}
-
-void Painter::depthRange(const float near, const float far) {
- if (gl_depthRange[0] != near || gl_depthRange[1] != far) {
- MBGL_CHECK_ERROR(glDepthRange(near, far));
- gl_depthRange = {{ near, far }};
- }
-}
-
-
void Painter::changeMatrix() {
// Initialize projection matrix
matrix::ortho(projMatrix, 0, state.getWidth(), state.getHeight(), 0, 0, 1);
@@ -185,24 +161,25 @@ void Painter::changeMatrix() {
void Painter::clear() {
gl::group group("clear");
- MBGL_CHECK_ERROR(glStencilMask(0xFF));
- depthMask(true);
-
- MBGL_CHECK_ERROR(glClearColor(0, 0, 0, 0));
+ config.stencilTest = true;
+ config.stencilMask = 0xFF;
+ config.depthTest = false;
+ config.depthMask = GL_TRUE;
+ config.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
}
void Painter::setOpaque() {
if (pass != RenderPass::Opaque) {
pass = RenderPass::Opaque;
- MBGL_CHECK_ERROR(glDisable(GL_BLEND));
+ config.blend = false;
}
}
void Painter::setTranslucent() {
if (pass != RenderPass::Translucent) {
pass = RenderPass::Translucent;
- MBGL_CHECK_ERROR(glEnable(GL_BLEND));
+ config.blend = true;
}
}
@@ -213,16 +190,12 @@ void Painter::setStrata(float value) {
void Painter::prepareTile(const Tile& tile) {
const GLint ref = (GLint)tile.clip.reference.to_ulong();
const GLuint mask = (GLuint)tile.clip.mask.to_ulong();
- MBGL_CHECK_ERROR(glStencilFunc(GL_EQUAL, ref, mask));
+ config.stencilFunc = { GL_EQUAL, ref, mask };
}
void Painter::render(const Style& style, TransformState state_, TimePoint time) {
state = state_;
- clear();
- resize();
- changeMatrix();
-
std::set<Source*> sources;
for (const auto& source : style.sources) {
if (source->enabled) {
@@ -230,14 +203,46 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
}
}
- // Update all clipping IDs.
- ClipIDGenerator generator;
- for (const auto& source : sources) {
- generator.update(source->getLoadedTiles());
- source->updateMatrices(projMatrix, state);
+ // Figure out what buckets we have to draw and what order we have to draw them in.
+ const auto order = determineRenderOrder(style);
+
+ // - UPLOAD PASS -------------------------------------------------------------------------------
+ // Uploads all required buffers and images before we do any actual rendering.
+ {
+ const gl::group upload("upload");
+
+ tileStencilBuffer.upload();
+ tileBorderBuffer.upload();
+ spriteAtlas.upload();
+ lineAtlas.upload();
+ glyphAtlas.upload();
+
+ for (const auto& item : order) {
+ if (item.bucket && item.bucket->needsUpload()) {
+ item.bucket->upload();
+ }
+ }
}
- drawClippingMasks(sources);
+
+ // - CLIPPING MASKS ----------------------------------------------------------------------------
+ // Draws the clipping masks to the stencil buffer.
+ {
+ const gl::group clip("clip");
+
+ // Update all clipping IDs.
+ ClipIDGenerator generator;
+ for (const auto& source : sources) {
+ generator.update(source->getLoadedTiles());
+ source->updateMatrices(projMatrix, state);
+ }
+
+ clear();
+ resize();
+ changeMatrix();
+
+ drawClippingMasks(sources);
+ }
frameHistory.record(time, state.getNormalizedZoom());
@@ -245,122 +250,169 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
// TODO: Correctly compute the number of layers recursively beforehand.
- float strata_thickness = 1.0f / (style.layers.size() + 1);
-
- // - FIRST PASS ------------------------------------------------------------
- // Render everything top-to-bottom by using reverse iterators. Render opaque
- // objects first.
+ const float strata_thickness = 1.0f / (order.size() + 1);
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "OPAQUE {");
- }
+ // Layer index
int i = 0;
- for (auto it = style.layers.rbegin(), end = style.layers.rend(); it != end; ++it, ++i) {
+
+ // - OPAQUE PASS -------------------------------------------------------------------------------
+ // Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
+ {
+ const gl::group _("opaque");
+
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "OPAQUE {");
+ }
+ i = 0;
setOpaque();
- setStrata(i * strata_thickness);
- renderLayer(**it);
- }
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ for (auto it = order.rbegin(), end = order.rend(); it != end; ++it, ++i) {
+ const auto& item = *it;
+ if (item.bucket && item.tile) {
+ if (item.hasRenderPass(RenderPass::Opaque)) {
+ const gl::group group(item.layer.id + " - " + std::string(item.tile->id));
+ setStrata(i * strata_thickness);
+ prepareTile(*item.tile);
+ item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
+ }
+ } else {
+ const gl::group group("background");
+ renderBackground(item.layer);
+ }
+ }
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ }
}
- // - SECOND PASS -----------------------------------------------------------
- // Make a second pass, rendering translucent objects. This time, we render
- // bottom-to-top.
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "TRANSLUCENT {");
- }
- --i;
- for (auto it = style.layers.begin(), end = style.layers.end(); it != end; ++it, --i) {
+ // - TRANSLUCENT PASS --------------------------------------------------------------------------
+ // Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
+ {
+ const gl::group _("translucent");
+
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "TRANSLUCENT {");
+ }
+ --i; // After the last iteration, this is incremented, so we have to decrement it again.
setTranslucent();
- setStrata(i * strata_thickness);
- renderLayer(**it);
- }
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ for (auto it = order.begin(), end = order.end(); it != end; ++it, --i) {
+ const auto& item = *it;
+ if (item.bucket && item.tile) {
+ if (item.hasRenderPass(RenderPass::Translucent)) {
+ const gl::group group(item.layer.id + " - " + std::string(item.tile->id));
+ setStrata(i * strata_thickness);
+ prepareTile(*item.tile);
+ item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
+ }
+ }
+ }
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ }
}
if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; }
- // Finalize the rendering, e.g. by calling debug render calls per tile.
- // This guarantees that we have at least one function per tile called.
- // When only rendering layers via the stylesheet, it's possible that we don't
- // ever visit a tile during rendering.
- for (const auto& source : sources) {
- source->finishRender(*this);
+ // - DEBUG PASS --------------------------------------------------------------------------------
+ // Renders debug overlays.
+ {
+ const gl::group _("debug");
+
+ // Finalize the rendering, e.g. by calling debug render calls per tile.
+ // This guarantees that we have at least one function per tile called.
+ // When only rendering layers via the stylesheet, it's possible that we don't
+ // ever visit a tile during rendering.
+ for (const auto& source : sources) {
+ source->finishRender(*this);
+ }
+ }
+
+ // TODO: Find a better way to unbind VAOs after we're done with them without introducing
+ // unnecessary bind(0)/bind(N) sequences.
+ {
+ const gl::group _("cleanup");
+
+ MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, 0));
+ MBGL_CHECK_ERROR(gl::BindVertexArray(0));
}
}
-void Painter::renderLayer(const StyleLayer &layer_desc) {
- if (layer_desc.bucket->visibility == VisibilityType::None) return;
- if (layer_desc.type == StyleLayerType::Background) {
- // This layer defines a background color/image.
+std::vector<RenderItem> Painter::determineRenderOrder(const Style& style) {
+ std::vector<RenderItem> order;
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s- %s (%s)", indent * 4, "", layer_desc.id.c_str(),
- StyleLayerTypeClass(layer_desc.type).c_str());
+ for (const auto& layerPtr : style.layers) {
+ const auto& layer = *layerPtr;
+ if (layer.bucket->visibility == VisibilityType::None) continue;
+ if (layer.type == StyleLayerType::Background) {
+ // This layer defines a background color/image.
+ order.emplace_back(layer);
+ continue;
}
- renderBackground(layer_desc);
- } else {
// This is a singular layer.
- if (!layer_desc.bucket) {
- Log::Warning(Event::Render, "layer '%s' is missing bucket", layer_desc.id.c_str());
- return;
+ if (!layer.bucket) {
+ Log::Warning(Event::Render, "layer '%s' is missing bucket", layer.id.c_str());
+ continue;
}
- if (!layer_desc.bucket->source) {
- Log::Warning(Event::Render, "can't find source for layer '%s'", layer_desc.id.c_str());
- return;
+ if (!layer.bucket->source) {
+ Log::Warning(Event::Render, "can't find source for layer '%s'", layer.id.c_str());
+ continue;
}
// Skip this layer if it's outside the range of min/maxzoom.
// This may occur when there /is/ a bucket created for this layer, but the min/max-zoom
// is set to a fractional value, or value that is larger than the source maxzoom.
const double zoom = state.getZoom();
- if (layer_desc.bucket->min_zoom > zoom ||
- layer_desc.bucket->max_zoom <= zoom) {
- return;
+ if (layer.bucket->min_zoom > zoom ||
+ layer.bucket->max_zoom <= zoom) {
+ continue;
}
- // Abort early if we can already deduce from the bucket type that
- // we're not going to render anything anyway during this pass.
- switch (layer_desc.type) {
- case StyleLayerType::Fill:
- if (!layer_desc.getProperties<FillProperties>().isVisible()) return;
- break;
- case StyleLayerType::Line:
- if (pass == RenderPass::Opaque) return;
- if (!layer_desc.getProperties<LineProperties>().isVisible()) return;
- break;
- case StyleLayerType::Symbol:
- if (pass == RenderPass::Opaque) return;
- if (!layer_desc.getProperties<SymbolProperties>().isVisible()) return;
- break;
- case StyleLayerType::Raster:
- if (pass == RenderPass::Opaque) return;
- if (!layer_desc.getProperties<RasterProperties>().isVisible()) return;
- break;
- default:
- break;
+ // Don't include invisible layers.
+ if (!layer.isVisible()) {
+ continue;
}
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s- %s (%s)", indent * 4, "", layer_desc.id.c_str(),
- StyleLayerTypeClass(layer_desc.type).c_str());
- }
+ // Determine what render passes we need for this layer.
+ const RenderPass passes = determineRenderPasses(layer);
+
+ const auto& tiles = layer.bucket->source->getTiles();
+ for (auto tile : tiles) {
+ assert(tile);
+ if (!tile->data && !tile->data->ready()) {
+ continue;
+ }
- layer_desc.bucket->source->render(*this, layer_desc);
+ auto bucket = tile->data->getBucket(layer);
+ if (bucket) {
+ order.emplace_back(layer, tile, bucket, passes);
+ }
+ }
}
+
+ return order;
}
-void Painter::renderTileLayer(const Tile& tile, const StyleLayer &layer_desc, const mat4 &matrix) {
- assert(tile.data);
- if (tile.data->hasData(layer_desc) || layer_desc.type == StyleLayerType::Raster) {
- gl::group group(std::string { "render " } + tile.data->name);
- prepareTile(tile);
- tile.data->render(*this, layer_desc, matrix);
+RenderPass Painter::determineRenderPasses(const StyleLayer& layer) {
+ RenderPass passes = RenderPass::None;
+
+ if (layer.properties.is<FillProperties>()) {
+ const FillProperties &properties = layer.properties.get<FillProperties>();
+ const float alpha = properties.fill_color[3] * properties.opacity;
+
+ if (properties.antialias) {
+ passes |= RenderPass::Translucent;
+ }
+ if (properties.image.from.size() || alpha < 1.0f) {
+ passes |= RenderPass::Translucent;
+ } else {
+ passes |= RenderPass::Opaque;
+ }
+ } else {
+ passes |= RenderPass::Translucent;
}
+
+ return passes;
}
void Painter::renderBackground(const StyleLayer &layer_desc) {
@@ -437,10 +489,10 @@ void Painter::renderBackground(const StyleLayer &layer_desc) {
backgroundArray.bind(*plainShader, backgroundBuffer, BUFFER_OFFSET(0));
}
- MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
- depthRange(strata + strata_epsilon, 1.0f);
+ config.stencilTest = false;
+ config.depthTest = true;
+ config.depthRange = { strata + strata_epsilon, 1.0f };
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
- MBGL_CHECK_ERROR(glEnable(GL_STENCIL_TEST));
}
mat4 Painter::translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor) {
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
index cf01c03918..54c23b393a 100644
--- a/src/mbgl/renderer/painter.hpp
+++ b/src/mbgl/renderer/painter.hpp
@@ -1,61 +1,81 @@
#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/renderer/bucket.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/renderer/gl_config.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/linejoin_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;
+struct RenderItem {
+ inline RenderItem(const StyleLayer& layer_,
+ const Tile* tile_ = nullptr,
+ Bucket* bucket_ = nullptr,
+ RenderPass passes_ = RenderPass::Opaque)
+ : tile(tile_), bucket(bucket_), layer(layer_), passes(passes_) {
+ }
+
+ const Tile* const tile;
+ Bucket* const bucket;
+ const StyleLayer& layer;
+ const RenderPass passes;
+
+ inline bool hasRenderPass(RenderPass pass) const {
+ return bool(passes & pass);
+ }
+};
+
class Painter : private util::noncopyable {
public:
Painter(SpriteAtlas&, GlyphAtlas&, LineAtlas&);
@@ -63,11 +83,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();
@@ -79,11 +94,6 @@ public:
TransformState state,
TimePoint time);
- void renderLayer(const StyleLayer&);
-
- // Renders a particular layer from a tile.
- void renderTileLayer(const Tile& tile, const StyleLayer &layer_desc, const mat4 &matrix);
-
// Renders debug information for a tile.
void renderTileDebug(const Tile& tile);
@@ -133,9 +143,11 @@ public:
private:
void setupShaders();
- void deleteShaders();
mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor);
+ std::vector<RenderItem> determineRenderOrder(const Style& style);
+ static RenderPass determineRenderPasses(const StyleLayer&);
+
void prepareTile(const Tile& tile);
template <typename BucketProperties, typename StyleProperties>
@@ -152,8 +164,6 @@ private:
public:
void useProgram(uint32_t program);
void lineWidth(float lineWidth);
- void depthMask(bool value);
- void depthRange(float near, float far);
public:
mat4 projMatrix;
@@ -180,11 +190,11 @@ private:
bool debug = false;
int indent = 0;
+ gl::Config config;
+
uint32_t gl_program = 0;
float gl_lineWidth = 0;
- bool gl_depthMask = true;
std::array<uint16_t, 2> gl_viewport = {{ 0, 0 }};
- std::array<float, 2> gl_depthRange = {{ 0, 1 }};
float strata = 0;
RenderPass pass = RenderPass::Opaque;
const float strata_epsilon = 1.0f / (1 << 16);
@@ -199,7 +209,6 @@ public:
std::unique_ptr<PlainShader> plainShader;
std::unique_ptr<OutlineShader> outlineShader;
std::unique_ptr<LineShader> lineShader;
- std::unique_ptr<LinejoinShader> linejoinShader;
std::unique_ptr<LineSDFShader> linesdfShader;
std::unique_ptr<LinepatternShader> linepatternShader;
std::unique_ptr<PatternShader> patternShader;
diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp
index 3a981ed1ec..c495359282 100644
--- a/src/mbgl/renderer/painter_clipping.cpp
+++ b/src/mbgl/renderer/painter_clipping.cpp
@@ -1,20 +1,23 @@
#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) {
gl::group group("clipping masks");
useProgram(plainShader->program);
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
- depthMask(false);
- MBGL_CHECK_ERROR(glColorMask(false, false, false, false));
- depthRange(1.0f, 1.0f);
+ config.stencilTest = true;
+ config.depthTest = true;
+ config.depthMask = GL_FALSE;
+ config.colorMask = { false, false, false, false };
+ config.depthRange = { 1.0f, 1.0f };
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
@@ -22,10 +25,10 @@ void Painter::drawClippingMasks(const std::set<Source*>& sources) {
source->drawClippingMasks(*this);
}
- MBGL_CHECK_ERROR(glEnable(GL_DEPTH_TEST));
- MBGL_CHECK_ERROR(glColorMask(true, true, true, true));
- depthMask(true);
- MBGL_CHECK_ERROR(glStencilMask(0x0));
+ config.depthTest = true;
+ config.colorMask = { true, true, true, true };
+ config.depthMask = GL_TRUE;
+ config.stencilMask = 0x0;
}
void Painter::drawClippingMask(const mat4& matrix, const ClipID &clip) {
@@ -33,8 +36,7 @@ void Painter::drawClippingMask(const mat4& matrix, const ClipID &clip) {
const GLint ref = (GLint)(clip.reference.to_ulong());
const GLuint mask = (GLuint)(clip.mask.to_ulong());
- MBGL_CHECK_ERROR(glStencilFunc(GL_ALWAYS, ref, mask));
- MBGL_CHECK_ERROR(glStencilMask(mask));
-
+ config.stencilFunc = { GL_ALWAYS, ref, mask };
+ config.stencilMask = mask;
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index()));
}
diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp
index 2931473283..b252e2e835 100644
--- a/src/mbgl/renderer/painter_debug.cpp
+++ b/src/mbgl/renderer/painter_debug.cpp
@@ -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) {
@@ -19,7 +24,7 @@ void Painter::renderTileDebug(const Tile& tile) {
void Painter::renderDebugText(DebugBucket& bucket, const mat4 &matrix) {
gl::group group("debug text");
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
+ config.depthTest = false;
useProgram(plainShader->program);
plainShader->u_matrix = matrix;
@@ -40,7 +45,7 @@ void Painter::renderDebugText(DebugBucket& bucket, const mat4 &matrix) {
lineWidth(2.0f * state.getPixelRatio());
bucket.drawLines(*plainShader);
- MBGL_CHECK_ERROR(glEnable(GL_DEPTH_TEST));
+ config.depthTest = true;
}
void Painter::renderDebugFrame(const mat4 &matrix) {
@@ -49,7 +54,8 @@ void Painter::renderDebugFrame(const mat4 &matrix) {
// Disable depth test and don't count this towards the depth buffer,
// but *don't* disable stencil test, as we want to clip the red tile border
// to the tile viewport.
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
+ config.depthTest = false;
+ config.stencilTest = true;
useProgram(plainShader->program);
plainShader->u_matrix = matrix;
@@ -59,8 +65,6 @@ void Painter::renderDebugFrame(const mat4 &matrix) {
plainShader->u_color = {{ 1.0f, 0.0f, 0.0f, 1.0f }};
lineWidth(4.0f * state.getPixelRatio());
MBGL_CHECK_ERROR(glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index()));
-
- MBGL_CHECK_ERROR(glEnable(GL_DEPTH_TEST));
}
void Painter::renderDebugText(const std::vector<std::string> &strings) {
@@ -70,8 +74,9 @@ void Painter::renderDebugText(const std::vector<std::string> &strings) {
gl::group group("debug text");
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
- MBGL_CHECK_ERROR(glStencilFunc(GL_ALWAYS, 0xFF, 0xFF));
+ config.depthTest = false;
+ config.stencilTest = true;
+ config.stencilFunc = { GL_ALWAYS, 0xFF, 0xFF };
useProgram(plainShader->program);
plainShader->u_matrix = nativeMatrix;
@@ -98,6 +103,4 @@ void Painter::renderDebugText(const std::vector<std::string> &strings) {
lineWidth(2.0f * state.getPixelRatio());
MBGL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, (GLsizei)debugFontBuffer.index()));
}
-
- MBGL_CHECK_ERROR(glEnable(GL_DEPTH_TEST));
}
diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp
index 8025cf3469..6f6783f0e2 100644
--- a/src/mbgl/renderer/painter_fill.cpp
+++ b/src/mbgl/renderer/painter_fill.cpp
@@ -3,18 +3,18 @@
#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>
using namespace mbgl;
void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix) {
- // Abort early.
- if (!bucket.hasData()) return;
-
const FillProperties &properties = layer_desc.getProperties<FillProperties>();
mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor);
@@ -39,6 +39,9 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
bool outline = properties.antialias && !pattern && stroke_color != fill_color;
bool fringeline = properties.antialias && !pattern && stroke_color == fill_color;
+ config.stencilTest = true;
+ config.depthTest = true;
+
// Because we're drawing top-to-bottom, and we update the stencil mask
// befrom, we have to draw the outline first (!)
if (outline && pass == RenderPass::Translucent) {
@@ -53,7 +56,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
static_cast<float>(state.getFramebufferWidth()),
static_cast<float>(state.getFramebufferHeight())
}};
- depthRange(strata, 1.0f);
+ config.depthRange = { strata, 1.0f };
bucket.drawVertices(*outlineShader);
}
@@ -92,8 +95,8 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
spriteAtlas.bind(true);
// Draw the actual triangles into the color & stencil buffer.
- depthMask(true);
- depthRange(strata, 1.0f);
+ config.depthMask = GL_TRUE;
+ config.depthRange = { strata, 1.0f };
bucket.drawElements(*patternShader);
}
}
@@ -109,8 +112,8 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
plainShader->u_color = fill_color;
// Draw the actual triangles into the color & stencil buffer.
- depthMask(true);
- depthRange(strata + strata_epsilon, 1.0f);
+ config.depthMask = GL_TRUE;
+ config.depthRange = { strata + strata_epsilon, 1.0f };
bucket.drawElements(*plainShader);
}
}
@@ -130,7 +133,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
static_cast<float>(state.getFramebufferHeight())
}};
- depthRange(strata + strata_epsilon + strata_epsilon, 1.0f);
+ config.depthRange = { strata + strata_epsilon + strata_epsilon, 1.0f };
bucket.drawVertices(*outlineShader);
}
}
diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp
index 2f8c3face3..f552ea6c43 100644
--- a/src/mbgl/renderer/painter_line.cpp
+++ b/src/mbgl/renderer/painter_line.cpp
@@ -4,18 +4,22 @@
#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;
void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix) {
// Abort early.
if (pass == RenderPass::Opaque) return;
- if (!bucket.hasData()) return;
- depthMask(false);
+ config.stencilTest = true;
+ config.depthTest = true;
+ config.depthMask = GL_FALSE;
const auto &properties = layer_desc.getProperties<LineProperties>();
const auto &layout = bucket.layout;
@@ -49,30 +53,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
float ratio = state.getPixelRatio();
mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor);
- depthRange(strata, 1.0f);
-
- // We're only drawing end caps + round line joins if the line is > 2px. Otherwise, they aren't visible anyway.
- if (bucket.hasPoints() && outset > 1.0f) {
- useProgram(linejoinShader->program);
- linejoinShader->u_matrix = vtxMatrix;
- linejoinShader->u_color = color;
- linejoinShader->u_world = {{
- state.getFramebufferWidth() * 0.5f,
- state.getFramebufferHeight() * 0.5f
- }};
- linejoinShader->u_linewidth = {{
- ((outset - 0.25f) * state.getPixelRatio()),
- ((inset - 0.25f) * state.getPixelRatio())
- }};
-
- float pointSize = std::ceil(state.getPixelRatio() * outset * 2.0);
-#if defined(GL_ES_VERSION_2_0)
- linejoinShader->u_size = pointSize;
-#else
- MBGL_CHECK_ERROR(glPointSize(pointSize));
-#endif
- bucket.drawPoints(*linejoinShader);
- }
+ config.depthRange = { strata, 1.0f };
if (properties.dash_array.from.size()) {
@@ -130,7 +111,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0));
spriteAtlas.bind(true);
- MBGL_CHECK_ERROR(glDepthRange(strata + strata_epsilon, 1.0f)); // may or may not matter
+ config.depthRange = { strata + strata_epsilon, 1.0f }; // may or may not matter
bucket.drawLinePatterns(*linepatternShader);
diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp
index 5fac248ee6..61aff9c1a8 100644
--- a/src/mbgl/renderer/painter_raster.cpp
+++ b/src/mbgl/renderer/painter_raster.cpp
@@ -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;
@@ -23,8 +23,9 @@ void Painter::renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, c
rasterShader->u_contrast_factor = contrastFactor(properties.contrast);
rasterShader->u_spin_weights = spinWeights(properties.hue_rotate);
- depthRange(strata + strata_epsilon, 1.0f);
-
+ config.stencilTest = true;
+ config.depthTest = true;
+ config.depthRange = { strata + strata_epsilon, 1.0f };
bucket.drawRaster(*rasterShader, tileStencilBuffer, coveringRasterArray);
}
}
diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp
index 2785e8bae3..1841f2bc98 100644
--- a/src/mbgl/renderer/painter_symbol.cpp
+++ b/src/mbgl/renderer/painter_symbol.cpp
@@ -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>
@@ -35,7 +36,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
}
// If layerStyle.size > bucket.info.fontSize then labels may collide
- float fontSize = std::fmin(styleProperties.size, bucketProperties.max_size);
+ float fontSize = styleProperties.size;
float fontScale = fontSize / sdfFontSize;
matrix::scale(exMatrix, exMatrix, fontScale, fontScale, 1.0f);
@@ -86,7 +87,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
sdfShader.u_buffer = (haloOffset - styleProperties.halo_width / fontScale) / sdfPx;
- depthRange(strata, 1.0f);
+ config.depthRange = { strata, 1.0f };
(bucket.*drawSDF)(sdfShader);
}
@@ -107,7 +108,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
sdfShader.u_buffer = (256.0f - 64.0f) / 256.0f;
- depthRange(strata + strata_epsilon, 1.0f);
+ config.depthRange = { strata + strata_epsilon, 1.0f };
(bucket.*drawSDF)(sdfShader);
}
}
@@ -121,8 +122,9 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
const auto &properties = layer_desc.getProperties<SymbolProperties>();
const auto &layout = bucket.layout;
- MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
- depthMask(false);
+ config.stencilTest = false;
+ config.depthTest = true;
+ config.depthMask = GL_FALSE;
if (bucket.hasIconData()) {
bool sdf = bucket.sdfIcons;
@@ -184,7 +186,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
iconShader->u_fadezoom = state.getNormalizedZoom() * 10;
iconShader->u_opacity = properties.icon.opacity;
- depthRange(strata, 1.0f);
+ config.depthRange = { strata, 1.0f };
bucket.drawIcons(*iconShader);
}
}
@@ -202,6 +204,4 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
*sdfGlyphShader,
&SymbolBucket::drawGlyphs);
}
-
- MBGL_CHECK_ERROR(glEnable(GL_STENCIL_TEST));
}
diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp
index b00933d24b..7ee64baaf5 100644
--- a/src/mbgl/renderer/raster_bucket.cpp
+++ b/src/mbgl/renderer/raster_bucket.cpp
@@ -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_)
@@ -8,8 +13,17 @@ RasterBucket::RasterBucket(TexturePool& texturePool, const StyleLayoutRaster& la
raster(texturePool) {
}
-void RasterBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) {
+void RasterBucket::upload() {
+ if (hasData()) {
+ raster.upload();
+ uploaded = true;
+ }
+}
+
+void RasterBucket::render(Painter& painter,
+ const StyleLayer& layer_desc,
+ const TileID& id,
+ const mat4& matrix) {
painter.renderRaster(*this, layer_desc, id, matrix);
}
@@ -24,13 +38,6 @@ void RasterBucket::drawRaster(RasterShader& shader, StaticVertexBuffer &vertices
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices.index()));
}
-void RasterBucket::drawRaster(RasterShader& shader, StaticVertexBuffer &vertices, VertexArrayObject &array, GLuint texture_) {
- raster.bind(texture_);
- shader.u_image = 0;
- array.bind(shader, vertices, BUFFER_OFFSET(0));
- MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices.index()));
-}
-
bool RasterBucket::hasData() const {
return raster.isLoaded();
}
diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp
index 22a151cf7d..70434b7d60 100644
--- a/src/mbgl/renderer/raster_bucket.hpp
+++ b/src/mbgl/renderer/raster_bucket.hpp
@@ -5,8 +5,6 @@
#include <mbgl/util/raster.hpp>
#include <mbgl/style/style_bucket.hpp>
-
-
namespace mbgl {
class StyleLayoutRaster;
@@ -18,9 +16,9 @@ class RasterBucket : public Bucket {
public:
RasterBucket(TexturePool&, const StyleLayoutRaster&);
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ bool hasData() const;
bool setImage(const std::string &data);
@@ -28,8 +26,6 @@ public:
void drawRaster(RasterShader& shader, StaticVertexBuffer &vertices, VertexArrayObject &array);
- void drawRaster(RasterShader& shader, StaticVertexBuffer &vertices, VertexArrayObject &array, GLuint texture);
-
Raster raster;
};
diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp
new file mode 100644
index 0000000000..a298b8b57e
--- /dev/null
+++ b/src/mbgl/renderer/render_pass.hpp
@@ -0,0 +1,31 @@
+#ifndef MBGL_RENDERER_RENDER_PASS
+#define MBGL_RENDERER_RENDER_PASS
+
+#include <cstdint>
+#include <type_traits>
+
+namespace mbgl {
+
+enum class RenderPass : uint8_t {
+ None = 0,
+ Opaque = 1 << 0,
+ Translucent = 1 << 1,
+};
+
+constexpr inline RenderPass operator|(RenderPass a, RenderPass b) {
+ return static_cast<RenderPass>(static_cast<std::underlying_type<RenderPass>::type>(a) |
+ static_cast<std::underlying_type<RenderPass>::type>(b));
+}
+
+inline RenderPass operator|=(RenderPass& a, RenderPass b) {
+ return (a = a | b);
+}
+
+constexpr inline RenderPass operator&(RenderPass a, RenderPass b) {
+ return static_cast<RenderPass>(static_cast<std::underlying_type<RenderPass>::type>(a) &
+ static_cast<std::underlying_type<RenderPass>::type>(b));
+}
+
+}
+
+#endif
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp
index 517c8835c6..769feb67a4 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/symbol_bucket.cpp
@@ -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_)
@@ -30,8 +36,23 @@ SymbolBucket::~SymbolBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
-void SymbolBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) {
+void SymbolBucket::upload() {
+ if (hasTextData()) {
+ text.vertices.upload();
+ text.triangles.upload();
+ }
+ if (hasIconData()) {
+ icon.vertices.upload();
+ icon.triangles.upload();
+ }
+
+ uploaded = true;
+}
+
+void SymbolBucket::render(Painter& painter,
+ const StyleLayer& layer_desc,
+ const TileID& id,
+ const mat4& matrix) {
painter.renderSymbol(*this, layer_desc, id, matrix);
}
@@ -183,7 +204,8 @@ void SymbolBucket::addFeatures(const GeometryTileLayer& layer,
if (feature.label.length()) {
shaping = fontStack->getShaping(
/* string */ feature.label,
- /* maxWidth: ems */ layout.text.max_width * 24,
+ /* maxWidth: ems */ layout.placement != PlacementType::Line ?
+ layout.text.max_width * 24 : 0,
/* lineHeight: ems */ layout.text.line_height * 24,
/* horizontalAlign */ horizontalAlign,
/* verticalAlign */ verticalAlign,
diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp
index 9ddd653c5d..b1dc44a113 100644
--- a/src/mbgl/renderer/symbol_bucket.hpp
+++ b/src/mbgl/renderer/symbol_bucket.hpp
@@ -55,9 +55,9 @@ public:
SymbolBucket(Collision &collision);
~SymbolBucket() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ bool hasData() const;
bool hasTextData() const;
bool hasIconData() const;
diff --git a/src/mbgl/shader/linejoin.fragment.glsl b/src/mbgl/shader/linejoin.fragment.glsl
deleted file mode 100644
index 705a57766e..0000000000
--- a/src/mbgl/shader/linejoin.fragment.glsl
+++ /dev/null
@@ -1,14 +0,0 @@
-uniform vec4 u_color;
-uniform vec2 u_linewidth;
-
-varying vec2 v_pos;
-
-void main() {
- float dist = length(v_pos - gl_FragCoord.xy);
-
- // Calculate the antialiasing fade factor. This is either when fading in
- // the line in case of an offset line (v_linewidth.t) or when fading out
- // (v_linewidth.s)
- float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0);
- gl_FragColor = u_color * alpha;
-}
diff --git a/src/mbgl/shader/linejoin.vertex.glsl b/src/mbgl/shader/linejoin.vertex.glsl
deleted file mode 100644
index 2e03561e5b..0000000000
--- a/src/mbgl/shader/linejoin.vertex.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-attribute vec2 a_pos;
-
-uniform mat4 u_matrix;
-uniform vec2 u_world;
-uniform float u_size;
-
-varying vec2 v_pos;
-
-void main() {
- gl_Position = u_matrix * vec4(floor(a_pos / 2.0), 0.0, 1.0);
- v_pos = (gl_Position.xy + 1.0) * u_world;
- gl_PointSize = u_size;
-}
diff --git a/src/mbgl/shader/linejoin_shader.cpp b/src/mbgl/shader/linejoin_shader.cpp
deleted file mode 100644
index b3c5638b5d..0000000000
--- a/src/mbgl/shader/linejoin_shader.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <mbgl/shader/linejoin_shader.hpp>
-#include <mbgl/shader/shaders.hpp>
-#include <mbgl/platform/gl.hpp>
-
-#include <cstdio>
-
-using namespace mbgl;
-
-LinejoinShader::LinejoinShader()
- : Shader(
- "linejoin",
- shaders[LINEJOIN_SHADER].vertex,
- shaders[LINEJOIN_SHADER].fragment
- ) {
- a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
-}
-
-void LinejoinShader::bind(char *offset) {
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
- // Note: We're referring to the vertices in a line array, which are 8 bytes long!
- MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset));
-}
diff --git a/src/mbgl/shader/linejoin_shader.hpp b/src/mbgl/shader/linejoin_shader.hpp
deleted file mode 100644
index 61406fd45c..0000000000
--- a/src/mbgl/shader/linejoin_shader.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef MBGL_SHADER_SHADER_LINEJOIN
-#define MBGL_SHADER_SHADER_LINEJOIN
-
-#include <mbgl/shader/shader.hpp>
-#include <mbgl/shader/uniform.hpp>
-
-namespace mbgl {
-
-class LinejoinShader : public Shader {
-public:
- LinejoinShader();
-
- void bind(char *offset);
-
- UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<float, 4>> u_color = {"u_color", *this};
- Uniform<std::array<float, 2>> u_world = {"u_world", *this};
- Uniform<std::array<float, 2>> u_linewidth = {"u_linewidth", *this};
- Uniform<float> u_size = {"u_size", *this};
-
-private:
- int32_t a_pos = -1;
-};
-
-}
-
-#endif
diff --git a/src/mbgl/shader/uniform.hpp b/src/mbgl/shader/uniform.hpp
index 9a25d105cf..d2a248c609 100644
--- a/src/mbgl/shader/uniform.hpp
+++ b/src/mbgl/shader/uniform.hpp
@@ -9,7 +9,7 @@ namespace mbgl {
template <typename T>
class Uniform {
public:
- Uniform(const GLchar* name, const Shader& shader) {
+ Uniform(const GLchar* name, const Shader& shader) : current() {
location = MBGL_CHECK_ERROR(glGetUniformLocation(shader.program, name));
}
@@ -32,7 +32,7 @@ class UniformMatrix {
public:
typedef std::array<float, C*R> T;
- UniformMatrix(const GLchar* name, const Shader& shader) {
+ UniformMatrix(const GLchar* name, const Shader& shader) : current() {
location = MBGL_CHECK_ERROR(glGetUniformLocation(shader.program, name));
}
diff --git a/src/mbgl/storage/asset_context.hpp b/src/mbgl/storage/asset_context.hpp
new file mode 100644
index 0000000000..44ebdba4ff
--- /dev/null
+++ b/src/mbgl/storage/asset_context.hpp
@@ -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
diff --git a/src/mbgl/storage/asset_request.hpp b/src/mbgl/storage/asset_request.hpp
deleted file mode 100644
index 48d421c3be..0000000000
--- a/src/mbgl/storage/asset_request.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef MBGL_STORAGE_DEFAULT_ASSET_REQUEST
-#define MBGL_STORAGE_DEFAULT_ASSET_REQUEST
-
-#include "shared_request_base.hpp"
-
-namespace mbgl {
-
-class AssetRequest : public SharedRequestBase {
-public:
- AssetRequest(DefaultFileSource::Impl *source, const Resource &resource);
-
- void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr);
- void cancel();
-
-private:
- ~AssetRequest();
- void *ptr = nullptr;
-
- friend class AssetRequestImpl;
-};
-
-}
-
-#endif
diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp
index 4055001fc4..ddfcc89845 100644
--- a/src/mbgl/storage/default_file_source.cpp
+++ b/src/mbgl/storage/default_file_source.cpp
@@ -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>
@@ -10,13 +10,11 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/platform/log.hpp>
-#include <mbgl/map/environment.hpp>
#pragma GCC diagnostic push
-#ifndef __clang__
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wshadow"
-#endif
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop
@@ -28,45 +26,28 @@ namespace algo = boost::algorithm;
namespace mbgl {
-DefaultFileSource::Impl::Impl(FileCache* cache_, const std::string& root)
- : assetRoot(root.empty() ? platform::assetRoot() : root), cache(cache_) {
-}
-
DefaultFileSource::DefaultFileSource(FileCache* cache, const std::string& root)
- : thread(util::make_unique<util::Thread<Impl>>("FileSource", cache, root)) {
+ : thread(util::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) {
}
DefaultFileSource::~DefaultFileSource() {
MBGL_VERIFY_THREAD(tid);
}
-SharedRequestBase *DefaultFileSource::Impl::find(const Resource &resource) {
- // We're using a set of pointers here instead of a map between url and SharedRequestBase because
- // we need to find the requests both by pointer and by URL. Given that the number of requests
- // is generally very small (typically < 10 at a time), hashing by URL incurs too much overhead
- // anyway.
- const auto it = pending.find(resource);
- if (it != pending.end()) {
- return it->second;
- }
- return nullptr;
-}
-
Request* DefaultFileSource::request(const Resource& resource,
uv_loop_t* l,
- const Environment& env,
Callback callback) {
- auto req = new Request(resource, l, env, std::move(callback));
+ auto req = new Request(resource, l, std::move(callback));
// 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, std::move(req), thread->get());
+ thread->invoke(&Impl::add, req);
return req;
}
-void DefaultFileSource::request(const Resource& resource, const Environment& env, Callback callback) {
- request(resource, nullptr, env, std::move(callback));
+void DefaultFileSource::request(const Resource& resource, Callback callback) {
+ request(resource, nullptr, std::move(callback));
}
void DefaultFileSource::cancel(Request *req) {
@@ -74,133 +55,127 @@ void DefaultFileSource::cancel(Request *req) {
// 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::cancel, std::move(req));
+ thread->invoke(&Impl::cancel, req);
}
-void DefaultFileSource::abort(const Environment &env) {
- thread->invoke(&Impl::abort, std::ref(env));
-}
+// ----- Impl -----
-void DefaultFileSource::Impl::add(Request* req, uv_loop_t* loop) {
- const Resource &resource = req->resource;
+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_)) {
+}
- // We're adding a new Request.
- SharedRequestBase *sharedRequest = find(resource);
- if (!sharedRequest) {
- // There is no request for this URL yet. Create a new one and start it.
- if (algo::starts_with(resource.url, "asset://")) {
- sharedRequest = new AssetRequest(this, resource);
- } else {
- sharedRequest = new HTTPRequest(this, resource);
- }
+DefaultFileRequest* DefaultFileSource::Impl::find(const Resource& resource) {
+ const auto it = pending.find(resource);
+ if (it != pending.end()) {
+ return &it->second;
+ }
+ return nullptr;
+}
- const bool inserted = pending.emplace(resource, sharedRequest).second;
- assert(inserted);
- (void (inserted)); // silence unused variable warning on Release builds.
+void DefaultFileSource::Impl::add(Request* req) {
+ const Resource& resource = req->resource;
+ DefaultFileRequest* request = find(resource);
- // But first, we're going to start querying the database if it exists.
- if (!cache) {
- sharedRequest->start(loop);
- } else {
- // Otherwise, first check the cache for existing data so that we can potentially
- // revalidate the information without having to redownload everything.
- cache->get(resource, [this, resource, loop](std::unique_ptr<Response> response) {
- processResult(resource, std::move(response), loop);
- });
- }
+ if (request) {
+ request->observers.insert(req);
+ return;
}
- sharedRequest->subscribe(req);
-}
-void DefaultFileSource::Impl::cancel(Request* req) {
- SharedRequestBase *sharedRequest = find(req->resource);
- if (sharedRequest) {
- // If the number of dependent requests of the SharedRequestBase drops to zero, the
- // unsubscribe callback triggers the removal of the SharedRequestBase pointer from the list
- // of pending requests and initiates cancelation.
- sharedRequest->unsubscribe(req);
+ request = &pending.emplace(resource, DefaultFileRequest(resource)).first->second;
+ request->observers.insert(req);
+
+ if (cache) {
+ startCacheRequest(resource);
} else {
- // There is no request for this URL anymore. Likely, the request already completed
- // before we got around to process the cancelation request.
+ startRealRequest(resource);
}
-
- // Send a message back to the requesting thread and notify it that this request has been
- // canceled and is now safe to be deleted.
- req->destruct();
}
-void DefaultFileSource::Impl::processResult(const Resource& resource, std::shared_ptr<const Response> response, uv_loop_t* loop) {
- SharedRequestBase *sharedRequest = find(resource);
- if (sharedRequest) {
- if (response) {
- // This entry was stored in the cache. Now determine if we need to revalidate.
+void DefaultFileSource::Impl::startCacheRequest(const Resource& resource) {
+ // Check the cache for existing data so that we can potentially
+ // revalidate the information without having to redownload everything.
+ cache->get(resource, [this, resource](std::unique_ptr<Response> response) {
+ DefaultFileRequest* request = find(resource);
+
+ if (!request) {
+ // There is no request for this URL anymore. Likely, the request was canceled
+ // before we got around to process the cache result.
+ return;
+ }
+
+ auto expired = [&response] {
const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
SystemClock::now().time_since_epoch()).count();
- if (response->expires > now) {
- // The response is fresh. We're good to notify the caller.
- sharedRequest->notify(response, FileCache::Hint::No);
- sharedRequest->cancel();
- return;
- } else {
- // The cached response is stale. Now run the real request.
- sharedRequest->start(loop, response);
- }
+ return response->expires <= now;
+ };
+
+ if (!response || expired()) {
+ // No response or stale cache. Run the real request.
+ startRealRequest(resource, std::move(response));
} else {
- // There is no response. Now run the real request.
- sharedRequest->start(loop);
+ // The response is fresh. We're good to notify the caller.
+ notify(request, std::move(response), FileCache::Hint::No);
}
+ });
+}
+
+void DefaultFileSource::Impl::startRealRequest(const Resource& resource, std::shared_ptr<const Response> response) {
+ DefaultFileRequest* request = find(resource);
+
+ auto callback = [request, this] (std::shared_ptr<const Response> res, FileCache::Hint hint) {
+ notify(request, res, hint);
+ };
+
+ if (algo::starts_with(resource.url, "asset://")) {
+ request->request = assetContext->createRequest(resource, callback, loop, assetRoot);
} else {
- // There is no request for this URL anymore. Likely, the request was canceled
- // before we got around to process the cache result.
+ request->request = httpContext->createRequest(resource, callback, loop, response);
}
}
-// Aborts all requests that are part of the current environment.
-void DefaultFileSource::Impl::abort(const Environment& env) {
- // Construct a cancellation response.
- auto res = util::make_unique<Response>();
- res->status = Response::Error;
- res->message = "Environment is terminating";
- std::shared_ptr<const Response> response = std::move(res);
-
- // Iterate through all pending requests and remove them in case they're abandoned.
- util::erase_if(pending, [&](const std::pair<Resource, SharedRequestBase *> &it) -> bool {
- // Obtain all pending requests that are in the current environment.
- const auto aborted = it.second->removeAllInEnvironment(env);
-
- // Notify all observers.
- for (auto req : aborted) {
- req->notify(response);
+void DefaultFileSource::Impl::cancel(Request* req) {
+ DefaultFileRequest* request = find(req->resource);
+
+ if (request) {
+ // If the number of dependent requests of the DefaultFileRequest drops to zero,
+ // cancel the request and remove it from the pending list.
+ request->observers.erase(req);
+ if (request->observers.empty()) {
+ if (request->request) {
+ request->request->cancel();
+ }
+ pending.erase(request->resource);
}
+ } else {
+ // There is no request for this URL anymore. Likely, the request already completed
+ // before we got around to process the cancelation request.
+ }
- // Finally, remove all requests that are now abandoned.
- if (it.second->abandoned()) {
- it.second->cancel();
- return true;
- } else {
- return false;
- }
- });
+ // Send a message back to the requesting thread and notify it that this request has been
+ // canceled and is now safe to be deleted.
+ req->destruct();
}
-void DefaultFileSource::Impl::notify(SharedRequestBase *sharedRequest,
- const std::set<Request *> &observers,
- std::shared_ptr<const Response> response, FileCache::Hint hint) {
+void DefaultFileSource::Impl::notify(DefaultFileRequest* request, std::shared_ptr<const Response> response, FileCache::Hint hint) {
// First, remove the request, since it might be destructed at any point now.
- assert(find(sharedRequest->resource) == sharedRequest);
- pending.erase(sharedRequest->resource);
+ assert(find(request->resource) == request);
+ assert(response);
- if (response) {
- if (cache) {
- // Store response in database
- cache->put(sharedRequest->resource, response, hint);
- }
+ // Notify all observers.
+ for (auto req : request->observers) {
+ req->notify(response);
+ }
- // Notify all observers.
- for (auto req : observers) {
- req->notify(response);
- }
+ if (cache) {
+ // Store response in database
+ cache->put(request->resource, response, hint);
}
+
+ pending.erase(request->resource);
}
}
diff --git a/src/mbgl/storage/default_file_source_impl.hpp b/src/mbgl/storage/default_file_source_impl.hpp
index 97210dc442..ed2d248d0a 100644
--- a/src/mbgl/storage/default_file_source_impl.hpp
+++ b/src/mbgl/storage/default_file_source_impl.hpp
@@ -2,33 +2,47 @@
#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 SharedRequestBase;
+class RequestBase;
-class DefaultFileSource::Impl {
-public:
- Impl(FileCache *cache, const std::string &root = "");
+struct DefaultFileRequest {
+ const Resource resource;
+ std::set<Request*> observers;
+ RequestBase* request = nullptr;
- void notify(SharedRequestBase *sharedRequest, const std::set<Request *> &observers,
- std::shared_ptr<const Response> response, FileCache::Hint hint);
- SharedRequestBase *find(const Resource &resource);
+ DefaultFileRequest(const Resource& resource_)
+ : resource(resource_) {}
+};
- void add(Request* request, uv_loop_t* loop);
- void cancel(Request* request);
- void abort(const Environment& env);
+class DefaultFileSource::Impl {
+public:
+ Impl(uv_loop_t*, FileCache*, const std::string& = "");
- const std::string assetRoot;
+ void add(Request*);
+ void cancel(Request*);
private:
- void processResult(const Resource& resource, std::shared_ptr<const Response> response, uv_loop_t* loop);
+ DefaultFileRequest* find(const Resource&);
- std::unordered_map<Resource, SharedRequestBase *, Resource::Hash> pending;
- FileCache *cache = nullptr;
+ void startCacheRequest(const Resource&);
+ void startRealRequest(const Resource&, std::shared_ptr<const Response> = nullptr);
+ 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;
};
}
diff --git a/src/mbgl/storage/http_context.cpp b/src/mbgl/storage/http_context.cpp
new file mode 100644
index 0000000000..c747490804
--- /dev/null
+++ b/src/mbgl/storage/http_context.cpp
@@ -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();
+ }
+}
+
+}
diff --git a/src/mbgl/storage/http_context.hpp b/src/mbgl/storage/http_context.hpp
index 6b9518dab3..64a8afa6dd 100644
--- a/src/mbgl/storage/http_context.hpp
+++ b/src/mbgl/storage/http_context.hpp
@@ -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;
+class HTTPContext {
+public:
+ static std::unique_ptr<HTTPContext> createContext(uv_loop_t*);
-// 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.
+ HTTPContext(uv_loop_t*);
+ virtual ~HTTPContext();
-template <typename Context>
-class HTTPContext : public ThreadContext<Context> {
-public:
- HTTPContext(uv_loop_t *loop);
- ~HTTPContext();
+ virtual RequestBase* createRequest(const Resource&,
+ RequestBase::Callback,
+ uv_loop_t*,
+ std::shared_ptr<const Response>) = 0;
- void addRequest(HTTPRequest *request);
- void removeRequest(HTTPRequest *baton);
+ 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
diff --git a/src/mbgl/storage/http_request.hpp b/src/mbgl/storage/http_request.hpp
deleted file mode 100644
index 54e9a77ef0..0000000000
--- a/src/mbgl/storage/http_request.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef MBGL_STORAGE_DEFAULT_HTTP_REQUEST
-#define MBGL_STORAGE_DEFAULT_HTTP_REQUEST
-
-#include "shared_request_base.hpp"
-
-namespace mbgl {
-
-class HTTPRequest : public SharedRequestBase {
-public:
- HTTPRequest(DefaultFileSource::Impl *source, const Resource &resource);
-
- void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr);
- void cancel();
-
- void retryImmediately();
-
-private:
- ~HTTPRequest();
- void *ptr = nullptr;
-
- friend class HTTPRequestImpl;
-};
-
-}
-
-#endif
diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp
index ea80e59503..d413cabbe7 100644
--- a/src/mbgl/storage/request.cpp
+++ b/src/mbgl/storage/request.cpp
@@ -16,8 +16,8 @@ namespace mbgl {
struct Request::Canceled { std::mutex mutex; bool confirmed = false; };
// Note: This requires that loop is running in the current thread (or not yet running).
-Request::Request(const Resource &resource_, uv_loop_t *loop, const Environment &env_, Callback callback_)
- : callback(callback_), resource(resource_), env(env_) {
+Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
+ : callback(callback_), resource(resource_) {
// When there is no loop supplied (== nullptr), the callback will be fired in an arbitrary
// thread (the thread notify() is called from) rather than kicking back to the calling thread.
if (loop) {
diff --git a/src/mbgl/storage/request_base.hpp b/src/mbgl/storage/request_base.hpp
new file mode 100644
index 0000000000..c8dfad4778
--- /dev/null
+++ b/src/mbgl/storage/request_base.hpp
@@ -0,0 +1,35 @@
+#ifndef MBGL_STORAGE_REQUEST
+#define MBGL_STORAGE_REQUEST
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/storage/file_cache.hpp>
+#include <mbgl/storage/resource.hpp>
+
+#include <memory>
+#include <functional>
+
+namespace mbgl {
+
+class Response;
+
+class RequestBase : private util::noncopyable {
+public:
+ using Callback = std::function<void (std::shared_ptr<const Response> response, FileCache::Hint hint)>;
+
+ RequestBase(const Resource& resource_, Callback notify_)
+ : resource(resource_)
+ , notify(notify_) {
+ }
+
+ virtual ~RequestBase() = default;
+ virtual void cancel() = 0;
+ virtual void retry() {};
+
+protected:
+ Resource resource;
+ Callback notify;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/storage/shared_request_base.hpp b/src/mbgl/storage/shared_request_base.hpp
deleted file mode 100644
index d7ed00264a..0000000000
--- a/src/mbgl/storage/shared_request_base.hpp
+++ /dev/null
@@ -1,106 +0,0 @@
-#ifndef MBGL_STORAGE_DEFAULT_SHARED_REQUEST_BASE
-#define MBGL_STORAGE_DEFAULT_SHARED_REQUEST_BASE
-
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/file_cache.hpp>
-#include <mbgl/storage/default_file_source_impl.hpp>
-#include <mbgl/storage/request.hpp>
-#include <mbgl/util/util.hpp>
-#include <mbgl/util/noncopyable.hpp>
-
-#include <string>
-#include <set>
-#include <vector>
-#include <cassert>
-
-typedef struct uv_loop_s uv_loop_t;
-
-namespace mbgl {
-
-class Request;
-class Response;
-class DefaultFileSource;
-
-class SharedRequestBase : private util::noncopyable {
-protected:
- MBGL_STORE_THREAD(tid)
-
-public:
- SharedRequestBase(DefaultFileSource::Impl *source_, const Resource &resource_)
- : resource(resource_), source(source_) {}
-
- virtual void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr) = 0;
- virtual void cancel() = 0;
-
- void notify(std::shared_ptr<const Response> response, FileCache::Hint hint) {
- MBGL_VERIFY_THREAD(tid);
-
- if (source) {
- source->notify(this, observers, response, hint);
- }
- }
-
- void subscribe(Request *request) {
- MBGL_VERIFY_THREAD(tid);
-
- observers.insert(request);
- }
-
- void unsubscribe(Request *request) {
- MBGL_VERIFY_THREAD(tid);
-
- observers.erase(request);
-
- if (abandoned()) {
- // There are no observers anymore. We are initiating cancelation.
- if (source) {
- // First, remove this SharedRequestBase from the source.
- source->notify(this, observers, nullptr, FileCache::Hint::No);
- }
-
- // Then, initiate cancelation of this request
- cancel();
- }
- }
-
- bool abandoned() const {
- return observers.empty();
- }
-
- std::vector<Request *> removeAllInEnvironment(const Environment &env) {
- MBGL_VERIFY_THREAD(tid);
-
- std::vector<Request *> result;
-
- // Removes all Requests in the supplied environment and returns a list
- // of them.
- util::erase_if(observers, [&](Request *req) -> bool {
- if (&req->env == &env) {
- result.push_back(req);
- return true;
- } else {
- return false;
- }
- });
-
- return result;
- }
-
-protected:
- virtual ~SharedRequestBase() {
- MBGL_VERIFY_THREAD(tid);
- }
-
-public:
- const Resource resource;
-
-protected:
- DefaultFileSource::Impl *source = nullptr;
-
-private:
- std::set<Request *> observers;
-};
-
-}
-
-#endif
diff --git a/src/mbgl/storage/thread_context.hpp b/src/mbgl/storage/thread_context.hpp
deleted file mode 100644
index 763c83a25b..0000000000
--- a/src/mbgl/storage/thread_context.hpp
+++ /dev/null
@@ -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
diff --git a/src/mbgl/style/property_fallback.cpp b/src/mbgl/style/property_fallback.cpp
index dce226cdef..655a75df9b 100644
--- a/src/mbgl/style/property_fallback.cpp
+++ b/src/mbgl/style/property_fallback.cpp
@@ -22,7 +22,6 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::LineBlur, defaultStyleProperties<LineProperties>().blur },
{ PropertyKey::IconOpacity, defaultStyleProperties<SymbolProperties>().icon.opacity },
- { PropertyKey::IconRotate, defaultStyleProperties<SymbolProperties>().icon.rotate },
{ PropertyKey::IconSize, defaultStyleProperties<SymbolProperties>().icon.size },
{ PropertyKey::IconColor, defaultStyleProperties<SymbolProperties>().icon.color },
{ PropertyKey::IconHaloColor, defaultStyleProperties<SymbolProperties>().icon.halo_color },
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 0a2a53075b..d592e61317 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -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;
+}
+
}
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index dde21173f3..42e94984dd 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -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;
};
}
diff --git a/src/mbgl/style/style_layer.cpp b/src/mbgl/style/style_layer.cpp
index 542c4eb8a4..8713b73b12 100644
--- a/src/mbgl/style/style_layer.cpp
+++ b/src/mbgl/style/style_layer.cpp
@@ -13,6 +13,21 @@ bool StyleLayer::isBackground() const {
return type == StyleLayerType::Background;
}
+bool StyleLayer::isVisible() const {
+ switch (type) {
+ case StyleLayerType::Fill:
+ return getProperties<FillProperties>().isVisible();
+ case StyleLayerType::Line:
+ return getProperties<LineProperties>().isVisible();
+ case StyleLayerType::Symbol:
+ return getProperties<SymbolProperties>().isVisible();
+ case StyleLayerType::Raster:
+ return getProperties<RasterProperties>().isVisible();
+ default:
+ return false;
+ }
+}
+
void StyleLayer::setClasses(const std::vector<std::string> &class_names, const TimePoint now,
const PropertyTransition &defaultTransition) {
// Stores all keys that we have already added transitions for.
@@ -194,7 +209,6 @@ void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const Tim
properties.set<SymbolProperties>();
SymbolProperties &symbol = properties.get<SymbolProperties>();
applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now, zoomHistory);
- applyTransitionedStyleProperty(PropertyKey::IconRotate, symbol.icon.rotate, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconSize, symbol.icon.size, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconColor, symbol.icon.color, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconHaloColor, symbol.icon.halo_color, z, now, zoomHistory);
diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp
index a00b7084fa..774ed39012 100644
--- a/src/mbgl/style/style_layer.hpp
+++ b/src/mbgl/style/style_layer.hpp
@@ -35,6 +35,9 @@ public:
// Determines whether this layer is the background layer.
bool isBackground() const;
+ // Checks whether the layer is currently visible at all.
+ bool isVisible() const;
+
// Updates the StyleProperties information in this layer by evaluating all
// pending transitions and applied classes in order.
void updateProperties(float z, TimePoint now, ZoomHistory &zoomHistory);
diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp
index afed2eeee6..313fe3df89 100644
--- a/src/mbgl/style/style_parser.cpp
+++ b/src/mbgl/style/style_parser.cpp
@@ -10,9 +10,9 @@
#include <csscolorparser/csscolorparser.hpp>
#pragma GCC diagnostic push
-#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#endif
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop
@@ -811,7 +811,6 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value);
parseOptionalProperty<PropertyTransition>("icon-opacity-transition", Key::IconOpacity, klass, value);
- parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, klass, value);
parseOptionalProperty<Function<float>>("icon-size", Key::IconSize, klass, value);
parseOptionalProperty<PropertyTransition>("icon-size-transition", Key::IconSize, klass, value);
parseOptionalProperty<Function<Color>>("icon-color", Key::IconColor, klass, value);
diff --git a/src/mbgl/style/style_properties.hpp b/src/mbgl/style/style_properties.hpp
index f50722542d..8e8619fb99 100644
--- a/src/mbgl/style/style_properties.hpp
+++ b/src/mbgl/style/style_properties.hpp
@@ -51,7 +51,6 @@ struct SymbolProperties {
struct {
float opacity = 1.0f;
- float rotate = 0.0f;
float size = 1.0f;
Color color = {{ 0, 0, 0, 1 }};
Color halo_color = {{ 0, 0, 0, 0 }};
diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp
index 3b24d63998..f6ffcd6865 100644
--- a/src/mbgl/style/types.hpp
+++ b/src/mbgl/style/types.hpp
@@ -91,13 +91,15 @@ MBGL_DEFINE_ENUM_CLASS(CapTypeClass, CapType, {
enum class JoinType : uint8_t {
Miter,
Bevel,
- Round
+ Round,
+ FlipBevel
};
MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, {
{ JoinType::Miter, "miter" },
{ JoinType::Bevel, "bevel" },
{ JoinType::Round, "round" },
+ { JoinType::FlipBevel, "flipbevel" },
});
// -------------------------------------------------------------------------------------------------
diff --git a/src/mbgl/text/collision.hpp b/src/mbgl/text/collision.hpp
index bba07b0fb1..50b8e5b218 100644
--- a/src/mbgl/text/collision.hpp
+++ b/src/mbgl/text/collision.hpp
@@ -9,12 +9,13 @@
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wshadow"
#ifdef __clang__
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#endif
+#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wdeprecated-register"
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
-#else
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#endif
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp
index 50270d9217..cabd8e2842 100644
--- a/src/mbgl/util/mat4.cpp
+++ b/src/mbgl/util/mat4.cpp
@@ -87,7 +87,7 @@ void matrix::copy(mat4& out, const mat4& a) {
}
void matrix::translate(mat4& out, const mat4& a, float x, float y, float z) {
- if (a == out) {
+ if (&a == &out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
@@ -124,7 +124,7 @@ void matrix::rotate_z(mat4& out, const mat4& a, float rad) {
a12 = a[6],
a13 = a[7];
- if (a != out) { // If the source and destination differ, copy the unchanged last row
+ if (&a != &out) { // If the source and destination differ, copy the unchanged last row
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp
index d0e305c33a..f2171a6165 100644
--- a/src/mbgl/util/raster.cpp
+++ b/src/mbgl/util/raster.cpp
@@ -46,16 +46,7 @@ void Raster::bind(bool linear) {
}
if (img && !textured) {
- texture = texturePool.getTextureID();
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
-#ifndef GL_ES_VERSION_2_0
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
-#endif
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getData()));
- img.reset();
- textured = true;
+ upload();
} else if (textured) {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
}
@@ -68,24 +59,17 @@ void Raster::bind(bool linear) {
}
}
-// overload ::bind for prerendered raster textures
-void Raster::bind(const GLuint custom_texture) {
+void Raster::upload() {
if (img && !textured) {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, custom_texture));
+ texture = texturePool.getTextureID();
+ MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
+#ifndef GL_ES_VERSION_2_0
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
+#endif
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getData()));
img.reset();
textured = true;
- } else if (textured) {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, custom_texture));
}
-
- GLuint new_filter = GL_LINEAR;
- if (new_filter != this->filter) {
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, new_filter));
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, new_filter));
- filter = new_filter;
- }
-
}
diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp
index f19ff7178a..1789ae87f7 100644
--- a/src/mbgl/util/raster.hpp
+++ b/src/mbgl/util/raster.hpp
@@ -25,8 +25,8 @@ public:
// bind current texture
void bind(bool linear = false);
- // bind prerendered texture
- void bind(const GLuint texture);
+ // uploads the texture if it hasn't been uploaded yet.
+ void upload();
// loaded status
bool isLoaded() const;
diff --git a/src/mbgl/util/run_loop.cpp b/src/mbgl/util/run_loop.cpp
index fd9ad43060..7f277c9885 100644
--- a/src/mbgl/util/run_loop.cpp
+++ b/src/mbgl/util/run_loop.cpp
@@ -5,8 +5,13 @@ namespace util {
uv::tls<RunLoop> RunLoop::current;
-RunLoop::RunLoop()
- : async(*loop, std::bind(&RunLoop::process, this)) {
+RunLoop::RunLoop(uv_loop_t* loop)
+ : async(loop, std::bind(&RunLoop::process, this)) {
+ current.set(this);
+}
+
+RunLoop::~RunLoop() {
+ current.set(nullptr);
}
void RunLoop::withMutex(std::function<void()>&& fn) {
@@ -24,13 +29,6 @@ void RunLoop::process() {
}
}
-void RunLoop::run() {
- assert(!current.get());
- current.set(this);
- loop.run();
- current.set(nullptr);
-}
-
void RunLoop::stop() {
invoke([&] { async.unref(); });
}
diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp
index d785854e79..767a47b754 100644
--- a/src/mbgl/util/run_loop.hpp
+++ b/src/mbgl/util/run_loop.hpp
@@ -14,9 +14,9 @@ namespace util {
class RunLoop : private util::noncopyable {
public:
- RunLoop();
+ RunLoop(uv_loop_t*);
+ ~RunLoop();
- void run();
void stop();
// Invoke fn() in the runloop thread.
@@ -29,7 +29,7 @@ public:
// Invoke fn() in the runloop thread, then invoke callback(result) in the current thread.
template <class Fn, class R>
- void invokeWithResult(Fn&& fn, std::function<void (R)> callback) {
+ void invokeWithResult(Fn&& fn, std::function<void (R)>&& callback) {
RunLoop* outer = current.get();
assert(outer);
@@ -37,21 +37,35 @@ public:
/*
With C++14, we could write:
- outer->invoke([callback, result = std::move(fn())] () mutable {
- callback(std::move(result));
+ outer->invoke([cb = std::move(callback), result = std::move(fn())] () mutable {
+ cb(std::move(result));
});
Instead we're using a workaround with std::bind
to obtain move-capturing semantics with C++11:
http://stackoverflow.com/a/12744730/52207
*/
- outer->invoke(std::bind([callback] (R& result) {
- callback(std::move(result));
- }, std::move(fn())));
+ outer->invoke(std::bind([] (std::function<void (R)>& cb, R& result) {
+ cb(std::move(result));
+ }, std::move(callback), std::move(fn())));
});
}
- uv_loop_t* get() { return *loop; }
+ // 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 async.get()->loop; }
+
+ static uv::tls<RunLoop> current;
private:
// A movable type-erasing invokable entity wrapper. This allows to store arbitrary invokable
@@ -71,15 +85,11 @@ private:
using Queue = std::queue<std::unique_ptr<Message>>;
- static uv::tls<RunLoop> current;
-
void withMutex(std::function<void()>&&);
void process();
Queue queue;
std::mutex mutex;
-
- uv::loop loop;
uv::async async;
};
diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp
index 4831b9efc2..e97872a502 100644
--- a/src/mbgl/util/thread.hpp
+++ b/src/mbgl/util/thread.hpp
@@ -3,9 +3,11 @@
#include <future>
#include <thread>
+#include <atomic>
#include <functional>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/platform/platform.hpp>
namespace {
@@ -33,11 +35,16 @@ namespace util {
// Thread<> constructor blocks until the thread and the Object are fully created, so after the
// object creation, it's safe to obtain the Object stored in this thread.
+enum class ThreadPriority : bool {
+ Regular,
+ Low,
+};
+
template <class Object>
class Thread {
public:
template <class... Args>
- Thread(const std::string& name, Args&&... args);
+ Thread(const std::string& name, ThreadPriority priority, Args&&... args);
~Thread();
// Invoke object->fn(args...) in the runloop thread.
@@ -48,11 +55,33 @@ public:
// Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread.
template <typename Fn, class R, class... Args>
- void invokeWithResult(Fn fn, std::function<void (R)> callback, Args&&... args) {
- loop->invokeWithResult(std::bind(fn, object, args...), callback);
+ void invokeWithResult(Fn fn, std::function<void (R)>&& callback, Args&&... args) {
+ loop->invokeWithResult(std::bind(fn, object, args...), std::move(callback));
+ }
+
+ // 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...), std::move(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::packaged_task<R ()> task(std::bind(fn, object, args...));
+ std::future<R> future = task.get_future();
+ loop->invoke(std::move(task));
+ return future.get();
}
- uv_loop_t* get() { return loop->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::packaged_task<void ()> task(std::bind(fn, object, args...));
+ std::future<void> future = task.get_future();
+ loop->invoke(std::move(task));
+ return future.get();
+ }
private:
Thread(const Thread&) = delete;
@@ -74,7 +103,7 @@ private:
template <class Object>
template <class... Args>
-Thread<Object>::Thread(const std::string& name, Args&&... args) {
+Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&... args) {
// Note: We're using std::tuple<> to store the arguments because GCC 4.9 has a bug
// when expanding parameters packs captured in lambdas.
std::tuple<Args...> params = std::forward_as_tuple(::std::forward<Args>(args)...);
@@ -86,6 +115,10 @@ Thread<Object>::Thread(const std::string& name, Args&&... args) {
(void(name));
#endif
+ if (priority == ThreadPriority::Low) {
+ platform::makeThreadLowPriority();
+ }
+
constexpr auto seq = typename integer_sequence<sizeof...(Args)>::type();
run(std::move(params), seq);
});
@@ -96,14 +129,24 @@ 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_;
+ uv::loop l;
+
+ {
+ RunLoop loop_(l.get());
+ loop = &loop_;
- RunLoop loop_;
- loop = &loop_;
+ Object object_(l.get(), std::get<I>(std::forward<P>(params))...);
+ object = &object_;
+
+ running.set_value();
+ l.run();
+
+ loop = nullptr;
+ object = nullptr;
+ }
- running.set_value();
- loop_.run();
+ // Run the loop again to ensure that async close callbacks have been called.
+ l.run();
joinable.get_future().get();
}
diff --git a/src/mbgl/util/uv.cpp b/src/mbgl/util/uv.cpp
index 5dae34ebd0..d76e42f67d 100644
--- a/src/mbgl/util/uv.cpp
+++ b/src/mbgl/util/uv.cpp
@@ -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) {
diff --git a/src/mbgl/util/uv_detail.hpp b/src/mbgl/util/uv_detail.hpp
index 96d5442462..059d7c0ddb 100644
--- a/src/mbgl/util/uv_detail.hpp
+++ b/src/mbgl/util/uv_detail.hpp
@@ -28,13 +28,6 @@ UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
namespace uv {
-template <class T>
-void close(std::unique_ptr<T> ptr) {
- uv_close(reinterpret_cast<uv_handle_t*>(ptr.release()), [](uv_handle_t* handle) {
- delete reinterpret_cast<T*>(handle);
- });
-}
-
class loop : public mbgl::util::noncopyable {
public:
inline loop() {
@@ -74,46 +67,93 @@ private:
uv_loop_t *l = nullptr;
};
-class async : public mbgl::util::noncopyable {
+template <class T>
+class handle : public mbgl::util::noncopyable {
+public:
+ inline handle() : t(reinterpret_cast<uv_handle_t*>(new T)) {
+ t->data = this;
+ }
+
+ inline ~handle() {
+ uv_close(t.release(), [](uv_handle_t* handle) {
+ delete reinterpret_cast<T*>(handle);
+ });
+ }
+
+ inline void ref() {
+ uv_ref(t.get());
+ }
+
+ inline void unref() {
+ uv_unref(t.get());
+ }
+
+ inline T* get() {
+ return reinterpret_cast<T*>(t.get());
+ }
+
+private:
+ std::unique_ptr<uv_handle_t> t;
+};
+
+class async : public handle<uv_async_t> {
public:
inline async(uv_loop_t* loop, std::function<void ()> fn_)
- : a(new uv_async_t)
- , fn(fn_)
- {
- a->data = this;
- if (uv_async_init(loop, a.get(), async_cb) != 0) {
+ : fn(fn_) {
+ if (uv_async_init(loop, get(), async_cb) != 0) {
throw std::runtime_error("failed to initialize async");
}
}
- inline ~async() {
- close(std::move(a));
- }
-
inline void send() {
- if (uv_async_send(a.get()) != 0) {
+ if (uv_async_send(get()) != 0) {
throw std::runtime_error("failed to async send");
}
}
- inline void ref() {
- uv_ref(reinterpret_cast<uv_handle_t*>(a.get()));
+private:
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+ static void async_cb(uv_async_t* a, int) {
+#else
+ static void async_cb(uv_async_t* a) {
+#endif
+ reinterpret_cast<async*>(a->data)->fn();
}
- inline void unref() {
- uv_unref(reinterpret_cast<uv_handle_t*>(a.get()));
+ std::function<void ()> fn;
+};
+
+class timer : public handle<uv_timer_t> {
+public:
+ inline timer(uv_loop_t* loop) {
+ if (uv_timer_init(loop, get()) != 0) {
+ throw std::runtime_error("failed to initialize timer");
+ }
+ }
+
+ inline void start(uint64_t timeout, uint64_t repeat, std::function<void ()> fn_) {
+ fn = fn_;
+ if (uv_timer_start(get(), timer_cb, timeout, repeat) != 0) {
+ throw std::runtime_error("failed to start timer");
+ }
+ }
+
+ inline void stop() {
+ fn = nullptr;
+ if (uv_timer_stop(get()) != 0) {
+ throw std::runtime_error("failed to stop timer");
+ }
}
private:
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
- static void async_cb(uv_async_t* a, int) {
+ static void timer_cb(uv_timer_t* t, int) {
#else
- static void async_cb(uv_async_t* a) {
+ static void timer_cb(uv_timer_t* t) {
#endif
- reinterpret_cast<async*>(a->data)->fn();
+ reinterpret_cast<timer*>(t->data)->fn();
}
- std::unique_ptr<uv_async_t> a;
std::function<void ()> fn;
};
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
index 3559cdd71f..9792f1a099 100644
--- a/src/mbgl/util/worker.cpp
+++ b/src/mbgl/util/worker.cpp
@@ -1,73 +1,30 @@
#include <mbgl/util/worker.hpp>
+#include <mbgl/platform/platform.hpp>
#include <cassert>
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*) {}
- for (std::size_t i = 0; i < count; i++) {
- threads.emplace_back(&Worker::workLoop, this);
- }
-}
-
-Worker::~Worker() {
- MBGL_VERIFY_THREAD(tid);
-
- if (active++ == 0) {
- queue->ref();
- }
-
- channel.send(Work());
-
- for (auto& thread : threads) {
- thread.join();
- }
-
- queue->stop();
-}
-
-void Worker::send(Fn work, Fn after) {
- MBGL_VERIFY_THREAD(tid);
- assert(work);
-
- if (active++ == 0) {
- queue->ref();
+ void doWork(Fn work) {
+ work();
}
+};
- 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));
+Worker::Worker(std::size_t count) {
+ for (std::size_t i = 0; i < count; i++) {
+ threads.emplace_back(util::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low));
}
-
- // Make sure to close all other workers too.
- channel.send(Work());
}
-void Worker::afterWork(Fn after) {
- if (after) {
- after();
- }
+Worker::~Worker() = default;
- if (--active == 0) {
- queue->unref();
- }
+void Worker::send(Fn&& work, Fn&& after) {
+ threads[current]->invokeWithResult(&Worker::Impl::doWork, std::move(after), std::move(work));
+ current = (current + 1) % threads.size();
}
}
diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp
index 86c2e6acf4..d8fbf6df7d 100644
--- a/src/mbgl/util/worker.hpp
+++ b/src/mbgl/util/worker.hpp
@@ -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);
+ 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;
};
}