diff options
Diffstat (limited to 'src/mbgl/renderer')
85 files changed, 3332 insertions, 2677 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/buckets/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp index 8b5743d500..d23f0861f4 100644 --- a/src/mbgl/renderer/buckets/circle_bucket.cpp +++ b/src/mbgl/renderer/buckets/circle_bucket.cpp @@ -1,6 +1,5 @@ #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/layers/render_circle_layer.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(); } @@ -57,7 +49,7 @@ void CircleBucket::addFeature(const GeometryTileFeature& feature, // Do not include points that are outside the tile boundaries. // Include all points in Still mode. You need to include points from // neighbouring tiles so that they are not clipped at tile boundaries. - if ((mode != MapMode::Still) && + if ((mode == MapMode::Continuous) && (x < 0 || x >= util::EXTENT || y < 0 || y >= util::EXTENT)) continue; if (segments.empty() || segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { diff --git a/src/mbgl/renderer/buckets/circle_bucket.hpp b/src/mbgl/renderer/buckets/circle_bucket.hpp index b048fd7675..78b6351bcb 100644 --- a/src/mbgl/renderer/buckets/circle_bucket.hpp +++ b/src/mbgl/renderer/buckets/circle_bucket.hpp @@ -5,7 +5,7 @@ #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> -#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/segment.hpp> #include <mbgl/programs/circle_program.hpp> #include <mbgl/style/layers/circle_layer_properties.hpp> @@ -23,13 +23,11 @@ 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; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<CircleAttributes> segments; + SegmentVector<CircleAttributes> segments; optional<gl::VertexBuffer<CircleLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; diff --git a/src/mbgl/renderer/buckets/debug_bucket.cpp b/src/mbgl/renderer/buckets/debug_bucket.cpp index acfe15d2fb..53c751c443 100644 --- a/src/mbgl/renderer/buckets/debug_bucket.cpp +++ b/src/mbgl/renderer/buckets/debug_bucket.cpp @@ -1,7 +1,7 @@ #include <mbgl/renderer/buckets/debug_bucket.hpp> -#include <mbgl/renderer/painter.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/buckets/debug_bucket.hpp b/src/mbgl/renderer/buckets/debug_bucket.hpp index 756e58a6de..fc3128e944 100644 --- a/src/mbgl/renderer/buckets/debug_bucket.hpp +++ b/src/mbgl/renderer/buckets/debug_bucket.hpp @@ -33,7 +33,7 @@ public: const optional<Timestamp> expires; const MapDebugOptions debugMode; - gl::SegmentVector<DebugAttributes> segments; + SegmentVector<DebugAttributes> segments; optional<gl::VertexBuffer<DebugLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Lines>> indexBuffer; }; diff --git a/src/mbgl/renderer/buckets/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp index 042d7b7506..110db887a1 100644 --- a/src/mbgl/renderer/buckets/fill_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_bucket.cpp @@ -1,5 +1,4 @@ #include <mbgl/renderer/buckets/fill_bucket.hpp> -#include <mbgl/renderer/painter.hpp> #include <mbgl/programs/fill_program.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/fill_layer_impl.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/buckets/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp index 421d8b332b..a50e1971f5 100644 --- a/src/mbgl/renderer/buckets/fill_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_bucket.hpp @@ -4,7 +4,7 @@ #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> -#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/segment.hpp> #include <mbgl/programs/fill_program.hpp> #include <mbgl/style/layers/fill_layer_properties.hpp> @@ -23,15 +23,14 @@ 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; gl::VertexVector<FillLayoutVertex> vertices; gl::IndexVector<gl::Lines> lines; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<FillAttributes> lineSegments; - gl::SegmentVector<FillAttributes> triangleSegments; + SegmentVector<FillAttributes> lineSegments; + SegmentVector<FillAttributes> triangleSegments; optional<gl::VertexBuffer<FillLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Lines>> lineIndexBuffer; diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp index f61f1d1549..5e2c937091 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -1,5 +1,4 @@ #include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp> -#include <mbgl/renderer/painter.hpp> #include <mbgl/programs/fill_extrusion_program.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> @@ -102,13 +101,17 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, const auto d2 = convertPoint<double>(p2); const Point<double> perp = util::unit(util::perp(d1 - d2)); + const auto dist = util::dist<int16_t>(d1, d2); + if (dist > std::numeric_limits<int16_t>::max()) { + edgeDistance = 0; + } vertices.emplace_back( FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 0, edgeDistance)); vertices.emplace_back( FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 1, edgeDistance)); - edgeDistance += util::dist<int16_t>(d1, d2); + edgeDistance += dist; vertices.emplace_back( FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 0, edgeDistance)); @@ -154,13 +157,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/buckets/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp index c54805d743..d57265ab16 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp @@ -4,7 +4,7 @@ #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> -#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/segment.hpp> #include <mbgl/programs/fill_extrusion_program.hpp> #include <mbgl/style/layers/fill_extrusion_layer_properties.hpp> @@ -21,13 +21,12 @@ 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; gl::VertexVector<FillExtrusionLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<FillExtrusionAttributes> triangleSegments; + SegmentVector<FillExtrusionAttributes> triangleSegments; optional<gl::VertexBuffer<FillExtrusionLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp index 3af3cd63d3..a96518df38 100644 --- a/src/mbgl/renderer/buckets/line_bucket.cpp +++ b/src/mbgl/renderer/buckets/line_bucket.cpp @@ -1,5 +1,4 @@ #include <mbgl/renderer/buckets/line_bucket.hpp> -#include <mbgl/renderer/painter.hpp> #include <mbgl/renderer/layers/render_line_layer.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/line_layer_impl.hpp> @@ -16,7 +15,8 @@ LineBucket::LineBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers, 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)); @@ -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) { @@ -398,7 +401,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, Point<double> extrude = normal; if (endLeft) extrude = extrude - (util::perp(normal) * endLeft); - vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, false }, endLeft, distance * LINE_DISTANCE_SCALE)); + vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, false, endLeft, distance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -409,7 +412,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, extrude = normal * -1.0; if (endRight) extrude = extrude - (util::perp(normal) * endRight); - vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, true }, -endRight, distance * LINE_DISTANCE_SCALE)); + vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, true, -endRight, distance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -434,7 +437,7 @@ void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex, std::size_t startVertex, std::vector<TriangleElement>& triangleStore) { Point<double> flippedExtrude = extrude * (lineTurnsLeft ? -1.0 : 1.0); - vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, { false, lineTurnsLeft }, 0, distance * LINE_DISTANCE_SCALE)); + vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, false, lineTurnsLeft, 0, distance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -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(); } diff --git a/src/mbgl/renderer/buckets/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp index 34d8935953..4fb77c377e 100644 --- a/src/mbgl/renderer/buckets/line_bucket.hpp +++ b/src/mbgl/renderer/buckets/line_bucket.hpp @@ -4,7 +4,7 @@ #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> -#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/segment.hpp> #include <mbgl/programs/line_program.hpp> #include <mbgl/style/layers/line_layer_properties.hpp> @@ -26,7 +26,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; @@ -34,7 +33,7 @@ public: gl::VertexVector<LineLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<LineAttributes> segments; + SegmentVector<LineAttributes> segments; optional<gl::VertexBuffer<LineLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; @@ -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 index 49ec0065c3..a66dd42d74 100644 --- a/src/mbgl/renderer/buckets/raster_bucket.cpp +++ b/src/mbgl/renderer/buckets/raster_bucket.cpp @@ -1,22 +1,28 @@ #include <mbgl/renderer/buckets/raster_bucket.hpp> #include <mbgl/renderer/layers/render_raster_layer.hpp> #include <mbgl/programs/raster_program.hpp> -#include <mbgl/renderer/painter.hpp> #include <mbgl/gl/context.hpp> -#include <mbgl/renderer/render_tile.hpp> namespace mbgl { using namespace style; -RasterBucket::RasterBucket(UnassociatedImage&& image_) : image(std::move(image_)) { +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); + texture = context.createTexture(*image); } - if (!vertices.empty()) { + if (!segments.empty()) { vertexBuffer = context.createVertexBuffer(std::move(vertices)); indexBuffer = context.createIndexBuffer(std::move(indices)); } @@ -32,22 +38,73 @@ void RasterBucket::clear() { uploaded = false; } -void RasterBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), tile.matrix, false); + +void RasterBucket::setImage(std::shared_ptr<PremultipliedImage> image_) { + image = std::move(image_); + texture = {}; + uploaded = false; } -void RasterBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const mat4& matrix) { - painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), matrix, true); +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 true; + return !!image; } } // namespace mbgl diff --git a/src/mbgl/renderer/buckets/raster_bucket.hpp b/src/mbgl/renderer/buckets/raster_bucket.hpp index b5cf7997d5..3800eadec8 100644 --- a/src/mbgl/renderer/buckets/raster_bucket.hpp +++ b/src/mbgl/renderer/buckets/raster_bucket.hpp @@ -5,6 +5,7 @@ #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> @@ -13,25 +14,25 @@ namespace mbgl { class RasterBucket : public Bucket { public: - RasterBucket(UnassociatedImage&&); + RasterBucket(PremultipliedImage&&); + RasterBucket(std::shared_ptr<PremultipliedImage>); void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; - void render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const mat4& matrix); bool hasData() const override; void clear(); - UnassociatedImage image; + 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; - gl::SegmentVector<RasterAttributes> segments; + SegmentVector<RasterAttributes> segments; optional<gl::VertexBuffer<RasterLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index cbddade899..60e8a0b504 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -1,5 +1,4 @@ #include <mbgl/renderer/buckets/symbol_bucket.hpp> -#include <mbgl/renderer/painter.hpp> #include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> @@ -17,10 +16,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo const style::DataDrivenPropertyValue<float>& iconSize, float zoom, bool sdfIcons_, - bool iconsNeedLinear_) + bool iconsNeedLinear_, + bool sortFeaturesByY_, + const std::vector<SymbolInstance>&& symbolInstances_) : layout(std::move(layout_)), sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()), + sortFeaturesByY(sortFeaturesByY_), + symbolInstances(std::move(symbolInstances_)), textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())), iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) { @@ -37,40 +40,88 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo void SymbolBucket::upload(gl::Context& context) { if (hasTextData()) { - text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); - text.indexBuffer = context.createIndexBuffer(std::move(text.triangles)); - textSizeBinder->upload(context); + if (!staticUploaded) { + text.indexBuffer = context.createIndexBuffer(std::move(text.triangles), sortFeaturesByY ? gl::BufferUsage::StreamDraw : gl::BufferUsage::StaticDraw); + text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); + } else if (!sortUploaded) { + context.updateIndexBuffer(*text.indexBuffer, std::move(text.triangles)); + } + + if (!dynamicUploaded) { + text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw); + } + if (!placementChangesUploaded) { + if (!text.opacityVertexBuffer) { + text.opacityVertexBuffer = context.createVertexBuffer(std::move(text.opacityVertices), gl::BufferUsage::StreamDraw); + } else { + context.updateVertexBuffer(*text.opacityVertexBuffer, std::move(text.opacityVertices)); + } + } } if (hasIconData()) { - icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); - icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); - iconSizeBinder->upload(context); + if (!staticUploaded) { + icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles), sortFeaturesByY ? gl::BufferUsage::StreamDraw : gl::BufferUsage::StaticDraw); + icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); + } else if (!sortUploaded) { + context.updateIndexBuffer(*icon.indexBuffer, std::move(icon.triangles)); + } + if (!dynamicUploaded) { + icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw); + } + if (!placementChangesUploaded) { + if (!icon.opacityVertexBuffer) { + icon.opacityVertexBuffer = context.createVertexBuffer(std::move(icon.opacityVertices), gl::BufferUsage::StreamDraw); + } else { + context.updateVertexBuffer(*icon.opacityVertexBuffer, std::move(icon.opacityVertices)); + } + } } - if (!collisionBox.vertices.empty()) { - collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices)); - collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines)); + if (hasCollisionBoxData()) { + if (!staticUploaded) { + collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines)); + collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices)); + } + if (!placementChangesUploaded) { + if (!collisionBox.dynamicVertexBuffer) { + collisionBox.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionBox.dynamicVertices), gl::BufferUsage::StreamDraw); + } else { + context.updateVertexBuffer(*collisionBox.dynamicVertexBuffer, std::move(collisionBox.dynamicVertices)); + } + } } - - for (auto& pair : paintPropertyBinders) { - pair.second.first.upload(context); - pair.second.second.upload(context); + + if (hasCollisionCircleData()) { + if (!staticUploaded) { + collisionCircle.indexBuffer = context.createIndexBuffer(std::move(collisionCircle.triangles)); + collisionCircle.vertexBuffer = context.createVertexBuffer(std::move(collisionCircle.vertices)); + } + if (!placementChangesUploaded) { + if (!collisionCircle.dynamicVertexBuffer) { + collisionCircle.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionCircle.dynamicVertices), gl::BufferUsage::StreamDraw); + } else { + context.updateVertexBuffer(*collisionCircle.dynamicVertexBuffer, std::move(collisionCircle.dynamicVertices)); + } + } } + if (!staticUploaded) { + for (auto& pair : paintPropertyBinders) { + pair.second.first.upload(context); + pair.second.second.upload(context); + } + } + uploaded = true; -} - -void SymbolBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderSymbol(parameters, *this, *layer.as<RenderSymbolLayer>(), tile); + staticUploaded = true; + placementChangesUploaded = true; + dynamicUploaded = true; + sortUploaded = true; } 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 { @@ -85,4 +136,83 @@ bool SymbolBucket::hasCollisionBoxData() const { return !collisionBox.segments.empty(); } +bool SymbolBucket::hasCollisionCircleData() const { + return !collisionCircle.segments.empty(); +} + +void SymbolBucket::updateOpacity() { + placementChangesUploaded = false; + uploaded = false; +} + +void addPlacedSymbol(gl::IndexVector<gl::Triangles>& triangles, const PlacedSymbol& placedSymbol) { + auto endIndex = placedSymbol.vertexStartIndex + placedSymbol.glyphOffsets.size() * 4; + for (auto vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) { + triangles.emplace_back(vertexIndex + 0, vertexIndex + 1, vertexIndex + 2); + triangles.emplace_back(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3); + } +} + +void SymbolBucket::sortFeatures(const float angle) { + if (!sortFeaturesByY) { + return; + } + + if (sortedAngle && *sortedAngle == angle) { + return; + } + + sortedAngle = angle; + + // The current approach to sorting doesn't sort across segments so don't try. + // Sorting within segments separately seemed not to be worth the complexity. + if (text.segments.size() > 1 || icon.segments.size() > 1) { + return; + } + + sortUploaded = false; + uploaded = false; + + // If the symbols are allowed to overlap sort them by their vertical screen position. + // The index array buffer is rewritten to reference the (unchanged) vertices in the + // sorted order. + + // To avoid sorting the actual symbolInstance array we sort an array of indexes. + std::vector<size_t> symbolInstanceIndexes; + symbolInstanceIndexes.reserve(symbolInstances.size()); + for (size_t i = 0; i < symbolInstances.size(); i++) { + symbolInstanceIndexes.push_back(i); + } + + const float sin = std::sin(angle); + const float cos = std::cos(angle); + + std::sort(symbolInstanceIndexes.begin(), symbolInstanceIndexes.end(), [sin, cos, this](size_t &aIndex, size_t &bIndex) { + const SymbolInstance& a = symbolInstances[aIndex]; + const SymbolInstance& b = symbolInstances[bIndex]; + const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y; + const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y; + return aRotated != bRotated ? + aRotated < bRotated : + a.index > b.index; + }); + + text.triangles.clear(); + icon.triangles.clear(); + + for (auto i : symbolInstanceIndexes) { + const SymbolInstance& symbolInstance = symbolInstances[i]; + + if (symbolInstance.placedTextIndex) { + addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]); + } + if (symbolInstance.placedVerticalTextIndex) { + addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]); + } + if (symbolInstance.placedIconIndex) { + addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]); + } + } +} + } // namespace mbgl diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index 002b6e28b3..4abea90508 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -4,17 +4,39 @@ #include <mbgl/map/mode.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> -#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/segment.hpp> #include <mbgl/programs/symbol_program.hpp> #include <mbgl/programs/collision_box_program.hpp> #include <mbgl/text/glyph_range.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> #include <mbgl/layout/symbol_feature.hpp> +#include <mbgl/layout/symbol_instance.hpp> #include <vector> namespace mbgl { +class PlacedSymbol { +public: + PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_, + std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) : + anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_), + lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0) + { + } + Point<float> anchorPoint; + uint16_t segment; + float lowerSize; + float upperSize; + std::array<float, 2> lineOffset; + WritingModeType writingModes; + GeometryCoordinates line; + std::vector<float> tileDistances; + std::vector<float> glyphOffsets; + bool hidden; + size_t vertexStartIndex; +}; + class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated, @@ -23,18 +45,33 @@ public: const style::DataDrivenPropertyValue<float>& iconSize, float zoom, bool sdfIcons, - bool iconsNeedLinear); + bool iconsNeedLinear, + bool sortFeaturesByY, + const std::vector<SymbolInstance>&&); void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; bool hasData() const override; bool hasTextData() const; bool hasIconData() const; bool hasCollisionBoxData() const; + bool hasCollisionCircleData() const; + + void updateOpacity(); + void sortFeatures(const float angle); const style::SymbolLayoutProperties::PossiblyEvaluated layout; const bool sdfIcons; const bool iconsNeedLinear; + const bool sortFeaturesByY; + + optional<float> sortedAngle; + + bool staticUploaded = false; + bool placementChangesUploaded = false; + bool dynamicUploaded = false; + bool sortUploaded = false; + + std::vector<SymbolInstance> symbolInstances; std::map<std::string, std::pair< SymbolIconProgram::PaintPropertyBinders, @@ -44,10 +81,15 @@ public: struct TextBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices; + gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<SymbolTextAttributes> segments; + SegmentVector<SymbolTextAttributes> segments; + std::vector<PlacedSymbol> placedSymbols; optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer; + optional<gl::VertexBuffer<SymbolOpacityAttributes::Vertex>> opacityVertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } text; @@ -55,22 +97,39 @@ public: struct IconBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices; + gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<SymbolIconAttributes> segments; + SegmentVector<SymbolIconAttributes> segments; + std::vector<PlacedSymbol> placedSymbols; PremultipliedImage atlasImage; optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer; + optional<gl::VertexBuffer<SymbolOpacityAttributes::Vertex>> opacityVertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } icon; - struct CollisionBoxBuffer { - gl::VertexVector<CollisionBoxVertex> vertices; - gl::IndexVector<gl::Lines> lines; - gl::SegmentVector<CollisionBoxAttributes> segments; + struct CollisionBuffer { + gl::VertexVector<CollisionBoxLayoutAttributes::Vertex> vertices; + gl::VertexVector<CollisionBoxDynamicAttributes::Vertex> dynamicVertices; + SegmentVector<CollisionBoxProgram::Attributes> segments; + + optional<gl::VertexBuffer<CollisionBoxLayoutAttributes::Vertex>> vertexBuffer; + optional<gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>> dynamicVertexBuffer; + }; - optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer; + struct CollisionBoxBuffer : public CollisionBuffer { + gl::IndexVector<gl::Lines> lines; optional<gl::IndexBuffer<gl::Lines>> indexBuffer; } collisionBox; + + struct CollisionCircleBuffer : public CollisionBuffer { + gl::IndexVector<gl::Triangles> triangles; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + } collisionCircle; + + uint32_t bucketInstanceId = 0; }; } // 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/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp deleted file mode 100644 index 869222b4eb..0000000000 --- a/src/mbgl/renderer/frame_history.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include <mbgl/renderer/frame_history.hpp> -#include <mbgl/math/minmax.hpp> -#include <mbgl/gl/context.hpp> - -#include <cassert> - -namespace mbgl { - -FrameHistory::FrameHistory() { - changeOpacities.fill(0); - opacities.fill(0); -} - -void FrameHistory::record(const TimePoint& now, float zoom, const Duration& duration) { - - int16_t zoomIndex = std::floor(zoom * 10.0); - - if (firstFrame) { - changeTimes.fill(now); - - for (int16_t z = 0; z <= zoomIndex; z++) { - opacities.data[z] = 255u; - } - firstFrame = false; - } - - if (zoomIndex < previousZoomIndex) { - for (int16_t z = zoomIndex + 1; z <= previousZoomIndex; z++) { - changeTimes[z] = now; - changeOpacities[z] = opacities.data[z]; - } - } else { - for (int16_t z = zoomIndex; z > previousZoomIndex; z--) { - changeTimes[z] = now; - changeOpacities[z] = opacities.data[z]; - } - } - - for (int16_t z = 0; z <= 255; z++) { - 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) { - opacities.data[z] = opacity; - dirty = true; - } - } - - if (zoomIndex != previousZoomIndex) { - previousZoomIndex = zoomIndex; - previousTime = now; - } - - time = now; -} - -bool FrameHistory::needsAnimation(const Duration& duration) const { - return (time - previousTime) < duration; -} - -void FrameHistory::upload(gl::Context& context, uint32_t unit) { - if (!texture) { - texture = context.createTexture(opacities, unit); - } else if (dirty) { - context.updateTexture(*texture, opacities, unit); - } - dirty = false; -} - -void FrameHistory::bind(gl::Context& context, uint32_t unit) { - upload(context, unit); - context.bindTexture(*texture, unit); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp deleted file mode 100644 index f2b11f5f41..0000000000 --- a/src/mbgl/renderer/frame_history.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <array> - -#include <mbgl/util/platform.hpp> -#include <mbgl/gl/texture.hpp> -#include <mbgl/util/chrono.hpp> -#include <mbgl/util/image.hpp> -#include <mbgl/util/optional.hpp> - -namespace mbgl { - -namespace gl { -class Context; -} // namespace gl - -class FrameHistory { -public: - FrameHistory(); - void record(const TimePoint&, float zoom, const Duration&); - - bool needsAnimation(const Duration&) const; - void bind(gl::Context&, uint32_t); - void upload(gl::Context&, uint32_t); - -private: - std::array<TimePoint, 256> changeTimes; - std::array<uint8_t, 256> changeOpacities; - AlphaImage opacities{ { 256, 1 } }; - - int16_t previousZoomIndex = 0; - TimePoint previousTime; - TimePoint time; - bool firstFrame = true; - bool dirty = true; - - mbgl::optional<gl::Texture> texture; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp index d0a106ede6..2ef6be0c4f 100644 --- a/src/mbgl/renderer/image_manager.cpp +++ b/src/mbgl/renderer/image_manager.cpp @@ -4,12 +4,23 @@ namespace mbgl { -void ImageManager::onSpriteLoaded() { - loaded = true; - for (const auto& entry : requestors) { - notify(*entry.first, entry.second); +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(); } - requestors.clear(); +} + +bool ImageManager::isLoaded() const { + return loaded; } void ImageManager::addImage(Immutable<style::Image::Impl> image_) { @@ -28,6 +39,13 @@ void ImageManager::removeImage(const std::string& 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); } @@ -41,23 +59,23 @@ const style::Image::Impl* ImageManager::getImage(const std::string& id) const { return nullptr; } -void ImageManager::getImages(ImageRequestor& requestor, ImageDependencies dependencies) { +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 : dependencies) { + for (const auto& dependency : pair.first) { if (images.find(dependency) == images.end()) { hasAllDependencies = false; } } } if (isLoaded() || hasAllDependencies) { - notify(requestor, dependencies); + notify(requestor, std::move(pair)); } else { - requestors.emplace(&requestor, std::move(dependencies)); + requestors.emplace(&requestor, std::move(pair)); } } @@ -65,17 +83,17 @@ void ImageManager::removeRequestor(ImageRequestor& requestor) { requestors.erase(&requestor); } -void ImageManager::notify(ImageRequestor& requestor, const ImageDependencies& dependencies) const { +void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const { ImageMap response; - for (const auto& dependency : dependencies) { + for (const auto& dependency : pair.first) { auto it = images.find(dependency); if (it != images.end()) { response.emplace(*it); } } - requestor.onImagesAvailable(response); + requestor.onImagesAvailable(response, pair.second); } void ImageManager::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp index 9a9a4ce997..f72ba9fb53 100644 --- a/src/mbgl/renderer/image_manager.hpp +++ b/src/mbgl/renderer/image_manager.hpp @@ -21,7 +21,7 @@ class Context; class ImageRequestor { public: virtual ~ImageRequestor() = default; - virtual void onImagesAvailable(ImageMap) = 0; + virtual void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) = 0; }; /* @@ -39,11 +39,8 @@ public: ImageManager(); ~ImageManager(); - void onSpriteLoaded(); - - bool isLoaded() const { - return loaded; - } + void setLoaded(bool); + bool isLoaded() const; void dumpDebugLogs() const; @@ -53,15 +50,15 @@ public: void updateImage(Immutable<style::Image::Impl>); void removeImage(const std::string&); - void getImages(ImageRequestor&, ImageDependencies); + void getImages(ImageRequestor&, ImageRequestPair&&); void removeRequestor(ImageRequestor&); private: - void notify(ImageRequestor&, const ImageDependencies&) const; + void notify(ImageRequestor&, const ImageRequestPair&) const; bool loaded = false; - std::unordered_map<ImageRequestor*, ImageDependencies> requestors; + std::unordered_map<ImageRequestor*, ImageRequestPair> requestors; ImageMap images; // Pattern stuff diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index 702977cd23..9fddba3f74 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -1,9 +1,17 @@ #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()) { @@ -34,4 +42,74 @@ 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/layers/render_background_layer.hpp b/src/mbgl/renderer/layers/render_background_layer.hpp index 0fba3d2bb1..a619670ee4 100644 --- a/src/mbgl/renderer/layers/render_background_layer.hpp +++ b/src/mbgl/renderer/layers/render_background_layer.hpp @@ -14,6 +14,7 @@ public: 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; diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp index 51a5ecd7d6..fe2e7cd42d 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.cpp +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -1,5 +1,10 @@ #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> @@ -7,6 +12,8 @@ namespace mbgl { +using namespace style; + RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl) : RenderLayer(style::LayerType::Circle, _impl), unevaluated(impl().paint.untransitioned()) { @@ -40,6 +47,52 @@ 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::Continuous + ? 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, diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp index 4ae7399ad1..f31715f98f 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.hpp +++ b/src/mbgl/renderer/layers/render_circle_layer.hpp @@ -14,6 +14,7 @@ public: void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp index 4d6084075d..7ece3970da 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.cpp +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -1,9 +1,10 @@ #include <mbgl/renderer/layers/render_custom_layer.hpp> -#include <mbgl/renderer/painter.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> -#include <mbgl/map/backend_scope.hpp> namespace mbgl { @@ -15,8 +16,12 @@ RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl) RenderCustomLayer::~RenderCustomLayer() { assert(BackendScope::exists()); - if (initialized && impl().deinitializeFn) { - impl().deinitializeFn(impl().context); + if (initialized) { + if (contextDestroyed && impl().contextLostFn ) { + impl().contextLostFn(impl().context); + } else if (!contextDestroyed && impl().deinitializeFn) { + impl().deinitializeFn(impl().context); + } } } @@ -37,21 +42,21 @@ std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, return nullptr; } -void RenderCustomLayer::render(Painter& painter, PaintParameters& paintParameters, RenderSource*) { +void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) { if (!initialized) { assert(impl().initializeFn); impl().initializeFn(impl().context); initialized = true; } - gl::Context& context = painter.context; - const TransformState& state = painter.state; + 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.vertexArrayObject = 0; - context.setDepthMode(painter.depthModeForSublayer(0, gl::DepthMode::ReadOnly)); + context.bindVertexArray = 0; + context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)); context.setStencilMode(gl::StencilMode::disabled()); - context.setColorMode(painter.colorModeForRenderPass()); + context.setColorMode(paintParameters.colorModeForRenderPass()); CustomLayerRenderParameters parameters; @@ -69,7 +74,7 @@ void RenderCustomLayer::render(Painter& painter, PaintParameters& paintParameter // Reset the view back to our original one, just in case the CustomLayer changed // the viewport or Framebuffer. - paintParameters.view.bind(); + paintParameters.backend.bind(); context.setDirtyState(); } diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp index dd52d315cf..32ed9da8da 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.hpp +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -15,12 +15,17 @@ public: bool hasTransition() const override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const final; - void render(Painter&, PaintParameters&, RenderSource*) final; + void render(PaintParameters&, RenderSource*) final; const style::CustomLayer::Impl& impl() const; + void markContextDestroyed() { + contextDestroyed = true; + }; + private: bool initialized = false; + bool contextDestroyed = false; }; template <> diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index cd69316670..fbd6160e8a 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -1,5 +1,13 @@ #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> @@ -7,6 +15,8 @@ namespace mbgl { +using namespace style; + RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl) : RenderLayer(style::LayerType::FillExtrusion, _impl), unevaluated(impl().paint.untransitioned()) { @@ -27,14 +37,116 @@ void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) { evaluated = unevaluated.evaluate(parameters); - passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent - : RenderPass::None; + 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, diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp index 1a55b56836..838494cf91 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp @@ -3,6 +3,8 @@ #include <mbgl/renderer/render_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 { @@ -14,6 +16,7 @@ public: void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,6 +32,8 @@ public: style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated; const style::FillExtrusionLayer::Impl& impl() const; + + optional<OffscreenTexture> renderTexture; }; template <> diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index f1c7e97067..22cb9563c1 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -1,5 +1,11 @@ #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> @@ -7,6 +13,8 @@ namespace mbgl { +using namespace style; + RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl) : RenderLayer(style::LayerType::Fill, _impl), unevaluated(impl().paint.untransitioned()) { @@ -50,6 +58,132 @@ 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, diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp index 1960fb653f..a51865698f 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_layer.hpp @@ -14,6 +14,7 @@ public: void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index c27e5ea990..1b4a1c0ff7 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -1,5 +1,12 @@ #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> @@ -7,6 +14,8 @@ namespace mbgl { +using namespace style; + RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl) : RenderLayer(style::LayerType::Line, _impl), unevaluated(impl().paint.untransitioned()) { @@ -43,6 +52,82 @@ 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 {}; diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 77551b6b7c..8bf7e2329d 100644 --- a/src/mbgl/renderer/layers/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -23,6 +23,7 @@ public: void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp index 84e27a3895..06616d90e5 100644 --- a/src/mbgl/renderer/layers/render_raster_layer.cpp +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -1,14 +1,18 @@ #include <mbgl/renderer/layers/render_raster_layer.hpp> -#include <mbgl/renderer/bucket.hpp> -#include <mbgl/style/layers/raster_layer_impl.hpp> -#include <mbgl/gl/context.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> #include <mbgl/renderer/render_tile.hpp> -#include <mbgl/tile/tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/renderer/sources/render_image_source.hpp> -#include <mbgl/renderer/painter.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()) { @@ -37,12 +41,113 @@ bool RenderRasterLayer::hasTransition() const { return unevaluated.hasTransition(); } -void RenderRasterLayer::render(Painter& painter, PaintParameters& parameters, RenderSource* source) { - RenderLayer::render(painter, parameters, source); - if (renderTiles.empty()) { - RenderImageSource* imageSource = source->as<RenderImageSource>(); - if (imageSource) { - imageSource->render(painter, parameters, *this); +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); + } } } } diff --git a/src/mbgl/renderer/layers/render_raster_layer.hpp b/src/mbgl/renderer/layers/render_raster_layer.hpp index ce46152a95..87de316f7c 100644 --- a/src/mbgl/renderer/layers/render_raster_layer.hpp +++ b/src/mbgl/renderer/layers/render_raster_layer.hpp @@ -15,7 +15,7 @@ public: void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; - void render(Painter&, PaintParameters&, RenderSource*) override; + void render(PaintParameters&, RenderSource*) override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 6540fc9612..04fcb2c3ab 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -1,13 +1,27 @@ #include <mbgl/renderer/layers/render_symbol_layer.hpp> -#include <mbgl/layout/symbol_layout.hpp> -#include <mbgl/renderer/bucket.hpp> +#include <mbgl/renderer/buckets/symbol_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/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.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()) { @@ -55,6 +69,224 @@ 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; + + auto draw = [&] (auto& program, + auto&& uniformValues, + const auto& buffers, + const auto& symbolSizeBinder, + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties) + { + program.get(paintProperties).draw( + parameters.context, + gl::Triangles(), + values_.pitchAlignment == AlignmentType::Map + ? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly) + : gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + std::move(uniformValues), + *buffers.vertexBuffer, + *buffers.dynamicVertexBuffer, + *buffers.opacityVertexBuffer, + *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.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, parameters.symbolFadeChange, 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, parameters.symbolFadeChange, 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, parameters.symbolFadeChange), + 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.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, parameters.symbolFadeChange, 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, parameters.symbolFadeChange, 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); + + auto pixelRatio = tile.id.pixelsToTileUnits(1, parameters.state.getZoom()); + auto scale = std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ)); + std::array<float,2> extrudeScale = + {{ + parameters.pixelsToGLUnits[0] / (pixelRatio * scale), + parameters.pixelsToGLUnits[1] / (pixelRatio * scale) + + }}; + parameters.programs.collisionBox.draw( + parameters.context, + gl::Lines { 1.0f }, + gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + CollisionBoxProgram::UniformValues { + uniforms::u_matrix::Value{ tile.matrix }, + uniforms::u_extrude_scale::Value{ extrudeScale }, + uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() } + }, + *bucket.collisionBox.vertexBuffer, + *bucket.collisionBox.dynamicVertexBuffer, + *bucket.collisionBox.indexBuffer, + bucket.collisionBox.segments, + paintAttributeData, + properties, + parameters.state.getZoom(), + getID() + ); + } + if (bucket.hasCollisionCircleData()) { + static const style::Properties<>::PossiblyEvaluated properties {}; + static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0); + + auto pixelRatio = tile.id.pixelsToTileUnits(1, parameters.state.getZoom()); + auto scale = std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ)); + std::array<float,2> extrudeScale = + {{ + parameters.pixelsToGLUnits[0] / (pixelRatio * scale), + parameters.pixelsToGLUnits[1] / (pixelRatio * scale) + + }}; + + parameters.programs.collisionCircle.draw( + parameters.context, + gl::Triangles(), + gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + CollisionBoxProgram::UniformValues { + uniforms::u_matrix::Value{ tile.matrix }, + uniforms::u_extrude_scale::Value{ extrudeScale }, + uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() } + }, + *bucket.collisionCircle.vertexBuffer, + *bucket.collisionCircle.dynamicVertexBuffer, + *bucket.collisionCircle.indexBuffer, + bucket.collisionCircle.segments, + paintAttributeData, + properties, + parameters.state.getZoom(), + getID() + ); + + } + } +} + style::IconPaintProperties::PossiblyEvaluated RenderSymbolLayer::iconPaintProperties() const { return style::IconPaintProperties::PossiblyEvaluated { evaluated.get<style::IconOpacity>(), @@ -82,25 +314,41 @@ style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProper 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::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 + 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 + evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0, + limitMaxDistance ? 1.5f : 10.0f }; } diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp index 2199103de2..83709b5122 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.hpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp @@ -40,6 +40,7 @@ public: // Layout AlignmentType pitchAlignment; AlignmentType rotationAlignment; + bool keepUpright; // Paint std::array<float, 2> translate; @@ -47,6 +48,8 @@ public: bool hasHalo; bool hasFill; + + float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else }; } // namespace style @@ -63,6 +66,7 @@ public: void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; style::IconPaintProperties::PossiblyEvaluated iconPaintProperties() const; style::TextPaintProperties::PossiblyEvaluated textPaintProperties() const; diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp new file mode 100644 index 0000000000..58dd5597a5 --- /dev/null +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -0,0 +1,96 @@ +#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_, + ImageManager& imageManager_, + LineAtlas& lineAtlas_) + : context(context_), + backend(backend_), + state(updateParameters.transformState), + evaluatedLight(evaluatedLight_), + staticData(staticData_), + 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..5c934c2239 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/renderer/mode.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 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&, + ImageManager&, + LineAtlas&); + + gl::Context& context; + RendererBackend& backend; + + const TransformState& state; + const EvaluatedLight& evaluatedLight; + + RenderStaticData& staticData; + 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); + + float symbolFadeChange; }; } // namespace mbgl diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp index f78147fc87..3a49882f12 100644 --- a/src/mbgl/renderer/paint_property_binder.hpp +++ b/src/mbgl/renderer/paint_property_binder.hpp @@ -3,6 +3,7 @@ #include <mbgl/programs/attributes.hpp> #include <mbgl/gl/attribute.hpp> #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> @@ -81,7 +82,7 @@ public: virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) = 0; virtual void upload(gl::Context& context) = 0; - virtual AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0; + virtual optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0; virtual float interpolationFactor(float currentZoom) const = 0; virtual T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0; @@ -103,8 +104,8 @@ public: void populateVertexVector(const GeometryTileFeature&, std::size_t) override {} void upload(gl::Context&) override {} - AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override { - return gl::DisabledAttribute(); + optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override { + return {}; } float interpolationFactor(float) const override { @@ -147,9 +148,9 @@ public: vertexBuffer = context.createVertexBuffer(std::move(vertexVector)); } - AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { + optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { if (currentValue.isConstant()) { - return gl::DisabledAttribute(); + return {}; } else { return Attribute::binding(*vertexBuffer, 0, BaseAttribute::Dimensions); } @@ -189,11 +190,11 @@ public: CompositeFunctionPaintPropertyBinder(style::CompositeFunction<T> function_, float zoom, T defaultValue_) : function(std::move(function_)), defaultValue(std::move(defaultValue_)), - rangeOfCoveringRanges(function.rangeOfCoveringRanges({zoom, zoom + 1})) { + zoomRange({zoom, zoom + 1}) { } void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override { - Range<T> range = function.evaluate(rangeOfCoveringRanges, feature, defaultValue); + Range<T> range = function.evaluate(zoomRange, feature, defaultValue); this->statistics.add(range.min); this->statistics.add(range.max); AttributeValue value = zoomInterpolatedAttributeValue( @@ -208,9 +209,9 @@ public: vertexBuffer = context.createVertexBuffer(std::move(vertexVector)); } - AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { + optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { if (currentValue.isConstant()) { - return gl::DisabledAttribute(); + return {}; } else { return Attribute::binding(*vertexBuffer, 0); } @@ -218,9 +219,9 @@ public: float interpolationFactor(float currentZoom) const override { if (function.useIntegerZoom) { - return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, std::floor(currentZoom)); + return function.interpolationFactor(zoomRange, std::floor(currentZoom)); } else { - return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom); + return function.interpolationFactor(zoomRange, currentZoom); } } @@ -236,8 +237,7 @@ public: private: style::CompositeFunction<T> function; T defaultValue; - using CoveringRanges = typename style::CompositeFunction<T>::CoveringRanges; - Range<CoveringRanges> rangeOfCoveringRanges; + Range<float> zoomRange; gl::VertexVector<Vertex> vertexVector; optional<gl::VertexBuffer<Vertex>> vertexBuffer; }; diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp deleted file mode 100644 index 47db8254e2..0000000000 --- a/src/mbgl/renderer/painter.cpp +++ /dev/null @@ -1,387 +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/renderer/render_style.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/layer_impl.hpp> -#include <mbgl/style/layers/custom_layer_impl.hpp> - -#include <mbgl/tile/tile.hpp> -#include <mbgl/renderer/layers/render_background_layer.hpp> -#include <mbgl/renderer/layers/render_custom_layer.hpp> -#include <mbgl/style/layers/custom_layer_impl.hpp> -#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> - -#include <mbgl/renderer/image_manager.hpp> -#include <mbgl/geometry/line_atlas.hpp> - -#include <mbgl/programs/program_parameters.hpp> -#include <mbgl/programs/programs.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_TRANSITION_DURATION); -} - -void Painter::cleanup() { - context.performCleanup(); -} - -void Painter::render(RenderStyle& style, const FrameData& frame_, View& view) { - frame = frame_; - if (frame.contextMode == GLContextMode::Shared) { - context.setDirtyState(); - } - - PaintParameters parameters { -#ifndef NDEBUG - paintMode() == PaintMode::Overdraw ? *overdrawPrograms : *programs, -#else - *programs, -#endif - view - }; - - imageManager = style.imageManager.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_TRANSITION_DURATION : Milliseconds(0)); - - - // - UPLOAD PASS ------------------------------------------------------------------------------- - // Uploads all required buffers and images before we do any actual rendering. - { - MBGL_DEBUG_GROUP(context, "upload"); - - imageManager->upload(context, 0); - lineAtlas->upload(context, 0); - frameHistory.upload(context, 0); - } - - // - 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. - clipIDGenerator = algorithm::ClipIDGenerator(); - for (const auto& source : sources) { - source->startRender(*this); - } - - MBGL_DEBUG_GROUP(context, "clipping masks"); - - for (const auto& stencil : clipIDGenerator.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.vertexArrayObject = 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<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, {}); - - renderItem(parameters, item); - - parameters.view.bind(); - context.bindTexture(extrusionTexture->getTexture()); - - mat4 viewportMat; - matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); - - const Properties<>::PossiblyEvaluated 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()); - } else { - renderItem(parameters, item); - } - } - - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}"); - } -} - -void Painter::renderItem(PaintParameters& parameters, const RenderItem& item) { - RenderLayer& layer = item.layer; - MBGL_DEBUG_GROUP(context, layer.getID()); - layer.render(*this, parameters, item.source); -} - -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 7e966adefa..0000000000 --- a/src/mbgl/renderer/painter.hpp +++ /dev/null @@ -1,181 +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/renderer/render_light.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/util/noncopyable.hpp> -#include <mbgl/util/chrono.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/offscreen_texture.hpp> - -#include <mbgl/algorithm/generate_clip_ids.hpp> - -#include <array> -#include <vector> -#include <set> -#include <map> - -namespace mbgl { - -class RenderStyle; -class RenderTile; -class ImageManager; -class View; -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; - -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(RenderStyle&, - const FrameData&, - View&); - - void cleanup(); - - void renderClippingMask(const UnwrappedTileID&, const ClipID&); - void renderTileDebug(const RenderTile&); - void renderTileDebug(const mat4& matrix); - 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 mat4&, bool useBucketBuffers /* = false */); - void renderBackground(PaintParameters&, const RenderBackgroundLayer&); - - void renderItem(PaintParameters&, const RenderItem&); - -#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; - - 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 - - gl::Context& context; - - algorithm::ClipIDGenerator clipIDGenerator; - - 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); - - ImageManager* imageManager = 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; - - gl::SegmentVector<FillAttributes> tileTriangleSegments; - gl::SegmentVector<DebugAttributes> tileBorderSegments; - gl::SegmentVector<RasterAttributes> rasterSegments; - gl::SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/painters/painter_background.cpp b/src/mbgl/renderer/painters/painter_background.cpp deleted file mode 100644 index 577d7d6cda..0000000000 --- a/src/mbgl/renderer/painters/painter_background.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/layers/render_background_layer.hpp> -#include <mbgl/renderer/image_manager.hpp> -#include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_program.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::PossiblyEvaluated& background = layer.evaluated; - - style::FillPaintProperties::PossiblyEvaluated 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<ImagePosition> imagePosA = imageManager->getPattern(background.get<BackgroundPattern>().from); - optional<ImagePosition> imagePosB = imageManager->getPattern(background.get<BackgroundPattern>().to); - - if (!imagePosA || !imagePosB) - return; - - imageManager->bind(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, - imageManager->getPixelSize(), - *imagePosA, - *imagePosB, - background.get<BackgroundPattern>(), - tileID, - state - ), - tileVertexBuffer, - quadTriangleIndexBuffer, - tileTriangleSegments, - paintAttibuteData, - properties, - state.getZoom() - ); - } - } 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() - ); - } - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painters/painter_circle.cpp b/src/mbgl/renderer/painters/painter_circle.cpp deleted file mode 100644 index 58e384979d..0000000000 --- a/src/mbgl/renderer/painters/painter_circle.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/buckets/circle_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/layers/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::PossiblyEvaluated& 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() - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painters/painter_clipping.cpp b/src/mbgl/renderer/painters/painter_clipping.cpp deleted file mode 100644 index cad092594e..0000000000 --- a/src/mbgl/renderer/painters/painter_clipping.cpp +++ /dev/null @@ -1,37 +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::PossiblyEvaluated 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() - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painters/painter_debug.cpp b/src/mbgl/renderer/painters/painter_debug.cpp deleted file mode 100644 index ee76aee54c..0000000000 --- a/src/mbgl/renderer/painters/painter_debug.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/buckets/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::Properties<>::PossiblyEvaluated 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() - ); - }; - - 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 }); - } -} - -void Painter::renderTileDebug(const mat4& matrix) { - if (frame.debugOptions == MapDebugOptions::NoDebug) - return; - - static const style::Properties<>::PossiblyEvaluated properties {}; - static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); - - if (frame.debugOptions & MapDebugOptions::TileBorders) { - programs->debug.draw( - context, - gl::LineStrip { 4.0f * frame.pixelRatio }, - gl::DepthMode::disabled(), - gl::StencilMode::disabled(), - gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::red() } - }, - tileVertexBuffer, - tileBorderIndexBuffer, - tileBorderSegments, - paintAttibuteData, - properties, - state.getZoom() - ); - } -} - -#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/painters/painter_fill.cpp b/src/mbgl/renderer/painters/painter_fill.cpp deleted file mode 100644 index 3a0bfed454..0000000000 --- a/src/mbgl/renderer/painters/painter_fill.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/buckets/fill_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/layers/render_fill_layer.hpp> -#include <mbgl/renderer/image_manager.hpp> -#include <mbgl/style/layers/fill_layer_impl.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::PossiblyEvaluated& properties = layer.evaluated; - - if (!properties.get<FillPattern>().from.empty()) { - if (pass != RenderPass::Translucent) { - return; - } - - optional<ImagePosition> imagePosA = imageManager->getPattern(properties.get<FillPattern>().from); - optional<ImagePosition> imagePosB = imageManager->getPattern(properties.get<FillPattern>().to); - - if (!imagePosA || !imagePosB) { - return; - } - - imageManager->bind(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, - imageManager->getPixelSize(), - *imagePosA, - *imagePosB, - properties.get<FillPattern>(), - tile.id, - state - ), - *bucket.vertexBuffer, - indexBuffer, - segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom() - ); - }; - - 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, gl::DepthMode::ReadWrite), - 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() - ); - }; - - 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/painters/painter_fill_extrusion.cpp b/src/mbgl/renderer/painters/painter_fill_extrusion.cpp deleted file mode 100644 index 165476944b..0000000000 --- a/src/mbgl/renderer/painters/painter_fill_extrusion.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> -#include <mbgl/renderer/image_manager.hpp> -#include <mbgl/style/layers/fill_extrusion_layer_impl.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::PossiblyEvaluated& properties = layer.evaluated; - - if (pass == RenderPass::Opaque) { - return; - } - - if (!properties.get<FillExtrusionPattern>().from.empty()) { - optional<ImagePosition> imagePosA = imageManager->getPattern(properties.get<FillExtrusionPattern>().from); - optional<ImagePosition> imagePosB = imageManager->getPattern(properties.get<FillExtrusionPattern>().to); - - if (!imagePosA || !imagePosB) { - return; - } - - imageManager->bind(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), - imageManager->getPixelSize(), - *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()); - - } 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()); - }; -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painters/painter_line.cpp b/src/mbgl/renderer/painters/painter_line.cpp deleted file mode 100644 index 58f4131d96..0000000000 --- a/src/mbgl/renderer/painters/painter_line.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/buckets/line_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/layers/render_line_layer.hpp> -#include <mbgl/renderer/image_manager.hpp> -#include <mbgl/style/layers/line_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/line_program.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 RenderLinePaintProperties::PossiblyEvaluated& 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() - ); - }; - - 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, - lineAtlas->getSize().width)); - - } else if (!properties.get<LinePattern>().from.empty()) { - optional<ImagePosition> posA = imageManager->getPattern(properties.get<LinePattern>().from); - optional<ImagePosition> posB = imageManager->getPattern(properties.get<LinePattern>().to); - - if (!posA || !posB) - return; - - imageManager->bind(context, 0); - - draw(parameters.programs.linePattern, - LinePatternProgram::uniformValues( - properties, - tile, - state, - pixelsToGLUnits, - imageManager->getPixelSize(), - *posA, - *posB)); - - } else { - draw(parameters.programs.line, - LineProgram::uniformValues( - properties, - tile, - state, - pixelsToGLUnits)); - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painters/painter_raster.cpp b/src/mbgl/renderer/painters/painter_raster.cpp deleted file mode 100644 index 56e38ae8f4..0000000000 --- a/src/mbgl/renderer/painters/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/buckets/raster_bucket.hpp> -#include <mbgl/renderer/layers/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 mat4& matrix, - bool useBucketBuffers = false) { - if (pass != RenderPass::Translucent) - return; - if (!bucket.hasData()) - return; - - const RasterPaintProperties::PossiblyEvaluated& 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{ 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 }} }, - }, - useBucketBuffers ? *bucket.vertexBuffer : rasterVertexBuffer, - useBucketBuffers ? *bucket.indexBuffer : quadTriangleIndexBuffer, - useBucketBuffers ? bucket.segments : rasterSegments, - paintAttributeData, - properties, - state.getZoom() - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp deleted file mode 100644 index d3a505aa3f..0000000000 --- a/src/mbgl/renderer/painters/painter_symbol.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/buckets/symbol_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/layers/render_symbol_layer.hpp> -#include <mbgl/style/layers/symbol_layer_impl.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/util/math.hpp> -#include <mbgl/tile/geometry_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() - ); - }; - - assert(dynamic_cast<GeometryTile*>(&tile.tile)); - GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile); - - if (bucket.hasIconData()) { - auto values = layer.iconPropertyValues(layout); - auto paintPropertyValues = layer.iconPaintProperties(); - - const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear; - const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0; - - context.bindTexture(*geometryTile.iconAtlasTexture, 0, - bucket.sdfIcons || 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, 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()) { - context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear); - - auto values = layer.textPropertyValues(layout); - auto paintPropertyValues = layer.textPaintProperties(); - - const Size texsize = geometryTile.glyphAtlasTexture->size; - - 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::Properties<>::PossiblyEvaluated 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() - ); - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/possibly_evaluated_property_value.hpp b/src/mbgl/renderer/possibly_evaluated_property_value.hpp index 8a5dfbe4ea..e662d5dfb1 100644 --- a/src/mbgl/renderer/possibly_evaluated_property_value.hpp +++ b/src/mbgl/renderer/possibly_evaluated_property_value.hpp @@ -45,7 +45,7 @@ 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); }, diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp deleted file mode 100644 index 4bf5629263..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(RenderLayer& layer_, - RenderSource* renderSource_) - : layer(layer_), source(renderSource_) { - } - - RenderLayer& layer; - RenderSource* source; -}; - -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 3f9d68003e..eb2b74ffe0 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -9,7 +9,6 @@ #include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/style/types.hpp> #include <mbgl/renderer/render_tile.hpp> -#include <mbgl/tile/tile.hpp> namespace mbgl { @@ -68,14 +67,5 @@ void RenderLayer::setRenderTiles(std::vector<std::reference_wrapper<RenderTile>> renderTiles = std::move(tiles); } -void RenderLayer::render(Painter& painter, PaintParameters& parameters, RenderSource*) { - for (auto& tileRef : renderTiles) { - auto& tile = tileRef.get(); - auto bucket = tile.tile.getBucket(*baseImpl); - bucket->render(painter, parameters, *this, tile); - } -} - - } //namespace mbgl diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index e06f479281..55831cb72c 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -14,7 +14,6 @@ class Bucket; class BucketParameters; class TransitionParameters; class PropertyEvaluationParameters; -class Painter; class PaintParameters; class RenderSource; class RenderTile; @@ -62,7 +61,7 @@ public: // Checks whether this layer can be rendered. bool needsRendering(float zoom) const; - virtual void render(Painter&, PaintParameters&, RenderSource*); + virtual void render(PaintParameters&, RenderSource*) = 0; // Check wether the given geometry intersects // with the feature @@ -83,13 +82,16 @@ public: friend std::string layoutKey(const RenderLayer&); protected: + // renderTiles are exposed directly to CrossTileSymbolIndex and Placement so they + // can update opacities in the symbol buckets immediately before rendering + friend class CrossTileSymbolIndex; + friend class Placement; + // Stores current set of tiles to be rendered for this layer. + std::vector<std::reference_wrapper<RenderTile>> renderTiles; + // 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_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_source.cpp b/src/mbgl/renderer/render_source.cpp index 7723a1c7ca..6624bb7d96 100644 --- a/src/mbgl/renderer/render_source.cpp +++ b/src/mbgl/renderer/render_source.cpp @@ -6,6 +6,7 @@ #include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/annotation/render_annotation_source.hpp> #include <mbgl/renderer/sources/render_image_source.hpp> +#include <mbgl/renderer/sources/render_custom_geometry_source.hpp> #include <mbgl/tile/tile.hpp> namespace mbgl { @@ -27,6 +28,8 @@ std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl) return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl)); case SourceType::Image: return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl)); + case SourceType::CustomVector: + return std::make_unique<RenderCustomGeometrySource>(staticImmutableCast<CustomGeometrySource::Impl>(impl)); } // Not reachable, but placate GCC. diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index a00a6c797d..8c84af4f1e 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -15,16 +15,16 @@ namespace mbgl { -class Painter; +class PaintParameters; class TransformState; class RenderTile; -class RenderStyle; class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; class Tile; class RenderSourceObserver; class TileParameters; +class CollisionIndex; class RenderSource : protected TileObserver { public: @@ -54,21 +54,22 @@ public: bool needsRelayout, const TileParameters&) = 0; - virtual void startRender(Painter&) = 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 RenderStyle& style, - const RenderedQueryOptions& options) const = 0; + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) 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; 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_style.cpp b/src/mbgl/renderer/render_style.cpp deleted file mode 100644 index f9c3f0ca9f..0000000000 --- a/src/mbgl/renderer/render_style.cpp +++ /dev/null @@ -1,456 +0,0 @@ -#include <mbgl/renderer/render_style.hpp> -#include <mbgl/renderer/render_style_observer.hpp> -#include <mbgl/renderer/update_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_source.hpp> -#include <mbgl/renderer/render_item.hpp> -#include <mbgl/renderer/render_tile.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/renderer/style_diff.hpp> -#include <mbgl/renderer/image_manager.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/style/source_impl.hpp> -#include <mbgl/style/transition_options.hpp> -#include <mbgl/sprite/sprite_loader.hpp> -#include <mbgl/text/glyph_manager.hpp> -#include <mbgl/geometry/line_atlas.hpp> -#include <mbgl/map/backend_scope.hpp> -#include <mbgl/map/query.hpp> -#include <mbgl/tile/tile.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/string.hpp> - -namespace mbgl { - -using namespace style; - -RenderStyleObserver nullObserver; - -RenderStyle::RenderStyle(Scheduler& scheduler_, FileSource& fileSource_) - : scheduler(scheduler_), - fileSource(fileSource_), - 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>()), - observer(&nullObserver) { - glyphManager->setObserver(this); -} - -RenderStyle::~RenderStyle() { - assert(BackendScope::exists()); // Required for custom layers. -} - -void RenderStyle::setObserver(RenderStyleObserver* observer_) { - observer = observer_; -} - -std::vector<const RenderLayer*> RenderStyle::getRenderLayers() const { - std::vector<const RenderLayer*> result; - result.reserve(renderLayers.size()); - for (const auto& layer : *layerImpls) { - result.push_back(getRenderLayer(layer->id)); - } - return result; -} - -RenderLayer* RenderStyle::getRenderLayer(const std::string& id) { - auto it = renderLayers.find(id); - return it != renderLayers.end() ? it->second.get() : nullptr; -} - -const RenderLayer* RenderStyle::getRenderLayer(const std::string& id) const { - auto it = renderLayers.find(id); - return it != renderLayers.end() ? it->second.get() : nullptr; -} - -const RenderLight& RenderStyle::getRenderLight() const { - return renderLight; -} - -void RenderStyle::update(const UpdateParameters& parameters) { - assert(BackendScope::exists()); // Required for custom layers. - - const bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint); - - const TransitionParameters transitionParameters { - parameters.mode == MapMode::Continuous ? parameters.timePoint : Clock::time_point::max(), - parameters.mode == MapMode::Continuous ? parameters.transitionOptions : TransitionOptions() - }; - - const PropertyEvaluationParameters evaluationParameters { - zoomHistory, - parameters.mode == MapMode::Continuous ? parameters.timePoint : Clock::time_point::max(), - parameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero() - }; - - const TileParameters tileParameters { - parameters.pixelRatio, - parameters.debugOptions, - parameters.transformState, - parameters.scheduler, - parameters.fileSource, - parameters.mode, - parameters.annotationManager, - *imageManager, - *glyphManager - }; - - glyphManager->setURL(parameters.glyphURL); - - // Update light. - const bool lightChanged = renderLight.impl != parameters.light; - - if (lightChanged) { - renderLight.impl = parameters.light; - renderLight.transition(transitionParameters); - } - - if (lightChanged || zoomChanged || renderLight.hasTransition()) { - renderLight.evaluate(evaluationParameters); - } - - - const ImageDifference imageDiff = diffImages(imageImpls, parameters.images); - imageImpls = parameters.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); - } - - if (parameters.spriteLoaded && !imageManager->isLoaded()) { - imageManager->onSpriteLoaded(); - } - - - const LayerDifference layerDiff = diffLayers(layerImpls, parameters.layers); - layerImpls = parameters.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, parameters.sources); - sourceImpls = parameters.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)); - } - - // 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 && ( - hasLayoutDifference(layerDiff, layer->id) || - !imageDiff.added.empty() || - !imageDiff.removed.empty() || - !imageDiff.changed.empty())) { - needsRelayout = true; - } - - filteredLayers.push_back(layer); - } - - renderSources.at(source->id)->update(source, - filteredLayers, - needsRendering, - needsRelayout, - tileParameters); - } -} - -RenderSource* RenderStyle::getRenderSource(const std::string& id) const { - auto it = renderSources.find(id); - return it != renderSources.end() ? it->second.get() : nullptr; -} - -bool RenderStyle::hasTransitions() const { - if (renderLight.hasTransition()) { - return true; - } - - for (const auto& entry : renderLayers) { - if (entry.second->hasTransition()) { - return true; - } - } - - return false; -} - -bool RenderStyle::isLoaded() const { - for (const auto& entry: renderSources) { - if (!entry.second->isLoaded()) { - return false; - } - } - - if (!imageManager->isLoaded()) { - return false; - } - - return true; -} - -RenderData RenderStyle::getRenderData(MapDebugOptions debugOptions, float angle) { - RenderData result; - - for (const auto& entry : renderSources) { - if (entry.second->isEnabled()) { - result.sources.insert(entry.second.get()); - } - } - - for (auto& layerImpl : *layerImpls) { - RenderLayer* layer = getRenderLayer(layerImpl->id); - assert(layer); - - if (!layer->needsRendering(zoomHistory.lastZoom)) { - continue; - } - - if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) { - if (debugOptions & MapDebugOptions::Overdraw) { - // We want to skip glClear optimization in overdraw mode. - result.order.emplace_back(*layer, nullptr); - continue; - } - 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(). - result.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. - result.order.emplace_back(*layer, nullptr); - } - continue; - } - - if (layer->is<RenderCustomLayer>()) { - result.order.emplace_back(*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; - } - - auto& renderTiles = source->getRenderTiles(); - const bool symbolLayer = layer->is<RenderSymbolLayer>(); - - // 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::vector<std::reference_wrapper<RenderTile>> sortedTiles; - std::transform(renderTiles.begin(), renderTiles.end(), std::back_inserter(sortedTiles), - [](auto& pair) { return std::ref(pair.second); }); - if (symbolLayer) { - std::sort(sortedTiles.begin(), sortedTiles.end(), - [angle](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, angle); - auto pbr = util::rotate(pb, angle); - - return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x); - }); - } - - 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)); - result.order.emplace_back(*layer, source); - } - - return result; -} - -std::vector<Feature> RenderStyle::queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const { - std::unordered_map<std::string, std::vector<Feature>> resultsByLayer; - - if (options.layerIDs) { - std::unordered_set<std::string> sourceIDs; - for (const auto& layerID : *options.layerIDs) { - if (const RenderLayer* layer = getRenderLayer(layerID)) { - sourceIDs.emplace(layer->baseImpl->source); - } - } - for (const auto& sourceID : sourceIDs) { - if (RenderSource* renderSource = getRenderSource(sourceID)) { - auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, *this, options); - std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); - } - } - } else { - for (const auto& entry : renderSources) { - auto sourceResults = entry.second->queryRenderedFeatures(geometry, transformState, *this, 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; -} - -void RenderStyle::setSourceTileCacheSize(size_t size) { - for (const auto& entry : renderSources) { - entry.second->setCacheSize(size); - } -} - -void RenderStyle::onLowMemory() { - for (const auto& entry : renderSources) { - entry.second->onLowMemory(); - } -} - -void RenderStyle::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 RenderStyle::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 RenderStyle::onTileChanged(RenderSource&, const OverscaledTileID&) { - observer->onInvalidate(); -} - -void RenderStyle::dumpDebugLogs() const { - for (const auto& entry : renderSources) { - entry.second->dumpDebugLogs(); - } - - imageManager->dumpDebugLogs(); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_style.hpp b/src/mbgl/renderer/render_style.hpp deleted file mode 100644 index dc33e7b2f4..0000000000 --- a/src/mbgl/renderer/render_style.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include <mbgl/style/image.hpp> -#include <mbgl/renderer/render_source.hpp> -#include <mbgl/renderer/render_source_observer.hpp> -#include <mbgl/renderer/render_layer.hpp> -#include <mbgl/renderer/render_light.hpp> -#include <mbgl/text/glyph_manager_observer.hpp> -#include <mbgl/map/zoom_history.hpp> -#include <mbgl/map/mode.hpp> - -#include <memory> -#include <string> -#include <vector> - -namespace mbgl { - -class FileSource; -class GlyphManager; -class ImageManager; -class LineAtlas; -class RenderData; -class TransformState; -class RenderedQueryOptions; -class Scheduler; -class UpdateParameters; -class RenderStyleObserver; - -namespace style { -class Image; -class Source; -class Layer; -} // namespace style - -class RenderStyle : public GlyphManagerObserver, - public RenderSourceObserver { -public: - RenderStyle(Scheduler&, FileSource&); - ~RenderStyle() final; - - void setObserver(RenderStyleObserver*); - void update(const UpdateParameters&); - - bool isLoaded() const; - bool hasTransitions() const; - - RenderSource* getRenderSource(const std::string& id) const; - - std::vector<const RenderLayer*> getRenderLayers() const; - - RenderLayer* getRenderLayer(const std::string& id); - const RenderLayer* getRenderLayer(const std::string& id) const; - - const RenderLight& getRenderLight() const; - - RenderData getRenderData(MapDebugOptions, float angle); - - std::vector<Feature> queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const; - - void setSourceTileCacheSize(size_t); - void onLowMemory(); - - void dumpDebugLogs() const; - - Scheduler& scheduler; - FileSource& fileSource; - std::unique_ptr<GlyphManager> glyphManager; - std::unique_ptr<ImageManager> imageManager; - std::unique_ptr<LineAtlas> lineAtlas; - -private: - 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; - - // 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; - - RenderStyleObserver* observer; - ZoomHistory zoomHistory; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_style_observer.hpp b/src/mbgl/renderer/render_style_observer.hpp deleted file mode 100644 index 5852dd68b8..0000000000 --- a/src/mbgl/renderer/render_style_observer.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include <exception> - -namespace mbgl { - -class RenderStyleObserver { -public: - virtual ~RenderStyleObserver() = default; - virtual void onInvalidate() {} - virtual void onResourceError(std::exception_ptr) {} -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index 59c3ea076b..8df31f8d7c 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -1,7 +1,11 @@ #include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/painter.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 { @@ -10,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); } @@ -37,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::startRender(Painter& painter) { - tile.upload(painter.context); +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 - painter.state.matrixFor(matrix, id); - painter.state.matrixFor(nearClippedMatrix, id); - matrix::multiply(matrix, painter.projMatrix, matrix); - matrix::multiply(nearClippedMatrix, painter.nearClippedProjMatrix, 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 6677278873..3db02393d2 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,9 +12,9 @@ namespace mbgl { class Tile; class TransformState; -class Painter; +class PaintParameters; -class RenderTile { +class RenderTile final { public: RenderTile(UnwrappedTileID id_, Tile& tile_) : id(std::move(id_)), tile(tile_) {} RenderTile(const RenderTile&) = delete; @@ -27,6 +28,7 @@ public: mat4 matrix; mat4 nearClippedMatrix; bool used = false; + bool needsClipping = false; mat4 translatedMatrix(const std::array<float, 2>& translate, style::TranslateAnchorType anchor, @@ -36,13 +38,15 @@ public: style::TranslateAnchorType anchor, const TransformState&) const; - void startRender(Painter&); + void setMask(TileMask&&); + void startRender(PaintParameters&); + void finishRender(PaintParameters&); -private: 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..6d086c70b1 --- /dev/null +++ b/src/mbgl/renderer/renderer.cpp @@ -0,0 +1,102 @@ +#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_, + const optional<std::string> localFontFamily_) + : impl(std::make_unique<Impl>(backend, pixelRatio_, fileSource_, scheduler_, + contextMode_, std::move(programCacheDir_), std::move(localFontFamily_))) { +} + +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); + return getAnnotationIDs(features); +} + +AnnotationIDs Renderer::queryShapeAnnotations(const ScreenBox& box) const { + auto features = impl->queryShapeAnnotations({ + box.min, + {box.max.x, box.min.y}, + box.max, + {box.min.x, box.max.y}, + box.min + }); + return getAnnotationIDs(features); +} + +AnnotationIDs Renderer::getAnnotationIDs(const std::vector<Feature>& features) const { + 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..22d263313c --- /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::getExtensionFunctionPointer, 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..4bed0e251b --- /dev/null +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -0,0 +1,815 @@ +#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_, + const optional<std::string> localFontFamily_) + : backend(backend_) + , scheduler(scheduler_) + , fileSource(fileSource_) + , observer(&nullObserver()) + , contextMode(contextMode_) + , pixelRatio(pixelRatio_) + , programCacheDir(programCacheDir_) + , glyphManager(std::make_unique<GlyphManager>(fileSource, std::make_unique<LocalGlyphRasterizer>(localFontFamily_))) + , 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>()) + , placement(std::make_unique<Placement>(TransformState{}, MapMode::Static)) { + 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::Continuous) { + // 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, + *imageManager, + *lineAtlas + }; + + bool loaded = updateParameters.styleLoaded && isLoaded(); + if (updateParameters.mode != MapMode::Continuous && !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 (parameters.contextMode == GLContextMode::Unique + && 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(b.id.canonical.z, par.y, par.x) < std::tie(a.id.canonical.z, pbr.y, pbr.x); + }); + } else { + std::sort(sortedTiles.begin(), sortedTiles.end(), + [](const auto& a, const auto& b) { return a.get().id < b.get().id; }); + // Don't render non-symbol layers for tiles that we're only holding on to for symbol fading + sortedTiles.erase(std::remove_if(sortedTiles.begin(), sortedTiles.end(), + [](const auto& tile) { return tile.get().tile.holdForFade(); }), + sortedTiles.end()); + } + + std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion; + for (auto& sortedTile : sortedTiles) { + auto& tile = sortedTile.get(); + if (!tile.tile.isRenderable()) { + continue; + } + + auto bucket = tile.tile.getBucket(*layer->baseImpl); + if (bucket) { + sortedTilesForInsertion.emplace_back(tile); + tile.used = true; + + // We only need clipping when we're _not_ drawing a symbol layer. + if (!symbolLayer) { + tile.needsClipping = true; + } + } + } + layer->setRenderTiles(std::move(sortedTilesForInsertion)); + order.emplace_back(RenderItem { *layer, source }); + } + + bool symbolBucketsChanged = false; + if (parameters.mapMode != MapMode::Continuous) { + // TODO: Think about right way for symbol index to handle still rendering + crossTileSymbolIndex.reset(); + } + for (auto it = order.rbegin(); it != order.rend(); ++it) { + if (it->layer.is<RenderSymbolLayer>()) { + if (crossTileSymbolIndex.addLayer(*it->layer.as<RenderSymbolLayer>())) symbolBucketsChanged = true; + } + } + + bool placementChanged = false; + if (!placement->stillRecent(parameters.timePoint)) { + auto newPlacement = std::make_unique<Placement>(parameters.state, parameters.mapMode); + for (auto it = order.rbegin(); it != order.rend(); ++it) { + if (it->layer.is<RenderSymbolLayer>()) { + newPlacement->placeLayer(*it->layer.as<RenderSymbolLayer>(), parameters.projMatrix, parameters.debugOptions & MapDebugOptions::Collision); + } + } + + placementChanged = newPlacement->commit(*placement, parameters.timePoint); + if (placementChanged || symbolBucketsChanged) { + placement = std::move(newPlacement); + } + + placement->setRecent(parameters.timePoint); + + updateFadingTiles(); + } else { + placement->setStale(); + } + + parameters.symbolFadeChange = placement->symbolFadeChange(parameters.timePoint); + + if (placementChanged || symbolBucketsChanged) { + for (auto it = order.rbegin(); it != order.rend(); ++it) { + if (it->layer.is<RenderSymbolLayer>()) { + placement->updateLayerOpacities(*it->layer.as<RenderSymbolLayer>()); + } + } + } + + // - 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); + + // 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. + { + using namespace gl::value; + + MBGL_DEBUG_GROUP(parameters.context, "clear"); + parameters.backend.bind(); + if (parameters.debugOptions & MapDebugOptions::Overdraw) { + parameters.context.clear(Color::black(), ClearDepth::Default, ClearStencil::Default); + } else if (parameters.contextMode == GLContextMode::Shared) { + parameters.context.clear({}, ClearDepth::Default, ClearStencil::Default); + } else { + parameters.context.clear(backgroundColor, ClearDepth::Default, ClearStencil::Default); + } + } + + // - 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(parameters.timePoint) + ); + + 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()); + } + } + + return queryRenderedFeatures(geometry, options, layers); +} + +std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options, const std::vector<const RenderLayer*>& layers) const { + 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, placement->getCollisionIndex()); + 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::queryShapeAnnotations(const ScreenLineString& geometry) const { + std::vector<const RenderLayer*> shapeAnnotationLayers; + RenderedQueryOptions options; + for (const auto& layerImpl : *layerImpls) { + if (std::mismatch(layerImpl->id.begin(), layerImpl->id.end(), + AnnotationManager::ShapeLayerID.begin(), AnnotationManager::ShapeLayerID.end()).second == AnnotationManager::ShapeLayerID.end()) { + if (const RenderLayer* layer = getRenderLayer(layerImpl->id)) { + shapeAnnotationLayers.emplace_back(layer); + } + } + } + + return queryRenderedFeatures(geometry, options, shapeAnnotationLayers); +} + +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(TimePoint timePoint) const { + if (renderLight.hasTransition()) { + return true; + } + + for (const auto& entry : renderLayers) { + if (entry.second->hasTransition()) { + return true; + } + } + + if (placement->hasTransitions(timePoint)) { + return true; + } + + if (fadingTiles) { + return true; + } + + return false; +} + +void Renderer::Impl::updateFadingTiles() { + fadingTiles = false; + for (auto& source : renderSources) { + for (auto& renderTile : source.second->getRenderTiles()) { + Tile& tile = renderTile.get().tile; + if (tile.holdForFade()) { + fadingTiles = true; + tile.performedFadePlacement(); + } + } + } +} + +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..5d0200a5df --- /dev/null +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include <mbgl/renderer/mode.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/render_source_observer.hpp> +#include <mbgl/renderer/render_light.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/text/cross_tile_symbol_index.hpp> +#include <mbgl/text/glyph_manager_observer.hpp> +#include <mbgl/text/placement.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 CrossTileSymbolIndex; + +class Renderer::Impl : public GlyphManagerObserver, + public RenderSourceObserver{ +public: + Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode, + const optional<std::string> programCacheDir, const optional<std::string> localFontFamily); + ~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; + std::vector<Feature> queryShapeAnnotations(const ScreenLineString&) const; + + void onLowMemory(); + void dumDebugLogs(); + +private: + bool isLoaded() const; + bool hasTransitions(TimePoint) const; + + RenderSource* getRenderSource(const std::string& id) const; + + RenderLayer* getRenderLayer(const std::string& id); + const RenderLayer* getRenderLayer(const std::string& id) const; + + std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector<const RenderLayer*>&) 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; + + void updateFadingTiles(); + + 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; + 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; + + CrossTileSymbolIndex crossTileSymbolIndex; + std::unique_ptr<Placement> placement; + + bool contextLost = false; + bool fadingTiles = 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_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp new file mode 100644 index 0000000000..111f0234ed --- /dev/null +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -0,0 +1,86 @@ +#include <mbgl/renderer/sources/render_custom_geometry_source.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/tile/custom_geometry_tile.hpp> + +#include <mbgl/algorithm/generate_clip_ids.hpp> +#include <mbgl/algorithm/generate_clip_ids_impl.hpp> + +namespace mbgl { + +using namespace style; + +RenderCustomGeometrySource::RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl> impl_) + : RenderSource(impl_) { + tilePyramid.setObserver(this); +} + +const style::CustomGeometrySource::Impl& RenderCustomGeometrySource::impl() const { + return static_cast<const style::CustomGeometrySource::Impl&>(*baseImpl); +} + +bool RenderCustomGeometrySource::isLoaded() const { + return tilePyramid.isLoaded(); +} + +void RenderCustomGeometrySource::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_); + + enabled = needsRendering; + + auto tileLoader = impl().getTileLoader(); + if (!tileLoader) { + return; + } + + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::CustomVector, + util::tileSize, + impl().getZoomRange(), + [&] (const OverscaledTileID& tileID) { + return std::make_unique<CustomGeometryTile>(tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader); + }); +} + +void RenderCustomGeometrySource::startRender(PaintParameters& parameters) { + parameters.clipIDGenerator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); +} + +void RenderCustomGeometrySource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); +} + +std::vector<std::reference_wrapper<RenderTile>> RenderCustomGeometrySource::getRenderTiles() { + return tilePyramid.getRenderTiles(); +} + +std::unordered_map<std::string, std::vector<Feature>> +RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); +} + +std::vector<Feature> RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const { + return tilePyramid.querySourceFeatures(options); +} + +void RenderCustomGeometrySource::onLowMemory() { + tilePyramid.onLowMemory(); +} + +void RenderCustomGeometrySource::dumpDebugLogs() const { + tilePyramid.dumpDebugLogs(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp new file mode 100644 index 0000000000..82e691d5c9 --- /dev/null +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include <mbgl/renderer/render_source.hpp> +#include <mbgl/renderer/tile_pyramid.hpp> +#include <mbgl/style/sources/custom_geometry_source_impl.hpp> + +namespace mbgl { + +class RenderCustomGeometrySource : public RenderSource { +public: + RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl>); + + bool isLoaded() const final; + + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; + + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) 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 CollisionIndex& collisionIndex) const final; + + std::vector<Feature> + querySourceFeatures(const SourceQueryOptions&) const final; + + void onLowMemory() final; + void dumpDebugLogs() const final; + +private: + const style::CustomGeometrySource::Impl& impl() const; + + TilePyramid tilePyramid; +}; + +template <> +inline bool RenderSource::is<RenderCustomGeometrySource>() const { + return baseImpl->type == style::SourceType::CustomVector; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index 337b7b8b7a..d07cfcdc41 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -1,7 +1,8 @@ #include <mbgl/renderer/sources/render_geojson_source.hpp> #include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/painter.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> @@ -34,19 +35,26 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_, GeoJSONData* data_ = impl().getData(); - if (!data_) { - return; - } - 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)); + } + } } } + if (!data) { + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + return; + } + tilePyramid.update(layers, needsRendering, needsRelayout, @@ -59,35 +67,32 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_, }); } -void RenderGeoJSONSource::startRender(Painter& painter) { - painter.clipIDGenerator.update(tilePyramid.getRenderTiles()); - tilePyramid.startRender(painter); +void RenderGeoJSONSource::startRender(PaintParameters& parameters) { + parameters.clipIDGenerator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderGeoJSONSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); +void RenderGeoJSONSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); } -std::map<UnwrappedTileID, RenderTile>& RenderGeoJSONSource::getRenderTiles() { +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 RenderStyle& style, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options); + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); } 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 9b5477e1d0..55166ea901 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -22,21 +22,21 @@ public: bool needsRelayout, const TileParameters&) final; - void startRender(Painter&) final; - void finishRender(Painter&) final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) 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 RenderStyle& style, - const RenderedQueryOptions& options) const final; + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex&) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; @@ -44,12 +44,12 @@ private: 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; + return baseImpl->type == style::SourceType::GeoJSON; } } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index f5068b9d7f..d215dc8d13 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -1,19 +1,23 @@ #include <mbgl/map/transform_state.hpp> #include <mbgl/math/log2.hpp> #include <mbgl/renderer/buckets/raster_bucket.hpp> -#include <mbgl/renderer/painter.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_), shouldRender(false) { + : RenderSource(impl_) { } RenderImageSource::~RenderImageSource() = default; @@ -26,7 +30,7 @@ bool RenderImageSource::isLoaded() const { return !!bucket; } -void RenderImageSource::startRender(Painter& painter) { +void RenderImageSource::startRender(PaintParameters& parameters) { if (!isLoaded()) { return; } @@ -36,31 +40,53 @@ void RenderImageSource::startRender(Painter& painter) { for (size_t i = 0; i < tileIds.size(); i++) { mat4 matrix; matrix::identity(matrix); - painter.state.matrixFor(matrix, tileIds[i]); - matrix::multiply(matrix, painter.projMatrix, matrix); + parameters.state.matrixFor(matrix, tileIds[i]); + matrix::multiply(matrix, parameters.projMatrix, matrix); matrices.push_back(matrix); } - if (bucket->needsUpload() && shouldRender) { - bucket->upload(painter.context); + if (bucket->needsUpload()) { + bucket->upload(parameters.context); } } -void RenderImageSource::finishRender(Painter& painter) { - if (!isLoaded() || !shouldRender) { +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) { - painter.renderTileDebug(matrix); + 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 RenderStyle&, - const RenderedQueryOptions&) const { - return {}; + const std::vector<const RenderLayer*>&, + const RenderedQueryOptions&, + const CollisionIndex&) const { + return std::unordered_map<std::string, std::vector<Feature>> {}; } std::vector<Feature> RenderImageSource::querySourceFeatures(const SourceQueryOptions&) const { @@ -72,89 +98,106 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_, const bool needsRendering, const bool, const TileParameters& parameters) { - std::swap(baseImpl, baseImpl_); - enabled = needsRendering; + if (!needsRendering) { + return; + } auto transformState = parameters.transformState; - auto size = transformState.getSize(); - double viewportHeight = size.height; + std::swap(baseImpl, baseImpl_); auto coords = impl().getCoordinates(); + std::shared_ptr<PremultipliedImage> image = impl().getImage(); - // 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); - } - double width = nePixel.x - swPixel.x; - double height = nePixel.y - swPixel.y; - - // Don't bother drawing the ImageSource unless it occupies >4 screen pixels - shouldRender = (width * height > 4); - if (!shouldRender) { + if (!image || !image->valid()) { + enabled = false; return; } + // Compute the z0 tile coordinates for the given LatLngs + TileCoordinatePoint nePoint = { -INFINITY, -INFINITY }; + TileCoordinatePoint swPoint = { INFINITY, INFINITY }; + std::vector<TileCoordinatePoint> tileCoordinates; + for (LatLng latLng : coords) { + auto point = TileCoordinate::fromLatLng(0, latLng).p; + tileCoordinates.push_back(point); + swPoint.x = std::min(swPoint.x, point.x); + nePoint.x = std::max(nePoint.x, point.x); + swPoint.y = std::min(swPoint.y, point.y); + nePoint.y = std::max(nePoint.y, point.y); + } + // Calculate the optimum zoom level to determine the tile ids to use for transforms - double minScale = INFINITY; - if (width > 0 || height > 0) { - double scaleX = double(size.width) / width; - double scaleY = double(size.height) / height; - minScale = util::min(scaleX, scaleY); + auto dx = nePoint.x - swPoint.x; + auto dy = nePoint.y - swPoint.y; + auto dMax = std::max(dx, dy); + double zoom = std::max(0.0, std::floor(-util::log2(dMax))); + + // Only enable if the long side of the image is > 2 pixels. Resulting in a + // display of at least 2 x 1 px image + // A tile coordinate unit represents the length of one tile (tileSize) at a given zoom. + // To convert a tile coordinate to pixels, multiply by tileSize. + // Here dMax is in z0 tile units, so we also scale by 2^z to match current zoom. + enabled = dMax * std::pow(2.0, transformState.getZoom()) * util::tileSize > 2.0; + if (!enabled) { + return; } - double zoom = transformState.getZoom() + util::log2(minScale); - zoom = 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, ::floor(zoom)); + 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); + for (auto tileCoords : tileCoordinates) { + auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tileCoords); geomCoords.push_back(gc); } - - const UnassociatedImage& image = impl().getImage(); - if (!image.valid()) { - return; - } - - if (!bucket || image != bucket->image) { - bucket = std::make_unique<RasterBucket>(image.clone()); + 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 }, { 32767, 0 })); + RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { util::EXTENT, 0 })); bucket->vertices.emplace_back( - RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, 32767 })); + RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT })); bucket->vertices.emplace_back( - RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { 32767, 32767 })); + 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); @@ -162,16 +205,6 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_, bucket->segments.emplace_back(0, 0, 4, 6); } -void RenderImageSource::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer) { - if (isLoaded() && !bucket->needsUpload() && shouldRender) { - for (auto matrix : matrices) { - bucket->render(painter, parameters, layer, matrix); - } - } -} - 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"); diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp index 5175cbf4a4..72cf4cea61 100644 --- a/src/mbgl/renderer/sources/render_image_source.hpp +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -5,13 +5,8 @@ #include <mbgl/style/sources/image_source_impl.hpp> namespace mbgl { -class RenderLayer; -class PaintParameters; -class RasterBucket; -namespace gl { -class Context; -} // namespace gl +class RasterBucket; class RenderImageSource : public RenderSource { public: @@ -20,9 +15,8 @@ public: bool isLoaded() const final; - void startRender(Painter&) final; - void render(Painter&, PaintParameters&, const RenderLayer&); - void finishRender(Painter&) final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; void update(Immutable<style::Source::Impl>, const std::vector<Immutable<style::Layer::Impl>>&, @@ -30,37 +24,36 @@ public: bool needsRelayout, const TileParameters&) final; - std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final { - return tiles; + 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 RenderStyle& style, - const RenderedQueryOptions& options) const final; + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final { - } void onLowMemory() final { } void dumpDebugLogs() const final; private: + friend class RenderRasterLayer; + const style::ImageSource::Impl& impl() const; - std::map<UnwrappedTileID, RenderTile> tiles; std::vector<UnwrappedTileID> tileIds; std::unique_ptr<RasterBucket> bucket; std::vector<mat4> matrices; - bool shouldRender; }; template <> inline bool RenderSource::is<RenderImageSource>() const { - return baseImpl->type == SourceType::Image; + return baseImpl->type == style::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 385437af1d..f11f9b7aed 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -1,6 +1,7 @@ #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 { @@ -56,34 +57,32 @@ void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_, }); } -void RenderRasterSource::startRender(Painter& painter) { - tilePyramid.startRender(painter); +void RenderRasterSource::startRender(PaintParameters& parameters) { + algorithm::updateTileMasks(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderRasterSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); +void RenderRasterSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); } -std::map<UnwrappedTileID, RenderTile>& RenderRasterSource::getRenderTiles() { +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 RenderStyle&, - const RenderedQueryOptions&) const { - return {}; + const std::vector<const RenderLayer*>&, + const RenderedQueryOptions&, + const CollisionIndex& ) const { + 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 d1e37a3099..25041fde43 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -18,21 +18,21 @@ public: bool needsRelayout, const TileParameters&) final; - void startRender(Painter&) final; - void finishRender(Painter&) final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) 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 RenderStyle& style, - const RenderedQueryOptions& options) const final; + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; @@ -45,7 +45,7 @@ private: template <> inline bool RenderSource::is<RenderRasterSource>() const { - return baseImpl->type == SourceType::Raster; + return baseImpl->type == style::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 5b266b10a5..49f8fdff2c 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -1,6 +1,6 @@ #include <mbgl/renderer/sources/render_vector_source.hpp> #include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/tile/vector_tile.hpp> #include <mbgl/algorithm/generate_clip_ids.hpp> @@ -60,35 +60,32 @@ void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_, }); } -void RenderVectorSource::startRender(Painter& painter) { - painter.clipIDGenerator.update(tilePyramid.getRenderTiles()); - tilePyramid.startRender(painter); +void RenderVectorSource::startRender(PaintParameters& parameters) { + parameters.clipIDGenerator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderVectorSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); +void RenderVectorSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); } -std::map<UnwrappedTileID, RenderTile>& RenderVectorSource::getRenderTiles() { +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 RenderStyle& style, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options); + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); } 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 d5d9598a75..4a992e854f 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -18,21 +18,21 @@ public: bool needsRelayout, const TileParameters&) final; - void startRender(Painter&) final; - void finishRender(Painter&) final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) 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 RenderStyle& style, - const RenderedQueryOptions& options) const final; + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; @@ -45,7 +45,7 @@ private: template <> inline bool RenderSource::is<RenderVectorSource>() const { - return baseImpl->type == SourceType::Vector; + return baseImpl->type == style::SourceType::Vector; } } // 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 cf7a5b100a..665c7490d2 100644 --- a/src/mbgl/renderer/tile_parameters.hpp +++ b/src/mbgl/renderer/tile_parameters.hpp @@ -13,8 +13,8 @@ class GlyphManager; class TileParameters { public: - float pixelRatio; - MapDebugOptions debugOptions; + const float pixelRatio; + const MapDebugOptions debugOptions; const TransformState& transformState; Scheduler& workerScheduler; FileSource& fileSource; @@ -22,6 +22,7 @@ public: AnnotationManager& annotationManager; 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 c2806299e3..870d9050bc 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -1,11 +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> #include <mbgl/util/enum.hpp> @@ -39,23 +38,20 @@ bool TilePyramid::isLoaded() const { return true; } -void TilePyramid::startRender(Painter& painter) { - for (auto& pair : renderTiles) { - pair.second.startRender(painter); +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::update(const std::vector<Immutable<style::Layer::Impl>>& layers, @@ -89,14 +85,27 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer // 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) { + // Only attempt prefetching in continuous mode. + if (parameters.mode == MapMode::Continuous) { tileZoom = idealZoom; + + // Request lower zoom level tiles (if configured 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); @@ -107,10 +116,13 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer // use because they're still loading. In addition to that, we also need to retain all tiles that // we're actively using, e.g. as a replacement for tile that aren't loaded yet. std::set<OverscaledTileID> retain; + std::set<UnwrappedTileID> rendered; + + auto retainTileFn = [&](Tile& tile, TileNecessity necessity) -> void { + if (retain.emplace(tile.id).second) { + tile.setNecessity(necessity); + } - auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void { - retain.emplace(tile.id); - tile.setNecessity(necessity); if (needsRelayout) { tile.setLayers(layers); } @@ -133,13 +145,40 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer } return tiles.emplace(tileID, std::move(tile)).first->second.get(); }; + + std::map<UnwrappedTileID, Tile*> previouslyRenderedTiles; + for (auto& renderTile : renderTiles) { + previouslyRenderedTiles[renderTile.id] = &renderTile.tile; + } + auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) { - renderTiles.emplace(tileID, RenderTile{ tileID, tile }); + renderTiles.emplace_back(tileID, tile); + rendered.emplace(tileID); + previouslyRenderedTiles.erase(tileID); // Still rendering this tile, no need for special fading logic. + tile.markRenderedIdeal(); }; 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); + + for (auto previouslyRenderedTile : previouslyRenderedTiles) { + Tile& tile = *previouslyRenderedTile.second; + tile.markRenderedPreviously(); + if (tile.holdForFade()) { + // Since it was rendered in the last frame, we know we have it + // Don't mark the tile "Required" to avoid triggering a new network request + retainTileFn(tile, TileNecessity::Optional); + renderTiles.emplace_back(previouslyRenderedTile.first, tile); + rendered.emplace(previouslyRenderedTile.first); + } + } if (type != SourceType::Annotations) { size_t conservativeCacheSize = @@ -150,41 +189,44 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer 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()) { + auto renderedIt = rendered.find(tilesIt->first.toUnwrapped()); + if (renderedIt == rendered.end()) { + // Since this tile isn't in the render set, crossTileIDs won't be kept + // updated by CrossTileSymbolIndex. We need to reset the stored crossTileIDs + // so they're not reused if/when this tile is re-added to the render set + tilesIt->second->resetCrossTileIDs(); + } + if (retainIt == retain.end() || tilesIt->first < *retainIt) { + if (!needsRelayout) { + tilesIt->second->setNecessity(TileNecessity::Optional); + cache.add(tilesIt->first, std::move(tilesIt->second)); + } + tiles.erase(tilesIt++); + } else { + if (!(*retainIt < tilesIt->first)) { + ++tilesIt; + } + ++retainIt; } - ++retainIt; } } + + for (auto& pair : tiles) { + pair.second->setShowCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision); + } } std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, - const RenderedQueryOptions& options) const { + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) const { std::unordered_map<std::string, std::vector<Feature>> result; if (renderTiles.empty() || geometry.empty()) { return result; @@ -199,18 +241,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; @@ -230,8 +268,9 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered renderTile.tile.queryRenderedFeatures(result, tileSpaceQueryGeometry, transformState, - style, - options); + layers, + options, + collisionIndex); } return result; diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index 5846560808..feab8a838c 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -18,10 +18,10 @@ namespace mbgl { -class Painter; +class PaintParameters; class TransformState; class RenderTile; -class RenderStyle; +class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; class TileParameters; @@ -37,21 +37,22 @@ public: bool needsRendering, bool needsRelayout, const TileParameters&, - SourceType type, + style::SourceType type, uint16_t tileSize, Range<uint8_t> zoomRange, std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile); - void startRender(Painter&); - void finishRender(Painter&); + void startRender(PaintParameters&); + void finishRender(PaintParameters&); - 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 RenderStyle& style, - const RenderedQueryOptions& options) const; + const std::vector<const RenderLayer*>&, + const RenderedQueryOptions& options, + const CollisionIndex& collisionIndex) const; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const; @@ -63,12 +64,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/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp index ce79a4f31b..b54abc050d 100644 --- a/src/mbgl/renderer/update_parameters.hpp +++ b/src/mbgl/renderer/update_parameters.hpp @@ -2,20 +2,22 @@ #include <mbgl/map/mode.hpp> #include <mbgl/map/transform_state.hpp> -#include <mbgl/util/chrono.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 Scheduler; -class FileSource; class AnnotationManager; class UpdateParameters { public: + const bool styleLoaded; const MapMode mode; const float pixelRatio; const MapDebugOptions debugOptions; @@ -30,9 +32,12 @@ public: const Immutable<std::vector<Immutable<style::Source::Impl>>> sources; const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers; - Scheduler& scheduler; - FileSource& fileSource; AnnotationManager& annotationManager; + + const uint8_t prefetchZoomDelta; + + // For still image requests, render requested + const bool stillImageRequest; }; } // namespace mbgl |