diff options
Diffstat (limited to 'src/mbgl/renderer')
102 files changed, 4517 insertions, 2881 deletions
diff --git a/src/mbgl/renderer/backend_scope.cpp b/src/mbgl/renderer/backend_scope.cpp new file mode 100644 index 0000000000..fafeaabb39 --- /dev/null +++ b/src/mbgl/renderer/backend_scope.cpp @@ -0,0 +1,66 @@ +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/util/thread_local.hpp> + +#include <cassert> + +namespace mbgl { + +static util::ThreadLocal<BackendScope> currentScope; + +BackendScope::BackendScope(RendererBackend& backend_, ScopeType scopeType_) + : priorScope(currentScope.get()), + nextScope(nullptr), + backend(backend_), + scopeType(scopeType_) { + if (priorScope) { + assert(priorScope->nextScope == nullptr); + priorScope->nextScope = this; + priorScope->deactivate(); + } + + activate(); + + currentScope.set(this); +} + +BackendScope::~BackendScope() { + assert(nextScope == nullptr); + deactivate(); + + if (priorScope) { + priorScope->activate(); + currentScope.set(priorScope); + assert(priorScope->nextScope == this); + priorScope->nextScope = nullptr; + } else { + currentScope.set(nullptr); + } +} + +void BackendScope::activate() { + if (scopeType == ScopeType::Explicit && + !(priorScope && this->backend == priorScope->backend) && + !(nextScope && this->backend == nextScope->backend)) { + // Only activate when set to Explicit and + // only once per RenderBackend + backend.activate(); + activated = true; + } +} + +void BackendScope::deactivate() { + if (activated && + !(nextScope && this->backend == nextScope->backend)) { + // Only deactivate when set to Explicit and + // only once per RenderBackend + backend.deactivate(); + activated = false; + } +} + +bool BackendScope::exists() { + return currentScope.get(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp index 6c391cf014..9af511a03e 100644 --- a/src/mbgl/renderer/bucket.hpp +++ b/src/mbgl/renderer/bucket.hpp @@ -1,33 +1,26 @@ #pragma once -#include <mbgl/renderer/render_pass.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/tile/geometry_tile_data.hpp> -#include <mbgl/renderer/render_layer.hpp> #include <atomic> -#include <string> -#include <unordered_map> namespace mbgl { -class Painter; -class PaintParameters; -class RenderTile; - namespace gl { class Context; } // namespace gl -namespace style { -class Layer; -} // namespace style +class RenderLayer; class Bucket : private util::noncopyable { public: Bucket() = default; virtual ~Bucket() = default; + // Feature geometries are also used to populate the feature index. + // Obtaining these is a costly operation, so we do it only once, and + // pass-by-const-ref the geometries as a second parameter. virtual void addFeature(const GeometryTileFeature&, const GeometryCollection&) {}; @@ -35,10 +28,6 @@ public: // this only happens once when the bucket is being rendered for the first time. virtual void upload(gl::Context&) = 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&, PaintParameters&, const RenderLayer&, const RenderTile&) = 0; - virtual bool hasData() const = 0; virtual float getQueryRadius(const RenderLayer&) const { @@ -46,7 +35,7 @@ public: }; bool needsUpload() const { - return !uploaded; + return hasData() && !uploaded; } protected: diff --git a/src/mbgl/renderer/bucket_parameters.hpp b/src/mbgl/renderer/bucket_parameters.hpp index 1774ba2bbe..50ec4cf521 100644 --- a/src/mbgl/renderer/bucket_parameters.hpp +++ b/src/mbgl/renderer/bucket_parameters.hpp @@ -9,6 +9,7 @@ class BucketParameters { public: const OverscaledTileID tileID; const MapMode mode; + const float pixelRatio; }; } // namespace mbgl diff --git a/src/mbgl/renderer/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp index 1e08eca478..04126990b3 100644 --- a/src/mbgl/renderer/circle_bucket.cpp +++ b/src/mbgl/renderer/buckets/circle_bucket.cpp @@ -1,9 +1,8 @@ -#include <mbgl/renderer/circle_bucket.hpp> +#include <mbgl/renderer/buckets/circle_bucket.hpp> #include <mbgl/renderer/bucket_parameters.hpp> -#include <mbgl/renderer/painter.hpp> #include <mbgl/programs/circle_program.hpp> #include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/renderer/render_circle_layer.hpp> +#include <mbgl/renderer/layers/render_circle_layer.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/math.hpp> @@ -34,13 +33,6 @@ void CircleBucket::upload(gl::Context& context) { uploaded = true; } -void CircleBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderCircle(parameters, *this, *layer.as<RenderCircleLayer>(), tile); -} - bool CircleBucket::hasData() const { return !segments.empty(); } diff --git a/src/mbgl/renderer/circle_bucket.hpp b/src/mbgl/renderer/buckets/circle_bucket.hpp index 0f27e2a7e3..78b6351bcb 100644 --- a/src/mbgl/renderer/circle_bucket.hpp +++ b/src/mbgl/renderer/buckets/circle_bucket.hpp @@ -23,8 +23,6 @@ public: void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; - float getQueryRadius(const RenderLayer&) const override; gl::VertexVector<CircleLayoutVertex> vertices; diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/buckets/debug_bucket.cpp index 2a514989cf..53c751c443 100644 --- a/src/mbgl/renderer/debug_bucket.cpp +++ b/src/mbgl/renderer/buckets/debug_bucket.cpp @@ -1,7 +1,7 @@ -#include <mbgl/renderer/debug_bucket.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/buckets/debug_bucket.hpp> #include <mbgl/programs/fill_program.hpp> #include <mbgl/geometry/debug_font_data.hpp> +#include <mbgl/tile/tile_id.hpp> #include <mbgl/util/string.hpp> #include <cmath> diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/buckets/debug_bucket.hpp index fc3128e944..fc3128e944 100644 --- a/src/mbgl/renderer/debug_bucket.hpp +++ b/src/mbgl/renderer/buckets/debug_bucket.hpp diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp index 2409fd365b..110db887a1 100644 --- a/src/mbgl/renderer/fill_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_bucket.cpp @@ -1,9 +1,8 @@ -#include <mbgl/renderer/fill_bucket.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/buckets/fill_bucket.hpp> #include <mbgl/programs/fill_program.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/renderer/render_fill_layer.hpp> +#include <mbgl/renderer/layers/render_fill_layer.hpp> #include <mbgl/util/math.hpp> #include <mapbox/earcut.hpp> @@ -121,13 +120,6 @@ void FillBucket::upload(gl::Context& context) { uploaded = true; } -void FillBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderFill(parameters, *this, *layer.as<RenderFillLayer>(), tile); -} - bool FillBucket::hasData() const { return !triangleSegments.empty() || !lineSegments.empty(); } diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp index d3cd92d451..a50e1971f5 100644 --- a/src/mbgl/renderer/fill_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_bucket.hpp @@ -23,7 +23,6 @@ public: bool hasData() const override; void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; float getQueryRadius(const RenderLayer&) const override; diff --git a/src/mbgl/renderer/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp index 2b352ab66a..7f53326fe1 100644 --- a/src/mbgl/renderer/fill_extrusion_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -1,9 +1,8 @@ -#include <mbgl/renderer/fill_extrusion_bucket.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp> #include <mbgl/programs/fill_extrusion_program.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/constants.hpp> @@ -154,13 +153,6 @@ void FillExtrusionBucket::upload(gl::Context& context) { uploaded = true; } -void FillExtrusionBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderFillExtrusion(parameters, *this, *layer.as<RenderFillExtrusionLayer>(), tile); -} - bool FillExtrusionBucket::hasData() const { return !triangleSegments.empty(); } diff --git a/src/mbgl/renderer/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp index d1e695c5a3..d57265ab16 100644 --- a/src/mbgl/renderer/fill_extrusion_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp @@ -21,7 +21,6 @@ public: bool hasData() const override; void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; float getQueryRadius(const RenderLayer&) const override; diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp index c80b8900ea..a96518df38 100644 --- a/src/mbgl/renderer/line_bucket.cpp +++ b/src/mbgl/renderer/buckets/line_bucket.cpp @@ -1,6 +1,5 @@ -#include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/render_line_layer.hpp> +#include <mbgl/renderer/buckets/line_bucket.hpp> +#include <mbgl/renderer/layers/render_line_layer.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/util/math.hpp> @@ -14,9 +13,10 @@ using namespace style; LineBucket::LineBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers, - const style::LineLayoutProperties& layout_) + const style::LineLayoutProperties::Unevaluated& layout_) : layout(layout_.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ))), - overscaling(parameters.tileID.overscaleFactor()) { + overscaling(parameters.tileID.overscaleFactor()), + zoom(parameters.tileID.overscaledZ) { for (const auto& layer : layers) { paintPropertyBinders.emplace( std::piecewise_construct, @@ -30,7 +30,7 @@ LineBucket::LineBucket(const BucketParameters& parameters, void LineBucket::addFeature(const GeometryTileFeature& feature, const GeometryCollection& geometryCollection) { for (auto& line : geometryCollection) { - addGeometry(line, feature.getType()); + addGeometry(line, feature); } for (auto& pair : paintPropertyBinders) { @@ -63,7 +63,8 @@ const float LINE_DISTANCE_SCALE = 1.0 / 2.0; // The maximum line distance, in tile units, that fits in the buffer. const float MAX_LINE_DISTANCE = std::pow(2, LINE_DISTANCE_BUFFER_BITS) / LINE_DISTANCE_SCALE; -void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType type) { +void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const GeometryTileFeature& feature) { + const FeatureType type = feature.getType(); const std::size_t len = [&coordinates] { std::size_t l = coordinates.size(); // If the line has duplicate vertices at the end, adjust length to remove them. @@ -87,7 +88,9 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType return; } - const float miterLimit = layout.get<LineJoin>() == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>()); + const LineJoinType joinType = layout.evaluate<LineJoin>(zoom, feature); + + const float miterLimit = joinType == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>()); const double sharpCornerOffset = SHARP_CORNER_OFFSET * (float(util::EXTENT) / (util::tileSize * overscaling)); @@ -183,7 +186,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType const bool isSharpCorner = cosHalfAngle < COS_HALF_SHARP_CORNER && prevCoordinate && nextCoordinate; if (isSharpCorner && i > first) { - const double prevSegmentLength = util::dist<double>(*currentCoordinate, *prevCoordinate); + const auto prevSegmentLength = util::dist<double>(*currentCoordinate, *prevCoordinate); if (prevSegmentLength > 2.0 * sharpCornerOffset) { GeometryCoordinate newPrevVertex = *currentCoordinate - convertPoint<int16_t>(util::round(convertPoint<double>(*currentCoordinate - *prevCoordinate) * (sharpCornerOffset / prevSegmentLength))); distance += util::dist<double>(newPrevVertex, *prevCoordinate); @@ -194,7 +197,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType // The join if a middle vertex, otherwise the cap const bool middleVertex = prevCoordinate && nextCoordinate; - LineJoinType currentJoin = layout.get<LineJoin>(); + LineJoinType currentJoin = joinType; const LineCapType currentCap = nextCoordinate ? beginCap : endCap; if (middleVertex) { @@ -356,7 +359,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType } if (isSharpCorner && i < len - 1) { - const double nextSegmentLength = util::dist<double>(*currentCoordinate, *nextCoordinate); + const auto nextSegmentLength = util::dist<double>(*currentCoordinate, *nextCoordinate); if (nextSegmentLength > 2 * sharpCornerOffset) { GeometryCoordinate newCurrentVertex = *currentCoordinate + convertPoint<int16_t>(util::round(convertPoint<double>(*nextCoordinate - *currentCoordinate) * (sharpCornerOffset / nextSegmentLength))); distance += util::dist<double>(newCurrentVertex, *currentCoordinate); @@ -458,13 +461,6 @@ void LineBucket::upload(gl::Context& context) { uploaded = true; } -void LineBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderLine(parameters, *this, *layer.as<RenderLineLayer>(), tile); -} - bool LineBucket::hasData() const { return !segments.empty(); } @@ -480,7 +476,7 @@ static float get(const RenderLineLayer& layer, const std::map<std::string, LineP } float LineBucket::getLineWidth(const RenderLineLayer& layer) const { - float lineWidth = layer.evaluated.get<LineWidth>(); + float lineWidth = get<LineWidth>(layer, paintPropertyBinders); float gapWidth = get<LineGapWidth>(layer, paintPropertyBinders); if (gapWidth) { diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp index 95ef2f9a6f..4fb77c377e 100644 --- a/src/mbgl/renderer/line_bucket.hpp +++ b/src/mbgl/renderer/buckets/line_bucket.hpp @@ -19,14 +19,13 @@ class LineBucket : public Bucket { public: LineBucket(const BucketParameters&, const std::vector<const RenderLayer*>&, - const style::LineLayoutProperties&); + const style::LineLayoutProperties::Unevaluated&); void addFeature(const GeometryTileFeature&, const GeometryCollection&) override; bool hasData() const override; void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; float getQueryRadius(const RenderLayer&) const override; @@ -42,7 +41,7 @@ public: std::map<std::string, LineProgram::PaintPropertyBinders> paintPropertyBinders; private: - void addGeometry(const GeometryCoordinates&, FeatureType); + void addGeometry(const GeometryCoordinates&, const GeometryTileFeature&); struct TriangleElement { TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {} @@ -60,6 +59,7 @@ private: std::ptrdiff_t e3; const uint32_t overscaling; + const float zoom; float getLineWidth(const RenderLineLayer& layer) const; }; diff --git a/src/mbgl/renderer/buckets/raster_bucket.cpp b/src/mbgl/renderer/buckets/raster_bucket.cpp new file mode 100644 index 0000000000..a66dd42d74 --- /dev/null +++ b/src/mbgl/renderer/buckets/raster_bucket.cpp @@ -0,0 +1,110 @@ +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/layers/render_raster_layer.hpp> +#include <mbgl/programs/raster_program.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { + +using namespace style; + +RasterBucket::RasterBucket(PremultipliedImage&& image_) { + image = std::make_shared<PremultipliedImage>(std::move(image_)); +} + +RasterBucket::RasterBucket(std::shared_ptr<PremultipliedImage> image_): image(image_) { + +} + +void RasterBucket::upload(gl::Context& context) { + if (!hasData()) { + return; + } + if (!texture) { + texture = context.createTexture(*image); + } + if (!segments.empty()) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(indices)); + } + uploaded = true; +} + +void RasterBucket::clear() { + vertexBuffer = {}; + indexBuffer = {}; + segments.clear(); + vertices.clear(); + indices.clear(); + + uploaded = false; +} + +void RasterBucket::setImage(std::shared_ptr<PremultipliedImage> image_) { + image = std::move(image_); + texture = {}; + uploaded = false; +} + +void RasterBucket::setMask(TileMask&& mask_) { + if (mask == mask_) { + return; + } + + mask = std::move(mask_); + clear(); + + if (mask == TileMask{ { 0, 0, 0 } }) { + // We want to render the full tile, and keeping the segments/vertices/indices empty means + // using the global shared buffers for covering the entire tile. + return; + } + + // Create a new segment so that we will upload (empty) buffers even when there is nothing to + // draw for this tile. + segments.emplace_back(0, 0); + + constexpr const uint16_t vertexLength = 4; + + // Create the vertex buffer for the specified tile mask. + for (const auto& id : mask) { + // Create a quad for every masked tile. + const int32_t vertexExtent = util::EXTENT >> id.z; + + const Point<int16_t> tlVertex = { static_cast<int16_t>(id.x * vertexExtent), + static_cast<int16_t>(id.y * vertexExtent) }; + const Point<int16_t> brVertex = { static_cast<int16_t>(tlVertex.x + vertexExtent), + static_cast<int16_t>(tlVertex.y + vertexExtent) }; + + if (segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { + // Move to a new segments because the old one can't hold the geometry. + segments.emplace_back(vertices.vertexSize(), indices.indexSize()); + } + + vertices.emplace_back( + RasterProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(tlVertex.y) })); + vertices.emplace_back( + RasterProgram::layoutVertex({ brVertex.x, tlVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(tlVertex.y) })); + vertices.emplace_back( + RasterProgram::layoutVertex({ tlVertex.x, brVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(brVertex.y) })); + vertices.emplace_back( + RasterProgram::layoutVertex({ brVertex.x, brVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(brVertex.y) })); + + auto& segment = segments.back(); + assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max()); + const uint16_t offset = segment.vertexLength; + + // 0, 1, 2 + // 1, 2, 3 + indices.emplace_back(offset, offset + 1, offset + 2); + indices.emplace_back(offset + 1, offset + 2, offset + 3); + + segment.vertexLength += vertexLength; + segment.indexLength += 6; + } +} + +bool RasterBucket::hasData() const { + return !!image; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/raster_bucket.hpp b/src/mbgl/renderer/buckets/raster_bucket.hpp new file mode 100644 index 0000000000..3800eadec8 --- /dev/null +++ b/src/mbgl/renderer/buckets/raster_bucket.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/texture.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/programs/raster_program.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/renderer/tile_mask.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/util/optional.hpp> + +namespace mbgl { + +class RasterBucket : public Bucket { +public: + RasterBucket(PremultipliedImage&&); + RasterBucket(std::shared_ptr<PremultipliedImage>); + + void upload(gl::Context&) override; + bool hasData() const override; + + void clear(); + void setImage(std::shared_ptr<PremultipliedImage>); + void setMask(TileMask&&); + + std::shared_ptr<PremultipliedImage> image; + optional<gl::Texture> texture; + TileMask mask{ { 0, 0, 0 } }; + + // Bucket specific vertices are used for Image Sources only + // Raster Tile Sources use the default buffers from Painter + gl::VertexVector<RasterLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> indices; + SegmentVector<RasterAttributes> segments; + + optional<gl::VertexBuffer<RasterLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 9b016c16f9..a3f71f1f6e 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -1,8 +1,8 @@ -#include <mbgl/renderer/symbol_bucket.hpp> -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/text/glyph_atlas.hpp> namespace mbgl { @@ -10,7 +10,8 @@ using namespace style; SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layout_, const std::map<std::string, std::pair< - style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>& layerPaintProperties, + style::IconPaintProperties::PossiblyEvaluated, + style::TextPaintProperties::PossiblyEvaluated>>& layerPaintProperties, const style::DataDrivenPropertyValue<float>& textSize, const style::DataDrivenPropertyValue<float>& iconSize, float zoom, @@ -36,14 +37,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo void SymbolBucket::upload(gl::Context& context) { if (hasTextData()) { text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); + text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw); text.indexBuffer = context.createIndexBuffer(std::move(text.triangles)); - textSizeBinder->upload(context); } if (hasIconData()) { icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); + icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw); icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); - iconSizeBinder->upload(context); } if (!collisionBox.vertices.empty()) { @@ -59,16 +60,8 @@ void SymbolBucket::upload(gl::Context& context) { uploaded = true; } -void SymbolBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderSymbol(parameters, *this, *layer.as<RenderSymbolLayer>(), tile); -} - bool SymbolBucket::hasData() const { - assert(false); // Should be calling SymbolLayout::has{Text,Icon,CollisonBox}Data() instead. - return false; + return hasTextData() || hasIconData() || hasCollisionBoxData(); } bool SymbolBucket::hasTextData() const { diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index a9bc868db0..32f976bcb2 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -15,10 +15,27 @@ namespace mbgl { +class PlacedSymbol { +public: + PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_, + std::array<float, 2> lineOffset_, float placementZoom_, bool useVerticalMode_, GeometryCoordinates line_) : + anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_), + lineOffset(lineOffset_), placementZoom(placementZoom_), useVerticalMode(useVerticalMode_), line(std::move(line_)) {} + Point<float> anchorPoint; + uint16_t segment; + float lowerSize; + float upperSize; + std::array<float, 2> lineOffset; + float placementZoom; + bool useVerticalMode; + GeometryCoordinates line; + std::vector<float> glyphOffsets; +}; + class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated, - const std::map<std::string, std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>&, + const std::map<std::string, std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>>&, const style::DataDrivenPropertyValue<float>& textSize, const style::DataDrivenPropertyValue<float>& iconSize, float zoom, @@ -26,7 +43,6 @@ public: bool iconsNeedLinear); void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; bool hasData() const override; bool hasTextData() const; bool hasIconData() const; @@ -44,10 +60,13 @@ public: struct TextBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices; gl::IndexVector<gl::Triangles> triangles; SegmentVector<SymbolTextAttributes> segments; + std::vector<PlacedSymbol> placedSymbols; optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } text; @@ -55,10 +74,14 @@ public: struct IconBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices; gl::IndexVector<gl::Triangles> triangles; SegmentVector<SymbolIconAttributes> segments; + std::vector<PlacedSymbol> placedSymbols; + PremultipliedImage atlasImage; optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } icon; @@ -68,10 +91,9 @@ public: SegmentVector<CollisionBoxAttributes> segments; optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer; optional<gl::IndexBuffer<gl::Lines>> indexBuffer; } collisionBox; - - SpriteAtlas* spriteAtlas = nullptr; }; } // namespace mbgl diff --git a/src/mbgl/renderer/cross_faded_property_evaluator.cpp b/src/mbgl/renderer/cross_faded_property_evaluator.cpp index ee3c86614f..4dff9dbf12 100644 --- a/src/mbgl/renderer/cross_faded_property_evaluator.cpp +++ b/src/mbgl/renderer/cross_faded_property_evaluator.cpp @@ -27,7 +27,10 @@ Faded<T> CrossFadedPropertyEvaluator<T>::calculate(const T& min, const T& mid, c const float z = parameters.z; const float fraction = z - std::floor(z); const std::chrono::duration<float> d = parameters.defaultFadeDuration; - const float t = std::min((parameters.now - parameters.zoomHistory.lastIntegerZoomTime) / d, 1.0f); + const float t = + d != std::chrono::duration<float>::zero() + ? std::min((parameters.now - parameters.zoomHistory.lastIntegerZoomTime) / d, 1.0f) + : 1.0f; return z > parameters.zoomHistory.lastIntegerZoom ? Faded<T> { min, mid, 2.0f, 1.0f, fraction + (1.0f - fraction) * t } diff --git a/src/mbgl/renderer/data_driven_property_evaluator.hpp b/src/mbgl/renderer/data_driven_property_evaluator.hpp index 6406b3478b..79ecd0d495 100644 --- a/src/mbgl/renderer/data_driven_property_evaluator.hpp +++ b/src/mbgl/renderer/data_driven_property_evaluator.hpp @@ -24,12 +24,18 @@ public: } ResultType operator()(const style::CameraFunction<T>& function) const { - return ResultType(function.evaluate(parameters.z)); + if (!parameters.useIntegerZoom) { + return ResultType(function.evaluate(parameters.z)); + } else { + return ResultType(function.evaluate(floor(parameters.z))); + } } template <class Function> ResultType operator()(const Function& function) const { - return ResultType(function); + auto returnFunction = function; + returnFunction.useIntegerZoom = parameters.useIntegerZoom; + return ResultType(returnFunction); } private: diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp index 35e246f488..de153b6963 100644 --- a/src/mbgl/renderer/frame_history.cpp +++ b/src/mbgl/renderer/frame_history.cpp @@ -37,9 +37,9 @@ void FrameHistory::record(const TimePoint& now, float zoom, const Duration& dura } for (int16_t z = 0; z <= 255; z++) { - std::chrono::duration<float> timeDiff = now - changeTimes[z]; - int32_t opacityChange = (duration == Milliseconds(0) ? 1 : (timeDiff / duration)) * 255; - uint8_t opacity = z <= zoomIndex + const std::chrono::duration<float> timeDiff = now - changeTimes[z]; + const int32_t opacityChange = (duration == Milliseconds(0) ? 1 : (timeDiff / duration)) * 255; + const uint8_t opacity = z <= zoomIndex ? util::min(255, changeOpacities[z] + opacityChange) : util::max(0, changeOpacities[z] - opacityChange); if (opacities.data[z] != opacity) { @@ -74,4 +74,8 @@ void FrameHistory::bind(gl::Context& context, uint32_t unit) { context.bindTexture(*texture, unit); } +bool FrameHistory::isVisible(const float zoom) const { + return opacities.data[std::floor(zoom * 10)] != 0; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp index f2b11f5f41..75a8b60a71 100644 --- a/src/mbgl/renderer/frame_history.hpp +++ b/src/mbgl/renderer/frame_history.hpp @@ -22,6 +22,7 @@ public: bool needsAnimation(const Duration&) const; void bind(gl::Context&, uint32_t); void upload(gl::Context&, uint32_t); + bool isVisible(const float zoom) const; private: std::array<TimePoint, 256> changeTimes; diff --git a/src/mbgl/renderer/group_by_layout.cpp b/src/mbgl/renderer/group_by_layout.cpp index df1eb7c7dd..3b02727ff8 100644 --- a/src/mbgl/renderer/group_by_layout.cpp +++ b/src/mbgl/renderer/group_by_layout.cpp @@ -19,13 +19,13 @@ std::string layoutKey(const RenderLayer& layer) { writer.StartArray(); writer.Uint(static_cast<uint32_t>(layer.type)); - writer.String(layer.baseImpl.source); - writer.String(layer.baseImpl.sourceLayer); - writer.Double(layer.baseImpl.minZoom); - writer.Double(layer.baseImpl.maxZoom); - writer.Uint(static_cast<uint32_t>(layer.baseImpl.visibility)); - stringify(writer, layer.baseImpl.filter); - layer.baseImpl.stringifyLayout(writer); + writer.String(layer.baseImpl->source); + writer.String(layer.baseImpl->sourceLayer); + writer.Double(layer.baseImpl->minZoom); + writer.Double(layer.baseImpl->maxZoom); + writer.Uint(static_cast<uint32_t>(layer.baseImpl->visibility)); + stringify(writer, layer.baseImpl->filter); + layer.baseImpl->stringifyLayout(writer); writer.EndArray(); return s.GetString(); diff --git a/src/mbgl/renderer/image_atlas.cpp b/src/mbgl/renderer/image_atlas.cpp new file mode 100644 index 0000000000..8eee7c2095 --- /dev/null +++ b/src/mbgl/renderer/image_atlas.cpp @@ -0,0 +1,60 @@ +#include <mbgl/renderer/image_atlas.hpp> + +#include <mapbox/shelf-pack.hpp> + +namespace mbgl { + +static constexpr uint32_t padding = 1; + +ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& image) + : pixelRatio(image.pixelRatio), + textureRect( + bin.x + padding, + bin.y + padding, + bin.w - padding * 2, + bin.h - padding * 2 + ) { +} + +ImageAtlas makeImageAtlas(const ImageMap& images) { + ImageAtlas result; + + mapbox::ShelfPack::ShelfPackOptions options; + options.autoResize = true; + mapbox::ShelfPack pack(0, 0, options); + + for (const auto& entry : images) { + const style::Image::Impl& image = *entry.second; + + const mapbox::Bin& bin = *pack.packOne(-1, + image.image.size.width + 2 * padding, + image.image.size.height + 2 * padding); + + result.image.resize({ + static_cast<uint32_t>(pack.width()), + static_cast<uint32_t>(pack.height()) + }); + + PremultipliedImage::copy(image.image, + result.image, + { 0, 0 }, + { + bin.x + padding, + bin.y + padding + }, + image.image.size); + + result.positions.emplace(image.id, + ImagePosition { bin, image }); + } + + pack.shrink(); + result.image.resize({ + static_cast<uint32_t>(pack.width()), + static_cast<uint32_t>(pack.height()) + }); + + return result; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/image_atlas.hpp b/src/mbgl/renderer/image_atlas.hpp new file mode 100644 index 0000000000..b3cc166eff --- /dev/null +++ b/src/mbgl/renderer/image_atlas.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <mbgl/style/image_impl.hpp> +#include <mbgl/util/rect.hpp> + +#include <mapbox/shelf-pack.hpp> + +#include <array> + +namespace mbgl { + +class ImagePosition { +public: + ImagePosition(const mapbox::Bin&, const style::Image::Impl&); + + float pixelRatio; + Rect<uint16_t> textureRect; + + std::array<uint16_t, 2> tl() const { + return {{ + textureRect.x, + textureRect.y + }}; + } + + std::array<uint16_t, 2> br() const { + return {{ + static_cast<uint16_t>(textureRect.x + textureRect.w), + static_cast<uint16_t>(textureRect.y + textureRect.h) + }}; + } + + std::array<float, 2> displaySize() const { + return {{ + textureRect.w / pixelRatio, + textureRect.h / pixelRatio, + }}; + } +}; + +using ImagePositions = std::map<std::string, ImagePosition>; + +class ImageAtlas { +public: + PremultipliedImage image; + ImagePositions positions; +}; + +ImageAtlas makeImageAtlas(const ImageMap&); + +} // namespace mbgl diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp new file mode 100644 index 0000000000..2ef6be0c4f --- /dev/null +++ b/src/mbgl/renderer/image_manager.cpp @@ -0,0 +1,184 @@ +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { + +void ImageManager::setLoaded(bool loaded_) { + if (loaded == loaded_) { + return; + } + + loaded = loaded_; + + if (loaded) { + for (const auto& entry : requestors) { + notify(*entry.first, entry.second); + } + requestors.clear(); + } +} + +bool ImageManager::isLoaded() const { + return loaded; +} + +void ImageManager::addImage(Immutable<style::Image::Impl> image_) { + assert(images.find(image_->id) == images.end()); + images.emplace(image_->id, std::move(image_)); +} + +void ImageManager::updateImage(Immutable<style::Image::Impl> image_) { + removeImage(image_->id); + addImage(std::move(image_)); +} + +void ImageManager::removeImage(const std::string& id) { + assert(images.find(id) != images.end()); + images.erase(id); + + auto it = patterns.find(id); + if (it != patterns.end()) { + // Clear pattern from the atlas image. + const uint32_t x = it->second.bin->x; + const uint32_t y = it->second.bin->y; + const uint32_t w = it->second.bin->w; + const uint32_t h = it->second.bin->h; + PremultipliedImage::clear(atlasImage, { x, y }, { w, h }); + + shelfPack.unref(*it->second.bin); + patterns.erase(it); + } +} + +const style::Image::Impl* ImageManager::getImage(const std::string& id) const { + const auto it = images.find(id); + if (it != images.end()) { + return it->second.get(); + } + return nullptr; +} + +void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair) { + // If the sprite has been loaded, or if all the icon dependencies are already present + // (i.e. if they've been addeded via runtime styling), then notify the requestor immediately. + // Otherwise, delay notification until the sprite is loaded. At that point, if any of the + // dependencies are still unavailable, we'll just assume they are permanently missing. + bool hasAllDependencies = true; + if (!isLoaded()) { + for (const auto& dependency : pair.first) { + if (images.find(dependency) == images.end()) { + hasAllDependencies = false; + } + } + } + if (isLoaded() || hasAllDependencies) { + notify(requestor, std::move(pair)); + } else { + requestors.emplace(&requestor, std::move(pair)); + } +} + +void ImageManager::removeRequestor(ImageRequestor& requestor) { + requestors.erase(&requestor); +} + +void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const { + ImageMap response; + + for (const auto& dependency : pair.first) { + auto it = images.find(dependency); + if (it != images.end()) { + response.emplace(*it); + } + } + + requestor.onImagesAvailable(response, pair.second); +} + +void ImageManager::dumpDebugLogs() const { + Log::Info(Event::General, "ImageManager::loaded: %d", loaded); +} + +// When copied into the atlas texture, image data is padded by one pixel on each side. Icon +// images are padded with fully transparent pixels, while pattern images are padded with a +// copy of the image data wrapped from the opposite side. In both cases, this ensures the +// correct behavior of GL_LINEAR texture sampling mode. +static constexpr uint16_t padding = 1; + +static mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() { + mapbox::ShelfPack::ShelfPackOptions options; + options.autoResize = true; + return options; +} + +ImageManager::ImageManager() + : shelfPack(64, 64, shelfPackOptions()) { +} + +ImageManager::~ImageManager() = default; + +optional<ImagePosition> ImageManager::getPattern(const std::string& id) { + auto it = patterns.find(id); + if (it != patterns.end()) { + return it->second.position; + } + + const style::Image::Impl* image = getImage(id); + if (!image) { + return {}; + } + + const uint16_t width = image->image.size.width + padding * 2; + const uint16_t height = image->image.size.height + padding * 2; + + mapbox::Bin* bin = shelfPack.packOne(-1, width, height); + if (!bin) { + return {}; + } + + atlasImage.resize(getPixelSize()); + + const PremultipliedImage& src = image->image; + + const uint32_t x = bin->x + padding; + const uint32_t y = bin->y + padding; + const uint32_t w = src.size.width; + const uint32_t h = src.size.height; + + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h }); + + // Add 1 pixel wrapped padding on each side of the image. + PremultipliedImage::copy(src, atlasImage, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y + h }, { w, 1 }); // B + PremultipliedImage::copy(src, atlasImage, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x + w, y }, { 1, h }); // R + + dirty = true; + + return patterns.emplace(id, Pattern { bin, { *bin, *image } }).first->second.position; +} + +Size ImageManager::getPixelSize() const { + return Size { + static_cast<uint32_t>(shelfPack.width()), + static_cast<uint32_t>(shelfPack.height()) + }; +} + +void ImageManager::upload(gl::Context& context, gl::TextureUnit unit) { + if (!atlasTexture) { + atlasTexture = context.createTexture(atlasImage, unit); + } else if (dirty) { + context.updateTexture(*atlasTexture, atlasImage, unit); + } + + dirty = false; +} + +void ImageManager::bind(gl::Context& context, gl::TextureUnit unit) { + upload(context, unit); + context.bindTexture(*atlasTexture, unit, gl::TextureFilter::Linear); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp new file mode 100644 index 0000000000..f72ba9fb53 --- /dev/null +++ b/src/mbgl/renderer/image_manager.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include <mbgl/style/image_impl.hpp> +#include <mbgl/renderer/image_atlas.hpp> +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/gl/texture.hpp> + +#include <mapbox/shelf-pack.hpp> + +#include <set> +#include <string> + +namespace mbgl { + +namespace gl { +class Context; +} // namespace gl + +class ImageRequestor { +public: + virtual ~ImageRequestor() = default; + virtual void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) = 0; +}; + +/* + ImageManager does two things: + + 1. Tracks requests for icon images from tile workers and sends responses when the requests are fulfilled. + 2. Builds a texture atlas for pattern images. + + These are disparate responsibilities and should eventually be handled by different classes. When we implement + data-driven support for `*-pattern`, we'll likely use per-bucket pattern atlases, and that would be a good time + to refactor this. +*/ +class ImageManager : public util::noncopyable { +public: + ImageManager(); + ~ImageManager(); + + void setLoaded(bool); + bool isLoaded() const; + + void dumpDebugLogs() const; + + const style::Image::Impl* getImage(const std::string&) const; + + void addImage(Immutable<style::Image::Impl>); + void updateImage(Immutable<style::Image::Impl>); + void removeImage(const std::string&); + + void getImages(ImageRequestor&, ImageRequestPair&&); + void removeRequestor(ImageRequestor&); + +private: + void notify(ImageRequestor&, const ImageRequestPair&) const; + + bool loaded = false; + + std::unordered_map<ImageRequestor*, ImageRequestPair> requestors; + ImageMap images; + +// Pattern stuff +public: + optional<ImagePosition> getPattern(const std::string& name); + + void bind(gl::Context&, gl::TextureUnit unit); + void upload(gl::Context&, gl::TextureUnit unit); + + Size getPixelSize() const; + + // Only for use in tests. + const PremultipliedImage& getAtlasImage() const { + return atlasImage; + } + +private: + struct Pattern { + mapbox::Bin* bin; + ImagePosition position; + }; + + mapbox::ShelfPack shelfPack; + std::unordered_map<std::string, Pattern> patterns; + PremultipliedImage atlasImage; + mbgl::optional<gl::Texture> atlasTexture; + bool dirty = true; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp new file mode 100644 index 0000000000..9fddba3f74 --- /dev/null +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -0,0 +1,115 @@ +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/style/layers/background_layer_impl.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/util/tile_cover.hpp> + +namespace mbgl { + +using namespace style; + +RenderBackgroundLayer::RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl> _impl) + : RenderLayer(style::LayerType::Background, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::BackgroundLayer::Impl& RenderBackgroundLayer::impl() const { + return static_cast<const style::BackgroundLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderBackgroundLayer::createBucket(const BucketParameters &, + const std::vector<const RenderLayer *> &) const { + assert(false); + return nullptr; +} + +void RenderBackgroundLayer::transition(const TransitionParameters ¶meters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters ¶meters) { + evaluated = unevaluated.evaluate(parameters); + + passes = evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent + : RenderPass::None; +} + +bool RenderBackgroundLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { + // Note that for bottommost layers without a pattern, the background color is drawn with + // glClear rather than this method. + + style::FillPaintProperties::PossiblyEvaluated properties; + properties.get<FillPattern>() = evaluated.get<BackgroundPattern>(); + properties.get<FillOpacity>() = { evaluated.get<BackgroundOpacity>() }; + properties.get<FillColor>() = { evaluated.get<BackgroundColor>() }; + + const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + if (!evaluated.get<BackgroundPattern>().to.empty()) { + optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from); + optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().to); + + if (!imagePosA || !imagePosB) + return; + + parameters.imageManager.bind(parameters.context, 0); + + for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { + parameters.programs.fillPattern.get(properties).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + FillPatternUniforms::values( + parameters.matrixForTile(tileID), + parameters.context.viewport.getCurrentValue().size, + parameters.imageManager.getPixelSize(), + *imagePosA, + *imagePosB, + evaluated.get<BackgroundPattern>(), + tileID, + parameters.state + ), + parameters.staticData.tileVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } else { + for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { + parameters.programs.fill.get(properties).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + FillProgram::UniformValues { + uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) }, + uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_background_layer.hpp b/src/mbgl/renderer/layers/render_background_layer.hpp index b1f709953f..a619670ee4 100644 --- a/src/mbgl/renderer/render_background_layer.hpp +++ b/src/mbgl/renderer/layers/render_background_layer.hpp @@ -1,30 +1,28 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/background_layer.hpp> +#include <mbgl/style/layers/background_layer_impl.hpp> #include <mbgl/style/layers/background_layer_properties.hpp> namespace mbgl { class RenderBackgroundLayer: public RenderLayer { public: - - RenderBackgroundLayer(const style::BackgroundLayer::Impl&); + RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl>); ~RenderBackgroundLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; // Paint properties style::BackgroundPaintProperties::Unevaluated unevaluated; - style::BackgroundPaintProperties::Evaluated evaluated; + style::BackgroundPaintProperties::PossiblyEvaluated evaluated; - const style::BackgroundLayer::Impl* const impl; + const style::BackgroundLayer::Impl& impl() const; }; template <> @@ -32,4 +30,4 @@ inline bool RenderLayer::is<RenderBackgroundLayer>() const { return type == style::LayerType::Background; } -} +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp new file mode 100644 index 0000000000..e7b022f3ee --- /dev/null +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -0,0 +1,120 @@ +#include <mbgl/renderer/layers/render_circle_layer.hpp> +#include <mbgl/renderer/buckets/circle_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/circle_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/circle_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl) + : RenderLayer(style::LayerType::Circle, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::CircleLayer::Impl& RenderCircleLayer::impl() const { + return static_cast<const style::CircleLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderCircleLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<CircleBucket>(parameters, layers); +} + +void RenderCircleLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 || + evaluated.get<style::CircleStrokeWidth>().constantOr(1) > 0) + && (evaluated.get<style::CircleColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::CircleStrokeColor>().constantOr(Color::black()).a > 0) + && (evaluated.get<style::CircleOpacity>().constantOr(1) > 0 || + evaluated.get<style::CircleStrokeOpacity>().constantOr(1) > 0)) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderCircleLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + const bool scaleWithMap = evaluated.get<CirclePitchScale>() == CirclePitchScaleType::Map; + const bool pitchWithMap = evaluated.get<CirclePitchAlignment>() == AlignmentType::Map; + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl))); + CircleBucket& bucket = *reinterpret_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.circle.get(evaluated).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + parameters.mapMode == MapMode::Still + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + CircleProgram::UniformValues { + uniforms::u_matrix::Value{ + tile.translatedMatrix(evaluated.get<CircleTranslate>(), + evaluated.get<CircleTranslateAnchor>(), + parameters.state) + }, + uniforms::u_scale_with_map::Value{ scaleWithMap }, + uniforms::u_extrude_scale::Value{ pitchWithMap + ? std::array<float, 2> {{ + tile.id.pixelsToTileUnits(1, parameters.state.getZoom()), + tile.id.pixelsToTileUnits(1, parameters.state.getZoom()) }} + : parameters.pixelsToGLUnits }, + uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }, + uniforms::u_pitch_with_map::Value{ pitchWithMap } + }, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + } +} + +bool RenderCircleLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float zoom, + const float bearing, + const float pixelsToTileUnits) const { + + // Translate query geometry + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::CircleTranslate>(), + evaluated.get<style::CircleTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + // Evaluate function + auto circleRadius = evaluated.get<style::CircleRadius>() + .evaluate(feature, zoom, style::CircleRadius::defaultValue()) + * pixelsToTileUnits; + + // Test intersection + return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp index 3b82b5c988..f31715f98f 100644 --- a/src/mbgl/renderer/render_circle_layer.hpp +++ b/src/mbgl/renderer/layers/render_circle_layer.hpp @@ -1,22 +1,20 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/style/layers/circle_layer_impl.hpp> #include <mbgl/style/layers/circle_layer_properties.hpp> namespace mbgl { class RenderCircleLayer: public RenderLayer { public: - - RenderCircleLayer(const style::CircleLayer::Impl&); + RenderCircleLayer(Immutable<style::CircleLayer::Impl>); ~RenderCircleLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,9 +27,9 @@ public: // Paint properties style::CirclePaintProperties::Unevaluated unevaluated; - style::CirclePaintProperties::Evaluated evaluated; + style::CirclePaintProperties::PossiblyEvaluated evaluated; - const style::CircleLayer::Impl* const impl; + const style::CircleLayer::Impl& impl() const; }; template <> @@ -39,4 +37,4 @@ inline bool RenderLayer::is<RenderCircleLayer>() const { return type == style::LayerType::Circle; } -} +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp new file mode 100644 index 0000000000..7ece3970da --- /dev/null +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -0,0 +1,81 @@ +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/style/layers/custom_layer_impl.hpp> +#include <mbgl/map/transform_state.hpp> + +namespace mbgl { + +using namespace style; + +RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl) + : RenderLayer(LayerType::Custom, _impl) { +} + +RenderCustomLayer::~RenderCustomLayer() { + assert(BackendScope::exists()); + if (initialized) { + if (contextDestroyed && impl().contextLostFn ) { + impl().contextLostFn(impl().context); + } else if (!contextDestroyed && impl().deinitializeFn) { + impl().deinitializeFn(impl().context); + } + } +} + +const CustomLayer::Impl& RenderCustomLayer::impl() const { + return static_cast<const CustomLayer::Impl&>(*baseImpl); +} + +void RenderCustomLayer::evaluate(const PropertyEvaluationParameters&) { + passes = RenderPass::Translucent; +} + +bool RenderCustomLayer::hasTransition() const { + return false; +} + +std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); + return nullptr; +} + +void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) { + if (!initialized) { + assert(impl().initializeFn); + impl().initializeFn(impl().context); + initialized = true; + } + + gl::Context& context = paintParameters.context; + const TransformState& state = paintParameters.state; + + // Reset GL state to a known state so the CustomLayer always has a clean slate. + context.bindVertexArray = 0; + context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)); + context.setStencilMode(gl::StencilMode::disabled()); + context.setColorMode(paintParameters.colorModeForRenderPass()); + + CustomLayerRenderParameters parameters; + + parameters.width = state.getSize().width; + parameters.height = state.getSize().height; + parameters.latitude = state.getLatLng().latitude(); + parameters.longitude = state.getLatLng().longitude(); + parameters.zoom = state.getZoom(); + parameters.bearing = -state.getAngle() * util::RAD2DEG; + parameters.pitch = state.getPitch(); + parameters.fieldOfView = state.getFieldOfView(); + + assert(impl().renderFn); + impl().renderFn(impl().context, parameters); + + // Reset the view back to our original one, just in case the CustomLayer changed + // the viewport or Framebuffer. + paintParameters.backend.bind(); + context.setDirtyState(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp index c3af6c77b2..32ed9da8da 100644 --- a/src/mbgl/renderer/render_custom_layer.hpp +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -1,25 +1,31 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/custom_layer.hpp> +#include <mbgl/style/layers/custom_layer_impl.hpp> namespace mbgl { class RenderCustomLayer: public RenderLayer { public: + RenderCustomLayer(Immutable<style::CustomLayer::Impl>); + ~RenderCustomLayer() final; - RenderCustomLayer(const style::CustomLayer::Impl&); - ~RenderCustomLayer() final = default; - - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) final {} + void transition(const TransitionParameters&) final {} void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const final; + void render(PaintParameters&, RenderSource*) final; + + const style::CustomLayer::Impl& impl() const; - const style::CustomLayer::Impl* const impl; + void markContextDestroyed() { + contextDestroyed = true; + }; + +private: + bool initialized = false; + bool contextDestroyed = false; }; template <> @@ -27,4 +33,4 @@ inline bool RenderLayer::is<RenderCustomLayer>() const { return type == style::LayerType::Custom; } -} +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp new file mode 100644 index 0000000000..fbd6160e8a --- /dev/null +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -0,0 +1,167 @@ +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl) + : RenderLayer(style::LayerType::FillExtrusion, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::FillExtrusionLayer::Impl& RenderFillExtrusionLayer::impl() const { + return static_cast<const style::FillExtrusionLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<FillExtrusionBucket>(parameters, layers); +} + +void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) + ? (RenderPass::Translucent | RenderPass::Pass3D) + : RenderPass::None; +} + +bool RenderFillExtrusionLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + if (parameters.pass == RenderPass::Pass3D) { + const auto& size = parameters.staticData.backendSize; + + if (!renderTexture || renderTexture->getSize() != size) { + renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer); + } + + renderTexture->bind(); + + optional<float> depthClearValue = {}; + if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0; + // Flag the depth buffer as no longer needing to be cleared for the remainder of this pass. + parameters.staticData.depthRenderbuffer->shouldClear(false); + + parameters.context.setStencilMode(gl::StencilMode::disabled()); + parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {}); + + if (evaluated.get<FillExtrusionPattern>().from.empty()) { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); + FillExtrusionBucket& bucket = + *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.fillExtrusion.get(evaluated).draw( + parameters.context, gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + FillExtrusionUniforms::values( + tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), + evaluated.get<FillExtrusionTranslateAnchor>(), + parameters.state), + parameters.state, parameters.evaluatedLight), + *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, + bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), + getID()); + } + } else { + optional<ImagePosition> imagePosA = + parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from); + optional<ImagePosition> imagePosB = + parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to); + + if (!imagePosA || !imagePosB) { + return; + } + + parameters.imageManager.bind(parameters.context, 0); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); + FillExtrusionBucket& bucket = + *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.fillExtrusionPattern.get(evaluated).draw( + parameters.context, gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + FillExtrusionPatternUniforms::values( + tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), + evaluated.get<FillExtrusionTranslateAnchor>(), + parameters.state), + parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB, + evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state, + -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, + parameters.evaluatedLight), + *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, + bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), + getID()); + } + } + + } else if (parameters.pass == RenderPass::Translucent) { + parameters.context.bindTexture(renderTexture->getTexture()); + + const auto& size = parameters.staticData.backendSize; + + mat4 viewportMat; + matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); + + const Properties<>::PossiblyEvaluated properties; + + parameters.programs.extrusionTexture.draw( + parameters.context, gl::Triangles(), gl::DepthMode::disabled(), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + ExtrusionTextureProgram::UniformValues{ + uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, + uniforms::u_image::Value{ 0 }, + uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } }, + parameters.staticData.extrusionTextureVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.extrusionTextureSegments, + ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, + parameters.state.getZoom(), getID()); + } +} + +bool RenderFillExtrusionLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float, + const float bearing, + const float pixelsToTileUnits) const { + + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::FillExtrusionTranslate>(), + evaluated.get<style::FillExtrusionTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp index bd66d8e3b1..838494cf91 100644 --- a/src/mbgl/renderer/render_fill_extrusion_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp @@ -1,22 +1,22 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> #include <mbgl/style/layers/fill_extrusion_layer_properties.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/offscreen_texture.hpp> namespace mbgl { class RenderFillExtrusionLayer: public RenderLayer { public: - - RenderFillExtrusionLayer(const style::FillExtrusionLayer::Impl&); + RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl>); ~RenderFillExtrusionLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,9 +29,11 @@ public: // Paint properties style::FillExtrusionPaintProperties::Unevaluated unevaluated; - style::FillExtrusionPaintProperties::Evaluated evaluated; + style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated; + + const style::FillExtrusionLayer::Impl& impl() const; - const style::FillExtrusionLayer::Impl* const impl; + optional<OffscreenTexture> renderTexture; }; template <> diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp new file mode 100644 index 0000000000..22cb9563c1 --- /dev/null +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -0,0 +1,205 @@ +#include <mbgl/renderer/layers/render_fill_layer.hpp> +#include <mbgl/renderer/buckets/fill_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/fill_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl) + : RenderLayer(style::LayerType::Fill, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::FillLayer::Impl& RenderFillLayer::impl() const { + return static_cast<const style::FillLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderFillLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<FillBucket>(parameters, layers); +} + +void RenderFillLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + if (unevaluated.get<style::FillOutlineColor>().isUndefined()) { + evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>(); + } + + passes = RenderPass::None; + + if (evaluated.get<style::FillAntialias>()) { + passes |= RenderPass::Translucent; + } + + if (!unevaluated.get<style::FillPattern>().isUndefined() + || evaluated.get<style::FillColor>().constantOr(Color()).a < 1.0f + || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f) { + passes |= RenderPass::Translucent; + } else { + passes |= RenderPass::Opaque; + } +} + +bool RenderFillLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { + if (evaluated.get<FillPattern>().from.empty()) { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl))); + FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, + const auto& drawMode, + const auto& depthMode, + const auto& indexBuffer, + const auto& segments) { + program.get(evaluated).draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + FillProgram::UniformValues { + uniforms::u_matrix::Value{ + tile.translatedMatrix(evaluated.get<FillTranslate>(), + evaluated.get<FillTranslateAnchor>(), + parameters.state) + }, + uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + }, + *bucket.vertexBuffer, + indexBuffer, + segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + // Only draw the fill when it's opaque and we're drawing opaque fragments, + // or when it's translucent and we're drawing translucent fragments. + if ((evaluated.get<FillColor>().constantOr(Color()).a >= 1.0f + && evaluated.get<FillOpacity>().constantOr(0) >= 1.0f) == (parameters.pass == RenderPass::Opaque)) { + draw(parameters.programs.fill, + gl::Triangles(), + parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque + ? gl::DepthMode::ReadWrite + : gl::DepthMode::ReadOnly), + *bucket.triangleIndexBuffer, + bucket.triangleSegments); + } + + if (evaluated.get<FillAntialias>() && parameters.pass == RenderPass::Translucent) { + draw(parameters.programs.fillOutline, + gl::Lines{ 2.0f }, + parameters.depthModeForSublayer( + unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0, + gl::DepthMode::ReadOnly), + *bucket.lineIndexBuffer, + bucket.lineSegments); + } + } + } else { + if (parameters.pass != RenderPass::Translucent) { + return; + } + + optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillPattern>().from); + optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillPattern>().to); + + if (!imagePosA || !imagePosB) { + return; + } + + parameters.imageManager.bind(parameters.context, 0); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl))); + FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, + const auto& drawMode, + const auto& depthMode, + const auto& indexBuffer, + const auto& segments) { + program.get(evaluated).draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + FillPatternUniforms::values( + tile.translatedMatrix(evaluated.get<FillTranslate>(), + evaluated.get<FillTranslateAnchor>(), + parameters.state), + parameters.context.viewport.getCurrentValue().size, + parameters.imageManager.getPixelSize(), + *imagePosA, + *imagePosB, + evaluated.get<FillPattern>(), + tile.id, + parameters.state + ), + *bucket.vertexBuffer, + indexBuffer, + segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + draw(parameters.programs.fillPattern, + gl::Triangles(), + parameters.depthModeForSublayer(1, gl::DepthMode::ReadWrite), + *bucket.triangleIndexBuffer, + bucket.triangleSegments); + + if (evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined()) { + draw(parameters.programs.fillOutlinePattern, + gl::Lines { 2.0f }, + parameters.depthModeForSublayer(2, gl::DepthMode::ReadOnly), + *bucket.lineIndexBuffer, + bucket.lineSegments); + } + } + } +} + +bool RenderFillLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float, + const float bearing, + const float pixelsToTileUnits) const { + + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::FillTranslate>(), + evaluated.get<style::FillTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); +} + + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp index 8080cf289b..a51865698f 100644 --- a/src/mbgl/renderer/render_fill_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_layer.hpp @@ -1,22 +1,20 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_layer_impl.hpp> #include <mbgl/style/layers/fill_layer_properties.hpp> namespace mbgl { class RenderFillLayer: public RenderLayer { public: - - RenderFillLayer(const style::FillLayer::Impl&); + RenderFillLayer(Immutable<style::FillLayer::Impl>); ~RenderFillLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,9 +27,9 @@ public: // Paint properties style::FillPaintProperties::Unevaluated unevaluated; - style::FillPaintProperties::Evaluated evaluated; + style::FillPaintProperties::PossiblyEvaluated evaluated; - const style::FillLayer::Impl* const impl; + const style::FillLayer::Impl& impl() const; }; template <> diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp new file mode 100644 index 0000000000..1b4a1c0ff7 --- /dev/null +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -0,0 +1,204 @@ +#include <mbgl/renderer/layers/render_line_layer.hpp> +#include <mbgl/renderer/buckets/line_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/line_program.hpp> +#include <mbgl/geometry/line_atlas.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl) + : RenderLayer(style::LayerType::Line, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::LineLayer::Impl& RenderLineLayer::impl() const { + return static_cast<const style::LineLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<LineBucket>(parameters, layers, impl().layout); +} + +void RenderLineLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) { + style::Properties<LineFloorwidth>::Unevaluated extra(unevaluated.get<style::LineWidth>()); + + auto dashArrayParams = parameters; + dashArrayParams.useIntegerZoom = true; + + evaluated = RenderLinePaintProperties::PossiblyEvaluated( + unevaluated.evaluate(parameters).concat(extra.evaluate(dashArrayParams))); + + passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0 + && evaluated.get<style::LineColor>().constantOr(Color::black()).a > 0 + && evaluated.get<style::LineWidth>().constantOr(1.0) > 0) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderLineLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<LineBucket*>(tile.tile.getBucket(*baseImpl))); + LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, auto&& uniformValues) { + program.get(evaluated).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + std::move(uniformValues), + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + if (!evaluated.get<LineDasharray>().from.empty()) { + const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round + ? LinePatternCap::Round : LinePatternCap::Square; + LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().from, cap); + LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().to, cap); + + parameters.lineAtlas.bind(parameters.context, 0); + + draw(parameters.programs.lineSDF, + LineSDFProgram::uniformValues( + evaluated, + parameters.pixelRatio, + tile, + parameters.state, + parameters.pixelsToGLUnits, + posA, + posB, + parameters.lineAtlas.getSize().width)); + + } else if (!evaluated.get<LinePattern>().from.empty()) { + optional<ImagePosition> posA = parameters.imageManager.getPattern(evaluated.get<LinePattern>().from); + optional<ImagePosition> posB = parameters.imageManager.getPattern(evaluated.get<LinePattern>().to); + + if (!posA || !posB) + return; + + parameters.imageManager.bind(parameters.context, 0); + + draw(parameters.programs.linePattern, + LinePatternProgram::uniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits, + parameters.imageManager.getPixelSize(), + *posA, + *posB)); + + } else { + draw(parameters.programs.line, + LineProgram::uniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits)); + } + } +} + +optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) { + if (offset == 0) return {}; + + GeometryCollection newRings; + Point<double> zero(0, 0); + for (const auto& ring : rings) { + newRings.emplace_back(); + auto& newRing = newRings.back(); + + for (auto i = ring.begin(); i != ring.end(); i++) { + auto& p = *i; + + Point<double> aToB = i == ring.begin() ? + zero : + util::perp(util::unit(convertPoint<double>(p - *(i - 1)))); + Point<double> bToC = i + 1 == ring.end() ? + zero : + util::perp(util::unit(convertPoint<double>(*(i + 1) - p))); + Point<double> extrude = util::unit(aToB + bToC); + + const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y; + extrude *= (1.0 / cosHalfAngle); + + newRing.push_back(convertPoint<int16_t>(extrude * offset) + p); + } + } + + return newRings; +} + +bool RenderLineLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float zoom, + const float bearing, + const float pixelsToTileUnits) const { + + // Translate query geometry + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::LineTranslate>(), + evaluated.get<style::LineTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + // Evaluate function + auto offset = evaluated.get<style::LineOffset>() + .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits; + + // Apply offset to geometry + auto offsetGeometry = offsetLine(feature.getGeometries(), offset); + + // Test intersection + const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits; + return util::polygonIntersectsBufferedMultiLine( + translatedQueryGeometry.value_or(queryGeometry), + offsetGeometry.value_or(feature.getGeometries()), + halfWidth); +} + +float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { + float lineWidth = evaluated.get<style::LineWidth>() + .evaluate(feature, zoom, style::LineWidth::defaultValue()); + float gapWidth = evaluated.get<style::LineGapWidth>() + .evaluate(feature, zoom, style::LineGapWidth::defaultValue()); + if (gapWidth) { + return gapWidth + 2 * lineWidth; + } else { + return lineWidth; + } +} + + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 6d6fecc227..8bf7e2329d 100644 --- a/src/mbgl/renderer/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -1,22 +1,29 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/style/layers/line_layer_properties.hpp> +#include <mbgl/programs/uniforms.hpp> namespace mbgl { +struct LineFloorwidth : style::DataDrivenPaintProperty<float, attributes::a_floorwidth, uniforms::u_floorwidth> { + static float defaultValue() { return 1; } +}; + +class RenderLinePaintProperties : public style::ConcatenateProperties< + style::LinePaintProperties::PropertyTypes, + TypeList<LineFloorwidth>>::Type {}; + class RenderLineLayer: public RenderLayer { public: - - RenderLineLayer(const style::LineLayer::Impl&); + RenderLineLayer(Immutable<style::LineLayer::Impl>); ~RenderLineLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,16 +36,12 @@ public: // Paint properties style::LinePaintProperties::Unevaluated unevaluated; - style::LinePaintProperties::Evaluated evaluated; - - const style::LineLayer::Impl* const impl; + RenderLinePaintProperties::PossiblyEvaluated evaluated; - // Special case - float dashLineWidth = 1; + const style::LineLayer::Impl& impl() const; private: float getLineWidth(const GeometryTileFeature&, const float) const; - }; template <> diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp new file mode 100644 index 0000000000..06616d90e5 --- /dev/null +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -0,0 +1,155 @@ +#include <mbgl/renderer/layers/render_raster_layer.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/sources/render_image_source.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/raster_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/raster_layer_impl.hpp> + +namespace mbgl { + +using namespace style; + +RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl) + : RenderLayer(style::LayerType::Raster, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::RasterLayer::Impl& RenderRasterLayer::impl() const { + return static_cast<const style::RasterLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderRasterLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); + return nullptr; +} + +void RenderRasterLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderRasterLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +static float saturationFactor(float saturation) { + if (saturation > 0) { + return 1 - 1 / (1.001 - saturation); + } else { + return -saturation; + } +} + +static float contrastFactor(float contrast) { + if (contrast > 0) { + return 1 / (1 - contrast); + } else { + return 1 + contrast; + } +} + +static std::array<float, 3> spinWeights(float spin) { + spin *= util::DEG2RAD; + float s = std::sin(spin); + float c = std::cos(spin); + std::array<float, 3> spin_weights = {{ + (2 * c + 1) / 3, + (-std::sqrt(3.0f) * s - c + 1) / 3, + (std::sqrt(3.0f) * s - c + 1) / 3 + }}; + return spin_weights; +} + +void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source) { + if (parameters.pass != RenderPass::Translucent) + return; + + auto draw = [&] (const mat4& matrix, + const auto& vertexBuffer, + const auto& indexBuffer, + const auto& segments) { + parameters.programs.raster.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + RasterProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_image0::Value{ 0 }, + uniforms::u_image1::Value{ 1 }, + uniforms::u_opacity::Value{ evaluated.get<RasterOpacity>() }, + uniforms::u_fade_t::Value{ 1 }, + uniforms::u_brightness_low::Value{ evaluated.get<RasterBrightnessMin>() }, + uniforms::u_brightness_high::Value{ evaluated.get<RasterBrightnessMax>() }, + uniforms::u_saturation_factor::Value{ saturationFactor(evaluated.get<RasterSaturation>()) }, + uniforms::u_contrast_factor::Value{ contrastFactor(evaluated.get<RasterContrast>()) }, + uniforms::u_spin_weights::Value{ spinWeights(evaluated.get<RasterHueRotate>()) }, + uniforms::u_buffer_scale::Value{ 1.0f }, + uniforms::u_scale_parent::Value{ 1.0f }, + uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} }, + }, + vertexBuffer, + indexBuffer, + segments, + RasterProgram::PaintPropertyBinders { evaluated, 0 }, + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + if (RenderImageSource* imageSource = source->as<RenderImageSource>()) { + if (imageSource->isEnabled() && imageSource->isLoaded() && !imageSource->bucket->needsUpload()) { + RasterBucket& bucket = *imageSource->bucket; + + assert(bucket.texture); + parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); + parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); + + for (auto matrix_ : imageSource->matrices) { + draw(matrix_, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments); + } + } + } else { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl))); + RasterBucket& bucket = *reinterpret_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl)); + + if (!bucket.hasData()) + continue; + + assert(bucket.texture); + parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); + parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); + + if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) { + // Draw only the parts of the tile that aren't drawn by another tile in the layer. + draw(tile.matrix, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments); + } else { + // Draw the full tile. + draw(tile.matrix, + parameters.staticData.rasterVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.rasterSegments); + } + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_raster_layer.hpp b/src/mbgl/renderer/layers/render_raster_layer.hpp index 3ffeb8febf..87de316f7c 100644 --- a/src/mbgl/renderer/render_raster_layer.hpp +++ b/src/mbgl/renderer/layers/render_raster_layer.hpp @@ -1,30 +1,29 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/raster_layer.hpp> +#include <mbgl/style/layers/raster_layer_impl.hpp> #include <mbgl/style/layers/raster_layer_properties.hpp> namespace mbgl { class RenderRasterLayer: public RenderLayer { public: - - RenderRasterLayer(const style::RasterLayer::Impl&); + RenderRasterLayer(Immutable<style::RasterLayer::Impl>); ~RenderRasterLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; // Paint properties style::RasterPaintProperties::Unevaluated unevaluated; - style::RasterPaintProperties::Evaluated evaluated; + style::RasterPaintProperties::PossiblyEvaluated evaluated; - const style::RasterLayer::Impl* const impl; + const style::RasterLayer::Impl& impl() const; }; template <> diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp new file mode 100644 index 0000000000..1376e8a3d8 --- /dev/null +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -0,0 +1,325 @@ +#include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/renderer/property_evaluation_parameters.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/frame_history.hpp> +#include <mbgl/text/glyph_atlas.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/symbol_program.hpp> +#include <mbgl/programs/collision_box_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/tile/geometry_tile.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/layout/symbol_layout.hpp> +#include <mbgl/layout/symbol_projection.hpp> +#include <mbgl/util/math.hpp> + +#include <cmath> + +namespace mbgl { + +using namespace style; + +RenderSymbolLayer::RenderSymbolLayer(Immutable<style::SymbolLayer::Impl> _impl) + : RenderLayer(style::LayerType::Symbol, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::SymbolLayer::Impl& RenderSymbolLayer::impl() const { + return static_cast<const style::SymbolLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderSymbolLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); // Should be calling createLayout() instead. + return nullptr; +} + +std::unique_ptr<SymbolLayout> RenderSymbolLayer::createLayout(const BucketParameters& parameters, + const std::vector<const RenderLayer*>& group, + std::unique_ptr<GeometryTileLayer> layer, + GlyphDependencies& glyphDependencies, + ImageDependencies& imageDependencies) const { + return std::make_unique<SymbolLayout>(parameters, + group, + std::move(layer), + imageDependencies, + glyphDependencies); +} + +void RenderSymbolLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + auto hasIconOpacity = evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0; + auto hasTextOpacity = evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0; + + passes = ((evaluated.get<style::IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) + || (evaluated.get<style::TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderSymbolLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl))); + SymbolBucket& bucket = *reinterpret_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl)); + + const auto& layout = bucket.layout; + + parameters.frameHistory.bind(parameters.context, 1); + + auto draw = [&] (auto& program, + auto&& uniformValues, + const auto& buffers, + const auto& symbolSizeBinder, + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties) + { + // We clip symbols to their tile extent in still mode. + const bool needsClipping = parameters.mapMode == MapMode::Still; + + program.get(paintProperties).draw( + parameters.context, + gl::Triangles(), + values_.pitchAlignment == AlignmentType::Map + ? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly) + : gl::DepthMode::disabled(), + needsClipping + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + std::move(uniformValues), + *buffers.vertexBuffer, + *buffers.dynamicVertexBuffer, + *symbolSizeBinder, + *buffers.indexBuffer, + buffers.segments, + binders, + paintProperties, + parameters.state.getZoom(), + getID() + ); + }; + + assert(dynamic_cast<GeometryTile*>(&tile.tile)); + GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile); + + if (bucket.hasIconData()) { + auto values = iconPropertyValues(layout); + auto paintPropertyValues = iconPaintProperties(); + + const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line && + layout.get<IconRotationAlignment>() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.icon.dynamicVertices, + bucket.icon.placedSymbols, + tile.matrix, + values, + tile, + *bucket.iconSizeBinder, + parameters.state, + parameters.frameHistory); + + parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices)); + } + + const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear; + const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0; + + parameters.context.bindTexture(*geometryTile.iconAtlasTexture, 0, + bucket.sdfIcons || parameters.state.isChanging() || iconScaled || iconTransformed + ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest); + + const Size texsize = geometryTile.iconAtlasTexture->size; + + if (bucket.sdfIcons) { + if (values.hasHalo) { + draw(parameters.programs.symbolIconSDF, + SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + + if (values.hasFill) { + draw(parameters.programs.symbolIconSDF, + SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + } else { + draw(parameters.programs.symbolIcon, + SymbolIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + } + + if (bucket.hasTextData()) { + parameters.context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear); + + auto values = textPropertyValues(layout); + auto paintPropertyValues = textPaintProperties(); + + const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line && + layout.get<TextRotationAlignment>() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.text.dynamicVertices, + bucket.text.placedSymbols, + tile.matrix, + values, + tile, + *bucket.textSizeBinder, + parameters.state, + parameters.frameHistory); + + parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); + } + + const Size texsize = geometryTile.glyphAtlasTexture->size; + + if (values.hasHalo) { + draw(parameters.programs.symbolGlyph, + SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo), + bucket.text, + bucket.textSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).second, + paintPropertyValues); + } + + if (values.hasFill) { + draw(parameters.programs.symbolGlyph, + SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill), + bucket.text, + bucket.textSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).second, + paintPropertyValues); + } + } + + if (bucket.hasCollisionBoxData()) { + static const style::Properties<>::PossiblyEvaluated properties {}; + static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0); + + parameters.programs.collisionBox.draw( + parameters.context, + gl::Lines { 1.0f }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + CollisionBoxProgram::UniformValues { + uniforms::u_matrix::Value{ tile.matrix }, + uniforms::u_scale::Value{ std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ)) }, + uniforms::u_zoom::Value{ float(parameters.state.getZoom() * 10) }, + uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) }, + uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, + uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }, + uniforms::u_pitch::Value{ parameters.state.getPitch() }, + uniforms::u_fadetexture::Value{ 1 } + }, + *bucket.collisionBox.vertexBuffer, + *bucket.collisionBox.indexBuffer, + bucket.collisionBox.segments, + paintAttributeData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } +} + +style::IconPaintProperties::PossiblyEvaluated RenderSymbolLayer::iconPaintProperties() const { + return style::IconPaintProperties::PossiblyEvaluated { + evaluated.get<style::IconOpacity>(), + evaluated.get<style::IconColor>(), + evaluated.get<style::IconHaloColor>(), + evaluated.get<style::IconHaloWidth>(), + evaluated.get<style::IconHaloBlur>(), + evaluated.get<style::IconTranslate>(), + evaluated.get<style::IconTranslateAnchor>() + }; +} + +style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProperties() const { + return style::TextPaintProperties::PossiblyEvaluated { + evaluated.get<style::TextOpacity>(), + evaluated.get<style::TextColor>(), + evaluated.get<style::TextHaloColor>(), + evaluated.get<style::TextHaloWidth>(), + evaluated.get<style::TextHaloBlur>(), + evaluated.get<style::TextTranslate>(), + evaluated.get<style::TextTranslateAnchor>() + }; +} + + +style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { + return style::SymbolPropertyValues { + layout_.get<style::IconPitchAlignment>(), + layout_.get<style::IconRotationAlignment>(), + layout_.get<style::IconKeepUpright>(), + evaluated.get<style::IconTranslate>(), + evaluated.get<style::IconTranslateAnchor>(), + evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 && + evaluated.get<style::IconHaloWidth>().constantOr(1), + evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0, + 10.0f + }; +} + +style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { + // We hide line labels with viewport alignment as they move into the distance + // because the approximations we use for drawing their glyphs get progressively worse + // The "1.5" here means we start hiding them when the distance from the label + // to the camera is 50% greater than the distance from the center of the map + // to the camera. Depending on viewport properties, you might expect this to filter + // the top third of the screen at pitch 60, and do almost nothing at pitch 45 + // "10" is effectively infinite at any pitch we support + const bool limitMaxDistance = + layout_.get<style::SymbolPlacement>() == style::SymbolPlacementType::Line + && layout_.get<style::TextRotationAlignment>() == style::AlignmentType::Map + && layout_.get<style::TextPitchAlignment>() == style::AlignmentType::Viewport; + + return style::SymbolPropertyValues { + layout_.get<style::TextPitchAlignment>(), + layout_.get<style::TextRotationAlignment>(), + layout_.get<style::TextKeepUpright>(), + evaluated.get<style::TextTranslate>(), + evaluated.get<style::TextTranslateAnchor>(), + evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 && + evaluated.get<style::TextHaloWidth>().constantOr(1), + evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0, + limitMaxDistance ? 1.5f : 10.0f + }; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp index 80ffd95a06..83709b5122 100644 --- a/src/mbgl/renderer/render_symbol_layer.hpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp @@ -2,8 +2,8 @@ #include <mbgl/text/glyph.hpp> #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/style/layers/symbol_layer.hpp> +#include <mbgl/style/image_impl.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> namespace mbgl { @@ -13,7 +13,7 @@ namespace style { // {icon,text}-specific paint-property packs for use in the symbol Programs. // Since each program deals either with icons or text, using a smaller property set // lets us avoid unnecessarily binding attributes for properties the program wouldn't use. -class IconPaintProperties : public PaintProperties< +class IconPaintProperties : public Properties< IconOpacity, IconColor, IconHaloColor, @@ -23,7 +23,7 @@ class IconPaintProperties : public PaintProperties< IconTranslateAnchor > {}; -class TextPaintProperties : public PaintProperties< +class TextPaintProperties : public Properties< TextOpacity, TextColor, TextHaloColor, @@ -40,17 +40,16 @@ public: // Layout AlignmentType pitchAlignment; AlignmentType rotationAlignment; - PossiblyEvaluatedPropertyValue<float> layoutSize; + bool keepUpright; // Paint std::array<float, 2> translate; TranslateAnchorType translateAnchor; - float paintSize; - - float sdfScale; // Constant (1.0 or 24.0) bool hasHalo; bool hasFill; + + float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else }; } // namespace style @@ -61,33 +60,35 @@ class GeometryTileLayer; class RenderSymbolLayer: public RenderLayer { public: - RenderSymbolLayer(const style::SymbolLayer::Impl&); + RenderSymbolLayer(Immutable<style::SymbolLayer::Impl>); ~RenderSymbolLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; - style::IconPaintProperties::Evaluated iconPaintProperties() const; - style::TextPaintProperties::Evaluated textPaintProperties() const; + style::IconPaintProperties::PossiblyEvaluated iconPaintProperties() const; + style::TextPaintProperties::PossiblyEvaluated textPaintProperties() const; style::SymbolPropertyValues iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const; style::SymbolPropertyValues textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; - std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, const std::vector<const RenderLayer*>&, - const GeometryTileLayer&, GlyphDependencies&, IconDependencies&) const; + std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, + const std::vector<const RenderLayer*>&, + std::unique_ptr<GeometryTileLayer>, + GlyphDependencies&, + ImageDependencies&) const; // Paint properties style::SymbolPaintProperties::Unevaluated unevaluated; - style::SymbolPaintProperties::Evaluated evaluated; + style::SymbolPaintProperties::PossiblyEvaluated evaluated; float iconSize = 1.0f; float textSize = 16.0f; - const style::SymbolLayer::Impl* const impl; + const style::SymbolLayer::Impl& impl() const; }; template <> diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp new file mode 100644 index 0000000000..299db844bc --- /dev/null +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -0,0 +1,98 @@ +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/update_parameters.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/map/transform_state.hpp> + +namespace mbgl { + +PaintParameters::PaintParameters(gl::Context& context_, + float pixelRatio_, + GLContextMode contextMode_, + RendererBackend& backend_, + const UpdateParameters& updateParameters, + const EvaluatedLight& evaluatedLight_, + RenderStaticData& staticData_, + FrameHistory& frameHistory_, + ImageManager& imageManager_, + LineAtlas& lineAtlas_) + : context(context_), + backend(backend_), + state(updateParameters.transformState), + evaluatedLight(evaluatedLight_), + staticData(staticData_), + frameHistory(frameHistory_), + imageManager(imageManager_), + lineAtlas(lineAtlas_), + mapMode(updateParameters.mode), + debugOptions(updateParameters.debugOptions), + contextMode(contextMode_), + timePoint(updateParameters.timePoint), + pixelRatio(pixelRatio_), +#ifndef NDEBUG + programs((debugOptions & MapDebugOptions::Overdraw) ? staticData_.overdrawPrograms : staticData_.programs) +#else + programs(staticData_.programs) +#endif +{ + // Update the default matrices to the current viewport dimensions. + state.getProjMatrix(projMatrix); + + // Calculate a second projection matrix with the near plane clipped to 100 so as + // not to waste lots of depth buffer precision on very close empty space, for layer + // types (fill-extrusion) that use the depth buffer to emulate real-world space. + state.getProjMatrix(nearClippedProjMatrix, 100); + + pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }}; + + if (state.getViewportMode() == ViewportMode::FlippedY) { + pixelsToGLUnits[1] *= -1; + } +} + +mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID) { + mat4 matrix; + state.matrixFor(matrix, tileID); + matrix::multiply(matrix, projMatrix, matrix); + return matrix; +} + +gl::DepthMode PaintParameters::depthModeForSublayer(uint8_t n, gl::DepthMode::Mask mask) const { + float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon; + float farDepth = nearDepth + depthRangeSize; + return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } }; +} + +gl::DepthMode PaintParameters::depthModeFor3D(gl::DepthMode::Mask mask) const { + return gl::DepthMode { gl::DepthMode::LessEqual, mask, { 0.0, 1.0 } }; +} + +gl::StencilMode PaintParameters::stencilModeForClipping(const ClipID& id) const { + return gl::StencilMode { + gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) }, + static_cast<int32_t>(id.reference.to_ulong()), + 0, + gl::StencilMode::Keep, + gl::StencilMode::Keep, + gl::StencilMode::Replace + }; +} + +gl::ColorMode PaintParameters::colorModeForRenderPass() const { + if (debugOptions & MapDebugOptions::Overdraw) { + const float overdraw = 1.0f / 8.0f; + return gl::ColorMode { + gl::ColorMode::Add { + gl::ColorMode::ConstantColor, + gl::ColorMode::One + }, + Color { overdraw, overdraw, overdraw, 0.0f }, + gl::ColorMode::Mask { true, true, true, true } + }; + } else if (pass == RenderPass::Translucent) { + return gl::ColorMode::alphaBlended(); + } else { + return gl::ColorMode::unblended(); + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index 213c01cfbd..4a2c2c6f12 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -1,14 +1,78 @@ #pragma once +#include <mbgl/renderer/render_pass.hpp> +#include <mbgl/renderer/render_light.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/gl/depth_mode.hpp> +#include <mbgl/gl/stencil_mode.hpp> +#include <mbgl/gl/color_mode.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/algorithm/generate_clip_ids.hpp> + +#include <array> + namespace mbgl { +class RendererBackend; +class UpdateParameters; +class RenderStaticData; +class FrameHistory; class Programs; -class View; +class TransformState; +class ImageManager; +class LineAtlas; +class UnwrappedTileID; class PaintParameters { public: + PaintParameters(gl::Context&, + float pixelRatio, + GLContextMode, + RendererBackend&, + const UpdateParameters&, + const EvaluatedLight&, + RenderStaticData&, + FrameHistory&, + ImageManager&, + LineAtlas&); + + gl::Context& context; + RendererBackend& backend; + + const TransformState& state; + const EvaluatedLight& evaluatedLight; + + RenderStaticData& staticData; + FrameHistory& frameHistory; + ImageManager& imageManager; + LineAtlas& lineAtlas; + + RenderPass pass = RenderPass::Opaque; + MapMode mapMode; + MapDebugOptions debugOptions; + GLContextMode contextMode; + TimePoint timePoint; + + float pixelRatio; + std::array<float, 2> pixelsToGLUnits; + algorithm::ClipIDGenerator clipIDGenerator; + Programs& programs; - View& view; + + gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const; + gl::DepthMode depthModeFor3D(gl::DepthMode::Mask) const; + gl::StencilMode stencilModeForClipping(const ClipID&) const; + gl::ColorMode colorModeForRenderPass() const; + + mat4 matrixForTile(const UnwrappedTileID&); + + mat4 projMatrix; + mat4 nearClippedProjMatrix; + + int numSublayers = 3; + uint32_t currentLayer; + float depthRangeSize; + const float depthEpsilon = 1.0f / (1 << 16); }; } // namespace mbgl diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp index 36d2e98082..652948c8df 100644 --- a/src/mbgl/renderer/paint_property_binder.hpp +++ b/src/mbgl/renderer/paint_property_binder.hpp @@ -5,6 +5,7 @@ #include <mbgl/gl/uniform.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/util/type_list.hpp> +#include <mbgl/renderer/possibly_evaluated_property_value.hpp> #include <mbgl/renderer/paint_property_statistics.hpp> #include <bitset> @@ -217,7 +218,11 @@ public: } float interpolationFactor(float currentZoom) const override { - return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom); + if (function.useIntegerZoom) { + return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, std::floor(currentZoom)); + } else { + return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom); + } } T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp deleted file mode 100644 index fbaf40d5c0..0000000000 --- a/src/mbgl/renderer/painter.cpp +++ /dev/null @@ -1,430 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_source.hpp> - -#include <mbgl/style/source.hpp> -#include <mbgl/style/source_impl.hpp> - -#include <mbgl/map/view.hpp> - -#include <mbgl/util/logging.hpp> -#include <mbgl/gl/debugging.hpp> - -#include <mbgl/style/style.hpp> -#include <mbgl/style/layer_impl.hpp> - -#include <mbgl/tile/tile.hpp> -#include <mbgl/renderer/render_background_layer.hpp> -#include <mbgl/renderer/render_custom_layer.hpp> -#include <mbgl/style/layers/custom_layer_impl.hpp> -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> - -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/geometry/line_atlas.hpp> -#include <mbgl/text/glyph_atlas.hpp> - -#include <mbgl/programs/program_parameters.hpp> -#include <mbgl/programs/programs.hpp> - -#include <mbgl/algorithm/generate_clip_ids.hpp> -#include <mbgl/algorithm/generate_clip_ids_impl.hpp> - -#include <mbgl/util/constants.hpp> -#include <mbgl/util/mat3.hpp> -#include <mbgl/util/string.hpp> - -#include <mbgl/util/stopwatch.hpp> - -#include <cassert> -#include <algorithm> -#include <iostream> -#include <unordered_set> - -namespace mbgl { - -using namespace style; - -static gl::VertexVector<FillLayoutVertex> tileVertices() { - gl::VertexVector<FillLayoutVertex> result; - result.emplace_back(FillProgram::layoutVertex({ 0, 0 })); - result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, 0 })); - result.emplace_back(FillProgram::layoutVertex({ 0, util::EXTENT })); - result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, util::EXTENT })); - return result; -} - -static gl::IndexVector<gl::Triangles> quadTriangleIndices() { - gl::IndexVector<gl::Triangles> result; - result.emplace_back(0, 1, 2); - result.emplace_back(1, 2, 3); - return result; -} - -static gl::IndexVector<gl::LineStrip> tileLineStripIndices() { - gl::IndexVector<gl::LineStrip> result; - result.emplace_back(0); - result.emplace_back(1); - result.emplace_back(3); - result.emplace_back(2); - result.emplace_back(0); - return result; -} - -static gl::VertexVector<RasterLayoutVertex> rasterVertices() { - gl::VertexVector<RasterLayoutVertex> result; - result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 })); - result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { 32767, 0 })); - result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, 32767 })); - result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { 32767, 32767 })); - return result; -} - -static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() { - gl::VertexVector<ExtrusionTextureLayoutVertex> result; - result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 })); - result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 })); - result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 })); - result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 })); - return result; -} - - -Painter::Painter(gl::Context& context_, - const TransformState& state_, - float pixelRatio, - const optional<std::string>& programCacheDir) - : context(context_), - state(state_), - tileVertexBuffer(context.createVertexBuffer(tileVertices())), - rasterVertexBuffer(context.createVertexBuffer(rasterVertices())), - extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())), - quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())), - tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())) { - - tileTriangleSegments.emplace_back(0, 0, 4, 6); - tileBorderSegments.emplace_back(0, 0, 4, 5); - rasterSegments.emplace_back(0, 0, 4, 6); - extrusionTextureSegments.emplace_back(0, 0, 4, 6); - - programs = std::make_unique<Programs>(context, - ProgramParameters{ pixelRatio, false, programCacheDir }); -#ifndef NDEBUG - overdrawPrograms = - std::make_unique<Programs>(context, ProgramParameters{ pixelRatio, true, programCacheDir }); -#endif -} - -Painter::~Painter() = default; - -bool Painter::needsAnimation() const { - return frameHistory.needsAnimation(util::DEFAULT_FADE_DURATION); -} - -void Painter::cleanup() { - context.performCleanup(); -} - -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 ? *overdrawPrograms : *programs, -#else - *programs, -#endif - view - }; - - glyphAtlas = style.glyphAtlas.get(); - spriteAtlas = style.spriteAtlas.get(); - lineAtlas = style.lineAtlas.get(); - - evaluatedLight = style.getRenderLight()->getEvaluated(); - - RenderData renderData = style.getRenderData(frame.debugOptions, state.getAngle()); - const std::vector<RenderItem>& order = renderData.order; - const std::unordered_set<RenderSource*>& sources = renderData.sources; - - // Update the default matrices to the current viewport dimensions. - state.getProjMatrix(projMatrix); - // Calculate a second projection matrix with the near plane clipped to 100 so as - // not to waste lots of depth buffer precision on very close empty space, for layer - // types (fill-extrusion) that use the depth buffer to emulate real-world space. - state.getProjMatrix(nearClippedProjMatrix, 100); - - pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }}; - if (state.getViewportMode() == ViewportMode::FlippedY) { - pixelsToGLUnits[1] *= -1; - } - - 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(context, "upload"); - - spriteAtlas->upload(context, 0); - - lineAtlas->upload(context, 0); - glyphAtlas->upload(context, 0); - frameHistory.upload(context, 0); - annotationSpriteAtlas.upload(context, 0); - - for (const auto& item : order) { - for (const auto& tileRef : item.tiles) { - const auto& bucket = tileRef.get().tile.getBucket(item.layer); - if (bucket && bucket->needsUpload()) { - bucket->upload(context); - } - } - } - } - - // - CLEAR ------------------------------------------------------------------------------------- - // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any - // tiles whatsoever. - { - MBGL_DEBUG_GROUP(context, "clear"); - view.bind(); - context.clear(paintMode() == PaintMode::Overdraw - ? Color::black() - : renderData.backgroundColor, - 1.0f, - 0); - } - - // - CLIPPING MASKS ---------------------------------------------------------------------------- - // Draws the clipping masks to the stencil buffer. - { - MBGL_DEBUG_GROUP(context, "clip"); - - // Update all clipping IDs. - algorithm::ClipIDGenerator generator; - for (const auto& source : sources) { - source->startRender(generator, projMatrix, nearClippedProjMatrix, state); - } - - MBGL_DEBUG_GROUP(context, "clipping masks"); - - for (const auto& stencil : generator.getStencils()) { - MBGL_DEBUG_GROUP(context, std::string{ "mask: " } + util::toString(stencil.first)); - renderClippingMask(stencil.first, stencil.second); - } - } - -#if not MBGL_USE_GLES2 and not defined(NDEBUG) - if (frame.debugOptions & MapDebugOptions::StencilClip) { - renderClipMasks(parameters); - return; - } -#endif - - // Actually render the layers - if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; } - - depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon; - - // - OPAQUE PASS ------------------------------------------------------------------------------- - // Render everything top-to-bottom by using reverse iterators. Render opaque objects first. - renderPass(parameters, - RenderPass::Opaque, - order.rbegin(), order.rend(), - 0, 1); - - // - TRANSLUCENT PASS -------------------------------------------------------------------------- - // Make a second pass, rendering translucent objects. This time, we render bottom-to-top. - renderPass(parameters, - RenderPass::Translucent, - order.begin(), order.end(), - static_cast<uint32_t>(order.size()) - 1, -1); - - if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; } - - // - DEBUG PASS -------------------------------------------------------------------------------- - // Renders debug overlays. - { - MBGL_DEBUG_GROUP(context, "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); - } - } - -#if not MBGL_USE_GLES2 and not defined(NDEBUG) - if (frame.debugOptions & MapDebugOptions::DepthBuffer) { - renderDepthBuffer(parameters); - } -#endif - - // TODO: Find a better way to unbind VAOs after we're done with them without introducing - // unnecessary bind(0)/bind(N) sequences. - { - MBGL_DEBUG_GROUP(context, "cleanup"); - - context.activeTexture = 1; - context.texture[1] = 0; - context.activeTexture = 0; - context.texture[0] = 0; - - context.bindVertexArray = 0; - } -} - -template <class Iterator> -void Painter::renderPass(PaintParameters& parameters, - RenderPass pass_, - Iterator it, Iterator end, - uint32_t i, int8_t increment) { - pass = pass_; - - MBGL_DEBUG_GROUP(context, pass == RenderPass::Opaque ? "opaque" : "translucent"); - - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", - pass == RenderPass::Opaque ? "opaque" : "translucent"); - } - - for (; it != end; ++it, i += increment) { - currentLayer = i; - - const auto& item = *it; - const RenderLayer& layer = item.layer; - - if (!layer.hasRenderPass(pass)) - continue; - - if (layer.is<RenderBackgroundLayer>()) { - MBGL_DEBUG_GROUP(context, "background"); - renderBackground(parameters, *layer.as<RenderBackgroundLayer>()); - } else if (layer.is<RenderCustomLayer>()) { - MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - custom"); - - // Reset GL state to a known state so the CustomLayer always has a clean slate. - context.bindVertexArray = 0; - context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadOnly)); - context.setStencilMode(gl::StencilMode::disabled()); - context.setColorMode(colorModeForRenderPass()); - - layer.as<RenderCustomLayer>()->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(); - } else if (layer.is<RenderFillExtrusionLayer>()) { - const auto size = context.viewport.getCurrentValue().size; - - if (!extrusionTexture || extrusionTexture->getSize() != size) { - extrusionTexture = OffscreenTexture(context, size, OffscreenTextureAttachment::Depth); - } - - extrusionTexture->bind(); - - context.setStencilMode(gl::StencilMode::disabled()); - context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadWrite)); - context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {}); - - for (auto& tileRef : item.tiles) { - auto& tile = tileRef.get(); - - MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id)); - auto bucket = tile.tile.getBucket(layer); - bucket->render(*this, parameters, layer, tile); - } - - parameters.view.bind(); - context.bindTexture(extrusionTexture->getTexture()); - - mat4 viewportMat; - matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); - - const PaintProperties<>::Evaluated properties{}; - - parameters.programs.extrusionTexture.draw( - context, - gl::Triangles(), - gl::DepthMode::disabled(), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - ExtrusionTextureProgram::UniformValues{ - uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, - uniforms::u_image::Value{ 0 }, - uniforms::u_opacity::Value{ layer.as<RenderFillExtrusionLayer>() - ->evaluated.get<FillExtrusionOpacity>() } }, - extrusionTextureVertexBuffer, - quadTriangleIndexBuffer, - extrusionTextureSegments, - ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, - properties, - state.getZoom(), - layer.getID()); - } else { - for (auto& tileRef : item.tiles) { - auto& tile = tileRef.get(); - MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id)); - auto bucket = tile.tile.getBucket(layer); - bucket->render(*this, parameters, layer, tile); - } - } - } - - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}"); - } -} - -mat4 Painter::matrixForTile(const UnwrappedTileID& tileID) { - mat4 matrix; - state.matrixFor(matrix, tileID); - matrix::multiply(matrix, projMatrix, matrix); - return matrix; -} - -gl::DepthMode Painter::depthModeForSublayer(uint8_t n, gl::DepthMode::Mask mask) const { - float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon; - float farDepth = nearDepth + depthRangeSize; - return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } }; -} - -gl::StencilMode Painter::stencilModeForClipping(const ClipID& id) const { - return gl::StencilMode { - gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) }, - static_cast<int32_t>(id.reference.to_ulong()), - 0, - gl::StencilMode::Keep, - gl::StencilMode::Keep, - gl::StencilMode::Replace - }; -} - -gl::ColorMode Painter::colorModeForRenderPass() const { - if (paintMode() == PaintMode::Overdraw) { - const float overdraw = 1.0f / 8.0f; - return gl::ColorMode { - gl::ColorMode::Add { - gl::ColorMode::ConstantColor, - gl::ColorMode::One - }, - Color { overdraw, overdraw, overdraw, 0.0f }, - gl::ColorMode::Mask { true, true, true, true } - }; - } else if (pass == RenderPass::Translucent) { - return gl::ColorMode::alphaBlended(); - } else { - return gl::ColorMode::unblended(); - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp deleted file mode 100644 index 1919ad924e..0000000000 --- a/src/mbgl/renderer/painter.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include <mbgl/map/transform_state.hpp> - -#include <mbgl/tile/tile_id.hpp> - -#include <mbgl/renderer/frame_history.hpp> -#include <mbgl/renderer/render_item.hpp> -#include <mbgl/renderer/bucket.hpp> - -#include <mbgl/gl/context.hpp> -#include <mbgl/programs/debug_program.hpp> -#include <mbgl/programs/program_parameters.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/programs/extrusion_texture_program.hpp> -#include <mbgl/programs/raster_program.hpp> - -#include <mbgl/style/style.hpp> - -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/chrono.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/offscreen_texture.hpp> - -#include <array> -#include <vector> -#include <set> -#include <map> - -namespace mbgl { - -class RenderTile; -class SpriteAtlas; -class View; -class GlyphAtlas; -class LineAtlas; -struct FrameData; -class Tile; - -class DebugBucket; -class FillBucket; -class FillExtrusionBucket; -class LineBucket; -class CircleBucket; -class SymbolBucket; -class RasterBucket; - -class RenderFillLayer; -class RenderFillExtrusionLayer; -class RenderLineLayer; -class RenderCircleLayer; -class RenderSymbolLayer; -class RenderRasterLayer; -class RenderBackgroundLayer; - -class Programs; -class PaintParameters; -class TilePyramid; - -struct ClipID; - -namespace style { -class Style; -class Source; -} // namespace style - -struct FrameData { - TimePoint timePoint; - float pixelRatio; - MapMode mapMode; - GLContextMode contextMode; - MapDebugOptions debugOptions; -}; - -class Painter : private util::noncopyable { -public: - Painter(gl::Context&, const TransformState&, float pixelRatio, const optional<std::string>& programCacheDir); - ~Painter(); - - void render(const style::Style&, - const FrameData&, - View&, - SpriteAtlas& annotationSpriteAtlas); - - void cleanup(); - - void renderClippingMask(const UnwrappedTileID&, const ClipID&); - void renderTileDebug(const RenderTile&); - void renderFill(PaintParameters&, FillBucket&, const RenderFillLayer&, const RenderTile&); - void renderFillExtrusion(PaintParameters&, FillExtrusionBucket&, const RenderFillExtrusionLayer&, const RenderTile&); - void renderLine(PaintParameters&, LineBucket&, const RenderLineLayer&, const RenderTile&); - void renderCircle(PaintParameters&, CircleBucket&, const RenderCircleLayer&, const RenderTile&); - void renderSymbol(PaintParameters&, SymbolBucket&, const RenderSymbolLayer&, const RenderTile&); - void renderRaster(PaintParameters&, RasterBucket&, const RenderRasterLayer&, const RenderTile&); - void renderBackground(PaintParameters&, const RenderBackgroundLayer&); - -#ifndef NDEBUG - // Renders tile clip boundaries, using stencil buffer to calculate fill color. - void renderClipMasks(PaintParameters&); - // Renders the depth buffer. - void renderDepthBuffer(PaintParameters&); -#endif - - bool needsAnimation() const; - -private: - std::vector<RenderItem> determineRenderOrder(const style::Style&); - - template <class Iterator> - void renderPass(PaintParameters&, - RenderPass, - Iterator it, Iterator end, - uint32_t i, int8_t increment); - - mat4 matrixForTile(const UnwrappedTileID&); - gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const; - gl::StencilMode stencilModeForClipping(const ClipID&) const; - gl::ColorMode colorModeForRenderPass() const; - -#ifndef NDEBUG - PaintMode paintMode() const { - return frame.debugOptions & MapDebugOptions::Overdraw ? PaintMode::Overdraw - : PaintMode::Regular; - } -#else - PaintMode paintMode() const { - return PaintMode::Regular; - } -#endif - -private: - gl::Context& context; - - mat4 projMatrix; - mat4 nearClippedProjMatrix; - - std::array<float, 2> pixelsToGLUnits; - - const mat4 identityMatrix = []{ - mat4 identity; - matrix::identity(identity); - return identity; - }(); - - const TransformState& state; - - FrameData frame; - - int indent = 0; - - RenderPass pass = RenderPass::Opaque; - - int numSublayers = 3; - uint32_t currentLayer; - float depthRangeSize; - const float depthEpsilon = 1.0f / (1 << 16); - - SpriteAtlas* spriteAtlas = nullptr; - GlyphAtlas* glyphAtlas = nullptr; - LineAtlas* lineAtlas = nullptr; - - optional<OffscreenTexture> extrusionTexture; - - EvaluatedLight evaluatedLight; - - FrameHistory frameHistory; - - std::unique_ptr<Programs> programs; -#ifndef NDEBUG - std::unique_ptr<Programs> overdrawPrograms; -#endif - - gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer; - gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer; - gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer; - - gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer; - gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer; - - SegmentVector<FillAttributes> tileTriangleSegments; - SegmentVector<DebugAttributes> tileBorderSegments; - SegmentVector<RasterAttributes> rasterSegments; - SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp deleted file mode 100644 index 9bd9431082..0000000000 --- a/src/mbgl/renderer/painter_background.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/render_background_layer.hpp> -#include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/util/tile_cover.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderBackground(PaintParameters& parameters, const RenderBackgroundLayer& layer) { - // Note that for bottommost layers without a pattern, the background color is drawn with - // glClear rather than this method. - const BackgroundPaintProperties::Evaluated& background = layer.evaluated; - - style::FillPaintProperties::Evaluated properties; - properties.get<FillPattern>() = background.get<BackgroundPattern>(); - properties.get<FillOpacity>() = { background.get<BackgroundOpacity>() }; - properties.get<FillColor>() = { background.get<BackgroundColor>() }; - - const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); - - if (!background.get<BackgroundPattern>().to.empty()) { - optional<SpriteAtlasElement> imagePosA = spriteAtlas->getPattern(background.get<BackgroundPattern>().from); - optional<SpriteAtlasElement> imagePosB = spriteAtlas->getPattern(background.get<BackgroundPattern>().to); - - if (!imagePosA || !imagePosB) - return; - - spriteAtlas->bind(true, context, 0); - - for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) { - parameters.programs.fillPattern.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - FillPatternUniforms::values( - matrixForTile(tileID), - context.viewport.getCurrentValue().size, - *imagePosA, - *imagePosB, - background.get<BackgroundPattern>(), - tileID, - state - ), - tileVertexBuffer, - quadTriangleIndexBuffer, - tileTriangleSegments, - paintAttibuteData, - properties, - state.getZoom(), - layer.getID() - ); - } - } else { - for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) { - parameters.programs.fill.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - FillProgram::UniformValues { - uniforms::u_matrix::Value{ matrixForTile(tileID) }, - uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, - }, - tileVertexBuffer, - quadTriangleIndexBuffer, - tileTriangleSegments, - paintAttibuteData, - properties, - state.getZoom(), - layer.getID() - ); - } - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_circle.cpp b/src/mbgl/renderer/painter_circle.cpp deleted file mode 100644 index ecd7598de9..0000000000 --- a/src/mbgl/renderer/painter_circle.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/circle_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_circle_layer.hpp> -#include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/circle_program.hpp> -#include <mbgl/gl/context.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderCircle(PaintParameters& parameters, - CircleBucket& bucket, - const RenderCircleLayer& layer, - const RenderTile& tile) { - if (pass == RenderPass::Opaque) { - return; - } - - const CirclePaintProperties::Evaluated& properties = layer.evaluated; - const bool scaleWithMap = properties.get<CirclePitchScale>() == CirclePitchScaleType::Map; - - parameters.programs.circle.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - frame.mapMode == MapMode::Still - ? stencilModeForClipping(tile.clip) - : gl::StencilMode::disabled(), - colorModeForRenderPass(), - CircleProgram::UniformValues { - uniforms::u_matrix::Value{ - tile.translatedMatrix(properties.get<CircleTranslate>(), - properties.get<CircleTranslateAnchor>(), - state) - }, - uniforms::u_scale_with_map::Value{ scaleWithMap }, - uniforms::u_extrude_scale::Value{ scaleWithMap - ? std::array<float, 2> {{ - pixelsToGLUnits[0] * state.getCameraToCenterDistance(), - pixelsToGLUnits[1] * state.getCameraToCenterDistance() - }} - : pixelsToGLUnits } - }, - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID() - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp deleted file mode 100644 index 162f3b1d96..0000000000 --- a/src/mbgl/renderer/painter_clipping.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/util/clip_id.hpp> - -namespace mbgl { - -void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& clip) { - static const style::FillPaintProperties::Evaluated properties {}; - static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); - programs->fill.get(properties).draw( - context, - gl::Triangles(), - gl::DepthMode::disabled(), - gl::StencilMode { - gl::StencilMode::Always(), - static_cast<int32_t>(clip.reference.to_ulong()), - 0b11111111, - gl::StencilMode::Keep, - gl::StencilMode::Keep, - gl::StencilMode::Replace - }, - gl::ColorMode::disabled(), - FillProgram::UniformValues { - uniforms::u_matrix::Value{ matrixForTile(tileID) }, - uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, - }, - tileVertexBuffer, - quadTriangleIndexBuffer, - tileTriangleSegments, - paintAttibuteData, - properties, - state.getZoom(), - "clipping" - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp deleted file mode 100644 index 9a24ab5422..0000000000 --- a/src/mbgl/renderer/painter_debug.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#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/programs/programs.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/util/string.hpp> -#include <mbgl/gl/debugging.hpp> -#include <mbgl/util/color.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderTileDebug(const RenderTile& renderTile) { - if (frame.debugOptions == MapDebugOptions::NoDebug) - return; - - MBGL_DEBUG_GROUP(context, std::string { "debug " } + util::toString(renderTile.id)); - - static const style::PaintProperties<>::Evaluated properties {}; - static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); - - auto draw = [&] (Color color, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments, auto drawMode) { - programs->debug.draw( - context, - drawMode, - gl::DepthMode::disabled(), - stencilModeForClipping(renderTile.clip), - gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ renderTile.matrix }, - uniforms::u_color::Value{ color } - }, - vertexBuffer, - indexBuffer, - segments, - paintAttibuteData, - properties, - state.getZoom(), - "debug" - ); - }; - - if (frame.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) { - Tile& tile = renderTile.tile; - if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() || - tile.debugBucket->complete != tile.isComplete() || - !(tile.debugBucket->modified == tile.modified) || - !(tile.debugBucket->expires == tile.expires) || - tile.debugBucket->debugMode != frame.debugOptions) { - tile.debugBucket = std::make_unique<DebugBucket>( - tile.id, tile.isRenderable(), tile.isComplete(), tile.modified, - tile.expires, frame.debugOptions, context); - } - - draw(Color::white(), - *tile.debugBucket->vertexBuffer, - *tile.debugBucket->indexBuffer, - tile.debugBucket->segments, - gl::Lines { 4.0f * frame.pixelRatio }); - - draw(Color::black(), - *tile.debugBucket->vertexBuffer, - *tile.debugBucket->indexBuffer, - tile.debugBucket->segments, - gl::Lines { 2.0f * frame.pixelRatio }); - } - - if (frame.debugOptions & MapDebugOptions::TileBorders) { - draw(Color::red(), - tileVertexBuffer, - tileBorderIndexBuffer, - tileBorderSegments, - gl::LineStrip { 4.0f * frame.pixelRatio }); - } -} - -#ifndef NDEBUG -void Painter::renderClipMasks(PaintParameters&) { - context.setStencilMode(gl::StencilMode::disabled()); - context.setDepthMode(gl::DepthMode::disabled()); - context.setColorMode(gl::ColorMode::unblended()); - context.program = 0; - -#if not MBGL_USE_GLES2 - // Reset the value in case someone else changed it, or it's dirty. - context.pixelTransferStencil = gl::value::PixelTransferStencil::Default; - - // Read the stencil buffer - const auto viewport = context.viewport.getCurrentValue(); - auto image = - context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false); - - // Scale the Stencil buffer to cover the entire color space. - auto it = image.data.get(); - auto end = it + viewport.size.width * viewport.size.height; - const auto factor = 255.0f / *std::max_element(it, end); - for (; it != end; ++it) { - *it *= factor; - } - - context.pixelZoom = { 1, 1 }; - context.rasterPos = { -1, -1, 0, 1 }; - context.drawPixels(image); -#endif // MBGL_USE_GLES2 -} - -void Painter::renderDepthBuffer(PaintParameters&) { - context.setStencilMode(gl::StencilMode::disabled()); - context.setDepthMode(gl::DepthMode::disabled()); - context.setColorMode(gl::ColorMode::unblended()); - context.program = 0; - -#if not MBGL_USE_GLES2 - // Scales the values in the depth buffer so that they cover the entire grayscale range. This - // makes it easier to spot tiny differences. - const float base = 1.0f / (1.0f - depthRangeSize); - context.pixelTransferDepth = { base, 1.0f - base }; - - // Read the stencil buffer - auto viewport = context.viewport.getCurrentValue(); - auto image = - context.readFramebuffer<AlphaImage, gl::TextureFormat::Depth>(viewport.size, false); - - context.pixelZoom = { 1, 1 }; - context.rasterPos = { -1, -1, 0, 1 }; - context.drawPixels(image); -#endif // MBGL_USE_GLES2 -} -#endif // NDEBUG - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp deleted file mode 100644 index 7264735692..0000000000 --- a/src/mbgl/renderer/painter_fill.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/fill_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_fill_layer.hpp> -#include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/util/convert.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderFill(PaintParameters& parameters, - FillBucket& bucket, - const RenderFillLayer& layer, - const RenderTile& tile) { - const FillPaintProperties::Evaluated& properties = layer.evaluated; - - if (!properties.get<FillPattern>().from.empty()) { - if (pass != RenderPass::Translucent) { - return; - } - - optional<SpriteAtlasElement> imagePosA = spriteAtlas->getPattern(properties.get<FillPattern>().from); - optional<SpriteAtlasElement> imagePosB = spriteAtlas->getPattern(properties.get<FillPattern>().to); - - if (!imagePosA || !imagePosB) { - return; - } - - spriteAtlas->bind(true, context, 0); - - auto draw = [&] (uint8_t sublayer, - auto& program, - const auto& drawMode, - const auto& indexBuffer, - const auto& segments) { - program.get(properties).draw( - context, - drawMode, - depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite), - stencilModeForClipping(tile.clip), - colorModeForRenderPass(), - FillPatternUniforms::values( - tile.translatedMatrix(properties.get<FillTranslate>(), - properties.get<FillTranslateAnchor>(), - state), - context.viewport.getCurrentValue().size, - *imagePosA, - *imagePosB, - properties.get<FillPattern>(), - tile.id, - state - ), - *bucket.vertexBuffer, - indexBuffer, - segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID() - ); - }; - - draw(0, - parameters.programs.fillPattern, - gl::Triangles(), - *bucket.triangleIndexBuffer, - bucket.triangleSegments); - - if (!properties.get<FillAntialias>() || !layer.unevaluated.get<FillOutlineColor>().isUndefined()) { - return; - } - - draw(2, - parameters.programs.fillOutlinePattern, - gl::Lines { 2.0f }, - *bucket.lineIndexBuffer, - bucket.lineSegments); - } else { - auto draw = [&] (uint8_t sublayer, - auto& program, - const auto& drawMode, - const auto& indexBuffer, - const auto& segments) { - program.get(properties).draw( - context, - drawMode, - depthModeForSublayer(sublayer, pass == RenderPass::Opaque - ? gl::DepthMode::ReadWrite - : gl::DepthMode::ReadOnly), - stencilModeForClipping(tile.clip), - colorModeForRenderPass(), - FillProgram::UniformValues { - uniforms::u_matrix::Value{ - tile.translatedMatrix(properties.get<FillTranslate>(), - properties.get<FillTranslateAnchor>(), - state) - }, - uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, - }, - *bucket.vertexBuffer, - indexBuffer, - segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID() - ); - }; - - if (properties.get<FillAntialias>() && !layer.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) { - draw(2, - parameters.programs.fillOutline, - gl::Lines { 2.0f }, - *bucket.lineIndexBuffer, - bucket.lineSegments); - } - - // Only draw the fill when it's opaque and we're drawing opaque fragments, - // or when it's translucent and we're drawing translucent fragments. - if ((properties.get<FillColor>().constantOr(Color()).a >= 1.0f - && properties.get<FillOpacity>().constantOr(0) >= 1.0f) == (pass == RenderPass::Opaque)) { - draw(1, - parameters.programs.fill, - gl::Triangles(), - *bucket.triangleIndexBuffer, - bucket.triangleSegments); - } - - if (properties.get<FillAntialias>() && layer.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) { - draw(2, - parameters.programs.fillOutline, - gl::Lines { 2.0f }, - *bucket.lineIndexBuffer, - bucket.lineSegments); - } - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_fill_extrusion.cpp b/src/mbgl/renderer/painter_fill_extrusion.cpp deleted file mode 100644 index 5581cfe983..0000000000 --- a/src/mbgl/renderer/painter_fill_extrusion.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/fill_extrusion_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> -#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_extrusion_program.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/convert.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderFillExtrusion(PaintParameters& parameters, - FillExtrusionBucket& bucket, - const RenderFillExtrusionLayer& layer, - const RenderTile& tile) { - const FillExtrusionPaintProperties::Evaluated& properties = layer.evaluated; - - if (pass == RenderPass::Opaque) { - return; - } - - if (!properties.get<FillExtrusionPattern>().from.empty()) { - optional<SpriteAtlasElement> imagePosA = - spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().from); - optional<SpriteAtlasElement> imagePosB = - spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().to); - - if (!imagePosA || !imagePosB) { - return; - } - - spriteAtlas->bind(true, context, 0); - - parameters.programs.fillExtrusionPattern.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - FillExtrusionPatternUniforms::values( - tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(), - properties.get<FillExtrusionTranslateAnchor>(), - state), - *imagePosA, - *imagePosB, - properties.get<FillExtrusionPattern>(), - tile.id, - state, - -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, - evaluatedLight - ), - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.triangleSegments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID()); - - } else { - parameters.programs.fillExtrusion.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - FillExtrusionUniforms::values( - tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(), - properties.get<FillExtrusionTranslateAnchor>(), - state), - state, - evaluatedLight - ), - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.triangleSegments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID()); - }; -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp deleted file mode 100644 index 209b1447d0..0000000000 --- a/src/mbgl/renderer/painter_line.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_line_layer.hpp> -#include <mbgl/style/layers/line_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/line_program.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/geometry/line_atlas.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderLine(PaintParameters& parameters, - LineBucket& bucket, - const RenderLineLayer& layer, - const RenderTile& tile) { - if (pass == RenderPass::Opaque) { - return; - } - - const LinePaintProperties::Evaluated& properties = layer.evaluated; - - auto draw = [&] (auto& program, auto&& uniformValues) { - program.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - stencilModeForClipping(tile.clip), - colorModeForRenderPass(), - std::move(uniformValues), - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID() - ); - }; - - if (!properties.get<LineDasharray>().from.empty()) { - const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round - ? LinePatternCap::Round : LinePatternCap::Square; - LinePatternPos posA = lineAtlas->getDashPosition(properties.get<LineDasharray>().from, cap); - LinePatternPos posB = lineAtlas->getDashPosition(properties.get<LineDasharray>().to, cap); - - lineAtlas->bind(context, 0); - - draw(parameters.programs.lineSDF, - LineSDFProgram::uniformValues( - properties, - frame.pixelRatio, - tile, - state, - pixelsToGLUnits, - posA, - posB, - layer.dashLineWidth, - lineAtlas->getSize().width)); - - } else if (!properties.get<LinePattern>().from.empty()) { - optional<SpriteAtlasElement> posA = spriteAtlas->getPattern(properties.get<LinePattern>().from); - optional<SpriteAtlasElement> posB = spriteAtlas->getPattern(properties.get<LinePattern>().to); - - if (!posA || !posB) - return; - - spriteAtlas->bind(true, context, 0); - - draw(parameters.programs.linePattern, - LinePatternProgram::uniformValues( - properties, - tile, - state, - pixelsToGLUnits, - *posA, - *posB)); - - } else { - draw(parameters.programs.line, - LineProgram::uniformValues( - properties, - tile, - state, - pixelsToGLUnits)); - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp deleted file mode 100644 index f0e5399f4a..0000000000 --- a/src/mbgl/renderer/painter_raster.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/raster_bucket.hpp> -#include <mbgl/renderer/render_raster_layer.hpp> -#include <mbgl/style/layers/raster_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/raster_program.hpp> - -namespace mbgl { - -using namespace style; - -static float saturationFactor(float saturation) { - if (saturation > 0) { - return 1 - 1 / (1.001 - saturation); - } else { - return -saturation; - } -} - -static float contrastFactor(float contrast) { - if (contrast > 0) { - return 1 / (1 - contrast); - } else { - return 1 + contrast; - } -} - -static std::array<float, 3> spinWeights(float spin) { - spin *= util::DEG2RAD; - float s = std::sin(spin); - float c = std::cos(spin); - std::array<float, 3> spin_weights = {{ - (2 * c + 1) / 3, - (-std::sqrt(3.0f) * s - c + 1) / 3, - (std::sqrt(3.0f) * s - c + 1) / 3 - }}; - return spin_weights; -} - -void Painter::renderRaster(PaintParameters& parameters, - RasterBucket& bucket, - const RenderRasterLayer& layer, - const RenderTile& tile) { - if (pass != RenderPass::Translucent) - return; - if (!bucket.hasData()) - return; - - const RasterPaintProperties::Evaluated& properties = layer.evaluated; - const RasterProgram::PaintPropertyBinders paintAttributeData(properties, 0); - - assert(bucket.texture); - context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); - context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); - - parameters.programs.raster.draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - RasterProgram::UniformValues { - uniforms::u_matrix::Value{ tile.matrix }, - uniforms::u_image0::Value{ 0 }, - uniforms::u_image1::Value{ 1 }, - uniforms::u_opacity::Value{ properties.get<RasterOpacity>() }, - uniforms::u_fade_t::Value{ 1 }, - uniforms::u_brightness_low::Value{ properties.get<RasterBrightnessMin>() }, - uniforms::u_brightness_high::Value{ properties.get<RasterBrightnessMax>() }, - uniforms::u_saturation_factor::Value{ saturationFactor(properties.get<RasterSaturation>()) }, - uniforms::u_contrast_factor::Value{ contrastFactor(properties.get<RasterContrast>()) }, - uniforms::u_spin_weights::Value{ spinWeights(properties.get<RasterHueRotate>()) }, - uniforms::u_buffer_scale::Value{ 1.0f }, - uniforms::u_scale_parent::Value{ 1.0f }, - uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} }, - }, - rasterVertexBuffer, - quadTriangleIndexBuffer, - rasterSegments, - paintAttributeData, - properties, - state.getZoom(), - layer.getID() - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp deleted file mode 100644 index 6462eebdcc..0000000000 --- a/src/mbgl/renderer/painter_symbol.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/symbol_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> -#include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/text/glyph_atlas.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/symbol_program.hpp> -#include <mbgl/programs/collision_box_program.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/tile/tile.hpp> - -#include <cmath> - -namespace mbgl { - -using namespace style; - -void Painter::renderSymbol(PaintParameters& parameters, - SymbolBucket& bucket, - const RenderSymbolLayer& layer, - const RenderTile& tile) { - if (pass == RenderPass::Opaque) { - return; - } - - const auto& layout = bucket.layout; - - frameHistory.bind(context, 1); - - auto draw = [&] (auto& program, - auto&& uniformValues, - const auto& buffers, - const auto& symbolSizeBinder, - const SymbolPropertyValues& values_, - const auto& binders, - const auto& paintProperties) - { - // We clip symbols to their tile extent in still mode. - const bool needsClipping = frame.mapMode == MapMode::Still; - - program.get(paintProperties).draw( - context, - gl::Triangles(), - values_.pitchAlignment == AlignmentType::Map - ? depthModeForSublayer(0, gl::DepthMode::ReadOnly) - : gl::DepthMode::disabled(), - needsClipping - ? stencilModeForClipping(tile.clip) - : gl::StencilMode::disabled(), - colorModeForRenderPass(), - std::move(uniformValues), - *buffers.vertexBuffer, - *symbolSizeBinder, - *buffers.indexBuffer, - buffers.segments, - binders, - paintProperties, - state.getZoom(), - layer.getID() - ); - }; - - if (bucket.hasIconData()) { - auto values = layer.iconPropertyValues(layout); - auto paintPropertyValues = layer.iconPaintProperties(); - - SpriteAtlas& atlas = *bucket.spriteAtlas; - const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || - frame.pixelRatio != atlas.getPixelRatio() || - bucket.iconsNeedLinear; - const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0; - atlas.bind(bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed, context, 0); - - const Size texsize = atlas.getSize(); - - if (bucket.sdfIcons) { - if (values.hasHalo) { - draw(parameters.programs.symbolIconSDF, - SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), - bucket.icon, - bucket.iconSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).first, - paintPropertyValues); - } - - if (values.hasFill) { - draw(parameters.programs.symbolIconSDF, - SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), - bucket.icon, - bucket.iconSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).first, - paintPropertyValues); - } - } else { - draw(parameters.programs.symbolIcon, - SymbolIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state), - bucket.icon, - bucket.iconSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).first, - paintPropertyValues); - } - } - - if (bucket.hasTextData()) { - glyphAtlas->bind(context, 0); - - auto values = layer.textPropertyValues(layout); - auto paintPropertyValues = layer.textPaintProperties(); - - const Size texsize = glyphAtlas->getSize(); - - if (values.hasHalo) { - draw(parameters.programs.symbolGlyph, - SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), - bucket.text, - bucket.textSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).second, - paintPropertyValues); - } - - if (values.hasFill) { - draw(parameters.programs.symbolGlyph, - SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), - bucket.text, - bucket.textSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).second, - paintPropertyValues); - } - } - - if (bucket.hasCollisionBoxData()) { - static const style::PaintProperties<>::Evaluated properties {}; - static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0); - - programs->collisionBox.draw( - context, - gl::Lines { 1.0f }, - gl::DepthMode::disabled(), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - CollisionBoxProgram::UniformValues { - uniforms::u_matrix::Value{ tile.matrix }, - uniforms::u_scale::Value{ std::pow(2.0f, float(state.getZoom() - tile.tile.id.overscaledZ)) }, - uniforms::u_zoom::Value{ float(state.getZoom() * 10) }, - uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) }, - }, - *bucket.collisionBox.vertexBuffer, - *bucket.collisionBox.indexBuffer, - bucket.collisionBox.segments, - paintAttributeData, - properties, - state.getZoom(), - layer.getID() - ); - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/possibly_evaluated_property_value.hpp b/src/mbgl/renderer/possibly_evaluated_property_value.hpp index a0bcec2bf1..e662d5dfb1 100644 --- a/src/mbgl/renderer/possibly_evaluated_property_value.hpp +++ b/src/mbgl/renderer/possibly_evaluated_property_value.hpp @@ -19,7 +19,9 @@ private: public: PossiblyEvaluatedPropertyValue() = default; - PossiblyEvaluatedPropertyValue(Value v) : value(std::move(v)) {} + PossiblyEvaluatedPropertyValue(Value v, bool useIntegerZoom_ = false) + : value(std::move(v)), + useIntegerZoom(useIntegerZoom_) {} bool isConstant() const { return value.template is<T>(); @@ -43,15 +45,21 @@ public: template <class Feature> T evaluate(const Feature& feature, float zoom, T defaultValue) const { return this->match( - [&] (const T& constant) { return constant; }, + [&] (const T& constant_) { return constant_; }, [&] (const style::SourceFunction<T>& function) { return function.evaluate(feature, defaultValue); }, [&] (const style::CompositeFunction<T>& function) { - return function.evaluate(zoom, feature, defaultValue); + if (useIntegerZoom) { + return function.evaluate(floor(zoom), feature, defaultValue); + } else { + return function.evaluate(zoom, feature, defaultValue); + } } ); } + + bool useIntegerZoom; }; namespace util { diff --git a/src/mbgl/renderer/property_evaluation_parameters.hpp b/src/mbgl/renderer/property_evaluation_parameters.hpp index 39b663bdb9..da6a4a0892 100644 --- a/src/mbgl/renderer/property_evaluation_parameters.hpp +++ b/src/mbgl/renderer/property_evaluation_parameters.hpp @@ -11,20 +11,24 @@ public: : z(z_), now(Clock::time_point::max()), zoomHistory(), - defaultFadeDuration(0) {} + defaultFadeDuration(0), + useIntegerZoom(false) {} PropertyEvaluationParameters(ZoomHistory zoomHistory_, TimePoint now_, - Duration defaultFadeDuration_) + Duration defaultFadeDuration_, + bool useIntegerZoom_ = false) : z(zoomHistory_.lastZoom), now(std::move(now_)), zoomHistory(std::move(zoomHistory_)), - defaultFadeDuration(std::move(defaultFadeDuration_)) {} + defaultFadeDuration(std::move(defaultFadeDuration_)), + useIntegerZoom(useIntegerZoom_) {} float z; TimePoint now; ZoomHistory zoomHistory; Duration defaultFadeDuration; + bool useIntegerZoom; }; } // namespace mbgl diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp deleted file mode 100644 index ee8ef24071..0000000000 --- a/src/mbgl/renderer/raster_bucket.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include <mbgl/renderer/raster_bucket.hpp> -#include <mbgl/renderer/render_raster_layer.hpp> -#include <mbgl/programs/raster_program.hpp> -#include <mbgl/renderer/painter.hpp> -#include <mbgl/gl/context.hpp> - -namespace mbgl { - -using namespace style; - -RasterBucket::RasterBucket(UnassociatedImage&& image_) : image(std::move(image_)) { -} - -void RasterBucket::upload(gl::Context& context) { - texture = context.createTexture(std::move(image)); - uploaded = true; -} - -void RasterBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), tile); -} - -bool RasterBucket::hasData() const { - return true; -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp deleted file mode 100644 index 334954e3f4..0000000000 --- a/src/mbgl/renderer/raster_bucket.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include <mbgl/renderer/bucket.hpp> -#include <mbgl/util/image.hpp> -#include <mbgl/util/optional.hpp> -#include <mbgl/gl/texture.hpp> - -namespace mbgl { - -class RasterBucket : public Bucket { -public: - RasterBucket(UnassociatedImage&&); - - void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; - bool hasData() const override; - - UnassociatedImage image; - optional<gl::Texture> texture; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_background_layer.cpp b/src/mbgl/renderer/render_background_layer.cpp deleted file mode 100644 index 485d4b16c6..0000000000 --- a/src/mbgl/renderer/render_background_layer.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include <mbgl/renderer/render_background_layer.hpp> -#include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/renderer/bucket.hpp> - -namespace mbgl { - -RenderBackgroundLayer::RenderBackgroundLayer(const style::BackgroundLayer::Impl& _impl) - : RenderLayer(style::LayerType::Background, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderBackgroundLayer::clone() const { - return std::make_unique<RenderBackgroundLayer>(*this); -} - -std::unique_ptr<Bucket> RenderBackgroundLayer::createBucket(const BucketParameters &, - const std::vector<const RenderLayer *> &) const { - assert(false); - return nullptr; -} - -void RenderBackgroundLayer::cascade(const CascadeParameters ¶meters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters ¶meters) { - evaluated = unevaluated.evaluate(parameters); - - passes = evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent - : RenderPass::None; -} - -bool RenderBackgroundLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -} diff --git a/src/mbgl/renderer/render_circle_layer.cpp b/src/mbgl/renderer/render_circle_layer.cpp deleted file mode 100644 index f59c174dd3..0000000000 --- a/src/mbgl/renderer/render_circle_layer.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include <mbgl/renderer/render_circle_layer.hpp> -#include <mbgl/renderer/circle_bucket.hpp> -#include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/intersection_tests.hpp> - -namespace mbgl { - -RenderCircleLayer::RenderCircleLayer(const style::CircleLayer::Impl& _impl) - : RenderLayer(style::LayerType::Circle, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderCircleLayer::clone() const { - return std::make_unique<RenderCircleLayer>(*this); -} - -std::unique_ptr<Bucket> RenderCircleLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<CircleBucket>(parameters, layers); -} - -void RenderCircleLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 || - evaluated.get<style::CircleStrokeWidth>().constantOr(1) > 0) - && (evaluated.get<style::CircleColor>().constantOr(Color::black()).a > 0 || - evaluated.get<style::CircleStrokeColor>().constantOr(Color::black()).a > 0) - && (evaluated.get<style::CircleOpacity>().constantOr(1) > 0 || - evaluated.get<style::CircleStrokeOpacity>().constantOr(1) > 0)) - ? RenderPass::Translucent : RenderPass::None; -} - -bool RenderCircleLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -bool RenderCircleLayer::queryIntersectsFeature( - const GeometryCoordinates& queryGeometry, - const GeometryTileFeature& feature, - const float zoom, - const float bearing, - const float pixelsToTileUnits) const { - - // Translate query geometry - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( - queryGeometry, - evaluated.get<style::CircleTranslate>(), - evaluated.get<style::CircleTranslateAnchor>(), - bearing, - pixelsToTileUnits); - - // Evaluate function - auto circleRadius = evaluated.get<style::CircleRadius>() - .evaluate(feature, zoom, style::CircleRadius::defaultValue()) - * pixelsToTileUnits; - - // Test intersection - return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius); -} - -} diff --git a/src/mbgl/renderer/render_custom_layer.cpp b/src/mbgl/renderer/render_custom_layer.cpp deleted file mode 100644 index 66dd57b3d3..0000000000 --- a/src/mbgl/renderer/render_custom_layer.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include <mbgl/renderer/render_custom_layer.hpp> -#include <mbgl/style/layers/custom_layer_impl.hpp> -#include <mbgl/renderer/bucket.hpp> - -namespace mbgl { - -RenderCustomLayer::RenderCustomLayer(const style::CustomLayer::Impl& _impl) - : RenderLayer(style::LayerType::Custom, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderCustomLayer::clone() const { - return std::make_unique<RenderCustomLayer>(*this); -} - -void RenderCustomLayer::evaluate(const PropertyEvaluationParameters&) { - passes = RenderPass::Translucent; -} - -bool RenderCustomLayer::hasTransition() const { - return false; -} - -std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { - assert(false); - return nullptr; -} - -} diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/render_fill_extrusion_layer.cpp deleted file mode 100644 index f6ba164d8c..0000000000 --- a/src/mbgl/renderer/render_fill_extrusion_layer.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> -#include <mbgl/renderer/fill_extrusion_bucket.hpp> -#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/intersection_tests.hpp> - -namespace mbgl { - -RenderFillExtrusionLayer::RenderFillExtrusionLayer(const style::FillExtrusionLayer::Impl& _impl) - : RenderLayer(style::LayerType::FillExtrusion, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderFillExtrusionLayer::clone() const { - return std::make_unique<RenderFillExtrusionLayer>(*this); -} - -std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<FillExtrusionBucket>(parameters, layers); -} - -void RenderFillExtrusionLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent - : RenderPass::None; -} - -bool RenderFillExtrusionLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -bool RenderFillExtrusionLayer::queryIntersectsFeature( - const GeometryCoordinates& queryGeometry, - const GeometryTileFeature& feature, - const float, - const float bearing, - const float pixelsToTileUnits) const { - - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( - queryGeometry, - evaluated.get<style::FillExtrusionTranslate>(), - evaluated.get<style::FillExtrusionTranslateAnchor>(), - bearing, - pixelsToTileUnits); - - return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_fill_layer.cpp b/src/mbgl/renderer/render_fill_layer.cpp deleted file mode 100644 index 1af139cded..0000000000 --- a/src/mbgl/renderer/render_fill_layer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include <mbgl/renderer/render_fill_layer.hpp> -#include <mbgl/renderer/fill_bucket.hpp> -#include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/intersection_tests.hpp> - -namespace mbgl { - -RenderFillLayer::RenderFillLayer(const style::FillLayer::Impl& _impl) - : RenderLayer(style::LayerType::Fill, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderFillLayer::clone() const { - return std::make_unique<RenderFillLayer>(*this); -} - -std::unique_ptr<Bucket> RenderFillLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<FillBucket>(parameters, layers); -} - -void RenderFillLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - if (unevaluated.get<style::FillOutlineColor>().isUndefined()) { - evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>(); - } - - passes = RenderPass::None; - - if (evaluated.get<style::FillAntialias>()) { - passes |= RenderPass::Translucent; - } - - if (!unevaluated.get<style::FillPattern>().isUndefined() - || evaluated.get<style::FillColor>().constantOr(Color()).a < 1.0f - || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f) { - passes |= RenderPass::Translucent; - } else { - passes |= RenderPass::Opaque; - } -} - -bool RenderFillLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -bool RenderFillLayer::queryIntersectsFeature( - const GeometryCoordinates& queryGeometry, - const GeometryTileFeature& feature, - const float, - const float bearing, - const float pixelsToTileUnits) const { - - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( - queryGeometry, - evaluated.get<style::FillTranslate>(), - evaluated.get<style::FillTranslateAnchor>(), - bearing, - pixelsToTileUnits); - - return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); -} - - -} diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp deleted file mode 100644 index 787211c30a..0000000000 --- a/src/mbgl/renderer/render_item.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include <mbgl/util/color.hpp> - -#include <unordered_set> -#include <vector> - -namespace mbgl { - -class RenderLayer; -class RenderSource; -class RenderTile; -class Bucket; - -namespace style { -} // namespace style - -class RenderItem { -public: - RenderItem(const RenderLayer& layer_, - std::vector<std::reference_wrapper<RenderTile>> tiles_ = {}) - : layer(layer_), tiles(std::move(tiles_)) { - } - - const RenderLayer& layer; - std::vector<std::reference_wrapper<RenderTile>> tiles; -}; - -class RenderData { -public: - Color backgroundColor; - std::unordered_set<RenderSource*> sources; - std::vector<RenderItem> order; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp index 6699f39144..eb2b74ffe0 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -1,14 +1,55 @@ #include <mbgl/renderer/render_layer.hpp> +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/renderer/layers/render_circle_layer.hpp> +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/layers/render_fill_layer.hpp> +#include <mbgl/renderer/layers/render_line_layer.hpp> +#include <mbgl/renderer/layers/render_raster_layer.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/renderer/render_tile.hpp> namespace mbgl { -RenderLayer::RenderLayer(style::LayerType type_, const style::Layer::Impl& baseImpl_) - : type(type_), baseImpl(baseImpl_) { +using namespace style; + +std::unique_ptr<RenderLayer> RenderLayer::create(Immutable<Layer::Impl> impl) { + switch (impl->type) { + case LayerType::Fill: + return std::make_unique<RenderFillLayer>(staticImmutableCast<FillLayer::Impl>(impl)); + case LayerType::Line: + return std::make_unique<RenderLineLayer>(staticImmutableCast<LineLayer::Impl>(impl)); + case LayerType::Circle: + return std::make_unique<RenderCircleLayer>(staticImmutableCast<CircleLayer::Impl>(impl)); + case LayerType::Symbol: + return std::make_unique<RenderSymbolLayer>(staticImmutableCast<SymbolLayer::Impl>(impl)); + case LayerType::Raster: + return std::make_unique<RenderRasterLayer>(staticImmutableCast<RasterLayer::Impl>(impl)); + case LayerType::Background: + return std::make_unique<RenderBackgroundLayer>(staticImmutableCast<BackgroundLayer::Impl>(impl)); + case LayerType::Custom: + return std::make_unique<RenderCustomLayer>(staticImmutableCast<CustomLayer::Impl>(impl)); + case LayerType::FillExtrusion: + return std::make_unique<RenderFillExtrusionLayer>(staticImmutableCast<FillExtrusionLayer::Impl>(impl)); + } + + // Not reachable, but placate GCC. + assert(false); + return nullptr; +} + +RenderLayer::RenderLayer(style::LayerType type_, Immutable<style::Layer::Impl> baseImpl_) + : type(type_), + baseImpl(baseImpl_) { +} + +void RenderLayer::setImpl(Immutable<style::Layer::Impl> impl) { + baseImpl = impl; } const std::string& RenderLayer::getID() const { - return baseImpl.id; + return baseImpl->id; } bool RenderLayer::hasRenderPass(RenderPass pass) const { @@ -17,9 +58,14 @@ bool RenderLayer::hasRenderPass(RenderPass pass) const { bool RenderLayer::needsRendering(float zoom) const { return passes != RenderPass::None - && baseImpl.visibility != style::VisibilityType::None - && baseImpl.minZoom <= zoom - && baseImpl.maxZoom >= zoom; + && baseImpl->visibility != style::VisibilityType::None + && baseImpl->minZoom <= zoom + && baseImpl->maxZoom >= zoom; +} + +void RenderLayer::setRenderTiles(std::vector<std::reference_wrapper<RenderTile>> tiles) { + renderTiles = std::move(tiles); } -}
\ No newline at end of file +} //namespace mbgl + diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index eea2ec1f61..dfc6bcf2fd 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -12,27 +12,27 @@ namespace mbgl { class Bucket; class BucketParameters; -class CascadeParameters; +class TransitionParameters; class PropertyEvaluationParameters; +class PaintParameters; +class RenderSource; +class RenderTile; class RenderLayer { - protected: - RenderLayer(style::LayerType, const style::Layer::Impl&); + RenderLayer(style::LayerType, Immutable<style::Layer::Impl>); const style::LayerType type; public: + static std::unique_ptr<RenderLayer> create(Immutable<style::Layer::Impl>); virtual ~RenderLayer() = default; - // Create an identical copy of this layer. - virtual std::unique_ptr<RenderLayer> clone() const = 0; - - // Partially evaluate paint properties based on a set of classes. - virtual void cascade(const CascadeParameters&) = 0; + // Begin transitions for any properties that have changed since the last frame. + virtual void transition(const TransitionParameters&) = 0; - // Fully evaluate cascaded paint properties based on a zoom level. + // Fully evaluate possibly-transitioning paint properties based on a zoom level. virtual void evaluate(const PropertyEvaluationParameters&) = 0; // Returns true if any paint properties have active transitions. @@ -61,6 +61,8 @@ public: // Checks whether this layer can be rendered. bool needsRendering(float zoom) const; + virtual void render(PaintParameters&, RenderSource*) = 0; + // Check wether the given geometry intersects // with the feature virtual bool queryIntersectsFeature( @@ -72,15 +74,21 @@ public: virtual std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const = 0; + void setRenderTiles(std::vector<std::reference_wrapper<RenderTile>>); // Private implementation - const style::Layer::Impl& baseImpl; + Immutable<style::Layer::Impl> baseImpl; + void setImpl(Immutable<style::Layer::Impl>); friend std::string layoutKey(const RenderLayer&); -protected: +protected: // Stores what render passes this layer is currently enabled for. This depends on the // evaluated StyleProperties object and is updated accordingly. RenderPass passes = RenderPass::None; + + //Stores current set of tiles to be rendered for this layer. + std::vector<std::reference_wrapper<RenderTile>> renderTiles; + }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_light.cpp b/src/mbgl/renderer/render_light.cpp index 134e1829e0..85768cff47 100644 --- a/src/mbgl/renderer/render_light.cpp +++ b/src/mbgl/renderer/render_light.cpp @@ -2,25 +2,17 @@ namespace mbgl { -RenderLight::RenderLight(std::shared_ptr<const style::Light::Impl> impl_) - : impl(std::move(impl_)) { +RenderLight::RenderLight(Immutable<style::Light::Impl> impl_) + : impl(std::move(impl_)), + transitioning(impl->properties.untransitioned()) { } -RenderLight::RenderLight(std::shared_ptr<const style::Light::Impl> impl_, const TransitioningLight transitioning_) - : impl(std::move(impl_)) - , transitioning(transitioning_) { -} - -std::unique_ptr<RenderLight> RenderLight::copy(std::shared_ptr<const style::Light::Impl> impl_) const { - return std::make_unique<RenderLight>(std::move(impl_), transitioning); -} - -void RenderLight::transition(const CascadeParameters& parameters) { - transitioning = TransitioningLight(impl->properties, std::move(transitioning), parameters); +void RenderLight::transition(const TransitionParameters& parameters) { + transitioning = impl->properties.transitioned(parameters, std::move(transitioning)); } void RenderLight::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = EvaluatedLight(transitioning, parameters); + evaluated = transitioning.evaluate(parameters); } bool RenderLight::hasTransition() const { diff --git a/src/mbgl/renderer/render_light.hpp b/src/mbgl/renderer/render_light.hpp index 275f3ae8ba..f13f925318 100644 --- a/src/mbgl/renderer/render_light.hpp +++ b/src/mbgl/renderer/render_light.hpp @@ -1,98 +1,29 @@ #pragma once #include <mbgl/style/light_impl.hpp> -#include <mbgl/style/light_properties.hpp> -#include <mbgl/renderer/transitioning_property.hpp> -#include <mbgl/renderer/cascade_parameters.hpp> -#include <mbgl/renderer/property_evaluator.hpp> -#include <mbgl/renderer/property_evaluation_parameters.hpp> -#include <mbgl/util/ignore.hpp> - -#include <memory> +#include <mbgl/util/immutable.hpp> namespace mbgl { -template <class TypeList> -class Transitioning; - -template <class... Ps> -class Transitioning<TypeList<Ps...>> : public IndexedTuple< - TypeList<Ps...>, - TypeList<TransitioningProperty<typename Ps::ValueType>...>> -{ -private: - using Properties = TypeList<Ps...>; - using Raw = IndexedTuple<Properties, Properties>; - using Super = IndexedTuple< - TypeList<Ps...>, - TypeList<TransitioningProperty<typename Ps::ValueType>...>>; - -public: - Transitioning() = default; - Transitioning(const Raw& raw, Transitioning&& prior, const CascadeParameters& params) - : Super { - TransitioningProperty<typename Ps::ValueType>( - raw.template get<Ps>().value, - std::move(prior.template get<Ps>()), - raw.template get<Ps>().transition.reverseMerge(params.transition), - params.now)... - } {} - - bool hasTransition() const { - bool result = false; - util::ignore({ result |= this->template get<Ps>().hasTransition()... }); - return result; - } -}; - -template <class TypeList> -class Evaluated; +class TransitionParameters; +class PropertyEvaluationParameters; -template <class... Ps> -class Evaluated<TypeList<Ps...>> : public IndexedTuple< - TypeList<Ps...>, - TypeList<typename Ps::Type...>> -{ -private: - using Properties = TypeList<Ps...>; - using TransitioningPs = Transitioning<Properties>; - using Super = IndexedTuple< - TypeList<Ps...>, - TypeList<typename Ps::Type...>>; - -public: - Evaluated() = default; - Evaluated(TransitioningPs& transitioning, const PropertyEvaluationParameters& params) - : Super { - transitioning.template get<Ps>() - .evaluate(PropertyEvaluator<typename Ps::Type>(params, Ps::defaultValue()), params.now)... - } {} -}; - -using TransitioningLight = Transitioning<style::LightProperties>; -using EvaluatedLight = Evaluated<style::LightProperties>; +using TransitioningLight = style::LightProperties::Unevaluated; +using EvaluatedLight = style::LightProperties::PossiblyEvaluated; class RenderLight { public: - RenderLight(std::shared_ptr<const style::Light::Impl>); - - // Creates a copy intitalized with previous transitioning light - RenderLight(std::shared_ptr<const style::Light::Impl>, const TransitioningLight); + RenderLight(Immutable<style::Light::Impl>); - // creates a copy initialized with previous transitioning - // values - std::unique_ptr<RenderLight> copy(std::shared_ptr<const style::Light::Impl>) const; - - void transition(const CascadeParameters&); + void transition(const TransitionParameters&); void evaluate(const PropertyEvaluationParameters&); bool hasTransition() const; const EvaluatedLight& getEvaluated() const; - const std::shared_ptr<const style::Light::Impl> impl; + Immutable<style::Light::Impl> impl; private: - TransitioningLight transitioning; EvaluatedLight evaluated; }; diff --git a/src/mbgl/renderer/render_line_layer.cpp b/src/mbgl/renderer/render_line_layer.cpp deleted file mode 100644 index 06c2564516..0000000000 --- a/src/mbgl/renderer/render_line_layer.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include <mbgl/renderer/render_line_layer.hpp> -#include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/style/layers/line_layer_impl.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/intersection_tests.hpp> - -namespace mbgl { - -RenderLineLayer::RenderLineLayer(const style::LineLayer::Impl& _impl) - : RenderLayer(style::LayerType::Line, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderLineLayer::clone() const { - return std::make_unique<RenderLineLayer>(*this); -} - -std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<LineBucket>(parameters, layers, impl->layout); -} - -void RenderLineLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) { - // for scaling dasharrays - auto dashArrayParams = parameters; - dashArrayParams.z = std::floor(dashArrayParams.z); - dashLineWidth = unevaluated.evaluate<style::LineWidth>(dashArrayParams); - - evaluated = unevaluated.evaluate(parameters); - - passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0 - && evaluated.get<style::LineColor>().constantOr(Color::black()).a > 0 - && evaluated.get<style::LineWidth>() > 0) - ? RenderPass::Translucent : RenderPass::None; -} - -bool RenderLineLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) { - if (offset == 0) return {}; - - GeometryCollection newRings; - Point<double> zero(0, 0); - for (const auto& ring : rings) { - newRings.emplace_back(); - auto& newRing = newRings.back(); - - for (auto i = ring.begin(); i != ring.end(); i++) { - auto& p = *i; - - Point<double> aToB = i == ring.begin() ? - zero : - util::perp(util::unit(convertPoint<double>(p - *(i - 1)))); - Point<double> bToC = i + 1 == ring.end() ? - zero : - util::perp(util::unit(convertPoint<double>(*(i + 1) - p))); - Point<double> extrude = util::unit(aToB + bToC); - - const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y; - extrude *= (1.0 / cosHalfAngle); - - newRing.push_back(convertPoint<int16_t>(extrude * offset) + p); - } - } - - return newRings; -} - -bool RenderLineLayer::queryIntersectsFeature( - const GeometryCoordinates& queryGeometry, - const GeometryTileFeature& feature, - const float zoom, - const float bearing, - const float pixelsToTileUnits) const { - - // Translate query geometry - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( - queryGeometry, - evaluated.get<style::LineTranslate>(), - evaluated.get<style::LineTranslateAnchor>(), - bearing, - pixelsToTileUnits); - - // Evaluate function - auto offset = evaluated.get<style::LineOffset>() - .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits; - - // Apply offset to geometry - auto offsetGeometry = offsetLine(feature.getGeometries(), offset); - - // Test intersection - const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits; - return util::polygonIntersectsBufferedMultiLine( - translatedQueryGeometry.value_or(queryGeometry), - offsetGeometry.value_or(feature.getGeometries()), - halfWidth); -} - -float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { - float lineWidth = evaluated.get<style::LineWidth>(); - float gapWidth = evaluated.get<style::LineGapWidth>() - .evaluate(feature, zoom, style::LineGapWidth::defaultValue()); - if (gapWidth) { - return gapWidth + 2 * lineWidth; - } else { - return lineWidth; - } -} - - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp index d273e34ff7..5d18304129 100644 --- a/src/mbgl/renderer/render_pass.hpp +++ b/src/mbgl/renderer/render_pass.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/util/traits.hpp> +#include <mbgl/util/util.hpp> #include <cstdint> @@ -10,17 +11,18 @@ enum class RenderPass : uint8_t { None = 0, Opaque = 1 << 0, Translucent = 1 << 1, + Pass3D = 1 << 2, }; -constexpr RenderPass operator|(RenderPass a, RenderPass b) { +MBGL_CONSTEXPR RenderPass operator|(RenderPass a, RenderPass b) { return RenderPass(mbgl::underlying_type(a) | mbgl::underlying_type(b)); } -constexpr RenderPass& operator|=(RenderPass& a, RenderPass b) { +MBGL_CONSTEXPR RenderPass& operator|=(RenderPass& a, RenderPass b) { return (a = a | b); } -constexpr RenderPass operator&(RenderPass a, RenderPass b) { +MBGL_CONSTEXPR RenderPass operator&(RenderPass a, RenderPass b) { return RenderPass(mbgl::underlying_type(a) & mbgl::underlying_type(b)); } diff --git a/src/mbgl/renderer/render_raster_layer.cpp b/src/mbgl/renderer/render_raster_layer.cpp deleted file mode 100644 index 5e664e6f58..0000000000 --- a/src/mbgl/renderer/render_raster_layer.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <mbgl/renderer/render_raster_layer.hpp> -#include <mbgl/renderer/bucket.hpp> -#include <mbgl/style/layers/raster_layer_impl.hpp> - -namespace mbgl { - -RenderRasterLayer::RenderRasterLayer(const style::RasterLayer::Impl& _impl) - : RenderLayer(style::LayerType::Raster, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderRasterLayer::clone() const { - return std::make_unique<RenderRasterLayer>(*this); -} - -std::unique_ptr<Bucket> RenderRasterLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { - assert(false); - return nullptr; -} - -void RenderRasterLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - passes = evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None; -} - -bool RenderRasterLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp index 643d92fe81..7723a1c7ca 100644 --- a/src/mbgl/renderer/render_source.cpp +++ b/src/mbgl/renderer/render_source.cpp @@ -1,12 +1,42 @@ #include <mbgl/renderer/render_source.hpp> #include <mbgl/renderer/render_source_observer.hpp> +#include <mbgl/renderer/sources/render_geojson_source.hpp> +#include <mbgl/renderer/sources/render_raster_source.hpp> +#include <mbgl/renderer/sources/render_vector_source.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/annotation/render_annotation_source.hpp> +#include <mbgl/renderer/sources/render_image_source.hpp> #include <mbgl/tile/tile.hpp> namespace mbgl { +using namespace style; + +std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl) { + switch (impl->type) { + case SourceType::Vector: + return std::make_unique<RenderVectorSource>(staticImmutableCast<VectorSource::Impl>(impl)); + case SourceType::Raster: + return std::make_unique<RenderRasterSource>(staticImmutableCast<RasterSource::Impl>(impl)); + case SourceType::GeoJSON: + return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl)); + case SourceType::Video: + assert(false); + return nullptr; + case SourceType::Annotations: + return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl)); + case SourceType::Image: + return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl)); + } + + // Not reachable, but placate GCC. + assert(false); + return nullptr; +} + static RenderSourceObserver nullObserver; -RenderSource::RenderSource(const style::Source::Impl& impl) +RenderSource::RenderSource(Immutable<style::Source::Impl> impl) : baseImpl(impl), observer(&nullObserver) { } @@ -23,4 +53,8 @@ void RenderSource::onTileError(Tile& tile, std::exception_ptr error) { observer->onTileError(*this, tile.id, error); } +bool RenderSource::isEnabled() const { + return enabled; } + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index d31347579e..8293923ff6 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -6,6 +6,7 @@ #include <mbgl/util/geo.hpp> #include <mbgl/util/feature.hpp> #include <mbgl/style/source_impl.hpp> +#include <mbgl/style/layer_impl.hpp> #include <unordered_map> #include <vector> @@ -14,69 +15,73 @@ namespace mbgl { -class Painter; +class PaintParameters; class TransformState; class RenderTile; +class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; class Tile; class RenderSourceObserver; class TileParameters; -namespace algorithm { -class ClipIDGenerator; -} // namespace algorithm - class RenderSource : protected TileObserver { public: - RenderSource(const style::Source::Impl&); - virtual ~RenderSource() = default; + static std::unique_ptr<RenderSource> create(Immutable<style::Source::Impl>); - virtual bool isLoaded() const = 0; + // Check whether this source is of the given subtype. + template <class T> + bool is() const; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - virtual void updateTiles(const TileParameters&) = 0; + // Dynamically cast this source to the given subtype. + template <class T> + T* as() { + return is<T>() ? reinterpret_cast<T*>(this) : nullptr; + } - // Removes all tiles (by putting them into the cache). - virtual void removeTiles() = 0; + template <class T> + const T* as() const { + return is<T>() ? reinterpret_cast<const T*>(this) : nullptr; + } - // Remove all tiles and clear the cache. - virtual void invalidateTiles() = 0; + bool isEnabled() const; + virtual bool isLoaded() const = 0; - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - virtual void reloadTiles() = 0; + virtual void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) = 0; - virtual void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) = 0; - virtual void finishRender(Painter&) = 0; + virtual void startRender(PaintParameters&) = 0; + virtual void finishRender(PaintParameters&) = 0; - virtual std::map<UnwrappedTileID, RenderTile>& getRenderTiles() = 0; + // Returns an unsorted list of RenderTiles. + virtual std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() = 0; virtual std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const = 0; virtual std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const = 0; - virtual void setCacheSize(size_t) = 0; virtual void onLowMemory() = 0; virtual void dumpDebugLogs() const = 0; void setObserver(RenderSourceObserver*); - const style::Source::Impl& baseImpl; - bool enabled = false; + Immutable<style::Source::Impl> baseImpl; protected: + RenderSource(Immutable<style::Source::Impl>); RenderSourceObserver* observer; + bool enabled = false; + void onTileChanged(Tile&) final; void onTileError(Tile&, std::exception_ptr) final; }; diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp new file mode 100644 index 0000000000..ccf239e643 --- /dev/null +++ b/src/mbgl/renderer/render_static_data.cpp @@ -0,0 +1,67 @@ +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/program_parameters.hpp> + +namespace mbgl { + +static gl::VertexVector<FillLayoutVertex> tileVertices() { + gl::VertexVector<FillLayoutVertex> result; + result.emplace_back(FillProgram::layoutVertex({ 0, 0 })); + result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, 0 })); + result.emplace_back(FillProgram::layoutVertex({ 0, util::EXTENT })); + result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, util::EXTENT })); + return result; +} + +static gl::IndexVector<gl::Triangles> quadTriangleIndices() { + gl::IndexVector<gl::Triangles> result; + result.emplace_back(0, 1, 2); + result.emplace_back(1, 2, 3); + return result; +} + +static gl::IndexVector<gl::LineStrip> tileLineStripIndices() { + gl::IndexVector<gl::LineStrip> result; + result.emplace_back(0); + result.emplace_back(1); + result.emplace_back(3); + result.emplace_back(2); + result.emplace_back(0); + return result; +} + +static gl::VertexVector<RasterLayoutVertex> rasterVertices() { + gl::VertexVector<RasterLayoutVertex> result; + result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 })); + result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { util::EXTENT, 0 })); + result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, util::EXTENT })); + result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { util::EXTENT, util::EXTENT })); + return result; +} + +static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() { + gl::VertexVector<ExtrusionTextureLayoutVertex> result; + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 })); + return result; +} + +RenderStaticData::RenderStaticData(gl::Context& context, float pixelRatio, const optional<std::string>& programCacheDir) + : tileVertexBuffer(context.createVertexBuffer(tileVertices())), + rasterVertexBuffer(context.createVertexBuffer(rasterVertices())), + extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())), + quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())), + tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())), + programs(context, ProgramParameters { pixelRatio, false, programCacheDir }) +#ifndef NDEBUG + , overdrawPrograms(context, ProgramParameters { pixelRatio, true, programCacheDir }) +#endif +{ + tileTriangleSegments.emplace_back(0, 0, 4, 6); + tileBorderSegments.emplace_back(0, 0, 4, 5); + rasterSegments.emplace_back(0, 0, 4, 6); + extrusionTextureSegments.emplace_back(0, 0, 4, 6); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp new file mode 100644 index 0000000000..cf58c31f4d --- /dev/null +++ b/src/mbgl/renderer/render_static_data.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/util/optional.hpp> + +#include <string> + +namespace mbgl { + +class RenderStaticData { +public: + RenderStaticData(gl::Context&, float pixelRatio, const optional<std::string>& programCacheDir); + + gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer; + gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer; + gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer; + + gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer; + gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer; + + SegmentVector<FillAttributes> tileTriangleSegments; + SegmentVector<DebugAttributes> tileBorderSegments; + SegmentVector<RasterAttributes> rasterSegments; + SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments; + + optional<gl::Renderbuffer<gl::RenderbufferType::DepthComponent>> depthRenderbuffer; + bool has3D = false; + Size backendSize; + + Programs programs; + +#ifndef NDEBUG + Programs overdrawPrograms; +#endif +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_symbol_layer.cpp b/src/mbgl/renderer/render_symbol_layer.cpp deleted file mode 100644 index 30d769e032..0000000000 --- a/src/mbgl/renderer/render_symbol_layer.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include <mbgl/renderer/render_symbol_layer.hpp> -#include <mbgl/layout/symbol_layout.hpp> -#include <mbgl/renderer/bucket.hpp> -#include <mbgl/renderer/bucket_parameters.hpp> -#include <mbgl/renderer/property_evaluation_parameters.hpp> -#include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/tile/geometry_tile_data.hpp> - -namespace mbgl { - -RenderSymbolLayer::RenderSymbolLayer(const style::SymbolLayer::Impl& _impl) - : RenderLayer(style::LayerType::Symbol, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderSymbolLayer::clone() const { - return std::make_unique<RenderSymbolLayer>(*this); -} - -std::unique_ptr<Bucket> RenderSymbolLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { - assert(false); // Should be calling createLayout() instead. - return nullptr; -} - -std::unique_ptr<SymbolLayout> RenderSymbolLayer::createLayout(const BucketParameters& parameters, - const std::vector<const RenderLayer*>& group, - const GeometryTileLayer& layer, - GlyphDependencies& glyphDependencies, - IconDependencies& iconDependencies) const { - return std::make_unique<SymbolLayout>(parameters, - group, - layer, - iconDependencies, - glyphDependencies); -} - -void RenderSymbolLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - auto hasIconOpacity = evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 || - evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0; - auto hasTextOpacity = evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 || - evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0; - - passes = ((evaluated.get<style::IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) - || (evaluated.get<style::TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) - ? RenderPass::Translucent : RenderPass::None; -} - -bool RenderSymbolLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -style::IconPaintProperties::Evaluated RenderSymbolLayer::iconPaintProperties() const { - return style::IconPaintProperties::Evaluated { - evaluated.get<style::IconOpacity>(), - evaluated.get<style::IconColor>(), - evaluated.get<style::IconHaloColor>(), - evaluated.get<style::IconHaloWidth>(), - evaluated.get<style::IconHaloBlur>(), - evaluated.get<style::IconTranslate>(), - evaluated.get<style::IconTranslateAnchor>() - }; -} - -style::TextPaintProperties::Evaluated RenderSymbolLayer::textPaintProperties() const { - return style::TextPaintProperties::Evaluated { - evaluated.get<style::TextOpacity>(), - evaluated.get<style::TextColor>(), - evaluated.get<style::TextHaloColor>(), - evaluated.get<style::TextHaloWidth>(), - evaluated.get<style::TextHaloBlur>(), - evaluated.get<style::TextTranslate>(), - evaluated.get<style::TextTranslateAnchor>() - }; -} - - -style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { - return style::SymbolPropertyValues { - layout_.get<style::IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment - layout_.get<style::IconRotationAlignment>(), - layout_.get<style::IconSize>(), - evaluated.get<style::IconTranslate>(), - evaluated.get<style::IconTranslateAnchor>(), - iconSize, - 1.0f, - evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 && - evaluated.get<style::IconHaloWidth>().constantOr(1), - evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 - }; -} - -style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { - return style::SymbolPropertyValues { - layout_.get<style::TextPitchAlignment>(), - layout_.get<style::TextRotationAlignment>(), - layout_.get<style::TextSize>(), - evaluated.get<style::TextTranslate>(), - evaluated.get<style::TextTranslateAnchor>(), - textSize, - 24.0f, - evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 && - evaluated.get<style::TextHaloWidth>().constantOr(1), - evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 - }; -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index ce59186e61..8df31f8d7c 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -1,5 +1,11 @@ #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/buckets/debug_bucket.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> #include <mbgl/map/transform_state.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/util/math.hpp> namespace mbgl { @@ -8,24 +14,26 @@ using namespace style; mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix, const std::array<float, 2>& translation, TranslateAnchorType anchor, - const TransformState& state) const { + const TransformState& state, + const bool inViewportPixelUnits) const { if (translation[0] == 0 && translation[1] == 0) { return tileMatrix; } mat4 vtxMatrix; - if (anchor == TranslateAnchorType::Viewport) { - const double sin_a = std::sin(-state.getAngle()); - const double cos_a = std::cos(-state.getAngle()); - matrix::translate(vtxMatrix, tileMatrix, - id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()), - id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()), - 0); + const float angle = inViewportPixelUnits ? + (anchor == TranslateAnchorType::Map ? state.getAngle() : 0) : + (anchor == TranslateAnchorType::Viewport ? -state.getAngle() : 0); + + Point<float> translate = util::rotate(Point<float>{ translation[0], translation[1] }, angle); + + if (inViewportPixelUnits) { + matrix::translate(vtxMatrix, tileMatrix, translate.x, translate.y, 0); } else { matrix::translate(vtxMatrix, tileMatrix, - id.pixelsToTileUnits(translation[0], state.getZoom()), - id.pixelsToTileUnits(translation[1], state.getZoom()), + id.pixelsToTileUnits(translate.x, state.getZoom()), + id.pixelsToTileUnits(translate.y, state.getZoom()), 0); } @@ -35,24 +43,107 @@ mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix, mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation, TranslateAnchorType anchor, const TransformState& state) const { - return translateVtxMatrix(matrix, translation, anchor, state); + return translateVtxMatrix(matrix, translation, anchor, state, false); } mat4 RenderTile::translatedClipMatrix(const std::array<float, 2>& translation, TranslateAnchorType anchor, const TransformState& state) const { - return translateVtxMatrix(nearClippedMatrix, translation, anchor, state); + return translateVtxMatrix(nearClippedMatrix, translation, anchor, state, false); } -void RenderTile::calculateMatrices(const mat4& projMatrix, - const mat4& projClipMatrix, - const TransformState& transform) { +void RenderTile::setMask(TileMask&& mask) { + tile.setMask(std::move(mask)); +} + +void RenderTile::startRender(PaintParameters& parameters) { + tile.upload(parameters.context); + // Calculate two matrices for this tile: matrix is the standard tile matrix; nearClippedMatrix // clips the near plane to 100 to save depth buffer precision - transform.matrixFor(matrix, id); - transform.matrixFor(nearClippedMatrix, id); - matrix::multiply(matrix, projMatrix, matrix); - matrix::multiply(nearClippedMatrix, projClipMatrix, nearClippedMatrix); + parameters.state.matrixFor(matrix, id); + parameters.state.matrixFor(nearClippedMatrix, id); + matrix::multiply(matrix, parameters.projMatrix, matrix); + matrix::multiply(nearClippedMatrix, parameters.nearClippedProjMatrix, nearClippedMatrix); +} + +void RenderTile::finishRender(PaintParameters& parameters) { + if (!used || parameters.debugOptions == MapDebugOptions::NoDebug) + return; + + static const style::Properties<>::PossiblyEvaluated properties {}; + static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) { + if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() || + tile.debugBucket->complete != tile.isComplete() || + !(tile.debugBucket->modified == tile.modified) || + !(tile.debugBucket->expires == tile.expires) || + tile.debugBucket->debugMode != parameters.debugOptions) { + tile.debugBucket = std::make_unique<DebugBucket>( + tile.id, tile.isRenderable(), tile.isComplete(), tile.modified, + tile.expires, parameters.debugOptions, parameters.context); + } + + parameters.programs.debug.draw( + parameters.context, + gl::Lines { 4.0f * parameters.pixelRatio }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(clip), + gl::ColorMode::unblended(), + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::white() } + }, + *tile.debugBucket->vertexBuffer, + *tile.debugBucket->indexBuffer, + tile.debugBucket->segments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "debug" + ); + + parameters.programs.debug.draw( + parameters.context, + gl::Lines { 2.0f * parameters.pixelRatio }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(clip), + gl::ColorMode::unblended(), + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::black() } + }, + *tile.debugBucket->vertexBuffer, + *tile.debugBucket->indexBuffer, + tile.debugBucket->segments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "debug" + ); + } + + if (parameters.debugOptions & MapDebugOptions::TileBorders) { + parameters.programs.debug.draw( + parameters.context, + gl::LineStrip { 4.0f * parameters.pixelRatio }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(clip), + gl::ColorMode::unblended(), + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::red() } + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.tileBorderIndexBuffer, + parameters.staticData.tileBorderSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "debug" + ); + } } } // namespace mbgl diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp index 02e8667eec..b498972f5c 100644 --- a/src/mbgl/renderer/render_tile.hpp +++ b/src/mbgl/renderer/render_tile.hpp @@ -4,6 +4,7 @@ #include <mbgl/util/mat4.hpp> #include <mbgl/util/clip_id.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/renderer/tile_mask.hpp> #include <array> @@ -11,8 +12,9 @@ namespace mbgl { class Tile; class TransformState; +class PaintParameters; -class RenderTile { +class RenderTile final { public: RenderTile(UnwrappedTileID id_, Tile& tile_) : id(std::move(id_)), tile(tile_) {} RenderTile(const RenderTile&) = delete; @@ -35,14 +37,15 @@ public: style::TranslateAnchorType anchor, const TransformState&) const; - void calculateMatrices(const mat4& projMatrix, - const mat4& projClipMatrix, - const TransformState&); -private: + void setMask(TileMask&&); + void startRender(PaintParameters&); + void finishRender(PaintParameters&); + mat4 translateVtxMatrix(const mat4& tileMatrix, const std::array<float, 2>& translation, style::TranslateAnchorType anchor, - const TransformState& state) const; + const TransformState& state, + const bool inViewportPixelUnits) const; }; } // namespace mbgl diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp new file mode 100644 index 0000000000..e915f5e146 --- /dev/null +++ b/src/mbgl/renderer/renderer.cpp @@ -0,0 +1,86 @@ +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/renderer_impl.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/annotation/annotation_manager.hpp> + +namespace mbgl { + +Renderer::Renderer(RendererBackend& backend, + float pixelRatio_, + FileSource& fileSource_, + Scheduler& scheduler_, + GLContextMode contextMode_, + const optional<std::string> programCacheDir_) + : impl(std::make_unique<Impl>(backend, pixelRatio_, fileSource_, scheduler_, + contextMode_, std::move(programCacheDir_))) { +} + +Renderer::~Renderer() { + BackendScope guard { impl->backend }; + impl.reset(); +} + +void Renderer::markContextLost() { + impl->markContextLost(); +} + +void Renderer::setObserver(RendererObserver* observer) { + impl->setObserver(observer); +} + +void Renderer::render(const UpdateParameters& updateParameters) { + impl->render(updateParameters); +} + +std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const { + return impl->queryRenderedFeatures(geometry, options); +} + +std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options) const { + return impl->queryRenderedFeatures({ point }, options); +} + +std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options) const { + return impl->queryRenderedFeatures( + { + box.min, + {box.max.x, box.min.y}, + box.max, + {box.min.x, box.max.y}, + box.min + }, + options + ); +} + +AnnotationIDs Renderer::queryPointAnnotations(const ScreenBox& box) const { + RenderedQueryOptions options; + options.layerIDs = {{ AnnotationManager::PointLayerID }}; + auto features = queryRenderedFeatures(box, options); + std::set<AnnotationID> set; + for (auto &feature : features) { + assert(feature.id); + assert(feature.id->is<uint64_t>()); + assert(feature.id->get<uint64_t>() <= std::numeric_limits<AnnotationID>::max()); + set.insert(static_cast<AnnotationID>(feature.id->get<uint64_t>())); + } + AnnotationIDs ids; + ids.reserve(set.size()); + std::move(set.begin(), set.end(), std::back_inserter(ids)); + return ids; +} + +std::vector<Feature> Renderer::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const { + return impl->querySourceFeatures(sourceID, options); +} + +void Renderer::dumpDebugLogs() { + impl->dumDebugLogs(); +} + +void Renderer::onLowMemory() { + BackendScope guard { impl->backend }; + impl->onLowMemory(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_backend.cpp b/src/mbgl/renderer/renderer_backend.cpp new file mode 100644 index 0000000000..159ef432b3 --- /dev/null +++ b/src/mbgl/renderer/renderer_backend.cpp @@ -0,0 +1,69 @@ +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/extension.hpp> +#include <mbgl/gl/debugging.hpp> + +#include <cassert> + +namespace mbgl { + +RendererBackend::RendererBackend() = default; + +gl::Context& RendererBackend::getContext() { + assert(BackendScope::exists()); + std::call_once(initialized, [this] { + context = std::make_unique<gl::Context>(); + context->enableDebugging(); + context->initializeExtensions( + std::bind(&RendererBackend::initializeExtension, this, std::placeholders::_1)); + }); + return *context; +} + +PremultipliedImage RendererBackend::readFramebuffer(const Size& size) const { + assert(context); + return context->readFramebuffer<PremultipliedImage>(size); +} + +void RendererBackend::assumeFramebufferBinding(const gl::FramebufferID fbo) { + getContext().bindFramebuffer.setCurrentValue(fbo); + if (fbo != ImplicitFramebufferBinding) { + assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); + } +} + +void RendererBackend::assumeViewport(int32_t x, int32_t y, const Size& size) { + getContext().viewport.setCurrentValue({ x, y, size }); + assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue()); +} + +void RendererBackend::assumeScissorTest(bool enabled) { + getContext().scissorTest.setCurrentValue(enabled); + assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue()); +} + +bool RendererBackend::implicitFramebufferBound() { + return getContext().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding; +} + +void RendererBackend::setFramebufferBinding(const gl::FramebufferID fbo) { + getContext().bindFramebuffer = fbo; + if (fbo != ImplicitFramebufferBinding) { + assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); + } +} + +void RendererBackend::setViewport(int32_t x, int32_t y, const Size& size) { + getContext().viewport = { x, y, size }; + assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue()); +} + +void RendererBackend::setScissorTest(bool enabled) { + getContext().scissorTest = enabled; + assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue()); +} + +RendererBackend::~RendererBackend() = default; + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp new file mode 100644 index 0000000000..1a828b80a3 --- /dev/null +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -0,0 +1,742 @@ +#include <mbgl/annotation/annotation_manager.hpp> +#include <mbgl/renderer/renderer_impl.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/renderer_observer.hpp> +#include <mbgl/renderer/render_source.hpp> +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/renderer/update_parameters.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/transition_parameters.hpp> +#include <mbgl/renderer/property_evaluation_parameters.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/style_diff.hpp> +#include <mbgl/renderer/query.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/gl/debugging.hpp> +#include <mbgl/geometry/line_atlas.hpp> +#include <mbgl/style/source_impl.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/text/glyph_manager.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/logging.hpp> + +namespace mbgl { + +using namespace style; + +static RendererObserver& nullObserver() { + static RendererObserver observer; + return observer; +} + +Renderer::Impl::Impl(RendererBackend& backend_, + float pixelRatio_, + FileSource& fileSource_, + Scheduler& scheduler_, + GLContextMode contextMode_, + const optional<std::string> programCacheDir_) + : backend(backend_) + , scheduler(scheduler_) + , fileSource(fileSource_) + , observer(&nullObserver()) + , contextMode(contextMode_) + , pixelRatio(pixelRatio_) + , programCacheDir(programCacheDir_) + , glyphManager(std::make_unique<GlyphManager>(fileSource)) + , imageManager(std::make_unique<ImageManager>()) + , lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })) + , imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>()) + , sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()) + , layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()) + , renderLight(makeMutable<Light::Impl>()) { + glyphManager->setObserver(this); +} + +Renderer::Impl::~Impl() { + assert(BackendScope::exists()); + + if (contextLost) { + // Signal all RenderCustomLayers that the context was lost + // before cleaning up + for (const auto& entry : renderLayers) { + RenderLayer& layer = *entry.second; + if (layer.is<RenderCustomLayer>()) { + layer.as<RenderCustomLayer>()->markContextDestroyed(); + } + } + } +}; + +void Renderer::Impl::setObserver(RendererObserver* observer_) { + observer = observer_ ? observer_ : &nullObserver(); +} + +void Renderer::Impl::render(const UpdateParameters& updateParameters) { + if (updateParameters.mode == MapMode::Still) { + // Don't load/render anyting in still mode until explicitly requested. + if (!updateParameters.stillImageRequest) { + return; + } + + // Reset zoom history state. + zoomHistory.first = true; + } + + assert(BackendScope::exists()); + + updateParameters.annotationManager.updateData(); + + const bool zoomChanged = zoomHistory.update(updateParameters.transformState.getZoom(), updateParameters.timePoint); + + const TransitionParameters transitionParameters { + updateParameters.timePoint, + updateParameters.mode == MapMode::Continuous ? updateParameters.transitionOptions : TransitionOptions() + }; + + const PropertyEvaluationParameters evaluationParameters { + zoomHistory, + updateParameters.timePoint, + updateParameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero() + }; + + const TileParameters tileParameters { + updateParameters.pixelRatio, + updateParameters.debugOptions, + updateParameters.transformState, + scheduler, + fileSource, + updateParameters.mode, + updateParameters.annotationManager, + *imageManager, + *glyphManager, + updateParameters.prefetchZoomDelta + }; + + glyphManager->setURL(updateParameters.glyphURL); + + // Update light. + const bool lightChanged = renderLight.impl != updateParameters.light; + + if (lightChanged) { + renderLight.impl = updateParameters.light; + renderLight.transition(transitionParameters); + } + + if (lightChanged || zoomChanged || renderLight.hasTransition()) { + renderLight.evaluate(evaluationParameters); + } + + + const ImageDifference imageDiff = diffImages(imageImpls, updateParameters.images); + imageImpls = updateParameters.images; + + // Remove removed images from sprite atlas. + for (const auto& entry : imageDiff.removed) { + imageManager->removeImage(entry.first); + } + + // Add added images to sprite atlas. + for (const auto& entry : imageDiff.added) { + imageManager->addImage(entry.second); + } + + // Update changed images. + for (const auto& entry : imageDiff.changed) { + imageManager->updateImage(entry.second.after); + } + + imageManager->setLoaded(updateParameters.spriteLoaded); + + + const LayerDifference layerDiff = diffLayers(layerImpls, updateParameters.layers); + layerImpls = updateParameters.layers; + + // Remove render layers for removed layers. + for (const auto& entry : layerDiff.removed) { + renderLayers.erase(entry.first); + } + + // Create render layers for newly added layers. + for (const auto& entry : layerDiff.added) { + renderLayers.emplace(entry.first, RenderLayer::create(entry.second)); + } + + // Update render layers for changed layers. + for (const auto& entry : layerDiff.changed) { + renderLayers.at(entry.first)->setImpl(entry.second.after); + } + + // Update layers for class and zoom changes. + for (const auto& entry : renderLayers) { + RenderLayer& layer = *entry.second; + const bool layerAdded = layerDiff.added.count(entry.first); + const bool layerChanged = layerDiff.changed.count(entry.first); + + if (layerAdded || layerChanged) { + layer.transition(transitionParameters); + } + + if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { + layer.evaluate(evaluationParameters); + } + } + + + const SourceDifference sourceDiff = diffSources(sourceImpls, updateParameters.sources); + sourceImpls = updateParameters.sources; + + // Remove render layers for removed sources. + for (const auto& entry : sourceDiff.removed) { + renderSources.erase(entry.first); + } + + // Create render sources for newly added sources. + for (const auto& entry : sourceDiff.added) { + std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second); + renderSource->setObserver(this); + renderSources.emplace(entry.first, std::move(renderSource)); + } + + const bool hasImageDiff = !(imageDiff.added.empty() && imageDiff.removed.empty() && imageDiff.changed.empty()); + + // Update all sources. + for (const auto& source : *sourceImpls) { + std::vector<Immutable<Layer::Impl>> filteredLayers; + bool needsRendering = false; + bool needsRelayout = false; + + for (const auto& layer : *layerImpls) { + if (layer->type == LayerType::Background || + layer->type == LayerType::Custom || + layer->source != source->id) { + continue; + } + + if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) { + needsRendering = true; + } + + if (!needsRelayout && (hasImageDiff || hasLayoutDifference(layerDiff, layer->id))) { + needsRelayout = true; + } + + filteredLayers.push_back(layer); + } + + renderSources.at(source->id)->update(source, + filteredLayers, + needsRendering, + needsRelayout, + tileParameters); + } + + transformState = updateParameters.transformState; + + if (!staticData) { + staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio, programCacheDir); + } + + PaintParameters parameters { + backend.getContext(), + pixelRatio, + contextMode, + backend, + updateParameters, + renderLight.getEvaluated(), + *staticData, + frameHistory, + *imageManager, + *lineAtlas + }; + + bool loaded = updateParameters.styleLoaded && isLoaded(); + if (updateParameters.mode == MapMode::Still && !loaded) { + return; + } + + if (renderState == RenderState::Never) { + observer->onWillStartRenderingMap(); + } + + observer->onWillStartRenderingFrame(); + + backend.updateAssumedState(); + + if (parameters.contextMode == GLContextMode::Shared) { + parameters.context.setDirtyState(); + } + + Color backgroundColor; + + class RenderItem { + public: + RenderLayer& layer; + RenderSource* source; + }; + + std::vector<RenderItem> order; + + for (auto& layerImpl : *layerImpls) { + RenderLayer* layer = getRenderLayer(layerImpl->id); + assert(layer); + + if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) { + parameters.staticData.has3D = true; + } + + if (!layer->needsRendering(zoomHistory.lastZoom)) { + continue; + } + + if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) { + const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated; + if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) { + // This is a solid background. We can use glClear(). + backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>(); + } else { + // This is a textured background, or not the bottommost layer. We need to render it with a quad. + order.emplace_back(RenderItem { *layer, nullptr }); + } + continue; + } + + if (layer->is<RenderCustomLayer>()) { + order.emplace_back(RenderItem { *layer, nullptr }); + continue; + } + + RenderSource* source = getRenderSource(layer->baseImpl->source); + if (!source) { + Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str()); + continue; + } + + const bool symbolLayer = layer->is<RenderSymbolLayer>(); + + auto sortedTiles = source->getRenderTiles(); + if (symbolLayer) { + // Sort symbol tiles in opposite y position, so tiles with overlapping symbols are drawn + // on top of each other, with lower symbols being drawn on top of higher symbols. + std::sort(sortedTiles.begin(), sortedTiles.end(), [&](const RenderTile& a, const RenderTile& b) { + Point<float> pa(a.id.canonical.x, a.id.canonical.y); + Point<float> pb(b.id.canonical.x, b.id.canonical.y); + + auto par = util::rotate(pa, parameters.state.getAngle()); + auto pbr = util::rotate(pb, parameters.state.getAngle()); + + return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x); + }); + } else { + std::sort(sortedTiles.begin(), sortedTiles.end(), + [](const auto& a, const auto& b) { return a.get().id < b.get().id; }); + } + + std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion; + for (auto& sortedTile : sortedTiles) { + auto& tile = sortedTile.get(); + if (!tile.tile.isRenderable()) { + continue; + } + + // We're not clipping symbol layers, so when we have both parents and children of symbol + // layers, we drop all children in favor of their parent to avoid duplicate labels. + // See https://github.com/mapbox/mapbox-gl-native/issues/2482 + if (symbolLayer) { + bool skip = false; + // Look back through the buckets we decided to render to find out whether there is + // already a bucket from this layer that is a parent of this tile. Tiles are ordered + // by zoom level when we obtain them from getTiles(). + for (auto it = sortedTilesForInsertion.rbegin(); + it != sortedTilesForInsertion.rend(); ++it) { + if (tile.tile.id.isChildOf(it->get().tile.id)) { + skip = true; + break; + } + } + if (skip) { + continue; + } + } + + auto bucket = tile.tile.getBucket(*layer->baseImpl); + if (bucket) { + sortedTilesForInsertion.emplace_back(tile); + tile.used = true; + } + } + layer->setRenderTiles(std::move(sortedTilesForInsertion)); + order.emplace_back(RenderItem { *layer, source }); + } + + frameHistory.record(parameters.timePoint, + parameters.state.getZoom(), + parameters.mapMode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Milliseconds(0)); + + // - UPLOAD PASS ------------------------------------------------------------------------------- + // Uploads all required buffers and images before we do any actual rendering. + { + MBGL_DEBUG_GROUP(parameters.context, "upload"); + + parameters.imageManager.upload(parameters.context, 0); + parameters.lineAtlas.upload(parameters.context, 0); + parameters.frameHistory.upload(parameters.context, 0); + + // Update all clipping IDs + upload buckets. + for (const auto& entry : renderSources) { + if (entry.second->isEnabled()) { + entry.second->startRender(parameters); + } + } + } + + // - 3D PASS ------------------------------------------------------------------------------------- + // Renders any 3D layers bottom-to-top to unique FBOs with texture attachments, but share the same + // depth rbo between them. + if (parameters.staticData.has3D) { + parameters.staticData.backendSize = parameters.backend.getFramebufferSize(); + + MBGL_DEBUG_GROUP(parameters.context, "3d"); + parameters.pass = RenderPass::Pass3D; + + if (!parameters.staticData.depthRenderbuffer || + parameters.staticData.depthRenderbuffer->size != parameters.staticData.backendSize) { + parameters.staticData.depthRenderbuffer = + parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(parameters.staticData.backendSize); + } + parameters.staticData.depthRenderbuffer->shouldClear(true); + + uint32_t i = static_cast<uint32_t>(order.size()) - 1; + for (auto it = order.begin(); it != order.end(); ++it, --i) { + parameters.currentLayer = i; + if (it->layer.hasRenderPass(parameters.pass)) { + MBGL_DEBUG_GROUP(parameters.context, it->layer.getID()); + it->layer.render(parameters, it->source); + } + } + } + + // - CLEAR ------------------------------------------------------------------------------------- + // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any + // tiles whatsoever. + { + MBGL_DEBUG_GROUP(parameters.context, "clear"); + parameters.backend.bind(); + parameters.context.clear((parameters.debugOptions & MapDebugOptions::Overdraw) + ? Color::black() + : backgroundColor, + 1.0f, + 0); + } + + // - CLIPPING MASKS ---------------------------------------------------------------------------- + // Draws the clipping masks to the stencil buffer. + { + MBGL_DEBUG_GROUP(parameters.context, "clipping masks"); + + static const style::FillPaintProperties::PossiblyEvaluated properties {}; + static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) { + parameters.staticData.programs.fill.get(properties).draw( + parameters.context, + gl::Triangles(), + gl::DepthMode::disabled(), + gl::StencilMode { + gl::StencilMode::Always(), + static_cast<int32_t>(clipID.second.reference.to_ulong()), + 0b11111111, + gl::StencilMode::Keep, + gl::StencilMode::Keep, + gl::StencilMode::Replace + }, + gl::ColorMode::disabled(), + FillProgram::UniformValues { + uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) }, + uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "clipping" + ); + } + } + +#if not MBGL_USE_GLES2 and not defined(NDEBUG) + // Render tile clip boundaries, using stencil buffer to calculate fill color. + if (parameters.debugOptions & MapDebugOptions::StencilClip) { + parameters.context.setStencilMode(gl::StencilMode::disabled()); + parameters.context.setDepthMode(gl::DepthMode::disabled()); + parameters.context.setColorMode(gl::ColorMode::unblended()); + parameters.context.program = 0; + + // Reset the value in case someone else changed it, or it's dirty. + parameters.context.pixelTransferStencil = gl::value::PixelTransferStencil::Default; + + // Read the stencil buffer + const auto viewport = parameters.context.viewport.getCurrentValue(); + auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false); + + // Scale the Stencil buffer to cover the entire color space. + auto it = image.data.get(); + auto end = it + viewport.size.width * viewport.size.height; + const auto factor = 255.0f / *std::max_element(it, end); + for (; it != end; ++it) { + *it *= factor; + } + + parameters.context.pixelZoom = { 1, 1 }; + parameters.context.rasterPos = { -1, -1, 0, 1 }; + parameters.context.drawPixels(image); + + return; + } +#endif + + // Actually render the layers + + parameters.depthRangeSize = 1 - (order.size() + 2) * parameters.numSublayers * parameters.depthEpsilon; + + // - OPAQUE PASS ------------------------------------------------------------------------------- + // Render everything top-to-bottom by using reverse iterators. Render opaque objects first. + { + parameters.pass = RenderPass::Opaque; + MBGL_DEBUG_GROUP(parameters.context, "opaque"); + + uint32_t i = 0; + for (auto it = order.rbegin(); it != order.rend(); ++it, ++i) { + parameters.currentLayer = i; + if (it->layer.hasRenderPass(parameters.pass)) { + MBGL_DEBUG_GROUP(parameters.context, it->layer.getID()); + it->layer.render(parameters, it->source); + } + } + } + + // - TRANSLUCENT PASS -------------------------------------------------------------------------- + // Make a second pass, rendering translucent objects. This time, we render bottom-to-top. + { + parameters.pass = RenderPass::Translucent; + MBGL_DEBUG_GROUP(parameters.context, "translucent"); + + uint32_t i = static_cast<uint32_t>(order.size()) - 1; + for (auto it = order.begin(); it != order.end(); ++it, --i) { + parameters.currentLayer = i; + if (it->layer.hasRenderPass(parameters.pass)) { + MBGL_DEBUG_GROUP(parameters.context, it->layer.getID()); + it->layer.render(parameters, it->source); + } + } + } + + // - DEBUG PASS -------------------------------------------------------------------------------- + // Renders debug overlays. + { + MBGL_DEBUG_GROUP(parameters.context, "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& entry : renderSources) { + if (entry.second->isEnabled()) { + entry.second->finishRender(parameters); + } + } + } + +#if not MBGL_USE_GLES2 and not defined(NDEBUG) + // Render the depth buffer. + if (parameters.debugOptions & MapDebugOptions::DepthBuffer) { + parameters.context.setStencilMode(gl::StencilMode::disabled()); + parameters.context.setDepthMode(gl::DepthMode::disabled()); + parameters.context.setColorMode(gl::ColorMode::unblended()); + parameters.context.program = 0; + + // Scales the values in the depth buffer so that they cover the entire grayscale range. This + // makes it easier to spot tiny differences. + const float base = 1.0f / (1.0f - parameters.depthRangeSize); + parameters.context.pixelTransferDepth = { base, 1.0f - base }; + + // Read the stencil buffer + auto viewport = parameters.context.viewport.getCurrentValue(); + auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Depth>(viewport.size, false); + + parameters.context.pixelZoom = { 1, 1 }; + parameters.context.rasterPos = { -1, -1, 0, 1 }; + parameters.context.drawPixels(image); + } +#endif + + // TODO: Find a better way to unbind VAOs after we're done with them without introducing + // unnecessary bind(0)/bind(N) sequences. + { + MBGL_DEBUG_GROUP(parameters.context, "cleanup"); + + parameters.context.activeTextureUnit = 1; + parameters.context.texture[1] = 0; + parameters.context.activeTextureUnit = 0; + parameters.context.texture[0] = 0; + + parameters.context.bindVertexArray = 0; + } + + observer->onDidFinishRenderingFrame( + loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial, + updateParameters.mode == MapMode::Continuous && (hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION)) + ); + + if (!loaded) { + renderState = RenderState::Partial; + } else if (renderState != RenderState::Fully) { + renderState = RenderState::Fully; + observer->onDidFinishRenderingMap(); + } + + // Cleanup only after signaling completion + parameters.context.performCleanup(); +} + +std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const { + std::vector<const RenderLayer*> layers; + if (options.layerIDs) { + for (const auto& layerID : *options.layerIDs) { + if (const RenderLayer* layer = getRenderLayer(layerID)) { + layers.emplace_back(layer); + } + } + } else { + for (const auto& entry : renderLayers) { + layers.emplace_back(entry.second.get()); + } + } + + std::unordered_set<std::string> sourceIDs; + for (const RenderLayer* layer : layers) { + sourceIDs.emplace(layer->baseImpl->source); + } + + std::unordered_map<std::string, std::vector<Feature>> resultsByLayer; + for (const auto& sourceID : sourceIDs) { + if (RenderSource* renderSource = getRenderSource(sourceID)) { + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options); + std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); + } + } + + std::vector<Feature> result; + + if (resultsByLayer.empty()) { + return result; + } + + // Combine all results based on the style layer order. + for (const auto& layerImpl : *layerImpls) { + const RenderLayer* layer = getRenderLayer(layerImpl->id); + if (!layer->needsRendering(zoomHistory.lastZoom)) { + continue; + } + auto it = resultsByLayer.find(layer->baseImpl->id); + if (it != resultsByLayer.end()) { + std::move(it->second.begin(), it->second.end(), std::back_inserter(result)); + } + } + + return result; +} + +std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const { + const RenderSource* source = getRenderSource(sourceID); + if (!source) return {}; + + return source->querySourceFeatures(options); +} + +void Renderer::Impl::onLowMemory() { + assert(BackendScope::exists()); + backend.getContext().performCleanup(); + for (const auto& entry : renderSources) { + entry.second->onLowMemory(); + } + observer->onInvalidate(); +} + +void Renderer::Impl::dumDebugLogs() { + for (const auto& entry : renderSources) { + entry.second->dumpDebugLogs(); + } + + imageManager->dumpDebugLogs(); +} + +RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) { + auto it = renderLayers.find(id); + return it != renderLayers.end() ? it->second.get() : nullptr; +} + +const RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) const { + auto it = renderLayers.find(id); + return it != renderLayers.end() ? it->second.get() : nullptr; +} + +RenderSource* Renderer::Impl::getRenderSource(const std::string& id) const { + auto it = renderSources.find(id); + return it != renderSources.end() ? it->second.get() : nullptr; +} + +bool Renderer::Impl::hasTransitions() const { + if (renderLight.hasTransition()) { + return true; + } + + for (const auto& entry : renderLayers) { + if (entry.second->hasTransition()) { + return true; + } + } + + return false; +} + +bool Renderer::Impl::isLoaded() const { + for (const auto& entry: renderSources) { + if (!entry.second->isLoaded()) { + return false; + } + } + + if (!imageManager->isLoaded()) { + return false; + } + + return true; +} + +void Renderer::Impl::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { + Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s", + glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str()); + observer->onResourceError(error); +} + +void Renderer::Impl::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) { + Log::Error(Event::Style, "Failed to load tile %s for source %s: %s", + util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str()); + observer->onResourceError(error); +} + +void Renderer::Impl::onTileChanged(RenderSource&, const OverscaledTileID&) { + observer->onInvalidate(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp new file mode 100644 index 0000000000..30e7f70722 --- /dev/null +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/render_source_observer.hpp> +#include <mbgl/renderer/render_light.hpp> +#include <mbgl/renderer/frame_history.hpp> +#include <mbgl/style/image.hpp> +#include <mbgl/style/source.hpp> +#include <mbgl/style/layer.hpp> +#include <mbgl/map/transform_state.hpp> +#include <mbgl/map/zoom_history.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/text/glyph_manager_observer.hpp> + +#include <memory> +#include <string> +#include <vector> + +namespace mbgl { + +class RendererBackend; +class RendererObserver; +class RenderSource; +class RenderLayer; +class UpdateParameters; +class RenderStaticData; +class RenderedQueryOptions; +class SourceQueryOptions; +class FileSource; +class Scheduler; +class GlyphManager; +class ImageManager; +class LineAtlas; + +class Renderer::Impl : public GlyphManagerObserver, + public RenderSourceObserver{ +public: + Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode, + const optional<std::string> programCacheDir); + ~Impl() final; + + void markContextLost() { + contextLost = true; + }; + + void setObserver(RendererObserver*); + + void render(const UpdateParameters&); + + std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&) const; + std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const; + + void onLowMemory(); + void dumDebugLogs(); + +private: + bool isLoaded() const; + bool hasTransitions() const; + + RenderSource* getRenderSource(const std::string& id) const; + + RenderLayer* getRenderLayer(const std::string& id); + const RenderLayer* getRenderLayer(const std::string& id) const; + + // GlyphManagerObserver implementation. + void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override; + + // RenderSourceObserver implementation. + void onTileChanged(RenderSource&, const OverscaledTileID&) override; + void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; + + friend class Renderer; + + RendererBackend& backend; + Scheduler& scheduler; + FileSource& fileSource; + + RendererObserver* observer; + + const GLContextMode contextMode; + const float pixelRatio; + const optional<std::string> programCacheDir; + + enum class RenderState { + Never, + Partial, + Fully, + }; + + RenderState renderState = RenderState::Never; + FrameHistory frameHistory; + ZoomHistory zoomHistory; + TransformState transformState; + + std::unique_ptr<GlyphManager> glyphManager; + std::unique_ptr<ImageManager> imageManager; + std::unique_ptr<LineAtlas> lineAtlas; + std::unique_ptr<RenderStaticData> staticData; + + Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls; + Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls; + Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls; + + std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources; + std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers; + RenderLight renderLight; + + bool contextLost = false; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_observer.hpp b/src/mbgl/renderer/renderer_observer.hpp new file mode 100644 index 0000000000..551b5c803e --- /dev/null +++ b/src/mbgl/renderer/renderer_observer.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include <exception> + +namespace mbgl { + +class RendererObserver { +public: + virtual ~RendererObserver() = default; + + enum class RenderMode : uint32_t { + Partial, + Full + }; + + // Signals that a repaint is required + virtual void onInvalidate() {} + + // Resource failed to download / parse + virtual void onResourceError(std::exception_ptr) {} + + // First frame + virtual void onWillStartRenderingMap() {} + + // Start of frame, initial is the first frame for this map + virtual void onWillStartRenderingFrame() {} + + // End of frame, boolean flags that a repaint is required + virtual void onDidFinishRenderingFrame(RenderMode, bool) {} + + // Final frame + virtual void onDidFinishRenderingMap() {} +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index 2b1eeea73b..504db78ea3 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -1,6 +1,8 @@ #include <mbgl/renderer/sources/render_geojson_source.hpp> #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/tile/geojson_tile.hpp> +#include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/algorithm/generate_clip_ids.hpp> #include <mbgl/algorithm/generate_clip_ids_impl.hpp> @@ -9,81 +11,87 @@ namespace mbgl { using namespace style; -RenderGeoJSONSource::RenderGeoJSONSource(const style::GeoJSONSource::Impl& impl_) - : RenderSource(impl_), - impl(impl_) { +RenderGeoJSONSource::RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl> impl_) + : RenderSource(impl_) { tilePyramid.setObserver(this); } -bool RenderGeoJSONSource::isLoaded() const { - return tilePyramid.isLoaded(); -} - -void RenderGeoJSONSource::invalidateTiles() { - tilePyramid.invalidateTiles(); -} - -void RenderGeoJSONSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { - generator.update(tilePyramid.getRenderTiles()); - tilePyramid.startRender(projMatrix, clipMatrix, transform); +const style::GeoJSONSource::Impl& RenderGeoJSONSource::impl() const { + return static_cast<const style::GeoJSONSource::Impl&>(*baseImpl); } -void RenderGeoJSONSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); +bool RenderGeoJSONSource::isLoaded() const { + return tilePyramid.isLoaded(); } -std::map<UnwrappedTileID, RenderTile>& RenderGeoJSONSource::getRenderTiles() { - return tilePyramid.getRenderTiles(); -} +void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters) { + std::swap(baseImpl, baseImpl_); -void RenderGeoJSONSource::updateTiles(const TileParameters& parameters) { - GeoJSONData* data_ = impl.getData(); + enabled = needsRendering; - if (!data_) { - return; - } + GeoJSONData* data_ = impl().getData(); if (data_ != data) { data = data_; tilePyramid.cache.clear(); - for (auto const& item : tilePyramid.tiles) { - static_cast<GeoJSONTile*>(item.second.get())->updateData(data->getTile(item.first.canonical)); + if (data) { + const uint8_t maxZ = impl().getZoomRange().max; + for (const auto& pair : tilePyramid.tiles) { + if (pair.first.canonical.z <= maxZ) { + static_cast<GeoJSONTile*>(pair.second.get())->updateData(data->getTile(pair.first.canonical)); + } + } } } - tilePyramid.updateTiles(parameters, - SourceType::GeoJSON, - util::tileSize, - impl.getZoomRange(), - [&] (const OverscaledTileID& tileID) { - return std::make_unique<GeoJSONTile>(tileID, impl.id, parameters, data->getTile(tileID.canonical)); - }); + if (!data) { + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + return; + } + + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::GeoJSON, + util::tileSize, + impl().getZoomRange(), + [&] (const OverscaledTileID& tileID) { + return std::make_unique<GeoJSONTile>(tileID, impl().id, parameters, data->getTile(tileID.canonical)); + }); } -void RenderGeoJSONSource::removeTiles() { - tilePyramid.removeTiles(); +void RenderGeoJSONSource::startRender(PaintParameters& parameters) { + parameters.clipIDGenerator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderGeoJSONSource::reloadTiles() { - tilePyramid.reloadTiles(); +void RenderGeoJSONSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); +} + +std::vector<std::reference_wrapper<RenderTile>> RenderGeoJSONSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); } std::unordered_map<std::string, std::vector<Feature>> RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, options); + const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const { return tilePyramid.querySourceFeatures(options); } -void RenderGeoJSONSource::setCacheSize(size_t size) { - tilePyramid.setCacheSize(size); -} - void RenderGeoJSONSource::onLowMemory() { tilePyramid.onLowMemory(); } diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index 262ab29276..72ab4879ef 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -12,48 +12,43 @@ class GeoJSONData; class RenderGeoJSONSource : public RenderSource { public: - RenderGeoJSONSource(const style::GeoJSONSource::Impl&); + RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl>); bool isLoaded() const final; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&) final; + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; - // Removes all tiles (by putting them into the cache). - void removeTiles() final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; - // Remove all tiles and clear the cache. - void invalidateTiles() final; - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles() final; - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) final; - void finishRender(Painter&) final; - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final; + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final; std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; private: - const style::GeoJSONSource::Impl& impl; + const style::GeoJSONSource::Impl& impl() const; + TilePyramid tilePyramid; - style::GeoJSONData* data; + style::GeoJSONData* data = nullptr; }; +template <> +inline bool RenderSource::is<RenderGeoJSONSource>() const { + return baseImpl->type == SourceType::GeoJSON; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp new file mode 100644 index 0000000000..9140e01711 --- /dev/null +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -0,0 +1,214 @@ +#include <mbgl/map/transform_state.hpp> +#include <mbgl/math/log2.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/sources/render_image_source.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/util/tile_coordinate.hpp> +#include <mbgl/util/tile_cover.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/constants.hpp> + +namespace mbgl { + +using namespace style; + +RenderImageSource::RenderImageSource(Immutable<style::ImageSource::Impl> impl_) + : RenderSource(impl_) { +} + +RenderImageSource::~RenderImageSource() = default; + +const style::ImageSource::Impl& RenderImageSource::impl() const { + return static_cast<const style::ImageSource::Impl&>(*baseImpl); +} + +bool RenderImageSource::isLoaded() const { + return !!bucket; +} + +void RenderImageSource::startRender(PaintParameters& parameters) { + if (!isLoaded()) { + return; + } + + matrices.clear(); + + for (size_t i = 0; i < tileIds.size(); i++) { + mat4 matrix; + matrix::identity(matrix); + parameters.state.matrixFor(matrix, tileIds[i]); + matrix::multiply(matrix, parameters.projMatrix, matrix); + matrices.push_back(matrix); + } + + if (bucket->needsUpload()) { + bucket->upload(parameters.context); + } +} + +void RenderImageSource::finishRender(PaintParameters& parameters) { + if (!isLoaded() || !(parameters.debugOptions & MapDebugOptions::TileBorders)) { + return; + } + + static const style::Properties<>::PossiblyEvaluated properties {}; + static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + for (auto matrix : matrices) { + parameters.programs.debug.draw( + parameters.context, + gl::LineStrip { 4.0f * parameters.pixelRatio }, + gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + gl::ColorMode::unblended(), + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::red() } + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.tileBorderIndexBuffer, + parameters.staticData.tileBorderSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "debug" + ); + } +} + +std::unordered_map<std::string, std::vector<Feature>> +RenderImageSource::queryRenderedFeatures(const ScreenLineString&, + const TransformState&, + const std::vector<const RenderLayer*>&, + const RenderedQueryOptions&) const { + return std::unordered_map<std::string, std::vector<Feature>> {}; +} + +std::vector<Feature> RenderImageSource::querySourceFeatures(const SourceQueryOptions&) const { + return {}; +} + +void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>&, + const bool needsRendering, + const bool, + const TileParameters& parameters) { + enabled = needsRendering; + if (!needsRendering) { + return; + } + + auto transformState = parameters.transformState; + std::swap(baseImpl, baseImpl_); + + auto coords = impl().getCoordinates(); + std::shared_ptr<PremultipliedImage> image = impl().getImage(); + + if (!image || !image->valid()) { + enabled = false; + return; + } + + auto size = transformState.getSize(); + const double viewportHeight = size.height; + + // Compute the screen coordinates at wrap=0 for the given LatLng + ScreenCoordinate nePixel = { -INFINITY, -INFINITY }; + ScreenCoordinate swPixel = { INFINITY, INFINITY }; + + for (LatLng latLng : coords) { + ScreenCoordinate pixel = transformState.latLngToScreenCoordinate(latLng); + swPixel.x = std::min(swPixel.x, pixel.x); + nePixel.x = std::max(nePixel.x, pixel.x); + swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y); + nePixel.y = std::max(nePixel.y, viewportHeight - pixel.y); + } + const double width = nePixel.x - swPixel.x; + const double height = nePixel.y - swPixel.y; + + // Don't bother drawing the ImageSource unless it occupies >4 screen pixels + enabled = (width * height > 4); + if (!enabled) { + return; + } + + // Calculate the optimum zoom level to determine the tile ids to use for transforms + double minScale = INFINITY; + double scaleX = double(size.width) / width; + double scaleY = double(size.height) / height; + minScale = util::min(scaleX, scaleY); + double zoom = transformState.getZoom() + util::log2(minScale); + zoom = std::floor(util::clamp(zoom, transformState.getMinZoom(), transformState.getMaxZoom())); + auto imageBounds = LatLngBounds::hull(coords[0], coords[1]); + imageBounds.extend(coords[2]); + imageBounds.extend(coords[3]); + auto tileCover = util::tileCover(imageBounds, zoom); + tileIds.clear(); + tileIds.push_back(tileCover[0]); + bool hasVisibleTile = false; + + // Add additional wrapped tile ids if neccessary + auto idealTiles = util::tileCover(transformState, transformState.getZoom()); + for (auto tile : idealTiles) { + if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) { + tileIds.push_back({ tile.wrap, tileCover[0].canonical }); + hasVisibleTile = true; + } + else if (!hasVisibleTile) { + for (auto coveringTile: tileCover) { + if(coveringTile.canonical == tile.canonical || + coveringTile.canonical.isChildOf(tile.canonical) || + tile.canonical.isChildOf(coveringTile.canonical)) { + hasVisibleTile = true; + } + } + } + } + + enabled = hasVisibleTile; + if (!enabled) { + return; + } + + // Calculate Geometry Coordinates based on tile cover at ideal zoom + GeometryCoordinates geomCoords; + for (auto latLng : coords) { + auto tc = TileCoordinate::fromLatLng(0, latLng); + auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tc.p); + geomCoords.push_back(gc); + } + if (!bucket) { + bucket = std::make_unique<RasterBucket>(image); + } else { + bucket->clear(); + if (image != bucket->image) { + bucket->setImage(image); + } + } + + // Set Bucket Vertices, Indices, and segments + bucket->vertices.emplace_back( + RasterProgram::layoutVertex({ geomCoords[0].x, geomCoords[0].y }, { 0, 0 })); + bucket->vertices.emplace_back( + RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { util::EXTENT, 0 })); + bucket->vertices.emplace_back( + RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT })); + bucket->vertices.emplace_back( + RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { util::EXTENT, util::EXTENT })); + + bucket->indices.emplace_back(0, 1, 2); + bucket->indices.emplace_back(1, 2, 3); + + bucket->segments.emplace_back(0, 0, 4, 6); +} + +void RenderImageSource::dumpDebugLogs() const { + Log::Info(Event::General, "RenderImageSource::id: %s", impl().id.c_str()); + Log::Info(Event::General, "RenderImageSource::loaded: %s", isLoaded() ? "yes" : "no"); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp new file mode 100644 index 0000000000..7b69d09fa7 --- /dev/null +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include <mbgl/renderer/render_source.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/style/sources/image_source_impl.hpp> + +namespace mbgl { + +class RasterBucket; + +class RenderImageSource : public RenderSource { +public: + RenderImageSource(Immutable<style::ImageSource::Impl>); + ~RenderImageSource() override; + + bool isLoaded() const final; + + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; + + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; + + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final { + return {}; + } + + std::unordered_map<std::string, std::vector<Feature>> + queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options) const final; + + std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; + + void onLowMemory() final { + } + void dumpDebugLogs() const final; + +private: + friend class RenderRasterLayer; + + const style::ImageSource::Impl& impl() const; + + std::vector<UnwrappedTileID> tileIds; + std::unique_ptr<RasterBucket> bucket; + std::vector<mat4> matrices; +}; + +template <> +inline bool RenderSource::is<RenderImageSource>() const { + return baseImpl->type == SourceType::Image; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index c5a29eebf5..bcd719365d 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -1,39 +1,35 @@ #include <mbgl/renderer/sources/render_raster_source.hpp> #include <mbgl/renderer/render_tile.hpp> #include <mbgl/tile/raster_tile.hpp> +#include <mbgl/algorithm/update_tile_masks.hpp> namespace mbgl { using namespace style; -RenderRasterSource::RenderRasterSource(const style::RasterSource::Impl& impl_) - : RenderSource(impl_), - impl(impl_) { +RenderRasterSource::RenderRasterSource(Immutable<style::RasterSource::Impl> impl_) + : RenderSource(impl_) { tilePyramid.setObserver(this); } -bool RenderRasterSource::isLoaded() const { - return tilePyramid.isLoaded(); -} - -void RenderRasterSource::invalidateTiles() { - tilePyramid.invalidateTiles(); +const style::RasterSource::Impl& RenderRasterSource::impl() const { + return static_cast<const style::RasterSource::Impl&>(*baseImpl); } -void RenderRasterSource::startRender(algorithm::ClipIDGenerator&, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { - tilePyramid.startRender(projMatrix, clipMatrix, transform); +bool RenderRasterSource::isLoaded() const { + return tilePyramid.isLoaded(); } -void RenderRasterSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); -} +void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters) { + std::swap(baseImpl, baseImpl_); -std::map<UnwrappedTileID, RenderTile>& RenderRasterSource::getRenderTiles() { - return tilePyramid.getRenderTiles(); -} + enabled = needsRendering; -void RenderRasterSource::updateTiles(const TileParameters& parameters) { - optional<Tileset> tileset = impl.getTileset(); + optional<Tileset> tileset = impl().getTileset(); if (!tileset) { return; @@ -41,41 +37,51 @@ void RenderRasterSource::updateTiles(const TileParameters& parameters) { if (tileURLTemplates != tileset->tiles) { tileURLTemplates = tileset->tiles; - tilePyramid.invalidateTiles(); + + // TODO: this removes existing buckets, and will cause flickering. + // Should instead refresh tile data in place. + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + tilePyramid.cache.clear(); } - tilePyramid.updateTiles(parameters, - SourceType::Raster, - impl.getTileSize(), - tileset->zoomRange, - [&] (const OverscaledTileID& tileID) { - return std::make_unique<RasterTile>(tileID, parameters, *tileset); - }); + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::Raster, + impl().getTileSize(), + tileset->zoomRange, + [&] (const OverscaledTileID& tileID) { + return std::make_unique<RasterTile>(tileID, parameters, *tileset); + }); +} + +void RenderRasterSource::startRender(PaintParameters& parameters) { + algorithm::updateTileMasks(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderRasterSource::removeTiles() { - tilePyramid.removeTiles(); +void RenderRasterSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); } -void RenderRasterSource::reloadTiles() { - tilePyramid.reloadTiles(); +std::vector<std::reference_wrapper<RenderTile>> RenderRasterSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); } std::unordered_map<std::string, std::vector<Feature>> RenderRasterSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions&) const { - return {}; + return std::unordered_map<std::string, std::vector<Feature>> {}; } std::vector<Feature> RenderRasterSource::querySourceFeatures(const SourceQueryOptions&) const { return {}; } -void RenderRasterSource::setCacheSize(size_t size) { - tilePyramid.setCacheSize(size); -} - void RenderRasterSource::onLowMemory() { tilePyramid.onLowMemory(); } diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 5690ba80ea..01de812309 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -8,48 +8,43 @@ namespace mbgl { class RenderRasterSource : public RenderSource { public: - RenderRasterSource(const style::RasterSource::Impl&); + RenderRasterSource(Immutable<style::RasterSource::Impl>); bool isLoaded() const final; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&) final; + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; - // Removes all tiles (by putting them into the cache). - void removeTiles() final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; - // Remove all tiles and clear the cache. - void invalidateTiles() final; - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles() final; - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) final; - void finishRender(Painter&) final; - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final; + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final; std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; private: - const style::RasterSource::Impl& impl; + const style::RasterSource::Impl& impl() const; + TilePyramid tilePyramid; optional<std::vector<std::string>> tileURLTemplates; }; +template <> +inline bool RenderSource::is<RenderRasterSource>() const { + return baseImpl->type == SourceType::Raster; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index 0db4698a81..ca3071c6b0 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -1,5 +1,6 @@ #include <mbgl/renderer/sources/render_vector_source.hpp> #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/tile/vector_tile.hpp> #include <mbgl/algorithm/generate_clip_ids.hpp> @@ -9,35 +10,29 @@ namespace mbgl { using namespace style; -RenderVectorSource::RenderVectorSource(const style::VectorSource::Impl& impl_) - : RenderSource(impl_), - impl(impl_) { +RenderVectorSource::RenderVectorSource(Immutable<style::VectorSource::Impl> impl_) + : RenderSource(impl_) { tilePyramid.setObserver(this); } -bool RenderVectorSource::isLoaded() const { - return tilePyramid.isLoaded(); +const style::VectorSource::Impl& RenderVectorSource::impl() const { + return static_cast<const style::VectorSource::Impl&>(*baseImpl); } -void RenderVectorSource::invalidateTiles() { - tilePyramid.invalidateTiles(); +bool RenderVectorSource::isLoaded() const { + return tilePyramid.isLoaded(); } -void RenderVectorSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { - generator.update(tilePyramid.getRenderTiles()); - tilePyramid.startRender(projMatrix, clipMatrix, transform); -} +void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters) { + std::swap(baseImpl, baseImpl_); -void RenderVectorSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); -} + enabled = needsRendering; -std::map<UnwrappedTileID, RenderTile>& RenderVectorSource::getRenderTiles() { - return tilePyramid.getRenderTiles(); -} - -void RenderVectorSource::updateTiles(const TileParameters& parameters) { - optional<Tileset> tileset = impl.getTileset(); + optional<Tileset> tileset = impl().getTileset(); if (!tileset) { return; @@ -45,41 +40,51 @@ void RenderVectorSource::updateTiles(const TileParameters& parameters) { if (tileURLTemplates != tileset->tiles) { tileURLTemplates = tileset->tiles; - tilePyramid.invalidateTiles(); + + // TODO: this removes existing buckets, and will cause flickering. + // Should instead refresh tile data in place. + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + tilePyramid.cache.clear(); } - tilePyramid.updateTiles(parameters, - SourceType::Vector, - util::tileSize, - tileset->zoomRange, - [&] (const OverscaledTileID& tileID) { - return std::make_unique<VectorTile>(tileID, impl.id, parameters, *tileset); - }); + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::Vector, + util::tileSize, + tileset->zoomRange, + [&] (const OverscaledTileID& tileID) { + return std::make_unique<VectorTile>(tileID, impl().id, parameters, *tileset); + }); } -void RenderVectorSource::removeTiles() { - tilePyramid.removeTiles(); +void RenderVectorSource::startRender(PaintParameters& parameters) { + parameters.clipIDGenerator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderVectorSource::reloadTiles() { - tilePyramid.reloadTiles(); +void RenderVectorSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); +} + +std::vector<std::reference_wrapper<RenderTile>> RenderVectorSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); } std::unordered_map<std::string, std::vector<Feature>> RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, options); + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const { return tilePyramid.querySourceFeatures(options); } -void RenderVectorSource::setCacheSize(size_t size) { - tilePyramid.setCacheSize(size); -} - void RenderVectorSource::onLowMemory() { tilePyramid.onLowMemory(); } diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 36d75e0982..5e5c6d1108 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -8,48 +8,43 @@ namespace mbgl { class RenderVectorSource : public RenderSource { public: - RenderVectorSource(const style::VectorSource::Impl&); + RenderVectorSource(Immutable<style::VectorSource::Impl>); bool isLoaded() const final; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&) final; + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; - // Removes all tiles (by putting them into the cache). - void removeTiles() final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; - // Remove all tiles and clear the cache. - void invalidateTiles() final; - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles() final; - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) final; - void finishRender(Painter&) final; - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final; + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final; std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; private: - const style::VectorSource::Impl& impl; + const style::VectorSource::Impl& impl() const; + TilePyramid tilePyramid; optional<std::vector<std::string>> tileURLTemplates; }; +template <> +inline bool RenderSource::is<RenderVectorSource>() const { + return baseImpl->type == SourceType::Vector; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/style_diff.cpp b/src/mbgl/renderer/style_diff.cpp new file mode 100644 index 0000000000..0017280310 --- /dev/null +++ b/src/mbgl/renderer/style_diff.cpp @@ -0,0 +1,79 @@ +#include <mbgl/renderer/style_diff.hpp> +#include <mbgl/style/layer_impl.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/util/variant.hpp> +#include <mbgl/util/longest_common_subsequence.hpp> + +namespace mbgl { + +template <class T, class Eq> +StyleDifference<T> diff(const Immutable<std::vector<T>>& a, + const Immutable<std::vector<T>>& b, + const Eq& eq) { + StyleDifference<T> result; + + if (a == b) { + return result; + } + + std::vector<T> lcs; + + longest_common_subsequence(a->begin(), a->end(), b->begin(), b->end(), std::back_inserter(lcs), eq); + + auto aIt = a->begin(); + auto bIt = b->begin(); + auto lIt = lcs.begin(); + + while (aIt != a->end() || bIt != b->end()) { + if (aIt != a->end() && (lIt == lcs.end() || !eq(*lIt, *aIt))) { + result.removed.emplace((*aIt)->id, *aIt); + aIt++; + } else if (bIt != b->end() && (lIt == lcs.end() || !eq(*lIt, *bIt))) { + result.added.emplace((*bIt)->id, *bIt); + bIt++; + } else { + if (aIt->get() != bIt->get()) { + result.changed.emplace((*bIt)->id, StyleChange<T> { *aIt, *bIt }); + } + aIt++; + bIt++; + lIt++; + } + } + + return result; +} + +ImageDifference diffImages(const Immutable<std::vector<ImmutableImage>>& a, + const Immutable<std::vector<ImmutableImage>>& b) { + return diff(a, b, [] (const ImmutableImage& lhs, const ImmutableImage& rhs) { + return lhs->id == rhs->id; + }); +} + +SourceDifference diffSources(const Immutable<std::vector<ImmutableSource>>& a, + const Immutable<std::vector<ImmutableSource>>& b) { + return diff(a, b, [] (const ImmutableSource& lhs, const ImmutableSource& rhs) { + return std::tie(lhs->id, lhs->type) + == std::tie(rhs->id, rhs->type); + }); +} + +LayerDifference diffLayers(const Immutable<std::vector<ImmutableLayer>>& a, + const Immutable<std::vector<ImmutableLayer>>& b) { + return diff(a, b, [] (const ImmutableLayer& lhs, const ImmutableLayer& rhs) { + return std::tie(lhs->id, lhs->type) + == std::tie(rhs->id, rhs->type); + }); +} + +bool hasLayoutDifference(const LayerDifference& layerDiff, const std::string& layerID) { + if (layerDiff.added.count(layerID)) + return true; + const auto it = layerDiff.changed.find(layerID); + if (it == layerDiff.changed.end()) + return false; + return it->second.before->hasLayoutDifference(*it->second.after); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/style_diff.hpp b/src/mbgl/renderer/style_diff.hpp new file mode 100644 index 0000000000..a5b42fc662 --- /dev/null +++ b/src/mbgl/renderer/style_diff.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include <mbgl/style/image_impl.hpp> +#include <mbgl/style/source_impl.hpp> +#include <mbgl/style/layer_impl.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/util/variant.hpp> + +#include <unordered_map> + +namespace mbgl { + +template <class T> +class StyleChange { +public: + T before; + T after; +}; + +template <class T> +class StyleDifference { +public: + std::unordered_map<std::string, T> added; + std::unordered_map<std::string, T> removed; + std::unordered_map<std::string, StyleChange<T>> changed; +}; + +using ImmutableImage = Immutable<style::Image::Impl>; +using ImageDifference = StyleDifference<ImmutableImage>; + +ImageDifference diffImages(const Immutable<std::vector<ImmutableImage>>&, + const Immutable<std::vector<ImmutableImage>>&); + +using ImmutableSource = Immutable<style::Source::Impl>; +using SourceDifference = StyleDifference<ImmutableSource>; + +SourceDifference diffSources(const Immutable<std::vector<ImmutableSource>>&, + const Immutable<std::vector<ImmutableSource>>&); + +using ImmutableLayer = Immutable<style::Layer::Impl>; +using LayerDifference = StyleDifference<ImmutableLayer>; + +LayerDifference diffLayers(const Immutable<std::vector<ImmutableLayer>>&, + const Immutable<std::vector<ImmutableLayer>>&); + +bool hasLayoutDifference(const LayerDifference&, const std::string& layerID); + +} // namespace mbgl diff --git a/src/mbgl/renderer/tile_mask.hpp b/src/mbgl/renderer/tile_mask.hpp new file mode 100644 index 0000000000..5f24d63ba4 --- /dev/null +++ b/src/mbgl/renderer/tile_mask.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include <mbgl/tile/tile_id.hpp> + +#include <set> + +namespace mbgl { + +// A TileMask is a set of TileIDs that describe what part of a tile should be rendered. It omits +// those parts of the tile that are covered by other/better tiles. If the entire tile should be +// rendered, it contains the { 0, 0, 0 } tile. If it's empty, no part of the tile will be rendered. +// TileMasks are typically generated with algorithm::updateTileMasks(). +using TileMask = std::set<CanonicalTileID>; + +} // namespace mbgl diff --git a/src/mbgl/renderer/tile_parameters.hpp b/src/mbgl/renderer/tile_parameters.hpp index 8f04baaec5..665c7490d2 100644 --- a/src/mbgl/renderer/tile_parameters.hpp +++ b/src/mbgl/renderer/tile_parameters.hpp @@ -8,40 +8,21 @@ class TransformState; class Scheduler; class FileSource; class AnnotationManager; - -namespace style { -class Style; -} // namespace style +class ImageManager; +class GlyphManager; class TileParameters { public: - TileParameters(float pixelRatio_, - MapDebugOptions debugOptions_, - const TransformState& transformState_, - Scheduler& workerScheduler_, - FileSource& fileSource_, - const MapMode mode_, - AnnotationManager& annotationManager_, - style::Style& style_) - : pixelRatio(pixelRatio_), - debugOptions(debugOptions_), - transformState(transformState_), - workerScheduler(workerScheduler_), - fileSource(fileSource_), - mode(mode_), - annotationManager(annotationManager_), - style(style_) {} - - float pixelRatio; - MapDebugOptions debugOptions; + const float pixelRatio; + const MapDebugOptions debugOptions; const TransformState& transformState; Scheduler& workerScheduler; FileSource& fileSource; const MapMode mode; AnnotationManager& annotationManager; - - // TODO: remove - style::Style& style; + ImageManager& imageManager; + GlyphManager& glyphManager; + const uint8_t prefetchZoomDelta; }; } // namespace mbgl diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 144afcb4f6..6cd9bd9ebd 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -1,10 +1,10 @@ #include <mbgl/renderer/tile_pyramid.hpp> #include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/renderer/render_source.hpp> #include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/query.hpp> #include <mbgl/map/transform.hpp> -#include <mbgl/map/query.hpp> #include <mbgl/text/placement_config.hpp> #include <mbgl/math/clamp.hpp> #include <mbgl/util/tile_cover.hpp> @@ -39,50 +39,77 @@ bool TilePyramid::isLoaded() const { return true; } -void TilePyramid::invalidateTiles() { - tiles.clear(); - renderTiles.clear(); - cache.clear(); -} - -void TilePyramid::startRender(const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState& transform) { - for (auto& pair : renderTiles) { - auto& tile = pair.second; - tile.calculateMatrices(projMatrix, clipMatrix, transform); +void TilePyramid::startRender(PaintParameters& parameters) { + for (auto& tile : renderTiles) { + tile.startRender(parameters); } } -void TilePyramid::finishRender(Painter& painter) { - for (auto& pair : renderTiles) { - auto& tile = pair.second; - if (tile.used) { - painter.renderTileDebug(tile); - } +void TilePyramid::finishRender(PaintParameters& parameters) { + for (auto& tile : renderTiles) { + tile.finishRender(parameters); } } -std::map<UnwrappedTileID, RenderTile>& TilePyramid::getRenderTiles() { - return renderTiles; +std::vector<std::reference_wrapper<RenderTile>> TilePyramid::getRenderTiles() { + return { renderTiles.begin(), renderTiles.end() }; } -void TilePyramid::updateTiles(const TileParameters& parameters, - const SourceType type, - const uint16_t tileSize, - const Range<uint8_t> zoomRange, - std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) { +void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters, + const SourceType type, + const uint16_t tileSize, + const Range<uint8_t> zoomRange, + std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) { + // If we need a relayout, abandon any cached tiles; they're now stale. + if (needsRelayout) { + cache.clear(); + } + + // If we're not going to render anything, move our existing tiles into + // the cache (if they're not stale) or abandon them, and return. + if (!needsRendering) { + if (!needsRelayout) { + for (auto& entry : tiles) { + cache.add(entry.first, std::move(entry.second)); + } + } + + tiles.clear(); + renderTiles.clear(); + + return; + } + // Determine the overzooming/underzooming amounts and required tiles. int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize); int32_t tileZoom = overscaledZoom; + int32_t panZoom = zoomRange.max; std::vector<UnwrappedTileID> idealTiles; + std::vector<UnwrappedTileID> panTiles; + if (overscaledZoom >= zoomRange.min) { int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom); // Make sure we're not reparsing overzoomed raster tiles. if (type == SourceType::Raster) { tileZoom = idealZoom; + + // FIXME: Prefetching is only enabled for raster + // tiles until we fix #7026. + + // Request lower zoom level tiles (if configure to do so) in an attempt + // to show something on the screen faster at the cost of a little of bandwidth. + if (parameters.prefetchZoomDelta) { + panZoom = std::max<int32_t>(tileZoom - parameters.prefetchZoomDelta, zoomRange.min); + } + + if (panZoom < tileZoom) { + panTiles = util::tileCover(parameters.transformState, panZoom); + } } idealTiles = util::tileCover(parameters.transformState, idealZoom); @@ -95,8 +122,13 @@ void TilePyramid::updateTiles(const TileParameters& parameters, std::set<OverscaledTileID> retain; auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void { - retain.emplace(tile.id); - tile.setNecessity(necessity); + if (retain.emplace(tile.id).second) { + tile.setNecessity(necessity); + } + + if (needsRelayout) { + tile.setLayers(layers); + } }; auto getTileFn = [&](const OverscaledTileID& tileID) -> Tile* { auto it = tiles.find(tileID); @@ -108,6 +140,7 @@ void TilePyramid::updateTiles(const TileParameters& parameters, tile = createTile(tileID); if (tile) { tile->setObserver(observer); + tile->setLayers(layers); } } if (!tile) { @@ -116,10 +149,16 @@ void TilePyramid::updateTiles(const TileParameters& parameters, return tiles.emplace(tileID, std::move(tile)).first->second.get(); }; auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) { - renderTiles.emplace(tileID, RenderTile{ tileID, tile }); + renderTiles.emplace_back(tileID, tile); }; renderTiles.clear(); + + if (!panTiles.empty()) { + algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, + [](const UnwrappedTileID&, Tile&) {}, panTiles, zoomRange, panZoom); + } + algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn, idealTiles, zoomRange, tileZoom); @@ -132,54 +171,41 @@ void TilePyramid::updateTiles(const TileParameters& parameters, cache.setSize(conservativeCacheSize); } - removeStaleTiles(retain); - - const PlacementConfig config { parameters.transformState.getAngle(), - parameters.transformState.getPitch(), - parameters.debugOptions & MapDebugOptions::Collision }; - - for (auto& pair : tiles) { - pair.second->setPlacementConfig(config); - } -} - -// Moves all tiles to the cache except for those specified in the retain set. -void TilePyramid::removeStaleTiles(const std::set<OverscaledTileID>& retain) { // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep // and removes items from tiles that don't have the corresponding key in the retain set. - auto tilesIt = tiles.begin(); - auto retainIt = retain.begin(); - while (tilesIt != tiles.end()) { - if (retainIt == retain.end() || tilesIt->first < *retainIt) { - tilesIt->second->setNecessity(Tile::Necessity::Optional); - cache.add(tilesIt->first, std::move(tilesIt->second)); - tiles.erase(tilesIt++); - } else { - if (!(*retainIt < tilesIt->first)) { - ++tilesIt; + { + auto tilesIt = tiles.begin(); + auto retainIt = retain.begin(); + while (tilesIt != tiles.end()) { + if (retainIt == retain.end() || tilesIt->first < *retainIt) { + if (!needsRelayout) { + tilesIt->second->setNecessity(Tile::Necessity::Optional); + cache.add(tilesIt->first, std::move(tilesIt->second)); + } + tiles.erase(tilesIt++); + } else { + if (!(*retainIt < tilesIt->first)) { + ++tilesIt; + } + ++retainIt; } - ++retainIt; } } -} - -void TilePyramid::removeTiles() { - renderTiles.clear(); - if (!tiles.empty()) { - removeStaleTiles({}); - } -} - -void TilePyramid::reloadTiles() { - cache.clear(); for (auto& pair : tiles) { - pair.second->redoLayout(); + const PlacementConfig config { parameters.transformState.getAngle(), + parameters.transformState.getPitch(), + parameters.transformState.getCameraToCenterDistance(), + parameters.transformState.getCameraToTileDistance(pair.first.toUnwrapped()), + parameters.debugOptions & MapDebugOptions::Collision }; + + pair.second->setPlacementConfig(config); } } std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const { std::unordered_map<std::string, std::vector<Feature>> result; if (renderTiles.empty() || geometry.empty()) { @@ -195,18 +221,14 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry); - - auto sortRenderTiles = [](const RenderTile& a, const RenderTile& b) { + std::vector<std::reference_wrapper<const RenderTile>> sortedTiles{ renderTiles.begin(), + renderTiles.end() }; + std::sort(sortedTiles.begin(), sortedTiles.end(), [](const RenderTile& a, const RenderTile& b) { return std::tie(a.id.canonical.z, a.id.canonical.y, a.id.wrap, a.id.canonical.x) < std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x); - }; - std::vector<std::reference_wrapper<const RenderTile>> sortedTiles; - std::transform(renderTiles.cbegin(), renderTiles.cend(), std::back_inserter(sortedTiles), - [](const auto& pair) { return std::ref(pair.second); }); - std::sort(sortedTiles.begin(), sortedTiles.end(), sortRenderTiles); + }); - for (const auto& renderTileRef : sortedTiles) { - const RenderTile& renderTile = renderTileRef.get(); + for (const RenderTile& renderTile : sortedTiles) { GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { continue; @@ -226,6 +248,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered renderTile.tile.queryRenderedFeatures(result, tileSpaceQueryGeometry, transformState, + layers, options); } diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index b51c5342de..73a8d34c1c 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -5,6 +5,7 @@ #include <mbgl/tile/tile.hpp> #include <mbgl/tile/tile_cache.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/style/layer_impl.hpp> #include <mbgl/util/mat4.hpp> #include <mbgl/util/feature.hpp> @@ -17,9 +18,10 @@ namespace mbgl { -class Painter; +class PaintParameters; class TransformState; class RenderTile; +class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; class TileParameters; @@ -31,34 +33,24 @@ public: bool isLoaded() const; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&, - SourceType type, - uint16_t tileSize, - Range<uint8_t> zoomRange, - std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile); + void update(const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&, + SourceType type, + uint16_t tileSize, + Range<uint8_t> zoomRange, + std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile); - // Removes all tiles (by putting them into the cache). - void removeTiles(); + void startRender(PaintParameters&); + void finishRender(PaintParameters&); - // Remove all tiles and clear the cache. - void invalidateTiles(); - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles(); - - void startRender(const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&); - void finishRender(Painter&); - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles(); + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles(); std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions& options) const; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const; @@ -71,12 +63,10 @@ public: bool enabled = false; - void removeStaleTiles(const std::set<OverscaledTileID>&); - std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles; TileCache cache; - std::map<UnwrappedTileID, RenderTile> renderTiles; + std::vector<RenderTile> renderTiles; TileObserver* observer = nullptr; }; diff --git a/src/mbgl/renderer/cascade_parameters.hpp b/src/mbgl/renderer/transition_parameters.hpp index 4096cc5a6b..c47aa2e35f 100644 --- a/src/mbgl/renderer/cascade_parameters.hpp +++ b/src/mbgl/renderer/transition_parameters.hpp @@ -1,16 +1,14 @@ #pragma once #include <mbgl/util/chrono.hpp> -#include <mbgl/style/class_dictionary.hpp> #include <mbgl/style/transition_options.hpp> #include <vector> namespace mbgl { -class CascadeParameters { +class TransitionParameters { public: - std::vector<style::ClassID> classes; TimePoint now; style::TransitionOptions transition; }; diff --git a/src/mbgl/renderer/transitioning_property.hpp b/src/mbgl/renderer/transitioning_property.hpp deleted file mode 100644 index c211ccf116..0000000000 --- a/src/mbgl/renderer/transitioning_property.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include <mbgl/style/property_value.hpp> -#include <mbgl/style/data_driven_property_value.hpp> -#include <mbgl/style/transition_options.hpp> -#include <mbgl/util/interpolate.hpp> - -#include <utility> - -namespace mbgl { - -template <class Value> -class TransitioningProperty { -public: - TransitioningProperty() = default; - - TransitioningProperty(Value value_, - TransitioningProperty<Value> prior_, - style::TransitionOptions transition, - TimePoint now) - : begin(now + transition.delay.value_or(Duration::zero())), - end(begin + transition.duration.value_or(Duration::zero())), - value(std::move(value_)) { - if (transition.isDefined()) { - prior = { std::move(prior_) }; - } - } - - template <class Evaluator> - auto evaluate(const Evaluator& evaluator, TimePoint now) { - auto finalValue = value.evaluate(evaluator); - if (!prior) { - // No prior value. - return finalValue; - } else if (now >= end) { - // Transition from prior value is now complete. - prior = {}; - return finalValue; - } else if (value.isDataDriven()) { - // Transitions to data-driven properties are not supported. - // We snap immediately to the data-driven value so that, when we perform layout, - // we see the data-driven function and can use it to populate vertex buffers. - prior = {}; - return finalValue; - } else if (now < begin) { - // Transition hasn't started yet. - return prior->get().evaluate(evaluator, now); - } else { - // Interpolate between recursively-calculated prior value and final. - float t = std::chrono::duration<float>(now - begin) / (end - begin); - return util::interpolate(prior->get().evaluate(evaluator, now), finalValue, - util::DEFAULT_TRANSITION_EASE.solve(t, 0.001)); - } - } - - bool hasTransition() const { - return bool(prior); - } - - bool isUndefined() const { - return value.isUndefined(); - } - - const Value& getValue() const { - return value; - } - -private: - optional<mapbox::util::recursive_wrapper<TransitioningProperty<Value>>> prior; - TimePoint begin; - TimePoint end; - Value value; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp index ae54ac09e7..b54abc050d 100644 --- a/src/mbgl/renderer/update_parameters.hpp +++ b/src/mbgl/renderer/update_parameters.hpp @@ -1,26 +1,43 @@ #pragma once #include <mbgl/map/mode.hpp> -#include <mbgl/map/update.hpp> +#include <mbgl/map/transform_state.hpp> +#include <mbgl/style/light.hpp> +#include <mbgl/style/image.hpp> +#include <mbgl/style/source.hpp> +#include <mbgl/style/layer.hpp> +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/immutable.hpp> + +#include <vector> namespace mbgl { -class TransformState; -class Scheduler; -class FileSource; class AnnotationManager; class UpdateParameters { public: + const bool styleLoaded; const MapMode mode; - const Update updateFlags; const float pixelRatio; const MapDebugOptions debugOptions; const TimePoint timePoint; - const TransformState& transformState; - Scheduler& scheduler; - FileSource& fileSource; + const TransformState transformState; + + const std::string glyphURL; + const bool spriteLoaded; + const style::TransitionOptions transitionOptions; + const Immutable<style::Light::Impl> light; + const Immutable<std::vector<Immutable<style::Image::Impl>>> images; + const Immutable<std::vector<Immutable<style::Source::Impl>>> sources; + const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers; + AnnotationManager& annotationManager; + + const uint8_t prefetchZoomDelta; + + // For still image requests, render requested + const bool stillImageRequest; }; } // namespace mbgl |