diff options
Diffstat (limited to 'src/mbgl')
42 files changed, 733 insertions, 558 deletions
diff --git a/src/mbgl/actor/mailbox.cpp b/src/mbgl/actor/mailbox.cpp index ae3c0967af..5f60629833 100644 --- a/src/mbgl/actor/mailbox.cpp +++ b/src/mbgl/actor/mailbox.cpp @@ -52,4 +52,10 @@ void Mailbox::receive() { } } +void Mailbox::maybeReceive(std::weak_ptr<Mailbox> mailbox) { + if (auto locked = mailbox.lock()) { + locked->receive(); + } +} + } // namespace mbgl diff --git a/src/mbgl/actor/mailbox.hpp b/src/mbgl/actor/mailbox.hpp index 5d5e8cb924..cff0de243a 100644 --- a/src/mbgl/actor/mailbox.hpp +++ b/src/mbgl/actor/mailbox.hpp @@ -18,6 +18,8 @@ public: void close(); void receive(); + static void maybeReceive(std::weak_ptr<Mailbox>); + private: Scheduler& scheduler; diff --git a/src/mbgl/actor/thread_pool.cpp b/src/mbgl/actor/thread_pool.cpp deleted file mode 100644 index 2995ed26ad..0000000000 --- a/src/mbgl/actor/thread_pool.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include <mbgl/actor/thread_pool.hpp> -#include <mbgl/actor/mailbox.hpp> - -namespace mbgl { - -ThreadPool::ThreadPool(std::size_t count) { - threads.reserve(count); - for (std::size_t i = 0; i < count; ++i) { - threads.emplace_back([this] () { - while (true) { - std::unique_lock<std::mutex> lock(mutex); - - cv.wait(lock, [this] { - return !queue.empty() || terminate; - }); - - if (terminate) { - return; - } - - auto mailbox = queue.front(); - queue.pop(); - lock.unlock(); - - if (auto locked = mailbox.lock()) { - locked->receive(); - } - } - }); - } -} - -ThreadPool::~ThreadPool() { - { - std::lock_guard<std::mutex> lock(mutex); - terminate = true; - } - - cv.notify_all(); - - for (auto& thread : threads) { - thread.join(); - } -} - -void ThreadPool::schedule(std::weak_ptr<Mailbox> mailbox) { - { - std::lock_guard<std::mutex> lock(mutex); - queue.push(mailbox); - } - - cv.notify_one(); -} - -} // namespace mbgl diff --git a/src/mbgl/actor/thread_pool.hpp b/src/mbgl/actor/thread_pool.hpp deleted file mode 100644 index a14d16d771..0000000000 --- a/src/mbgl/actor/thread_pool.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include <mbgl/actor/scheduler.hpp> - -#include <condition_variable> -#include <mutex> -#include <queue> -#include <thread> - -namespace mbgl { - -class ThreadPool : public Scheduler { -public: - ThreadPool(std::size_t count); - ~ThreadPool() override; - - void schedule(std::weak_ptr<Mailbox>) override; - -private: - std::vector<std::thread> threads; - std::queue<std::weak_ptr<Mailbox>> queue; - std::mutex mutex; - std::condition_variable cv; - bool terminate { false }; -}; - -} // namespace mbgl diff --git a/src/mbgl/annotation/symbol_annotation_impl.cpp b/src/mbgl/annotation/symbol_annotation_impl.cpp index 040de21214..e5ae5f4b91 100644 --- a/src/mbgl/annotation/symbol_annotation_impl.cpp +++ b/src/mbgl/annotation/symbol_annotation_impl.cpp @@ -2,6 +2,7 @@ #include <mbgl/annotation/annotation_tile.hpp> #include <mbgl/tile/tile_id.hpp> #include <mbgl/math/clamp.hpp> +#include <mbgl/util/tile_coordinate.hpp> namespace mbgl { @@ -14,26 +15,10 @@ void SymbolAnnotationImpl::updateLayer(const CanonicalTileID& tileID, Annotation std::unordered_map<std::string, std::string> featureProperties; featureProperties.emplace("sprite", annotation.icon.empty() ? std::string("default_marker") : annotation.icon); - const Point<double>& p = annotation.geometry; - - // Clamp to the latitude limits of Web Mercator. - const double constrainedLatitude = util::clamp(p.y, -util::LATITUDE_MAX, util::LATITUDE_MAX); - - // Project a coordinate into unit space in a square map. - const double sine = std::sin(constrainedLatitude * util::DEG2RAD); - const double x = p.x / util::DEGREES_MAX + 0.5; - const double y = 0.5 - 0.25 * std::log((1.0 + sine) / (1.0 - sine)) / M_PI; - - Point<double> projected(x, y); - projected *= std::pow(2, tileID.z); - projected.x = std::fmod(projected.x, 1); - projected.y = std::fmod(projected.y, 1); - projected *= double(util::EXTENT); - - layer.features.emplace_back(id, - FeatureType::Point, - GeometryCollection {{ {{ convertPoint<int16_t>(projected) }} }}, - featureProperties); + LatLng latLng { annotation.geometry.y, annotation.geometry.x }; + TileCoordinate coordinate = TileCoordinate::fromLatLng(0, latLng); + GeometryCoordinate tilePoint = TileCoordinate::toGeometryCoordinate(UnwrappedTileID(0, tileID), coordinate.p); + layer.features.emplace_back(id, FeatureType::Point, GeometryCollection {{ {{ tilePoint }} }}, featureProperties); } } // namespace mbgl diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 2a04fcc18e..23b28a15df 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -1,3 +1,4 @@ +#include <mbgl/map/view.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/gl/gl.hpp> #include <mbgl/gl/vertex_array.hpp> @@ -142,6 +143,113 @@ UniqueFramebuffer Context::createFramebuffer() { return UniqueFramebuffer{ std::move(id), { this } }; } +UniqueRenderbuffer Context::createRenderbuffer(const RenderbufferType type, + const uint16_t width, + const uint16_t height) { + RenderbufferID id = 0; + MBGL_CHECK_ERROR(glGenRenderbuffers(1, &id)); + UniqueRenderbuffer renderbuffer{ std::move(id), { this } }; + + bindRenderbuffer = renderbuffer; + MBGL_CHECK_ERROR( + glRenderbufferStorage(GL_RENDERBUFFER, static_cast<GLenum>(type), width, height)); + return renderbuffer; +} + +namespace { + +void checkFramebuffer() { + GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER)); + if (status != GL_FRAMEBUFFER_COMPLETE) { + switch (status) { + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + throw std::runtime_error("Couldn't create framebuffer: incomplete attachment"); + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + throw std::runtime_error("Couldn't create framebuffer: incomplete missing attachment"); +#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + throw std::runtime_error("Couldn't create framebuffer: incomplete draw buffer"); +#endif +#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + throw std::runtime_error("Couldn't create framebuffer: incomplete read buffer"); +#endif +#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + throw std::runtime_error("Couldn't create framebuffer: incomplete dimensions"); +#endif + + case GL_FRAMEBUFFER_UNSUPPORTED: + throw std::runtime_error("Couldn't create framebuffer: unsupported"); + default: + throw std::runtime_error("Couldn't create framebuffer: other"); + } + } +} + +void bindDepthStencilRenderbuffer( + const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) { +#ifdef GL_DEPTH_STENCIL_ATTACHMENT + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, depthStencil.renderbuffer)); +#else + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, + depthStencil.renderbuffer)); + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, depthStencil.renderbuffer)); +#endif +} + +} // namespace + +Framebuffer +Context::createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>& color, + const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) { + if (color.size != depthStencil.size) { + throw new std::runtime_error("Renderbuffer size mismatch"); + } + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, color.renderbuffer)); + bindDepthStencilRenderbuffer(depthStencil); + checkFramebuffer(); + return { color.size, std::move(fbo) }; +} + +Framebuffer Context::createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>& color) { + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, color.renderbuffer)); + checkFramebuffer(); + return { color.size, std::move(fbo) }; +} + +Framebuffer +Context::createFramebuffer(const Texture& color, + const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) { + if (color.size != depthStencil.size) { + throw new std::runtime_error("Renderbuffer size mismatch"); + } + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + color.texture, 0)); + bindDepthStencilRenderbuffer(depthStencil); + checkFramebuffer(); + return { color.size, std::move(fbo) }; +} + +Framebuffer Context::createFramebuffer(const Texture& color) { + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + color.texture, 0)); + checkFramebuffer(); + return { color.size, std::move(fbo) }; +} + UniqueTexture Context::createTexture(uint16_t width, uint16_t height, const void* data, TextureUnit unit) { auto obj = createTexture(); @@ -186,50 +294,37 @@ void Context::reset() { performCleanup(); } -namespace { - -template <typename Fn> -void applyStateFunction(Context& context, Fn&& fn) { - fn(context.stencilFunc); - fn(context.stencilMask); - fn(context.stencilTest); - fn(context.stencilOp); - fn(context.depthRange); - fn(context.depthMask); - fn(context.depthTest); - fn(context.depthFunc); - fn(context.blend); - fn(context.blendFunc); - fn(context.blendColor); - fn(context.colorMask); - fn(context.clearDepth); - fn(context.clearColor); - fn(context.clearStencil); - fn(context.program); - fn(context.lineWidth); - fn(context.activeTexture); - fn(context.bindFramebuffer); - fn(context.viewport); +void Context::setDirtyState() { + // Note: does not set viewport/bindFramebuffer to dirty since they are handled separately in + // the view object. + stencilFunc.setDirty(); + stencilMask.setDirty(); + stencilTest.setDirty(); + stencilOp.setDirty(); + depthRange.setDirty(); + depthMask.setDirty(); + depthTest.setDirty(); + depthFunc.setDirty(); + blend.setDirty(); + blendFunc.setDirty(); + blendColor.setDirty(); + colorMask.setDirty(); + clearDepth.setDirty(); + clearColor.setDirty(); + clearStencil.setDirty(); + program.setDirty(); + lineWidth.setDirty(); + activeTexture.setDirty(); #if not MBGL_USE_GLES2 - fn(context.pixelZoom); - fn(context.rasterPos); + pixelZoom.setDirty(); + rasterPos.setDirty(); #endif // MBGL_USE_GLES2 - for (auto& tex : context.texture) { - fn(tex); + for (auto& tex : texture) { + tex.setDirty(); } - fn(context.vertexBuffer); - fn(context.elementBuffer); - fn(context.vertexArrayObject); -} - -} // namespace - -void Context::resetState() { - applyStateFunction(*this, [](auto& state) { state.reset(); }); -} - -void Context::setDirtyState() { - applyStateFunction(*this, [](auto& state) { state.setDirty(); }); + vertexBuffer.setDirty(); + elementBuffer.setDirty(); + vertexArrayObject.setDirty(); } void Context::performCleanup() { @@ -289,6 +384,12 @@ void Context::performCleanup() { glDeleteFramebuffers(int(abandonedFramebuffers.size()), abandonedFramebuffers.data())); abandonedFramebuffers.clear(); } + + if (!abandonedRenderbuffers.empty()) { + MBGL_CHECK_ERROR(glDeleteRenderbuffers(int(abandonedRenderbuffers.size()), + abandonedRenderbuffers.data())); + abandonedRenderbuffers.clear(); + } } } // namespace gl diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 6a5d44793a..cf8bb2658b 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -4,6 +4,8 @@ #include <mbgl/gl/state.hpp> #include <mbgl/gl/value.hpp> #include <mbgl/gl/texture.hpp> +#include <mbgl/gl/renderbuffer.hpp> +#include <mbgl/gl/framebuffer.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> #include <mbgl/gl/attribute.hpp> @@ -14,6 +16,9 @@ #include <array> namespace mbgl { + +class View; + namespace gl { constexpr size_t TextureMax = 64; @@ -27,7 +32,6 @@ public: UniqueShader createFragmentShader(); UniqueTexture createTexture(); UniqueVertexArray createVertexArray(); - UniqueFramebuffer createFramebuffer(); template <class V> VertexBuffer<V> createVertexBuffer(std::vector<V>&& v) { @@ -44,6 +48,20 @@ public: }; } + template <RenderbufferType type> + Renderbuffer<type> createRenderbuffer(const std::array<uint16_t, 2>& size) { + static_assert(type == RenderbufferType::RGBA || type == RenderbufferType::DepthStencil, + "invalid renderbuffer type"); + return { size, createRenderbuffer(type, size[0], size[1]) }; + } + + Framebuffer createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>&, + const Renderbuffer<RenderbufferType::DepthStencil>&); + Framebuffer createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>&); + Framebuffer createFramebuffer(const Texture&, + const Renderbuffer<RenderbufferType::DepthStencil>&); + Framebuffer createFramebuffer(const Texture&); + // Create a texture from an image with data. template <typename Image> Texture createTexture(const Image& image, TextureUnit unit = 0) { @@ -87,8 +105,6 @@ public: && abandonedFramebuffers.empty(); } - void resetState(); - void setDirtyState(); State<value::StencilFunc> stencilFunc; @@ -111,6 +127,7 @@ public: State<value::ActiveTexture> activeTexture; State<value::BindFramebuffer> bindFramebuffer; State<value::Viewport> viewport; + State<value::BindRenderbuffer> bindRenderbuffer; #if not MBGL_USE_GLES2 State<value::PixelZoom> pixelZoom; State<value::RasterPos> rasterPos; @@ -124,6 +141,8 @@ private: UniqueBuffer createVertexBuffer(const void* data, std::size_t size); UniqueBuffer createIndexBuffer(const void* data, std::size_t size); UniqueTexture createTexture(uint16_t width, uint16_t height, const void* data, TextureUnit); + UniqueFramebuffer createFramebuffer(); + UniqueRenderbuffer createRenderbuffer(RenderbufferType, uint16_t width, uint16_t height); void bindAttribute(const AttributeBinding&, std::size_t stride, const int8_t* offset); friend detail::ProgramDeleter; @@ -132,6 +151,7 @@ private: friend detail::TextureDeleter; friend detail::VertexArrayDeleter; friend detail::FramebufferDeleter; + friend detail::RenderbufferDeleter; std::vector<TextureID> pooledTextures; @@ -141,6 +161,7 @@ private: std::vector<TextureID> abandonedTextures; std::vector<VertexArrayID> abandonedVertexArrays; std::vector<FramebufferID> abandonedFramebuffers; + std::vector<RenderbufferID> abandonedRenderbuffers; }; } // namespace gl diff --git a/src/mbgl/gl/framebuffer.hpp b/src/mbgl/gl/framebuffer.hpp new file mode 100644 index 0000000000..880fed159e --- /dev/null +++ b/src/mbgl/gl/framebuffer.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include <mbgl/gl/object.hpp> + +#include <array> + +namespace mbgl { +namespace gl { + +class Framebuffer { +public: + std::array<uint16_t, 2> size; + gl::UniqueFramebuffer framebuffer; +}; + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/gl.cpp b/src/mbgl/gl/gl.cpp index 5cef254160..8999468dbd 100644 --- a/src/mbgl/gl/gl.cpp +++ b/src/mbgl/gl/gl.cpp @@ -51,6 +51,7 @@ constexpr const char* stringFromError(GLenum err) { } // namespace void checkError(const char* cmd, const char* file, int line) { +// fprintf(stderr, "cmd: %s\n", cmd); GLenum err = GL_NO_ERROR; if ((err = glGetError()) != GL_NO_ERROR) { std::string message = std::string(cmd) + ": Error " + stringFromError(err); diff --git a/src/mbgl/gl/object.cpp b/src/mbgl/gl/object.cpp index aee87f8836..e2d476e0c0 100644 --- a/src/mbgl/gl/object.cpp +++ b/src/mbgl/gl/object.cpp @@ -41,6 +41,11 @@ void FramebufferDeleter::operator()(FramebufferID id) const { context->abandonedFramebuffers.push_back(id); } +void RenderbufferDeleter::operator()(RenderbufferID id) const { + assert(context); + context->abandonedRenderbuffers.push_back(id); +} + } // namespace detail } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/object.hpp b/src/mbgl/gl/object.hpp index 1cc1f04a97..1408add65a 100644 --- a/src/mbgl/gl/object.hpp +++ b/src/mbgl/gl/object.hpp @@ -41,6 +41,11 @@ struct FramebufferDeleter { void operator()(FramebufferID) const; }; +struct RenderbufferDeleter { + Context* context; + void operator()(RenderbufferID) const; +}; + } // namespace detail using UniqueProgram = std_experimental::unique_resource<ProgramID, detail::ProgramDeleter>; @@ -49,6 +54,7 @@ using UniqueBuffer = std_experimental::unique_resource<BufferID, detail::BufferD using UniqueTexture = std_experimental::unique_resource<TextureID, detail::TextureDeleter>; using UniqueVertexArray = std_experimental::unique_resource<VertexArrayID, detail::VertexArrayDeleter>; using UniqueFramebuffer = std_experimental::unique_resource<FramebufferID, detail::FramebufferDeleter>; +using UniqueRenderbuffer = std_experimental::unique_resource<RenderbufferID, detail::RenderbufferDeleter>; } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/renderbuffer.hpp b/src/mbgl/gl/renderbuffer.hpp new file mode 100644 index 0000000000..9e8993bb77 --- /dev/null +++ b/src/mbgl/gl/renderbuffer.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include <mbgl/gl/object.hpp> + +#include <array> + +namespace mbgl { +namespace gl { + +template <RenderbufferType renderbufferType> +class Renderbuffer { +public: + using type = std::integral_constant<RenderbufferType, renderbufferType>; + std::array<uint16_t, 2> size; + gl::UniqueRenderbuffer renderbuffer; +}; + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/state.hpp b/src/mbgl/gl/state.hpp index dbb005e77d..efe869e5b9 100644 --- a/src/mbgl/gl/state.hpp +++ b/src/mbgl/gl/state.hpp @@ -3,20 +3,6 @@ namespace mbgl { namespace gl { -// Helper struct that allows obtaining the default value of a Value class -template <typename T, typename = void> -struct DefaultValue { - static typename T::Type Get() { - return T::Get(); - } -}; -template <typename T> -struct DefaultValue<T, decltype((void)T::Default, void())> { - static typename T::Type Get() { - return T::Default; - } -}; - // Wraps a piece of OpenGL state and remember its value to avoid redundant state calls. // Wrapped types need to implement to the Value class interface: // @@ -26,17 +12,12 @@ struct DefaultValue<T, decltype((void)T::Default, void())> { // static void Set(const Type& value); // static Type Get(); // }; -// -// The Get() function is optional, but if it is omitted, you must provide a Default. -// Default is also optional, but if it is omitted, you must provide a Get() function. -// If both are present, DefaultValue<T>::Get() will use the Default member. template <typename T> class State { public: void operator=(const typename T::Type& value) { if (*this != value) { - dirty = false; - currentValue = value; + setCurrentValue(value); T::Set(currentValue); } } @@ -49,9 +30,9 @@ public: return dirty || currentValue != value; } - // Explicitly resets the piece of OpenGL state to its default value. - void reset() { - *this = defaultValue; + void setCurrentValue(const typename T::Type& value) { + dirty = false; + currentValue = value; } // Mark the state as dirty. This means that the next time we are assigning a value to this @@ -68,14 +49,9 @@ public: return dirty; } - void setDefaultValue(const typename T::Type& value) { - defaultValue = value; - } - private: - typename T::Type defaultValue = DefaultValue<T>::Get(); - typename T::Type currentValue = defaultValue; - bool dirty = false; + typename T::Type currentValue = T::Default; + bool dirty = true; }; // Helper struct that stores the current state and restores it upon destruction. You should not use diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index f24674457a..dccc61b03a 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -48,6 +48,11 @@ enum class BufferType : uint32_t { Element = 0x8893 }; +enum class RenderbufferType : uint32_t { + RGBA = 0x8058, + DepthStencil = 0x88F0, +}; + enum class TextureMipMap : bool { No = false, Yes = true }; enum class TextureFilter : bool { Nearest = false, Linear = true }; diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp index e78b97ef2e..14cd03efc4 100644 --- a/src/mbgl/gl/value.cpp +++ b/src/mbgl/gl/value.cpp @@ -241,6 +241,8 @@ ActiveTexture::Type ActiveTexture::Get() { return static_cast<Type>(activeTexture - GL_TEXTURE0); } +const constexpr Viewport::Type Viewport::Default; + void Viewport::Set(const Type& value) { MBGL_CHECK_ERROR(glViewport(value.x, value.y, value.width, value.height)); } @@ -252,6 +254,8 @@ Viewport::Type Viewport::Get() { static_cast<uint16_t>(viewport[2]), static_cast<uint16_t>(viewport[3]) }; } +const constexpr BindFramebuffer::Type BindFramebuffer::Default; + void BindFramebuffer::Set(const Type& value) { MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, value)); } @@ -262,6 +266,18 @@ BindFramebuffer::Type BindFramebuffer::Get() { return binding; } +const constexpr BindRenderbuffer::Type BindRenderbuffer::Default; + +void BindRenderbuffer::Set(const Type& value) { + MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, value)); +} + +BindRenderbuffer::Type BindRenderbuffer::Get() { + GLint binding; + MBGL_CHECK_ERROR(glGetIntegerv(GL_RENDERBUFFER_BINDING, &binding)); + return binding; +} + const constexpr BindTexture::Type BindTexture::Default; void BindTexture::Set(const Type& value) { diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp index 9110b33a16..866ce389a4 100644 --- a/src/mbgl/gl/value.hpp +++ b/src/mbgl/gl/value.hpp @@ -180,6 +180,7 @@ struct Viewport { uint16_t width; uint16_t height; }; + static const constexpr Type Default = { 0, 0, 0, 0 }; static void Set(const Type&); static Type Get(); }; @@ -190,6 +191,14 @@ constexpr bool operator!=(const Viewport::Type& a, const Viewport::Type& b) { struct BindFramebuffer { using Type = FramebufferID; + static const constexpr Type Default = 0; + static void Set(const Type&); + static Type Get(); +}; + +struct BindRenderbuffer { + using Type = RenderbufferID; + static const constexpr Type Default = 0; static void Set(const Type&); static Type Get(); }; diff --git a/src/mbgl/map/backend.cpp b/src/mbgl/map/backend.cpp new file mode 100644 index 0000000000..8a06fe2d91 --- /dev/null +++ b/src/mbgl/map/backend.cpp @@ -0,0 +1,22 @@ +#include <mbgl/map/backend.hpp> +#include <mbgl/gl/context.hpp> + +#include <cassert> + +namespace mbgl { + +Backend::Backend() : context(std::make_unique<gl::Context>()) { +} + +gl::Context& Backend::getContext() { + return *context; +} + +Backend::~Backend() = default; + +void Backend::notifyMapChange(MapChange) { + // no-op +} + + +} // namespace mbgl diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index bf2462e2ab..7b58026386 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -1,6 +1,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/map/camera.hpp> #include <mbgl/map/view.hpp> +#include <mbgl/map/backend.hpp> #include <mbgl/map/transform.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/annotation/annotation_manager.hpp> @@ -21,7 +22,7 @@ #include <mbgl/util/async_task.hpp> #include <mbgl/util/mapbox.hpp> #include <mbgl/util/tile_coordinate.hpp> -#include <mbgl/actor/thread_pool.hpp> +#include <mbgl/actor/scheduler.hpp> #include <mbgl/platform/log.hpp> namespace mbgl { @@ -34,9 +35,25 @@ enum class RenderState : uint8_t { Fully, }; +struct StillImageRequest { + StillImageRequest(View& view_, Map::StillImageCallback&& callback_) + : view(view_), callback(std::move(callback_)) { + } + + View& view; + Map::StillImageCallback callback; +}; + class Map::Impl : public style::Observer { public: - Impl(View&, FileSource&, MapMode, GLContextMode, ConstrainMode, ViewportMode); + Impl(Backend&, + float pixelRatio, + FileSource&, + Scheduler&, + MapMode, + GLContextMode, + ConstrainMode, + ViewportMode); void onSourceAttributionChanged(style::Source&, const std::string&) override; void onUpdate(Update) override; @@ -45,12 +62,13 @@ public: void onResourceError(std::exception_ptr) override; void update(); - void render(); + void render(View&); void loadStyleJSON(const std::string&); - View& view; + Backend& backend; FileSource& fileSource; + Scheduler& scheduler; RenderState renderState = RenderState::Never; Transform transform; @@ -63,7 +81,6 @@ public: Update updateFlags = Update::Nothing; util::AsyncTask asyncUpdate; - ThreadPool workerThreadPool; std::unique_ptr<AnnotationManager> annotationManager; std::unique_ptr<Painter> painter; @@ -75,39 +92,55 @@ public: std::unique_ptr<AsyncRequest> styleRequest; - Map::StillImageCallback callback; + std::unique_ptr<StillImageRequest> stillImageRequest; size_t sourceCacheSize; TimePoint timePoint; bool loading = false; }; -Map::Map(View& view, FileSource& fileSource, MapMode mapMode, GLContextMode contextMode, ConstrainMode constrainMode, ViewportMode viewportMode) - : impl(std::make_unique<Impl>(view, fileSource, mapMode, contextMode, constrainMode, viewportMode)) { - view.initialize(this); - update(Update::Dimensions); -} - -Map::Impl::Impl(View& view_, +Map::Map(Backend& backend, + const std::array<uint16_t, 2> size, + const float pixelRatio, + FileSource& fileSource, + Scheduler& scheduler, + MapMode mapMode, + GLContextMode contextMode, + ConstrainMode constrainMode, + ViewportMode viewportMode) + : impl(std::make_unique<Impl>(backend, + pixelRatio, + fileSource, + scheduler, + mapMode, + contextMode, + constrainMode, + viewportMode)) { + impl->transform.resize(size); +} + +Map::Impl::Impl(Backend& backend_, + float pixelRatio_, FileSource& fileSource_, + Scheduler& scheduler_, MapMode mode_, GLContextMode contextMode_, ConstrainMode constrainMode_, ViewportMode viewportMode_) - : view(view_), + : backend(backend_), fileSource(fileSource_), - transform([this](MapChange change) { view.notifyMapChange(change); }, + scheduler(scheduler_), + transform([this](MapChange change) { backend.notifyMapChange(change); }, constrainMode_, viewportMode_), mode(mode_), contextMode(contextMode_), - pixelRatio(view.getPixelRatio()), + pixelRatio(pixelRatio_), asyncUpdate([this] { update(); }), - workerThreadPool(4), annotationManager(std::make_unique<AnnotationManager>(pixelRatio)) { } Map::~Map() { - impl->view.activate(); + impl->backend.activate(); impl->styleRequest = nullptr; @@ -117,60 +150,56 @@ Map::~Map() { impl->annotationManager.reset(); impl->painter.reset(); - impl->view.deactivate(); + impl->backend.deactivate(); } -void Map::renderStill(StillImageCallback callback) { +void Map::renderStill(View& view, StillImageCallback callback) { if (!callback) { Log::Error(Event::General, "StillImageCallback not set"); return; } if (impl->mode != MapMode::Still) { - callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")), {}); + callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode"))); return; } - if (impl->callback) { - callback(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")), {}); + if (impl->stillImageRequest) { + callback(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image"))); return; } if (!impl->style) { - callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")), {}); + callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style"))); return; } if (impl->style->getLastError()) { - callback(impl->style->getLastError(), {}); + callback(impl->style->getLastError()); return; } - impl->callback = callback; + impl->stillImageRequest = std::make_unique<StillImageRequest>(view, std::move(callback)); impl->updateFlags |= Update::RenderStill; impl->asyncUpdate.send(); } -void Map::update(Update flags) { - impl->onUpdate(flags); -} - -void Map::render() { +void Map::render(View& view) { if (!impl->style) { return; } if (impl->renderState == RenderState::Never) { - impl->view.notifyMapChange(MapChangeWillStartRenderingMap); + impl->backend.notifyMapChange(MapChangeWillStartRenderingMap); } - impl->view.notifyMapChange(MapChangeWillStartRenderingFrame); + impl->backend.notifyMapChange(MapChangeWillStartRenderingFrame); const Update flags = impl->transform.updateTransitions(Clock::now()); - impl->render(); + impl->render(view); - impl->view.notifyMapChange(isFullyLoaded() ? + impl->backend.notifyMapChange(isFullyLoaded() ? MapChangeDidFinishRenderingFrameFullyRendered : MapChangeDidFinishRenderingFrame); @@ -178,26 +207,30 @@ void Map::render() { impl->renderState = RenderState::Partial; } else if (impl->renderState != RenderState::Fully) { impl->renderState = RenderState::Fully; - impl->view.notifyMapChange(MapChangeDidFinishRenderingMapFullyRendered); + impl->backend.notifyMapChange(MapChangeDidFinishRenderingMapFullyRendered); if (impl->loading) { impl->loading = false; - impl->view.notifyMapChange(MapChangeDidFinishLoadingMap); + impl->backend.notifyMapChange(MapChangeDidFinishLoadingMap); } } // Triggers an asynchronous update, that eventually triggers a view // invalidation, causing renderSync to be called again if in transition. if (flags != Update::Nothing) { - update(flags); + impl->onUpdate(flags); } } +void Map::triggerRepaint() { + impl->backend.invalidate(); +} + void Map::Impl::update() { if (!style) { updateFlags = Update::Nothing; } - if (updateFlags == Update::Nothing || (mode == MapMode::Still && !callback)) { + if (updateFlags == Update::Nothing || (mode == MapMode::Still && !stillImageRequest)) { return; } @@ -230,7 +263,7 @@ void Map::Impl::update() { style::UpdateParameters parameters(pixelRatio, debugOptions, transform.getState(), - workerThreadPool, + scheduler, fileSource, mode, *annotationManager, @@ -239,23 +272,23 @@ void Map::Impl::update() { style->updateTiles(parameters); if (mode == MapMode::Continuous) { - view.invalidate(); - } else if (callback && style->isLoaded()) { - view.activate(); - render(); - view.deactivate(); + backend.invalidate(); + } else if (stillImageRequest && style->isLoaded()) { + // TODO: determine whether we need activate/deactivate + backend.activate(); + render(stillImageRequest->view); + backend.deactivate(); } updateFlags = Update::Nothing; } -void Map::Impl::render() { +void Map::Impl::render(View& view) { if (!painter) { - painter = std::make_unique<Painter>(transform.getState()); + painter = std::make_unique<Painter>(backend.getContext(), transform.getState()); } - FrameData frameData { view.getFramebufferSize(), - timePoint, + FrameData frameData { timePoint, pixelRatio, mode, contextMode, @@ -263,11 +296,12 @@ void Map::Impl::render() { painter->render(*style, frameData, + view, annotationManager->getSpriteAtlas()); if (mode == MapMode::Still) { - callback(nullptr, view.readStillImage()); - callback = nullptr; + auto request = std::move(stillImageRequest); + request->callback(nullptr); } painter->cleanup(); @@ -290,7 +324,7 @@ void Map::setStyleURL(const std::string& url) { impl->loading = true; - impl->view.notifyMapChange(MapChangeWillStartLoadingMap); + impl->backend.notifyMapChange(MapChangeWillStartLoadingMap); impl->styleRequest = nullptr; impl->styleURL = url; @@ -334,7 +368,7 @@ void Map::setStyleJSON(const std::string& json) { impl->loading = true; - impl->view.notifyMapChange(MapChangeWillStartLoadingMap); + impl->backend.notifyMapChange(MapChangeWillStartLoadingMap); impl->styleURL.clear(); impl->styleJSON.clear(); @@ -369,12 +403,12 @@ std::string Map::getStyleJSON() const { void Map::cancelTransitions() { impl->transform.cancelTransitions(); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setGestureInProgress(bool inProgress) { impl->transform.setGestureInProgress(inProgress); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } bool Map::isGestureInProgress() const { @@ -401,24 +435,24 @@ CameraOptions Map::getCameraOptions(optional<EdgeInsets> padding) const { void Map::jumpTo(const CameraOptions& camera) { impl->transform.jumpTo(camera); - update(camera.zoom ? Update::RecalculateStyle : Update::Repaint); + impl->onUpdate(camera.zoom ? Update::RecalculateStyle : Update::Repaint); } void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) { impl->transform.easeTo(camera, animation); - update(camera.zoom ? Update::RecalculateStyle : Update::Repaint); + impl->onUpdate(camera.zoom ? Update::RecalculateStyle : Update::Repaint); } void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) { impl->transform.flyTo(camera, animation); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } #pragma mark - Position void Map::moveBy(const ScreenCoordinate& point, const Duration& duration) { impl->transform.moveBy(point, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setLatLng(const LatLng& latLng, const Duration& duration) { @@ -427,12 +461,12 @@ void Map::setLatLng(const LatLng& latLng, const Duration& duration) { void Map::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const Duration& duration) { impl->transform.setLatLng(latLng, padding, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.setLatLng(latLng, anchor, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } LatLng Map::getLatLng(optional<EdgeInsets> padding) const { @@ -447,7 +481,7 @@ void Map::resetPosition(optional<EdgeInsets> padding) { camera.padding = padding; camera.zoom = 0; impl->transform.jumpTo(camera); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } @@ -455,12 +489,12 @@ void Map::resetPosition(optional<EdgeInsets> padding) { void Map::scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.scaleBy(ds, anchor, duration); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } void Map::setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.setScale(scale, anchor, duration); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } double Map::getScale() const { @@ -473,7 +507,7 @@ void Map::setZoom(double zoom, const Duration& duration) { void Map::setZoom(double zoom, optional<EdgeInsets> padding, const Duration& duration) { impl->transform.setZoom(zoom, padding, duration); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } double Map::getZoom() const { @@ -486,7 +520,7 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& durat void Map::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const Duration& duration) { impl->transform.setLatLngZoom(latLng, zoom, padding, duration); - update(Update::RecalculateStyle); + impl->onUpdate(Update::RecalculateStyle); } CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, optional<EdgeInsets> padding) const { @@ -583,6 +617,11 @@ double Map::getMaxZoom() const { #pragma mark - Size +void Map::setSize(const std::array<uint16_t, 2>& size) { + impl->transform.resize(size); + impl->onUpdate(Update::Repaint); +} + uint16_t Map::getWidth() const { return impl->transform.getState().getWidth(); } @@ -595,7 +634,7 @@ uint16_t Map::getHeight() const { void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& duration) { impl->transform.rotateBy(first, second, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setBearing(double degrees, const Duration& duration) { @@ -604,12 +643,12 @@ void Map::setBearing(double degrees, const Duration& duration) { void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::setBearing(double degrees, optional<EdgeInsets> padding, const Duration& duration) { impl->transform.setAngle(-degrees * util::DEG2RAD, padding, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } double Map::getBearing() const { @@ -618,7 +657,7 @@ double Map::getBearing() const { void Map::resetNorth(const Duration& duration) { impl->transform.setAngle(0, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } #pragma mark - Pitch @@ -629,7 +668,7 @@ void Map::setPitch(double pitch, const Duration& duration) { void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& duration) { impl->transform.setPitch(pitch * util::DEG2RAD, anchor, duration); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } double Map::getPitch() const { @@ -640,7 +679,7 @@ double Map::getPitch() const { void Map::setNorthOrientation(NorthOrientation orientation) { impl->transform.setNorthOrientation(orientation); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } NorthOrientation Map::getNorthOrientation() const { @@ -651,7 +690,7 @@ NorthOrientation Map::getNorthOrientation() const { void Map::setConstrainMode(mbgl::ConstrainMode mode) { impl->transform.setConstrainMode(mode); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } ConstrainMode Map::getConstrainMode() const { @@ -662,7 +701,7 @@ ConstrainMode Map::getConstrainMode() const { void Map::setViewportMode(mbgl::ViewportMode mode) { impl->transform.setViewportMode(mode); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } ViewportMode Map::getViewportMode() const { @@ -707,17 +746,17 @@ double Map::getTopOffsetPixelsForAnnotationIcon(const std::string& name) { AnnotationID Map::addAnnotation(const Annotation& annotation) { auto result = impl->annotationManager->addAnnotation(annotation, getMaxZoom()); - update(Update::AnnotationStyle | Update::AnnotationData); + impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData); return result; } void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) { - update(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom())); + impl->onUpdate(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom())); } void Map::removeAnnotation(AnnotationID annotation) { impl->annotationManager->removeAnnotation(annotation); - update(Update::AnnotationStyle | Update::AnnotationData); + impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData); } #pragma mark - Feature query api @@ -800,12 +839,12 @@ void Map::addLayer(std::unique_ptr<Layer> layer, const optional<std::string>& be } impl->styleMutated = true; - impl->view.activate(); + impl->backend.activate(); impl->style->addLayer(std::move(layer), before); - update(Update::Classes); + impl->onUpdate(Update::Classes); - impl->view.deactivate(); + impl->backend.deactivate(); } void Map::removeLayer(const std::string& id) { @@ -814,12 +853,12 @@ void Map::removeLayer(const std::string& id) { } impl->styleMutated = true; - impl->view.activate(); + impl->backend.activate(); impl->style->removeLayer(id); - update(Update::Classes); + impl->onUpdate(Update::Classes); - impl->view.deactivate(); + impl->backend.deactivate(); } void Map::addImage(const std::string& name, std::unique_ptr<const SpriteImage> image) { @@ -831,7 +870,7 @@ void Map::addImage(const std::string& name, std::unique_ptr<const SpriteImage> i impl->style->spriteAtlas->setSprite(name, std::move(image)); impl->style->spriteAtlas->updateDirty(); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::removeImage(const std::string& name) { @@ -843,7 +882,7 @@ void Map::removeImage(const std::string& name) { impl->style->spriteAtlas->removeSprite(name); impl->style->spriteAtlas->updateDirty(); - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } #pragma mark - Defaults @@ -887,7 +926,7 @@ double Map::getDefaultPitch() const { void Map::setDebug(MapDebugOptions debugOptions) { impl->debugOptions = debugOptions; - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } void Map::cycleDebugOptions() { @@ -911,7 +950,7 @@ void Map::cycleDebugOptions() { else impl->debugOptions = MapDebugOptions::TileBorders; - update(Update::Repaint); + impl->onUpdate(Update::Repaint); } MapDebugOptions Map::getDebug() const { @@ -924,20 +963,20 @@ bool Map::isFullyLoaded() const { void Map::addClass(const std::string& className) { if (impl->style && impl->style->addClass(className)) { - update(Update::Classes); + impl->onUpdate(Update::Classes); } } void Map::removeClass(const std::string& className) { if (impl->style && impl->style->removeClass(className)) { - update(Update::Classes); + impl->onUpdate(Update::Classes); } } void Map::setClasses(const std::vector<std::string>& classNames) { if (impl->style) { impl->style->setClasses(classNames); - update(Update::Classes); + impl->onUpdate(Update::Classes); } } @@ -970,7 +1009,7 @@ void Map::setSourceTileCacheSize(size_t size) { impl->sourceCacheSize = size; if (!impl->style) return; impl->style->setSourceTileCacheSize(size); - impl->view.invalidate(); + impl->backend.invalidate(); } } @@ -980,35 +1019,31 @@ void Map::onLowMemory() { } if (impl->style) { impl->style->onLowMemory(); - impl->view.invalidate(); + impl->backend.invalidate(); } } void Map::Impl::onSourceAttributionChanged(style::Source&, const std::string&) { - view.notifyMapChange(MapChangeSourceDidChange); + backend.notifyMapChange(MapChangeSourceDidChange); } void Map::Impl::onUpdate(Update flags) { - if (flags & Update::Dimensions) { - transform.resize(view.getSize()); - } - updateFlags |= flags; asyncUpdate.send(); } void Map::Impl::onStyleLoaded() { - view.notifyMapChange(MapChangeDidFinishLoadingStyle); + backend.notifyMapChange(MapChangeDidFinishLoadingStyle); } void Map::Impl::onStyleError() { - view.notifyMapChange(MapChangeDidFailLoadingMap); + backend.notifyMapChange(MapChangeDidFailLoadingMap); } void Map::Impl::onResourceError(std::exception_ptr error) { - if (mode == MapMode::Still && callback) { - callback(error, {}); - callback = nullptr; + if (mode == MapMode::Still && stillImageRequest) { + auto request = std::move(stillImageRequest); + request->callback(error); } } diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 41fc36ce27..85805a109d 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -7,6 +7,7 @@ #include <mbgl/util/unitbezier.hpp> #include <mbgl/util/interpolate.hpp> #include <mbgl/util/chrono.hpp> +#include <mbgl/util/projection.hpp> #include <mbgl/math/clamp.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> @@ -112,8 +113,8 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim // Find the shortest path otherwise. else startLatLng.unwrapForShortestPath(latLng); - const Point<double> startPoint = state.project(startLatLng); - const Point<double> endPoint = state.project(latLng); + const Point<double> startPoint = Projection::project(startLatLng, state.scale); + const Point<double> endPoint = Projection::project(latLng, state.scale); ScreenCoordinate center = getScreenCoordinate(padding); center.y = state.height - center.y; @@ -131,10 +132,6 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim Duration duration = animation.duration ? *animation.duration : Duration::zero(); - const double startWorldSize = state.worldSize(); - state.Bc = startWorldSize / util::DEGREES_MAX; - state.Cc = startWorldSize / util::M2PI; - const double startScale = state.scale; const double startAngle = state.angle; const double startPitch = state.pitch; @@ -144,7 +141,7 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim startTransition(camera, animation, [=](double t) { Point<double> framePoint = util::interpolate(startPoint, endPoint, t); - LatLng frameLatLng = state.unproject(framePoint, startWorldSize); + LatLng frameLatLng = Projection::unproject(framePoint, startScale); double frameScale = util::interpolate(startScale, scale, t); state.setLatLngZoom(frameLatLng, state.scaleZoom(frameScale)); @@ -186,8 +183,8 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima LatLng startLatLng = getLatLng(padding).wrapped(); startLatLng.unwrapForShortestPath(latLng); - const Point<double> startPoint = state.project(startLatLng); - const Point<double> endPoint = state.project(latLng); + const Point<double> startPoint = Projection::project(startLatLng, state.scale); + const Point<double> endPoint = Projection::project(latLng, state.scale); ScreenCoordinate center = getScreenCoordinate(padding); center.y = state.height - center.y; @@ -291,10 +288,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima return; } - const double startWorldSize = state.worldSize(); - state.Bc = startWorldSize / util::DEGREES_MAX; - state.Cc = startWorldSize / util::M2PI; - + const double startScale = state.scale; state.panning = true; state.scaling = true; state.rotating = angle != startAngle; @@ -310,7 +304,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima double frameZoom = startZoom + state.scaleZoom(1 / w(s)); // Convert to geographic coordinates and set the new viewpoint. - LatLng frameLatLng = state.unproject(framePoint, startWorldSize); + LatLng frameLatLng = Projection::unproject(framePoint, startScale); state.setLatLngZoom(frameLatLng, frameZoom); if (angle != startAngle) { diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index eb66513626..4f6bcecdb6 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -17,7 +17,7 @@ TransformState::TransformState(ConstrainMode constrainMode_, ViewportMode viewpo void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) const { const uint64_t tileScale = 1ull << tileID.canonical.z; - const double s = worldSize() / tileScale; + const double s = Projection::worldSize(scale) / tileScale; matrix::identity(matrix); matrix::translate(matrix, matrix, @@ -108,12 +108,12 @@ LatLng TransformState::getLatLng(LatLng::WrapMode wrapMode) const { } double TransformState::pixel_x() const { - const double center = (width - worldSize()) / 2; + const double center = (width - Projection::worldSize(scale)) / 2; return center + x; } double TransformState::pixel_y() const { - const double center = (height - worldSize()) / 2; + const double center = (height - Projection::worldSize(scale)) / 2; return center + y; } @@ -201,22 +201,6 @@ bool TransformState::isGestureInProgress() const { #pragma mark - Projection -Point<double> TransformState::project(const LatLng& ll) const { - return Point<double>( - (util::LONGITUDE_MAX + ll.longitude), - (util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + ll.latitude * M_PI / util::DEGREES_MAX))) - ) * worldSize() / util::DEGREES_MAX; -} - -LatLng TransformState::unproject(const Point<double>& p, double worldSize, LatLng::WrapMode wrapMode) const { - Point<double> p2 = p * util::DEGREES_MAX / worldSize; - return LatLng( - util::DEGREES_MAX / M_PI * std::atan(std::exp((util::LONGITUDE_MAX - p2.y) * util::DEG2RAD)) - 90.0f, - p2.x - util::LONGITUDE_MAX, - wrapMode - ); -} - double TransformState::zoomScale(double zoom) const { return std::pow(2.0f, zoom); } @@ -225,10 +209,6 @@ double TransformState::scaleZoom(double s) const { return util::log2(s); } -double TransformState::worldSize() const { - return scale * util::tileSize; -} - ScreenCoordinate TransformState::latLngToScreenCoordinate(const LatLng& latLng) const { if (width == 0 || height == 0) { return {}; @@ -236,7 +216,7 @@ ScreenCoordinate TransformState::latLngToScreenCoordinate(const LatLng& latLng) mat4 mat = coordinatePointMatrix(getZoom()); vec4 p; - Point<double> pt = project(latLng) / double(util::tileSize); + Point<double> pt = Projection::project(latLng, scale) / double(util::tileSize); vec4 c = {{ pt.x, pt.y, 0, 1 }}; matrix::transformMat4(p, c, mat); return { p[0] / p[3], height - p[1] / p[3] }; @@ -278,13 +258,13 @@ LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, L double z1 = coord1[2] / w1; double t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0); - return unproject(util::interpolate(p0, p1, t), scale, wrapMode); + return Projection::unproject(util::interpolate(p0, p1, t), scale / util::tileSize, wrapMode); } mat4 TransformState::coordinatePointMatrix(double z) const { mat4 proj; getProjMatrix(proj); - float s = worldSize() / std::pow(2, z); + float s = Projection::worldSize(scale) / std::pow(2, z); matrix::scale(proj, proj, s, s, 1); matrix::multiply(proj, getPixelMatrix(), proj); return proj; @@ -325,10 +305,10 @@ void TransformState::constrain(double& scale_, double& x_, double& y_) const { } void TransformState::moveLatLng(const LatLng& latLng, const ScreenCoordinate& anchor) { - auto centerCoord = project(getLatLng(LatLng::Unwrapped)); - auto latLngCoord = project(latLng); - auto anchorCoord = project(screenCoordinateToLatLng(anchor)); - setLatLngZoom(unproject(centerCoord + latLngCoord - anchorCoord, worldSize()), getZoom()); + auto centerCoord = Projection::project(getLatLng(LatLng::Unwrapped), scale); + auto latLngCoord = Projection::project(latLng, scale); + auto anchorCoord = Projection::project(screenCoordinateToLatLng(anchor), scale); + setLatLngZoom(Projection::unproject(centerCoord + latLngCoord - anchorCoord, scale), getZoom()); } void TransformState::setLatLngZoom(const LatLng &latLng, double zoom) { @@ -355,8 +335,8 @@ void TransformState::setScalePoint(const double newScale, const ScreenCoordinate scale = constrainedScale; x = constrainedPoint.x; y = constrainedPoint.y; - Bc = worldSize() / util::DEGREES_MAX; - Cc = worldSize() / util::M2PI; + Bc = Projection::worldSize(scale) / util::DEGREES_MAX; + Cc = Projection::worldSize(scale) / util::M2PI; } } // namespace mbgl diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 1e4b2054f7..8a12b62a9e 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -4,6 +4,7 @@ #include <mbgl/util/geo.hpp> #include <mbgl/util/geometry.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/projection.hpp> #include <mbgl/util/mat4.hpp> #include <cstdint> @@ -65,13 +66,10 @@ public: bool isPanning() const; bool isGestureInProgress() const; - // Conversion and projection + // Conversion ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const; LatLng screenCoordinateToLatLng(const ScreenCoordinate&, LatLng::WrapMode = LatLng::Unwrapped) const; - Point<double> project(const LatLng&) const; - LatLng unproject(const Point<double>&, double worldSize, LatLng::WrapMode = LatLng::Unwrapped) const; - double zoomScale(double zoom) const; double scaleZoom(double scale) const; @@ -88,8 +86,6 @@ private: // logical dimensions uint16_t width = 0, height = 0; - double worldSize() const; - mat4 coordinatePointMatrix(double z) const; mat4 getPixelMatrix() const; @@ -117,8 +113,8 @@ private: double pitch = 0.0; // cache values for spherical mercator math - double Bc = worldSize() / util::DEGREES_MAX; - double Cc = worldSize() / util::M2PI; + double Bc = Projection::worldSize(scale) / util::DEGREES_MAX; + double Cc = Projection::worldSize(scale) / util::M2PI; }; } // namespace mbgl diff --git a/src/mbgl/map/update.hpp b/src/mbgl/map/update.hpp new file mode 100644 index 0000000000..dc383b819e --- /dev/null +++ b/src/mbgl/map/update.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <mbgl/util/traits.hpp> + +namespace mbgl { + +enum class Update { + Nothing = 0, + Classes = 1 << 2, + RecalculateStyle = 1 << 3, + RenderStill = 1 << 4, + Repaint = 1 << 5, + AnnotationStyle = 1 << 6, + AnnotationData = 1 << 7, + Layout = 1 << 8 +}; + +constexpr Update operator|(Update lhs, Update rhs) { + return Update(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs)); +} + +constexpr Update& operator|=(Update& lhs, const Update& rhs) { + return (lhs = lhs | rhs); +} + +constexpr bool operator& (Update lhs, Update rhs) { + return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs); +} + +} // namespace mbgl diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp deleted file mode 100644 index 8541753c7a..0000000000 --- a/src/mbgl/map/view.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include <mbgl/map/view.hpp> -#include <mbgl/map/map.hpp> - -#include <cassert> - -namespace mbgl { - -void View::initialize(Map *map_) { - assert(map_); - map = map_; -} - -PremultipliedImage View::readStillImage(std::array<uint16_t, 2>) { - return {}; -} - -void View::notifyMapChange(MapChange) { - // no-op -} - - -} // namespace mbgl diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index 13bf21080d..bd67dc9cfd 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -3,10 +3,12 @@ namespace mbgl { class Shaders; +class View; class PaintParameters { public: Shaders& shaders; + View& view; }; } // namespace mbgl diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index ad0e75cd92..fc61d6e0a0 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -5,6 +5,8 @@ #include <mbgl/style/source.hpp> #include <mbgl/style/source_impl.hpp> +#include <mbgl/map/view.hpp> + #include <mbgl/platform/log.hpp> #include <mbgl/gl/gl.hpp> #include <mbgl/gl/debugging.hpp> @@ -29,6 +31,8 @@ #include <mbgl/util/mat3.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/offscreen_texture.hpp> + #include <cassert> #include <algorithm> #include <iostream> @@ -38,8 +42,9 @@ namespace mbgl { using namespace style; -Painter::Painter(const TransformState& state_) - : state(state_), +Painter::Painter(gl::Context& context_, const TransformState& state_) + : context(context_), + state(state_), tileTriangleVertexBuffer(context.createVertexBuffer(std::vector<FillVertex> {{ { 0, 0 }, { util::EXTENT, 0 }, @@ -69,9 +74,6 @@ Painter::Painter(const TransformState& state_) #ifndef NDEBUG overdrawShaders = std::make_unique<Shaders>(context, gl::Shader::Overdraw); #endif - - // Reset GL values - context.setDirtyState(); } Painter::~Painter() = default; @@ -90,19 +92,19 @@ void Painter::cleanup() { context.performCleanup(); } -void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& annotationSpriteAtlas) { - if (frame.framebufferSize != frame_.framebufferSize) { - context.viewport.setDefaultValue( - { 0, 0, frame_.framebufferSize[0], frame_.framebufferSize[1] }); - } +void Painter::render(const Style& style, const FrameData& frame_, View& view, SpriteAtlas& annotationSpriteAtlas) { frame = frame_; + if (frame.contextMode == GLContextMode::Shared) { + context.setDirtyState(); + } PaintParameters parameters { #ifndef NDEBUG - paintMode() == PaintMode::Overdraw ? *overdrawShaders : *shaders + paintMode() == PaintMode::Overdraw ? *overdrawShaders : *shaders, #else - *shaders + *shaders, #endif + view }; glyphAtlas = style.glyphAtlas.get(); @@ -125,12 +127,14 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a frameHistory.record(frame.timePoint, state.getZoom(), frame.mapMode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Milliseconds(0)); + // - UPLOAD PASS ------------------------------------------------------------------------------- // Uploads all required buffers and images before we do any actual rendering. { MBGL_DEBUG_GROUP("upload"); spriteAtlas->upload(context, 0); + lineAtlas->upload(context, 0); glyphAtlas->upload(context, 0); frameHistory.upload(context, 0); @@ -148,9 +152,8 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a // tiles whatsoever. { MBGL_DEBUG_GROUP("clear"); - context.bindFramebuffer.reset(); - context.viewport.reset(); - context.stencilFunc.reset(); + view.bind(); + context.stencilFunc = { gl::StencilTestFunction::Always, 0, ~0u }; context.stencilTest = true; context.stencilMask = 0xFF; context.depthTest = false; @@ -188,7 +191,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a #if not MBGL_USE_GLES2 and not defined(NDEBUG) if (frame.debugOptions & MapDebugOptions::StencilClip) { - renderClipMasks(); + renderClipMasks(parameters); return; } #endif @@ -231,7 +234,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a #if not MBGL_USE_GLES2 and not defined(NDEBUG) if (frame.debugOptions & MapDebugOptions::DepthBuffer) { - renderDepthBuffer(); + renderDepthBuffer(parameters); } #endif @@ -247,10 +250,6 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a context.vertexArrayObject = 0; } - - if (frame.contextMode == GLContextMode::Shared) { - context.setDirtyState(); - } } template <class Iterator> @@ -294,16 +293,21 @@ void Painter::renderPass(PaintParameters& parameters, renderBackground(parameters, *layer.as<BackgroundLayer>()); } else if (layer.is<CustomLayer>()) { MBGL_DEBUG_GROUP(layer.baseImpl->id + " - custom"); + + // Reset GL state to a known state so the CustomLayer always has a clean slate. context.vertexArrayObject = 0; context.depthFunc = gl::DepthTestFunction::LessEqual; context.depthTest = true; context.depthMask = false; context.stencilTest = false; setDepthSublayer(0); + layer.as<CustomLayer>()->impl->render(state); + + // Reset the view back to our original one, just in case the CustomLayer changed + // the viewport or Framebuffer. + parameters.view.bind(); context.setDirtyState(); - context.bindFramebuffer.reset(); - context.viewport.reset(); } else { MBGL_DEBUG_GROUP(layer.baseImpl->id + " - " + util::toString(item.tile->id)); if (item.bucket->needsClipping()) { diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 28aa5aab44..f339ed1aed 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -28,6 +28,7 @@ namespace mbgl { class RenderTile; class SpriteAtlas; +class View; class GlyphAtlas; class LineAtlas; struct FrameData; @@ -58,7 +59,6 @@ class BackgroundLayer; } // namespace style struct FrameData { - std::array<uint16_t, 2> framebufferSize = {{ 0, 0 }}; TimePoint timePoint; float pixelRatio; MapMode mapMode; @@ -68,11 +68,12 @@ struct FrameData { class Painter : private util::noncopyable { public: - Painter(const TransformState&); + Painter(gl::Context&, const TransformState&); ~Painter(); void render(const style::Style&, const FrameData&, + View&, SpriteAtlas& annotationSpriteAtlas); void cleanup(); @@ -85,9 +86,9 @@ public: #ifndef NDEBUG // Renders tile clip boundaries, using stencil buffer to calculate fill color. - void renderClipMasks(); + void renderClipMasks(PaintParameters&); // Renders the depth buffer. - void renderDepthBuffer(); + void renderDepthBuffer(PaintParameters&); #endif void renderDebugText(Tile&, const mat4&); @@ -152,6 +153,9 @@ private: } #endif +private: + gl::Context& context; + mat4 projMatrix; std::array<float, 2> pixelsToGLUnits; @@ -168,8 +172,6 @@ private: int indent = 0; - gl::Context context; - RenderPass pass = RenderPass::Opaque; int numSublayers = 3; diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp index 23a2a8e571..e57bb2205e 100644 --- a/src/mbgl/renderer/painter_debug.cpp +++ b/src/mbgl/renderer/painter_debug.cpp @@ -1,6 +1,8 @@ #include <mbgl/renderer/painter.hpp> #include <mbgl/renderer/debug_bucket.hpp> #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/map/view.hpp> #include <mbgl/tile/tile.hpp> #include <mbgl/shader/shaders.hpp> #include <mbgl/util/string.hpp> @@ -87,7 +89,7 @@ void Painter::renderDebugFrame(const mat4 &matrix) { } #ifndef NDEBUG -void Painter::renderClipMasks() { +void Painter::renderClipMasks(PaintParameters&) { context.stencilTest = false; context.depthTest = false; context.program = 0; @@ -98,13 +100,13 @@ void Painter::renderClipMasks() { context.rasterPos = { -1, -1, 0, 0 }; // Read the stencil buffer - const auto& fbSize = frame.framebufferSize; - auto pixels = std::make_unique<uint8_t[]>(fbSize[0] * fbSize[1]); + const auto viewport = context.viewport.getCurrentValue(); + auto pixels = std::make_unique<uint8_t[]>(viewport.width * viewport.height); MBGL_CHECK_ERROR(glReadPixels( - 0, // GLint x - 0, // GLint y - fbSize[0], // GLsizei width - fbSize[1], // GLsizei height + viewport.x, // GLint x + viewport.y, // GLint y + viewport.width, // GLsizei width + viewport.height, // GLsizei height GL_STENCIL_INDEX, // GLenum format GL_UNSIGNED_BYTE, // GLenum type pixels.get() // GLvoid * data @@ -112,20 +114,21 @@ void Painter::renderClipMasks() { // Scale the Stencil buffer to cover the entire color space. auto it = pixels.get(); - auto end = it + fbSize[0] * fbSize[1]; + auto end = it + viewport.width * viewport.height; const auto factor = 255.0f / *std::max_element(it, end); for (; it != end; ++it) { *it *= factor; } - MBGL_CHECK_ERROR(glWindowPos2i(0, 0)); - MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get())); + MBGL_CHECK_ERROR(glWindowPos2i(viewport.x, viewport.y)); + MBGL_CHECK_ERROR(glDrawPixels(viewport.width, viewport.height, GL_LUMINANCE, GL_UNSIGNED_BYTE, + pixels.get())); #endif // MBGL_USE_GLES2 } #endif // NDEBUG #ifndef NDEBUG -void Painter::renderDepthBuffer() { +void Painter::renderDepthBuffer(PaintParameters&) { context.stencilTest = false; context.depthTest = false; context.program = 0; @@ -136,25 +139,26 @@ void Painter::renderDepthBuffer() { context.rasterPos = { -1, -1, 0, 0 }; // Read the stencil buffer - const auto& fbSize = frame.framebufferSize; - auto pixels = std::make_unique<GLubyte[]>(fbSize[0] * fbSize[1]); + const auto viewport = context.viewport.getCurrentValue(); + auto pixels = std::make_unique<uint8_t[]>(viewport.width * viewport.height); const double base = 1.0 / (1.0 - depthRangeSize); glPixelTransferf(GL_DEPTH_SCALE, base); glPixelTransferf(GL_DEPTH_BIAS, 1.0 - base); MBGL_CHECK_ERROR(glReadPixels( - 0, // GLint x - 0, // GLint y - fbSize[0], // GLsizei width - fbSize[1], // GLsizei height + viewport.x, // GLint x + viewport.y, // GLint y + viewport.width, // GLsizei width + viewport.height, // GLsizei height GL_DEPTH_COMPONENT, // GLenum format GL_UNSIGNED_BYTE, // GLenum type pixels.get() // GLvoid * data )); - MBGL_CHECK_ERROR(glWindowPos2i(0, 0)); - MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get())); + MBGL_CHECK_ERROR(glWindowPos2i(viewport.x, viewport.y)); + MBGL_CHECK_ERROR(glDrawPixels(viewport.width, viewport.height, GL_LUMINANCE, GL_UNSIGNED_BYTE, + pixels.get())); #endif // MBGL_USE_GLES2 } #endif // NDEBUG diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 50ead900f6..b6606ca40b 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -1,6 +1,7 @@ #include <mbgl/renderer/painter.hpp> #include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/gl/gl.hpp> +#include <mbgl/map/view.hpp> #include <mbgl/renderer/fill_bucket.hpp> #include <mbgl/renderer/render_tile.hpp> @@ -29,7 +30,9 @@ void Painter::renderFill(PaintParameters& parameters, const bool isOutlineColorDefined = !properties.fillOutlineColor.isUndefined(); Color strokeColor = isOutlineColorDefined? properties.fillOutlineColor : fillColor; - auto worldSize = util::convert<GLfloat>(frame.framebufferSize); + const auto viewport = context.viewport.getCurrentValue(); + const std::array<GLfloat, 2> worldSize{ { static_cast<GLfloat>(viewport.width), + static_cast<GLfloat>(viewport.height) } }; bool pattern = !properties.fillPattern.value.from.empty(); bool outline = properties.fillAntialias && !pattern && isOutlineColorDefined; diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp index 608a552835..14ed9cd01c 100644 --- a/src/mbgl/style/source_impl.hpp +++ b/src/mbgl/style/source_impl.hpp @@ -86,6 +86,7 @@ protected: Source& base; SourceObserver* observer = nullptr; std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles; + TileCache cache; private: // TileObserver implementation. @@ -97,7 +98,6 @@ private: virtual std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) = 0; std::map<UnwrappedTileID, RenderTile> renderTiles; - TileCache cache; }; } // namespace style diff --git a/src/mbgl/style/source_observer.hpp b/src/mbgl/style/source_observer.hpp index a6cdab6ba2..dcbcaeabaf 100644 --- a/src/mbgl/style/source_observer.hpp +++ b/src/mbgl/style/source_observer.hpp @@ -20,6 +20,9 @@ public: virtual void onSourceAttributionChanged(Source&, const std::string&) {} virtual void onSourceError(Source&, std::exception_ptr) {} + //Source description needs to be reloaded + virtual void onSourceDescriptionChanged(Source&) {} + virtual void onTileChanged(Source&, const OverscaledTileID&) {} virtual void onTileError(Source&, const OverscaledTileID&, std::exception_ptr) {} }; diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index 5b7ba4fc77..4800b9c4be 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -38,15 +38,31 @@ GeoJSONSource::Impl::~Impl() = default; void GeoJSONSource::Impl::setURL(std::string url_) { url = std::move(url_); + + //Signal that the source description needs a reload + if (loaded || req) { + loaded = false; + req.reset(); + observer->onSourceDescriptionChanged(base); + } } optional<std::string> GeoJSONSource::Impl::getURL() { return url; } + void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) { + req.reset(); + _setGeoJSON(geoJSON); +} + +//Private implementation +void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { double scale = util::EXTENT / util::tileSize; + cache.clear(); + if (!options.cluster) { mapbox::geojsonvt::Options vtOptions; vtOptions.maxZoom = options.maxzoom; @@ -65,7 +81,7 @@ void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) { geoJSONOrSupercluster = std::make_unique<mapbox::supercluster::Supercluster>(features, clusterOptions); } - + for (auto const &item : tiles) { GeoJSONTile* geoJSONTile = static_cast<GeoJSONTile*>(item.second.get()); setTileData(*geoJSONTile, geoJSONTile->id); @@ -125,9 +141,9 @@ void GeoJSONSource::Impl::loadDescription(FileSource& fileSource) { geoJSON.error().message.c_str()); // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for // tiles to load. - setGeoJSON(GeoJSON{ FeatureCollection{} }); + _setGeoJSON(GeoJSON{ FeatureCollection{} }); } else { - setGeoJSON(*geoJSON); + _setGeoJSON(*geoJSON); } loaded = true; diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp index 9572355f2c..13994d9078 100644 --- a/src/mbgl/style/sources/geojson_source_impl.hpp +++ b/src/mbgl/style/sources/geojson_source_impl.hpp @@ -29,6 +29,8 @@ public: } private: + void _setGeoJSON(const GeoJSON&); + Range<uint8_t> getZoomRange() final; std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) final; diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index 522965b953..e1cbe56bc5 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -491,6 +491,13 @@ void Style::onSourceError(Source& source, std::exception_ptr error) { observer->onResourceError(error); } +void Style::onSourceDescriptionChanged(Source& source) { + observer->onSourceDescriptionChanged(source); + if (!source.baseImpl->loaded) { + source.baseImpl->loadDescription(fileSource); + } +} + void Style::onTileChanged(Source& source, const OverscaledTileID& tileID) { observer->onTileChanged(source, tileID); observer->onUpdate(Update::Repaint); diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index 648bd48030..21b25857d6 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -134,6 +134,7 @@ private: void onSourceLoaded(Source&) override; void onSourceAttributionChanged(Source&, const std::string&) override; void onSourceError(Source&, std::exception_ptr) override; + void onSourceDescriptionChanged(Source&) override; void onTileChanged(Source&, const OverscaledTileID&) override; void onTileError(Source&, const OverscaledTileID&, std::exception_ptr) override; diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp index d6fc1a6ada..e485fbf36c 100644 --- a/src/mbgl/text/collision_tile.cpp +++ b/src/mbgl/text/collision_tile.cpp @@ -3,6 +3,7 @@ #include <mbgl/util/constants.hpp> #include <mbgl/util/math.hpp> #include <mbgl/math/minmax.hpp> +#include <mbgl/util/intersection_tests.hpp> #include <mapbox/geometry/envelope.hpp> #include <mapbox/geometry/multi_point.hpp> @@ -11,20 +12,7 @@ namespace mbgl { -auto infinity = std::numeric_limits<float>::infinity(); - -CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_)), - edges({{ - // left - CollisionBox(Point<float>(0, 0), 0, -infinity, 0, infinity, infinity), - // right - CollisionBox(Point<float>(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity), - // top - CollisionBox(Point<float>(0, 0), -infinity, 0, infinity, 0, infinity), - // bottom - CollisionBox(Point<float>(0, util::EXTENT), -infinity, 0, infinity, 0, infinity), - }}) { - +CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_)) { // Compute the transformation matrix. const float angle_sin = std::sin(config.angle); const float angle_cos = std::cos(config.angle); @@ -36,13 +24,12 @@ CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_ // The amount the map is squished depends on the y position. // Sort of account for this by making all boxes a bit bigger. - yStretch = std::pow(_yStretch, 1.3); + yStretch = std::pow(_yStretch, 1.3f); } -float CollisionTile::findPlacementScale(float minPlacementScale, const Point<float>& anchor, - const CollisionBox& box, const Point<float>& blockingAnchor, const CollisionBox& blocking) { - +float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, const Point<float>& blockingAnchor, const CollisionBox& blocking) { + float minPlacementScale = minScale; // Find the lowest scale at which the two boxes can fit side by side without overlapping. // Original algorithm: @@ -80,7 +67,18 @@ float CollisionTile::findPlacementScale(float minPlacementScale, const Point<flo return minPlacementScale; } -float CollisionTile::placeFeature(const CollisionFeature& feature, const bool allowOverlap, const bool avoidEdges) { +float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOverlap, bool avoidEdges) { + static const float infinity = std::numeric_limits<float>::infinity(); + static const std::array<CollisionBox, 4> edges {{ + // left + CollisionBox(Point<float>(0, 0), 0, -infinity, 0, infinity, infinity), + // right + CollisionBox(Point<float>(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity), + // top + CollisionBox(Point<float>(0, 0), -infinity, 0, infinity, 0, infinity), + // bottom + CollisionBox(Point<float>(0, util::EXTENT), -infinity, 0, infinity, 0, infinity) + }}; float minPlacementScale = minScale; @@ -92,20 +90,16 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, const bool al const CollisionBox& blocking = std::get<1>(*it); Point<float> blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor); - minPlacementScale = findPlacementScale(minPlacementScale, anchor, box, blockingAnchor, blocking); + minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, blockingAnchor, blocking)); if (minPlacementScale >= maxScale) return minPlacementScale; } } if (avoidEdges) { - const Point<float> tl = { box.x1, box.y1 }; - const Point<float> tr = { box.x2, box.y1 }; - const Point<float> bl = { box.x1, box.y2 }; - const Point<float> br = { box.x2, box.y2 }; - const Point<float> rtl = util::matrixMultiply(reverseRotationMatrix, tl); - const Point<float> rtr = util::matrixMultiply(reverseRotationMatrix, tr); - const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, bl); - const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, br); + const Point<float> rtl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y1 }); + const Point<float> rtr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y1 }); + const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y2 }); + const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y2 }); CollisionBox rotatedBox(box.anchor, util::min(rtl.x, rtr.x, rbl.x, rbr.x), util::min(rtl.y, rtr.y, rbl.y, rbr.y), @@ -114,8 +108,7 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, const bool al box.maxScale); for (auto& blocking : edges) { - minPlacementScale = findPlacementScale(minPlacementScale, box.anchor, rotatedBox, blocking.anchor, blocking); - + minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, blocking.anchor, blocking)); if (minPlacementScale >= maxScale) return minPlacementScale; } } @@ -124,7 +117,7 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, const bool al return minPlacementScale; } -void CollisionTile::insertFeature(CollisionFeature& feature, const float minPlacementScale, const bool ignorePlacement) { +void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementScale, bool ignorePlacement) { for (auto& box : feature.boxes) { box.placementScale = minPlacementScale; } @@ -143,7 +136,25 @@ void CollisionTile::insertFeature(CollisionFeature& feature, const float minPlac } +// +---------------------------+ As you zoom, the size of the symbol changes +// |(x1,y1) | | relative to the tile e.g. when zooming in, +// | | | the symbol gets smaller relative to the tile. +// | (x1',y1') v | +// | +-------+-------+ | The boxes inserted into the tree represents +// | | | | | the bounds at the integer zoom level (where +// | | | | | the symbol is biggest relative to the tile). +// | | | | | +// |---->+-------+-------+<----| This happens because placement is updated +// | | |(xa,ya)| | once every new integer zoom level e.g. +// | | | | | std::floor(oldZoom) != std::floor(newZoom). +// | | | | | +// | +-------+-------+ | Thus, they don't represent the exact bounds +// | ^ (x2',y2') | of the symbol at the current zoom level. For +// | | | calculating the bounds at current zoom level +// | | (x2,y2)| we must unscale the box using its center as +// +---------------------------+ transform origin. Box CollisionTile::getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale) { + assert(box.x1 <= box.x2 && box.y1 <= box.y2); return Box{ CollisionPoint{ anchor.x + box.x1 / scale, @@ -156,49 +167,66 @@ Box CollisionTile::getTreeBox(const Point<float>& anchor, const CollisionBox& bo }; } -std::vector<IndexedSubfeature> CollisionTile::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const float scale) { - +std::vector<IndexedSubfeature> CollisionTile::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, float scale) { std::vector<IndexedSubfeature> result; - if (queryGeometry.empty()) return result; + if (queryGeometry.empty() || (tree.empty() && ignoredTree.empty())) { + return result; + } + + // Generate a rotated geometry out of the original query geometry. + // Scale has already been handled by the prior conversions. + GeometryCoordinates polygon; + for (const auto& point : queryGeometry) { + auto rotated = util::matrixMultiply(rotationMatrix, convertPoint<float>(point)); + polygon.push_back(convertPoint<int16_t>(rotated)); + } + // Predicate for ruling out already seen features. std::unordered_map<std::string, std::unordered_set<std::size_t>> sourceLayerFeatures; + auto seenFeature = [&] (const CollisionTreeBox& treeBox) -> bool { + const IndexedSubfeature& feature = std::get<2>(treeBox); + const auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName]; + return seenFeatures.find(feature.index) == seenFeatures.end(); + }; - mapbox::geometry::multi_point<float> rotatedPoints {}; - rotatedPoints.reserve(queryGeometry.size()); - std::transform(queryGeometry.cbegin(), queryGeometry.cend(), std::back_inserter(rotatedPoints), - [&](const auto& c) { return util::matrixMultiply(rotationMatrix, convertPoint<float>(c)); }); - const auto box = mapbox::geometry::envelope(rotatedPoints); + // Account for the rounding done when updating symbol shader variables. + const float roundedScale = std::pow(2.0f, std::ceil(util::log2(scale) * 10.0f) / 10.0f); - const auto& anchor = box.min; - CollisionBox queryBox(anchor, 0, 0, box.max.x - box.min.x, box.max.y - box.min.y, scale); - auto predicates = bgi::intersects(getTreeBox(anchor, queryBox)); + // Check if feature is rendered (collision free) at current scale. + auto visibleAtScale = [&] (const CollisionTreeBox& treeBox) -> bool { + const CollisionBox& box = std::get<1>(treeBox); + return roundedScale >= box.placementScale && roundedScale <= box.maxScale; + }; - auto fn = [&] (const Tree& tree_, bool ignorePlacement) { + // Check if query polygon intersects with the feature box at current scale. + auto intersectsAtScale = [&] (const CollisionTreeBox& treeBox) -> bool { + const CollisionBox& collisionBox = std::get<1>(treeBox); + const auto anchor = util::matrixMultiply(rotationMatrix, collisionBox.anchor); + const int16_t x1 = anchor.x + collisionBox.x1 / scale; + const int16_t y1 = anchor.y + collisionBox.y1 / scale * yStretch; + const int16_t x2 = anchor.x + collisionBox.x2 / scale; + const int16_t y2 = anchor.y + collisionBox.y2 / scale * yStretch; + auto bbox = GeometryCoordinates { + { x1, y1 }, { x2, y1 }, { x2, y2 }, { x1, y2 } + }; + return util::polygonIntersectsPolygon(polygon, bbox); + }; + + auto predicates = bgi::satisfies(seenFeature) + && bgi::satisfies(visibleAtScale) + && bgi::satisfies(intersectsAtScale); + + auto queryTree = [&](const auto& tree_) { for (auto it = tree_.qbegin(predicates); it != tree_.qend(); ++it) { - const CollisionBox& blocking = std::get<1>(*it); - const IndexedSubfeature& indexedFeature = std::get<2>(*it); - - auto& seenFeatures = sourceLayerFeatures[indexedFeature.sourceLayerName]; - if (seenFeatures.find(indexedFeature.index) == seenFeatures.end()) { - if (ignorePlacement) { - seenFeatures.insert(indexedFeature.index); - result.push_back(indexedFeature); - } else { - auto blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor); - float minPlacementScale = findPlacementScale(minScale, anchor, queryBox, blockingAnchor, blocking); - if (minPlacementScale >= scale) { - seenFeatures.insert(indexedFeature.index); - result.push_back(indexedFeature); - } - } - } + const IndexedSubfeature& feature = std::get<2>(*it); + auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName]; + seenFeatures.insert(feature.index); + result.push_back(feature); } }; - bool ignorePlacement = false; - fn(tree, ignorePlacement); - ignorePlacement = true; - fn(ignoredTree, ignorePlacement); + queryTree(tree); + queryTree(ignoredTree); return result; } diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp index dbc23b2a79..186cd19d28 100644 --- a/src/mbgl/text/collision_tile.hpp +++ b/src/mbgl/text/collision_tile.hpp @@ -39,10 +39,10 @@ class CollisionTile { public: explicit CollisionTile(PlacementConfig); - float placeFeature(const CollisionFeature&, const bool allowOverlap, const bool avoidEdges); - void insertFeature(CollisionFeature&, const float minPlacementScale, const bool ignorePlacement); + float placeFeature(const CollisionFeature&, bool allowOverlap, bool avoidEdges); + void insertFeature(CollisionFeature&, float minPlacementScale, bool ignorePlacement); - std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, const float scale); + std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, float scale); const PlacementConfig config; @@ -52,10 +52,9 @@ public: std::array<float, 4> rotationMatrix; std::array<float, 4> reverseRotationMatrix; - std::array<CollisionBox, 4> edges; private: - float findPlacementScale(float minPlacementScale, + float findPlacementScale( const Point<float>& anchor, const CollisionBox& box, const Point<float>& blockingAnchor, const CollisionBox& blocking); Box getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale = 1.0); diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp index a0feb8ec37..c580357298 100644 --- a/src/mbgl/util/intersection_tests.cpp +++ b/src/mbgl/util/intersection_tests.cpp @@ -16,14 +16,6 @@ bool polygonContainsPoint(const GeometryCoordinates& ring, const GeometryCoordin return c; } -bool multiPolygonContainsPoint(const GeometryCollection& rings, const GeometryCoordinate& p) { - bool c = false; - for (auto& ring : rings) { - c = (c != polygonContainsPoint(ring, p)); - } - return c; -} - // Code from http://stackoverflow.com/a/1501725/331379. float distToSegmentSquared(const GeometryCoordinate& p, const GeometryCoordinate& v, const GeometryCoordinate& w) { if (v == w) return util::distSqr<float>(p, v); @@ -114,23 +106,23 @@ bool polygonIntersectsBufferedMultiLine(const GeometryCoordinates& polygon, cons return false; } -bool polygonIntersectsMultiPolygon(const GeometryCoordinates& polygon, const GeometryCollection& multiPolygon) { - if (polygon.size() == 1) { - return multiPolygonContainsPoint(multiPolygon, polygon.at(0)); +bool polygonIntersectsPolygon(const GeometryCoordinates& polygonA, const GeometryCoordinates& polygonB) { + for (auto& p : polygonA) { + if (polygonContainsPoint(polygonB, p)) return true; } - for (auto& ring : multiPolygon) { - for (auto& p : ring) { - if (polygonContainsPoint(polygon, p)) return true; - } + for (auto& p : polygonB) { + if (polygonContainsPoint(polygonA, p)) return true; } - for (auto& p : polygon) { - if (multiPolygonContainsPoint(multiPolygon, p)) return true; - } + if (lineIntersectsLine(polygonA, polygonB)) return true; - for (auto& polygonB : multiPolygon) { - if (lineIntersectsLine(polygon, polygonB)) return true; + return false; +} + +bool polygonIntersectsMultiPolygon(const GeometryCoordinates& polygon, const GeometryCollection& multiPolygon) { + for (auto& ring : multiPolygon) { + if (polygonIntersectsPolygon(polygon, ring)) return true; } return false; diff --git a/src/mbgl/util/intersection_tests.hpp b/src/mbgl/util/intersection_tests.hpp index 6adb3b5fdf..5bcb29c767 100644 --- a/src/mbgl/util/intersection_tests.hpp +++ b/src/mbgl/util/intersection_tests.hpp @@ -7,6 +7,7 @@ namespace util { bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates&, const GeometryCollection&, float radius); bool polygonIntersectsBufferedMultiLine(const GeometryCoordinates&, const GeometryCollection&, float radius); +bool polygonIntersectsPolygon(const GeometryCoordinates&, const GeometryCoordinates&); bool polygonIntersectsMultiPolygon(const GeometryCoordinates&, const GeometryCollection&); } // namespace util diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp index 51049f8282..40bb70b70e 100644 --- a/src/mbgl/util/offscreen_texture.cpp +++ b/src/mbgl/util/offscreen_texture.cpp @@ -1,70 +1,48 @@ +#include <mbgl/util/offscreen_texture.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/gl/gl.hpp> -#include <mbgl/util/offscreen_texture.hpp> +#include <cstring> #include <cassert> namespace mbgl { -void OffscreenTexture::bind(gl::Context& context, - std::array<uint16_t, 2> size) { +OffscreenTexture::OffscreenTexture(gl::Context& context_, std::array<uint16_t, 2> size_) + : context(context_), size(std::move(size_)) { assert(size[0] > 0 && size[1] > 0); +} - if (!texture || texture->size != size) { - texture = context.createTexture(size); - } - +void OffscreenTexture::bind() { if (!framebuffer) { - framebuffer = context.createFramebuffer(); - context.bindFramebuffer = *framebuffer; - MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - texture->texture, 0)); - - GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER)); - if (status != GL_FRAMEBUFFER_COMPLETE) { - switch (status) { - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - throw std::runtime_error("Couldn't create framebuffer: incomplete attachment"); - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - throw std::runtime_error( - "Couldn't create framebuffer: incomplete missing attachment"); -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - throw std::runtime_error("Couldn't create framebuffer: incomplete draw buffer"); -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - throw std::runtime_error("Couldn't create framebuffer: incomplete read buffer"); -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - throw std::runtime_error("Couldn't create framebuffer: incomplete dimensions"); -#endif - - case GL_FRAMEBUFFER_UNSUPPORTED: - throw std::runtime_error("Couldn't create framebuffer: unsupported"); - default: - throw std::runtime_error("Couldn't create framebuffer: other"); - } - } + texture = context.createTexture(size); + framebuffer = context.createFramebuffer(*texture); } else { - context.bindFramebuffer = *framebuffer; + context.bindFramebuffer = framebuffer->framebuffer; } context.viewport = { 0, 0, size[0], size[1] }; } gl::Texture& OffscreenTexture::getTexture() { + assert(texture); return *texture; } -std::array<uint16_t, 2> OffscreenTexture::getSize() const { - if (texture) { - // Use explicit dereference instead of -> due to clang 3.5 bug - return (*texture).size; - } else { - return {{ 0, 0 }}; +PremultipliedImage OffscreenTexture::readStillImage() { + PremultipliedImage image { size[0], size[1] }; + MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get())); + + const auto stride = image.stride(); + auto tmp = std::make_unique<uint8_t[]>(stride); + uint8_t* rgba = image.data.get(); + for (int i = 0, j = size[1] - 1; i < j; i++, j--) { + std::memcpy(tmp.get(), rgba + i * stride, stride); + std::memcpy(rgba + i * stride, rgba + j * stride, stride); + std::memcpy(rgba + j * stride, tmp.get(), stride); } + + return image; } + } // namespace mbgl diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp index c71c0e51d9..8928bc2434 100644 --- a/src/mbgl/util/offscreen_texture.hpp +++ b/src/mbgl/util/offscreen_texture.hpp @@ -1,6 +1,10 @@ #pragma once +#include <mbgl/map/view.hpp> +#include <mbgl/gl/framebuffer.hpp> #include <mbgl/gl/texture.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/image.hpp> namespace mbgl { @@ -8,15 +12,20 @@ namespace gl { class Context; } // namespace gl -class OffscreenTexture { +class OffscreenTexture : public View { public: - void bind(gl::Context&, std::array<uint16_t, 2> size); + OffscreenTexture(gl::Context&, std::array<uint16_t, 2> size = {{ 256, 256 }}); + + void bind() override; + + PremultipliedImage readStillImage(); gl::Texture& getTexture(); - std::array<uint16_t, 2> getSize() const; private: - optional<gl::UniqueFramebuffer> framebuffer; + gl::Context& context; + std::array<uint16_t, 2> size; + optional<gl::Framebuffer> framebuffer; optional<gl::Texture> texture; }; diff --git a/src/mbgl/util/tile_coordinate.hpp b/src/mbgl/util/tile_coordinate.hpp index 194a62ecef..bcd1c8444f 100644 --- a/src/mbgl/util/tile_coordinate.hpp +++ b/src/mbgl/util/tile_coordinate.hpp @@ -5,6 +5,7 @@ #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/tile/tile_id.hpp> #include <mbgl/util/geometry.hpp> +#include <mbgl/util/projection.hpp> namespace mbgl { @@ -17,23 +18,25 @@ public: TileCoordinatePoint p; double z; - static TileCoordinate fromLatLng(const TransformState& state, double zoom, const LatLng& latLng) { - const double scale = std::pow(2, zoom - state.getZoom()); - return { state.project(latLng) * scale / double(util::tileSize), zoom }; + static TileCoordinate fromLatLng(double zoom, const LatLng& latLng) { + const double scale = std::pow(2.0, zoom); + return { Projection::project(latLng, scale) / double(util::tileSize), zoom }; } static TileCoordinate fromScreenCoordinate(const TransformState& state, double zoom, const ScreenCoordinate& screenCoordinate) { - return fromLatLng(state, zoom, state.screenCoordinateToLatLng(screenCoordinate)); + return fromLatLng(zoom, state.screenCoordinateToLatLng(screenCoordinate)); } TileCoordinate zoomTo(double zoom) const { - return { p * std::pow(2, zoom - z), zoom }; + const double scaleDiff = std::pow(2.0, zoom - z); + return { p * scaleDiff, zoom }; } static GeometryCoordinate toGeometryCoordinate(const UnwrappedTileID& tileID, const TileCoordinatePoint& point) { + const double scale = std::pow(2.0, tileID.canonical.z); auto zoomed = TileCoordinate { point, 0 }.zoomTo(tileID.canonical.z); return { - int16_t(util::clamp<int64_t>((zoomed.p.x - tileID.canonical.x - tileID.wrap * std::pow(2.0, tileID.canonical.z)) * util::EXTENT, + int16_t(util::clamp<int64_t>((zoomed.p.x - tileID.canonical.x - tileID.wrap * scale) * util::EXTENT, std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max())), int16_t(util::clamp<int64_t>((zoomed.p.y - tileID.canonical.y) * util::EXTENT, diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index 5487fb269c..c6bf7d362a 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -147,13 +147,12 @@ std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) { { std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() }, { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); - const TransformState state; return tileCover( - TileCoordinate::fromLatLng(state, z, bounds.northwest()).p, - TileCoordinate::fromLatLng(state, z, bounds.northeast()).p, - TileCoordinate::fromLatLng(state, z, bounds.southeast()).p, - TileCoordinate::fromLatLng(state, z, bounds.southwest()).p, - TileCoordinate::fromLatLng(state, z, bounds.center()).p, + TileCoordinate::fromLatLng(z, bounds.northwest()).p, + TileCoordinate::fromLatLng(z, bounds.northeast()).p, + TileCoordinate::fromLatLng(z, bounds.southeast()).p, + TileCoordinate::fromLatLng(z, bounds.southwest()).p, + TileCoordinate::fromLatLng(z, bounds.center()).p, z); } |