diff options
author | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2018-02-24 18:52:57 +0200 |
---|---|---|
committer | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2018-02-24 21:15:48 +0200 |
commit | 13d7f66c71eb94d0dbf6604da352605ed0f547da (patch) | |
tree | 5ebf27c566d433fdf785856cfc6811fb6f8c87c4 /src/mbgl/renderer | |
parent | 8c1be4ec01ef46bf453856531ebf53b48ce3dbe7 (diff) | |
download | qtlocation-mapboxgl-13d7f66c71eb94d0dbf6604da352605ed0f547da.tar.gz |
Bump Mapbox GL Native
mapbox-gl-native @ 5de373fff0e71496b6aa11ecb6556f958a28d80b
Diffstat (limited to 'src/mbgl/renderer')
43 files changed, 1178 insertions, 179 deletions
diff --git a/src/mbgl/renderer/buckets/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp index d23f0861f4..c442b661de 100644 --- a/src/mbgl/renderer/buckets/circle_bucket.cpp +++ b/src/mbgl/renderer/buckets/circle_bucket.cpp @@ -108,8 +108,9 @@ float CircleBucket::getQueryRadius(const RenderLayer& layer) const { auto circleLayer = layer.as<RenderCircleLayer>(); float radius = get<CircleRadius>(*circleLayer, paintPropertyBinders); + float stroke = get<CircleStrokeWidth>(*circleLayer, paintPropertyBinders); auto translate = circleLayer->evaluated.get<CircleTranslate>(); - return radius + util::length(translate[0], translate[1]); + return radius + stroke + util::length(translate[0], translate[1]); } } // namespace mbgl diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp index 7f53326fe1..5e2c937091 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -101,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)); diff --git a/src/mbgl/renderer/buckets/heatmap_bucket.cpp b/src/mbgl/renderer/buckets/heatmap_bucket.cpp new file mode 100644 index 0000000000..a185e04ad2 --- /dev/null +++ b/src/mbgl/renderer/buckets/heatmap_bucket.cpp @@ -0,0 +1,98 @@ +#include <mbgl/renderer/buckets/heatmap_bucket.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/programs/heatmap_program.hpp> +#include <mbgl/style/layers/heatmap_layer_impl.hpp> +#include <mbgl/renderer/layers/render_heatmap_layer.hpp> +#include <mbgl/util/constants.hpp> +#include <mbgl/util/math.hpp> + +namespace mbgl { + +using namespace style; + +HeatmapBucket::HeatmapBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) + : mode(parameters.mode) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace( + std::piecewise_construct, + std::forward_as_tuple(layer->getID()), + std::forward_as_tuple( + layer->as<RenderHeatmapLayer>()->evaluated, + parameters.tileID.overscaledZ)); + } +} + +void HeatmapBucket::upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(triangles)); + + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + + uploaded = true; +} + +bool HeatmapBucket::hasData() const { + return !segments.empty(); +} + +void HeatmapBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry) { + constexpr const uint16_t vertexLength = 4; + + for (auto& points : geometry) { + for(auto& point : points) { + auto x = point.x; + auto y = point.y; + + // 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::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()) { + // Move to a new segments because the old one can't hold the geometry. + segments.emplace_back(vertices.vertexSize(), triangles.indexSize()); + } + + // this geometry will be of the Point type, and we'll derive + // two triangles from it. + // + // ┌─────────┐ + // │ 4 3 │ + // │ │ + // │ 1 2 │ + // └─────────┘ + // + vertices.emplace_back(HeatmapProgram::vertex(point, -1, -1)); // 1 + vertices.emplace_back(HeatmapProgram::vertex(point, 1, -1)); // 2 + vertices.emplace_back(HeatmapProgram::vertex(point, 1, 1)); // 3 + vertices.emplace_back(HeatmapProgram::vertex(point, -1, 1)); // 4 + + auto& segment = segments.back(); + assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max()); + uint16_t index = segment.vertexLength; + + // 1, 2, 3 + // 1, 4, 3 + triangles.emplace_back(index, index + 1, index + 2); + triangles.emplace_back(index, index + 3, index + 2); + + segment.vertexLength += vertexLength; + segment.indexLength += 6; + } + } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } +} + +float HeatmapBucket::getQueryRadius(const RenderLayer& layer) const { + (void)layer; + return 0; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/heatmap_bucket.hpp b/src/mbgl/renderer/buckets/heatmap_bucket.hpp new file mode 100644 index 0000000000..3b9f1edb81 --- /dev/null +++ b/src/mbgl/renderer/buckets/heatmap_bucket.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/programs/segment.hpp> +#include <mbgl/programs/heatmap_program.hpp> +#include <mbgl/style/layers/heatmap_layer_properties.hpp> + +namespace mbgl { + +class BucketParameters; + +class HeatmapBucket : public Bucket { +public: + HeatmapBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; + bool hasData() const override; + + void upload(gl::Context&) override; + + float getQueryRadius(const RenderLayer&) const override; + + gl::VertexVector<HeatmapLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> triangles; + SegmentVector<HeatmapAttributes> segments; + + optional<gl::VertexBuffer<HeatmapLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + + std::map<std::string, HeatmapProgram::PaintPropertyBinders> paintPropertyBinders; + + const MapMode mode; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/hillshade_bucket.cpp b/src/mbgl/renderer/buckets/hillshade_bucket.cpp new file mode 100644 index 0000000000..00b9536894 --- /dev/null +++ b/src/mbgl/renderer/buckets/hillshade_bucket.cpp @@ -0,0 +1,113 @@ +#include <mbgl/renderer/buckets/hillshade_bucket.hpp> +#include <mbgl/renderer/layers/render_hillshade_layer.hpp> +#include <mbgl/programs/hillshade_program.hpp> +#include <mbgl/programs/hillshade_prepare_program.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { + +using namespace style; + +HillshadeBucket::HillshadeBucket(PremultipliedImage&& image_, Tileset::DEMEncoding encoding): demdata(image_, encoding) { +} + +HillshadeBucket::HillshadeBucket(DEMData&& demdata_) : demdata(std::move(demdata_)) { +} + +const DEMData& HillshadeBucket::getDEMData() const { + return demdata; +} + +DEMData& HillshadeBucket::getDEMData() { + return demdata; +} + +void HillshadeBucket::upload(gl::Context& context) { + if (!hasData()) { + return; + } + + + const PremultipliedImage* image = demdata.getImage(); + dem = context.createTexture(*image); + + if (!segments.empty()) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(indices)); + } + uploaded = true; +} + +void HillshadeBucket::clear() { + vertexBuffer = {}; + indexBuffer = {}; + segments.clear(); + vertices.clear(); + indices.clear(); + + uploaded = false; +} + +void HillshadeBucket::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( + HillshadeProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(tlVertex.y) })); + vertices.emplace_back( + HillshadeProgram::layoutVertex({ brVertex.x, tlVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(tlVertex.y) })); + vertices.emplace_back( + HillshadeProgram::layoutVertex({ tlVertex.x, brVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(brVertex.y) })); + vertices.emplace_back( + HillshadeProgram::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 HillshadeBucket::hasData() const { + return demdata.getImage()->valid(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/hillshade_bucket.hpp b/src/mbgl/renderer/buckets/hillshade_bucket.hpp new file mode 100644 index 0000000000..5335f7ceda --- /dev/null +++ b/src/mbgl/renderer/buckets/hillshade_bucket.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/texture.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/programs/hillshade_program.hpp> +#include <mbgl/programs/hillshade_prepare_program.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/renderer/tile_mask.hpp> +#include <mbgl/geometry/dem_data.hpp> +#include <mbgl/util/tileset.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/util/optional.hpp> + +namespace mbgl { + +class HillshadeBucket : public Bucket { +public: + HillshadeBucket(PremultipliedImage&&, Tileset::DEMEncoding encoding); + HillshadeBucket(std::shared_ptr<PremultipliedImage>, Tileset::DEMEncoding encoding); + HillshadeBucket(DEMData&&); + + + void upload(gl::Context&) override; + bool hasData() const override; + + void clear(); + void setMask(TileMask&&); + + optional<gl::Texture> dem; + optional<gl::Texture> texture; + + TileMask mask{ { 0, 0, 0 } }; + + const DEMData& getDEMData() const; + DEMData& getDEMData(); + + bool isPrepared() const { + return prepared; + } + + void setPrepared (bool preparedState) { + prepared = preparedState; + } + + // Raster-DEM Tile Sources use the default buffers from Painter + gl::VertexVector<HillshadeLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> indices; + SegmentVector<HillshadeAttributes> segments; + + optional<gl::VertexBuffer<HillshadeLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; +private: + DEMData demdata; + bool prepared = false; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index 4abea90508..ed8afb052c 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -130,6 +130,7 @@ public: } collisionCircle; uint32_t bucketInstanceId = 0; + bool justReloaded = false; }; } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index 9fddba3f74..aebc4cc9aa 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -5,7 +5,7 @@ #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/programs/background_program.hpp> #include <mbgl/util/tile_cover.hpp> namespace mbgl { @@ -46,12 +46,8 @@ 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); + const Properties<>::PossiblyEvaluated properties; + const BackgroundProgram::PaintPropertyBinders paintAttributeData(properties, 0); if (!evaluated.get<BackgroundPattern>().to.empty()) { optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from); @@ -63,15 +59,15 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { parameters.imageManager.bind(parameters.context, 0); for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { - parameters.programs.fillPattern.get(properties).draw( + parameters.programs.backgroundPattern.draw( parameters.context, gl::Triangles(), parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), - FillPatternUniforms::values( + BackgroundPatternUniforms::values( parameters.matrixForTile(tileID), - parameters.context.viewport.getCurrentValue().size, + evaluated.get<BackgroundOpacity>(), parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB, @@ -82,7 +78,7 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { parameters.staticData.tileVertexBuffer, parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.tileTriangleSegments, - paintAttibuteData, + paintAttributeData, properties, parameters.state.getZoom(), getID() @@ -90,20 +86,21 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { } } else { for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { - parameters.programs.fill.get(properties).draw( + parameters.programs.background.draw( parameters.context, gl::Triangles(), parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), - FillProgram::UniformValues { + BackgroundProgram::UniformValues { uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) }, - uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + uniforms::u_color::Value{ evaluated.get<BackgroundColor>() }, + uniforms::u_opacity::Value{ evaluated.get<BackgroundOpacity>() }, }, parameters.staticData.tileVertexBuffer, parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.tileTriangleSegments, - paintAttibuteData, + paintAttributeData, properties, parameters.state.getZoom(), getID() diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp index fe2e7cd42d..6092ff5452 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.cpp +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -108,13 +108,12 @@ bool RenderCircleLayer::queryIntersectsFeature( bearing, pixelsToTileUnits); - // Evaluate function - auto circleRadius = evaluated.get<style::CircleRadius>() - .evaluate(feature, zoom, style::CircleRadius::defaultValue()) - * pixelsToTileUnits; + // Evaluate functions + auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature) * pixelsToTileUnits; + auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(zoom, feature) * pixelsToTileUnits; // Test intersection - return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius); + return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), radius + stroke); } } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp index 7ece3970da..a429b8d82e 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.cpp +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -5,6 +5,7 @@ #include <mbgl/renderer/bucket.hpp> #include <mbgl/style/layers/custom_layer_impl.hpp> #include <mbgl/map/transform_state.hpp> +#include <mbgl/gl/gl.hpp> namespace mbgl { @@ -43,20 +44,25 @@ std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, } void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) { - if (!initialized) { + if (context != impl().context || !initialized) { + //If the context changed, deinitialize the previous one before initializing the new one. + if (context && !contextDestroyed && impl().deinitializeFn) { + MBGL_CHECK_ERROR(impl().deinitializeFn(context)); + } + context = impl().context; assert(impl().initializeFn); - impl().initializeFn(impl().context); + MBGL_CHECK_ERROR(impl().initializeFn(impl().context)); initialized = true; } - gl::Context& context = paintParameters.context; + gl::Context& glContext = paintParameters.context; const TransformState& state = paintParameters.state; // Reset GL state to a known state so the CustomLayer always has a clean slate. - context.bindVertexArray = 0; - context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)); - context.setStencilMode(gl::StencilMode::disabled()); - context.setColorMode(paintParameters.colorModeForRenderPass()); + glContext.bindVertexArray = 0; + glContext.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)); + glContext.setStencilMode(gl::StencilMode::disabled()); + glContext.setColorMode(paintParameters.colorModeForRenderPass()); CustomLayerRenderParameters parameters; @@ -70,12 +76,12 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) parameters.fieldOfView = state.getFieldOfView(); assert(impl().renderFn); - impl().renderFn(impl().context, parameters); + MBGL_CHECK_ERROR(impl().renderFn(context, parameters)); // Reset the view back to our original one, just in case the CustomLayer changed // the viewport or Framebuffer. paintParameters.backend.bind(); - context.setDirtyState(); + glContext.setDirtyState(); } } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp index 32ed9da8da..6d1fea99d3 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.hpp +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -26,6 +26,7 @@ public: private: bool initialized = false; bool contextDestroyed = false; + void * context = nullptr; }; template <> diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp new file mode 100644 index 0000000000..0f9e3239ef --- /dev/null +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -0,0 +1,178 @@ +#include <mbgl/renderer/layers/render_heatmap_layer.hpp> +#include <mbgl/renderer/buckets/heatmap_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/heatmap_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/heatmap_layer.hpp> +#include <mbgl/style/layers/heatmap_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderHeatmapLayer::RenderHeatmapLayer(Immutable<style::HeatmapLayer::Impl> _impl) + : RenderLayer(style::LayerType::Heatmap, _impl), + unevaluated(impl().paint.untransitioned()), colorRamp({256, 1}) { +} + +const style::HeatmapLayer::Impl& RenderHeatmapLayer::impl() const { + return static_cast<const style::HeatmapLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderHeatmapLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<HeatmapBucket>(parameters, layers); +} + +void RenderHeatmapLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderHeatmapLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = (evaluated.get<style::HeatmapOpacity>() > 0) + ? (RenderPass::Translucent | RenderPass::Pass3D) + : RenderPass::None; +} + +bool RenderHeatmapLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + if (parameters.pass == RenderPass::Pass3D) { + const auto& viewportSize = parameters.staticData.backendSize; + const auto size = Size{viewportSize.width / 4, viewportSize.height / 4}; + + if (!renderTexture || renderTexture->getSize() != size) { + if (parameters.context.supportsHalfFloatTextures) { + renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::HalfFloat); + + try { + renderTexture->bind(); + } catch (const std::runtime_error& ex) { + // can't render to a half-float texture; falling back to unsigned byte one + renderTexture = nullopt; + parameters.context.supportsHalfFloatTextures = false; + } + } + + if (!renderTexture) { + renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte); + renderTexture->bind(); + } + + } else { + renderTexture->bind(); + } + + if (!colorRampTexture) { + colorRampTexture = parameters.context.createTexture(colorRamp, 1, gl::TextureType::UnsignedByte); + } + + parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 1.0f }, {}, {}); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<HeatmapBucket*>(tile.tile.getBucket(*baseImpl))); + HeatmapBucket& bucket = *reinterpret_cast<HeatmapBucket*>(tile.tile.getBucket(*baseImpl)); + + const auto extrudeScale = tile.id.pixelsToTileUnits(1, parameters.state.getZoom()); + + const auto stencilMode = parameters.mapMode != MapMode::Continuous + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(); + + parameters.programs.heatmap.get(evaluated).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + stencilMode, + gl::ColorMode::additive(), + HeatmapProgram::UniformValues { + uniforms::u_intensity::Value{evaluated.get<style::HeatmapIntensity>()}, + uniforms::u_matrix::Value{tile.matrix}, + uniforms::heatmap::u_extrude_scale::Value{extrudeScale} + }, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + } + + } else if (parameters.pass == RenderPass::Translucent) { + parameters.context.bindTexture(renderTexture->getTexture(), 0, gl::TextureFilter::Linear); + parameters.context.bindTexture(*colorRampTexture, 1, gl::TextureFilter::Linear); + + 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.heatmapTexture.draw( + parameters.context, gl::Triangles(), gl::DepthMode::disabled(), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + HeatmapTextureProgram::UniformValues{ + uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, + uniforms::u_image::Value{ 0 }, + uniforms::u_color_ramp::Value{ 1 }, + uniforms::u_opacity::Value{ evaluated.get<HeatmapOpacity>() } }, + parameters.staticData.extrusionTextureVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.extrusionTextureSegments, + HeatmapTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, + parameters.state.getZoom(), getID()); + } +} + +void RenderHeatmapLayer::updateColorRamp() { + auto colorValue = unevaluated.get<HeatmapColor>().getValue(); + if (colorValue.isUndefined()) { + colorValue = HeatmapLayer::getDefaultHeatmapColor(); + } + + const auto length = colorRamp.bytes(); + + for (uint32_t i = 0; i < length; i += 4) { + const auto color = colorValue.evaluate(static_cast<double>(i) / length); + colorRamp.data[i + 0] = std::floor(color.r * 255); + colorRamp.data[i + 1] = std::floor(color.g * 255); + colorRamp.data[i + 2] = std::floor(color.b * 255); + colorRamp.data[i + 3] = std::floor(color.a * 255); + } + + if (colorRampTexture) { + colorRampTexture = nullopt; + } +} + +bool RenderHeatmapLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float zoom, + const float bearing, + const float pixelsToTileUnits) const { + (void) queryGeometry; + (void) feature; + (void) zoom; + (void) bearing; + (void) pixelsToTileUnits; + return false; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.hpp b/src/mbgl/renderer/layers/render_heatmap_layer.hpp new file mode 100644 index 0000000000..3f0b1f91b4 --- /dev/null +++ b/src/mbgl/renderer/layers/render_heatmap_layer.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/heatmap_layer_impl.hpp> +#include <mbgl/style/layers/heatmap_layer_properties.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/offscreen_texture.hpp> + +namespace mbgl { + +class RenderHeatmapLayer: public RenderLayer { +public: + RenderHeatmapLayer(Immutable<style::HeatmapLayer::Impl>); + ~RenderHeatmapLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + + bool queryIntersectsFeature( + const GeometryCoordinates&, + const GeometryTileFeature&, + const float, + const float, + const float) const override; + + void updateColorRamp(); + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + + // Paint properties + style::HeatmapPaintProperties::Unevaluated unevaluated; + style::HeatmapPaintProperties::PossiblyEvaluated evaluated; + + const style::HeatmapLayer::Impl& impl() const; + + PremultipliedImage colorRamp; + optional<OffscreenTexture> renderTexture; + optional<gl::Texture> colorRampTexture; +}; + +template <> +inline bool RenderLayer::is<RenderHeatmapLayer>() const { + return type == style::LayerType::Heatmap; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp new file mode 100644 index 0000000000..bcfd4ffe99 --- /dev/null +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -0,0 +1,164 @@ +#include <mbgl/renderer/layers/render_hillshade_layer.hpp> +#include <mbgl/renderer/buckets/hillshade_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/sources/render_raster_dem_source.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/hillshade_program.hpp> +#include <mbgl/programs/hillshade_prepare_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/hillshade_layer_impl.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/offscreen_texture.hpp> + +namespace mbgl { + +using namespace style; +RenderHillshadeLayer::RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl> _impl) + : RenderLayer(style::LayerType::Hillshade, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::HillshadeLayer::Impl& RenderHillshadeLayer::impl() const { + return static_cast<const style::HillshadeLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderHillshadeLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); + return nullptr; +} + +const std::array<float, 2> RenderHillshadeLayer::getLatRange(const UnwrappedTileID& id) { + const LatLng latlng0 = LatLng(id); + const LatLng latlng1 = LatLng(UnwrappedTileID(id.canonical.z, id.canonical.x, id.canonical.y + 1)); + return {{ (float)latlng0.latitude(), (float)latlng1.latitude() }}; +} + +const std::array<float, 2> RenderHillshadeLayer::getLight(const PaintParameters& parameters){ + float azimuthal = evaluated.get<HillshadeIlluminationDirection>() * util::DEG2RAD; + if (evaluated.get<HillshadeIlluminationAnchor>() == HillshadeIlluminationAnchorType::Viewport) azimuthal = azimuthal - parameters.state.getAngle(); + return {{evaluated.get<HillshadeExaggeration>(), azimuthal}}; +} + +void RenderHillshadeLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderHillshadeLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + passes = (evaluated.get<style::HillshadeExaggeration >() > 0) + ? (RenderPass::Translucent | RenderPass::Pass3D) + : RenderPass::None; +} + +bool RenderHillshadeLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src) { + if (parameters.pass != RenderPass::Translucent && parameters.pass != RenderPass::Pass3D) + return; + + RenderRasterDEMSource* demsrc = dynamic_cast<RenderRasterDEMSource*>(src); + const uint8_t TERRAIN_RGB_MAXZOOM = 15; + const uint8_t maxzoom = demsrc != nullptr ? demsrc->getMaxZoom() : TERRAIN_RGB_MAXZOOM; + + auto draw = [&] (const mat4& matrix, + const auto& vertexBuffer, + const auto& indexBuffer, + const auto& segments, + const UnwrappedTileID& id) { + parameters.programs.hillshade.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + HillshadeProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_image::Value{ 0 }, + uniforms::u_highlight::Value{ evaluated.get<HillshadeHighlightColor>() }, + uniforms::u_shadow::Value{ evaluated.get<HillshadeShadowColor>() }, + uniforms::u_accent::Value{ evaluated.get<HillshadeAccentColor>() }, + uniforms::u_light::Value{ getLight(parameters) }, + uniforms::u_latrange::Value{ getLatRange(id) }, + }, + vertexBuffer, + indexBuffer, + segments, + HillshadeProgram::PaintPropertyBinders { evaluated, 0 }, + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + mat4 mat; + matrix::ortho(mat, 0, util::EXTENT, -util::EXTENT, 0, 0, 1); + matrix::translate(mat, mat, 0, -util::EXTENT, 0); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<HillshadeBucket*>(tile.tile.getBucket(*baseImpl))); + HillshadeBucket& bucket = *reinterpret_cast<HillshadeBucket*>(tile.tile.getBucket(*baseImpl)); + if (!bucket.hasData()){ + continue; + } + + if (!bucket.isPrepared() && parameters.pass == RenderPass::Pass3D) { + const uint16_t tilesize = bucket.getDEMData().dim; + OffscreenTexture view(parameters.context, { tilesize, tilesize }); + view.bind(); + + parameters.context.bindTexture(*bucket.dem, 0, gl::TextureFilter::Nearest, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp); + const Properties<>::PossiblyEvaluated properties; + + parameters.programs.hillshadePrepare.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + HillshadePrepareProgram::UniformValues { + uniforms::u_matrix::Value { mat }, + uniforms::u_dimension::Value { {{uint16_t(tilesize * 2), uint16_t(tilesize * 2) }} }, + uniforms::u_zoom::Value{ float(tile.id.canonical.z) }, + uniforms::u_maxzoom::Value{ float(maxzoom) }, + uniforms::u_image::Value{ 0 } + }, + parameters.staticData.rasterVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.rasterSegments, + HillshadePrepareProgram::PaintPropertyBinders { properties, 0 }, + properties, + parameters.state.getZoom(), + getID() + ); + bucket.texture = std::move(view.getTexture()); + bucket.setPrepared(true); + } else if (parameters.pass == RenderPass::Translucent) { + assert(bucket.texture); + parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp); + + 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(parameters.matrixForTile(tile.id, true), + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + tile.id); + } else { + // Draw the full tile. + draw(parameters.matrixForTile(tile.id, true), + parameters.staticData.rasterVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.rasterSegments, + tile.id); + } + } + + + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.hpp b/src/mbgl/renderer/layers/render_hillshade_layer.hpp new file mode 100644 index 0000000000..13093ee7ef --- /dev/null +++ b/src/mbgl/renderer/layers/render_hillshade_layer.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/hillshade_layer_impl.hpp> +#include <mbgl/style/layers/hillshade_layer_properties.hpp> +#include <mbgl/tile/tile_id.hpp> + +namespace mbgl { + +class RenderHillshadeLayer: public RenderLayer { +public: + RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl>); + ~RenderHillshadeLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + + void render(PaintParameters&, RenderSource* src) override; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + + // Paint properties + style::HillshadePaintProperties::Unevaluated unevaluated; + style::HillshadePaintProperties::PossiblyEvaluated evaluated; + + const style::HillshadeLayer::Impl& impl() const; +private: + const std::array<float, 2> getLatRange(const UnwrappedTileID& id); + const std::array<float, 2> getLight(const PaintParameters& parameters); +}; + +template <> +inline bool RenderLayer::is<RenderHillshadeLayer>() const { + return type == style::LayerType::Hillshade; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp index 06616d90e5..b41b2ac560 100644 --- a/src/mbgl/renderer/layers/render_raster_layer.cpp +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -137,13 +137,13 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source 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, + draw(parameters.matrixForTile(tile.id, true), *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments); } else { // Draw the full tile. - draw(tile.matrix, + draw(parameters.matrixForTile(tile.id, true), parameters.staticData.rasterVertexBuffer, parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.rasterSegments); diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 04fcb2c3ab..9e493003c0 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -268,9 +268,10 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { gl::DepthMode::disabled(), gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), - CollisionBoxProgram::UniformValues { + CollisionCircleProgram::UniformValues { uniforms::u_matrix::Value{ tile.matrix }, uniforms::u_extrude_scale::Value{ extrudeScale }, + uniforms::u_overscale_factor::Value{ float(tile.tile.id.overscaleFactor()) }, uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() } }, *bucket.collisionCircle.vertexBuffer, diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index 58dd5597a5..a7f621eb61 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -35,6 +35,10 @@ PaintParameters::PaintParameters(gl::Context& context_, // Update the default matrices to the current viewport dimensions. state.getProjMatrix(projMatrix); + // Also compute a projection matrix that aligns with the current pixel grid, taking into account + // odd viewport sizes. + state.getProjMatrix(alignedProjMatrix, 1, true); + // 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. @@ -47,10 +51,10 @@ PaintParameters::PaintParameters(gl::Context& context_, } } -mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID) { +mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID, bool aligned) const { mat4 matrix; state.matrixFor(matrix, tileID); - matrix::multiply(matrix, projMatrix, matrix); + matrix::multiply(matrix, aligned ? alignedProjMatrix : projMatrix, matrix); return matrix; } diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index 5c934c2239..41f46ae34e 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -62,9 +62,10 @@ public: gl::StencilMode stencilModeForClipping(const ClipID&) const; gl::ColorMode colorModeForRenderPass() const; - mat4 matrixForTile(const UnwrappedTileID&); + mat4 matrixForTile(const UnwrappedTileID&, bool aligned = false) const; mat4 projMatrix; + mat4 alignedProjMatrix; mat4 nearClippedProjMatrix; int numSublayers = 3; diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp index eb2b74ffe0..bcdc175f14 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -4,9 +4,11 @@ #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_hillshade_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/layers/render_heatmap_layer.hpp> #include <mbgl/style/types.hpp> #include <mbgl/renderer/render_tile.hpp> @@ -26,12 +28,16 @@ std::unique_ptr<RenderLayer> RenderLayer::create(Immutable<Layer::Impl> impl) { return std::make_unique<RenderSymbolLayer>(staticImmutableCast<SymbolLayer::Impl>(impl)); case LayerType::Raster: return std::make_unique<RenderRasterLayer>(staticImmutableCast<RasterLayer::Impl>(impl)); + case LayerType::Hillshade: + return std::make_unique<RenderHillshadeLayer>(staticImmutableCast<HillshadeLayer::Impl>(impl)); case LayerType::Background: return std::make_unique<RenderBackgroundLayer>(staticImmutableCast<BackgroundLayer::Impl>(impl)); case LayerType::Custom: return std::make_unique<RenderCustomLayer>(staticImmutableCast<CustomLayer::Impl>(impl)); case LayerType::FillExtrusion: return std::make_unique<RenderFillExtrusionLayer>(staticImmutableCast<FillExtrusionLayer::Impl>(impl)); + case LayerType::Heatmap: + return std::make_unique<RenderHeatmapLayer>(staticImmutableCast<HeatmapLayer::Impl>(impl)); } // Not reachable, but placate GCC. diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp index 6624bb7d96..d160eb16e3 100644 --- a/src/mbgl/renderer/render_source.cpp +++ b/src/mbgl/renderer/render_source.cpp @@ -2,6 +2,7 @@ #include <mbgl/renderer/render_source_observer.hpp> #include <mbgl/renderer/sources/render_geojson_source.hpp> #include <mbgl/renderer/sources/render_raster_source.hpp> +#include <mbgl/renderer/sources/render_raster_dem_source.hpp> #include <mbgl/renderer/sources/render_vector_source.hpp> #include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/annotation/render_annotation_source.hpp> @@ -19,6 +20,8 @@ std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl) return std::make_unique<RenderVectorSource>(staticImmutableCast<VectorSource::Impl>(impl)); case SourceType::Raster: return std::make_unique<RenderRasterSource>(staticImmutableCast<RasterSource::Impl>(impl)); + case SourceType::RasterDEM: + return std::make_unique<RenderRasterDEMSource>(staticImmutableCast<RasterSource::Impl>(impl)); case SourceType::GeoJSON: return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl)); case SourceType::Video: diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index 8c84af4f1e..53519c763e 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -70,7 +70,7 @@ public: virtual std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const = 0; - virtual void onLowMemory() = 0; + virtual void reduceMemoryUse() = 0; virtual void dumpDebugLogs() const = 0; @@ -84,7 +84,7 @@ protected: bool enabled = false; - void onTileChanged(Tile&) final; + void onTileChanged(Tile&) override; void onTileError(Tile&, std::exception_ptr) final; }; diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp index ccf239e643..0b3937ded0 100644 --- a/src/mbgl/renderer/render_static_data.cpp +++ b/src/mbgl/renderer/render_static_data.cpp @@ -3,12 +3,12 @@ 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 })); +static gl::VertexVector<PositionOnlyLayoutAttributes::Vertex> tileVertices() { + gl::VertexVector<PositionOnlyLayoutAttributes::Vertex> result; + result.emplace_back(PositionOnlyLayoutAttributes::Vertex({{{ 0, 0 }}})); + result.emplace_back(PositionOnlyLayoutAttributes::Vertex({{{ util::EXTENT, 0 }}})); + result.emplace_back(PositionOnlyLayoutAttributes::Vertex({{{ 0, util::EXTENT }}})); + result.emplace_back(PositionOnlyLayoutAttributes::Vertex({{{ util::EXTENT, util::EXTENT }}})); return result; } diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp index cf58c31f4d..c2b54f3815 100644 --- a/src/mbgl/renderer/render_static_data.hpp +++ b/src/mbgl/renderer/render_static_data.hpp @@ -13,14 +13,14 @@ class RenderStaticData { public: RenderStaticData(gl::Context&, float pixelRatio, const optional<std::string>& programCacheDir); - gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer; + gl::VertexBuffer<PositionOnlyLayoutAttributes::Vertex> tileVertexBuffer; gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer; gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer; gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer; gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer; - SegmentVector<FillAttributes> tileTriangleSegments; + SegmentVector<BackgroundAttributes> tileTriangleSegments; SegmentVector<DebugAttributes> tileBorderSegments; SegmentVector<RasterAttributes> rasterSegments; SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments; diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index 8df31f8d7c..35b34833e4 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -72,7 +72,7 @@ void RenderTile::finishRender(PaintParameters& parameters) { return; static const style::Properties<>::PossiblyEvaluated properties {}; - static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0); if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) { if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() || @@ -98,7 +98,7 @@ void RenderTile::finishRender(PaintParameters& parameters) { *tile.debugBucket->vertexBuffer, *tile.debugBucket->indexBuffer, tile.debugBucket->segments, - paintAttibuteData, + paintAttributeData, properties, parameters.state.getZoom(), "debug" @@ -117,7 +117,7 @@ void RenderTile::finishRender(PaintParameters& parameters) { *tile.debugBucket->vertexBuffer, *tile.debugBucket->indexBuffer, tile.debugBucket->segments, - paintAttibuteData, + paintAttributeData, properties, parameters.state.getZoom(), "debug" @@ -138,7 +138,7 @@ void RenderTile::finishRender(PaintParameters& parameters) { parameters.staticData.tileVertexBuffer, parameters.staticData.tileBorderIndexBuffer, parameters.staticData.tileBorderSegments, - paintAttibuteData, + paintAttributeData, properties, parameters.state.getZoom(), "debug" diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp index 8953b419f7..1d2f2bb522 100644 --- a/src/mbgl/renderer/renderer.cpp +++ b/src/mbgl/renderer/renderer.cpp @@ -10,9 +10,10 @@ Renderer::Renderer(RendererBackend& backend, FileSource& fileSource_, Scheduler& scheduler_, GLContextMode contextMode_, - const optional<std::string> programCacheDir_) + const optional<std::string> programCacheDir_, + const optional<std::string> localFontFamily_) : impl(std::make_unique<Impl>(backend, pixelRatio_, fileSource_, scheduler_, - contextMode_, std::move(programCacheDir_))) { + contextMode_, std::move(programCacheDir_), std::move(localFontFamily_))) { } Renderer::~Renderer() { @@ -93,9 +94,9 @@ void Renderer::dumpDebugLogs() { impl->dumDebugLogs(); } -void Renderer::onLowMemory() { +void Renderer::reduceMemoryUse() { BackendScope guard { impl->backend }; - impl->onLowMemory(); + impl->reduceMemoryUse(); } } // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index aa138df662..2ac714e122 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -14,6 +14,8 @@ #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/layers/render_heatmap_layer.hpp> +#include <mbgl/renderer/layers/render_hillshade_layer.hpp> #include <mbgl/renderer/style_diff.hpp> #include <mbgl/renderer/query.hpp> #include <mbgl/renderer/backend_scope.hpp> @@ -42,7 +44,8 @@ Renderer::Impl::Impl(RendererBackend& backend_, FileSource& fileSource_, Scheduler& scheduler_, GLContextMode contextMode_, - const optional<std::string> programCacheDir_) + const optional<std::string> programCacheDir_, + const optional<std::string> localFontFamily_) : backend(backend_) , scheduler(scheduler_) , fileSource(fileSource_) @@ -50,7 +53,7 @@ Renderer::Impl::Impl(RendererBackend& backend_, , contextMode(contextMode_) , pixelRatio(pixelRatio_) , programCacheDir(programCacheDir_) - , glyphManager(std::make_unique<GlyphManager>(fileSource)) + , 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>>>()) @@ -183,6 +186,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (layerAdded || layerChanged) { layer.transition(transitionParameters); + + if (layer.is<RenderHeatmapLayer>()) { + layer.as<RenderHeatmapLayer>()->updateColorRamp(); + } } if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { @@ -288,7 +295,11 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { RenderLayer* layer = getRenderLayer(layerImpl->id); assert(layer); - if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) { + if (!parameters.staticData.has3D && ( + layer->is<RenderFillExtrusionLayer>() || + layer->is<RenderHillshadeLayer>() || + layer->is<RenderHeatmapLayer>())) { + parameters.staticData.has3D = true; } @@ -381,13 +392,20 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { bool placementChanged = false; if (!placement->stillRecent(parameters.timePoint)) { auto newPlacement = std::make_unique<Placement>(parameters.state, parameters.mapMode); + std::set<std::string> usedSymbolLayers; for (auto it = order.rbegin(); it != order.rend(); ++it) { if (it->layer.is<RenderSymbolLayer>()) { + usedSymbolLayers.insert(it->layer.getID()); newPlacement->placeLayer(*it->layer.as<RenderSymbolLayer>(), parameters.projMatrix, parameters.debugOptions & MapDebugOptions::Collision); } } placementChanged = newPlacement->commit(*placement, parameters.timePoint); + // commitFeatureIndexes depends on the assumption that no new FeatureIndex has been loaded since placement + // started. If we violate this assumption, then we need to either make CollisionIndex completely independendent of + // FeatureIndex, or find a way for its entries to point to multiple FeatureIndexes. + commitFeatureIndexes(); + crossTileSymbolIndex.pruneUnusedLayers(usedSymbolLayers); if (placementChanged || symbolBucketsChanged) { placement = std::move(newPlacement); } @@ -473,11 +491,11 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { { MBGL_DEBUG_GROUP(parameters.context, "clipping masks"); - static const style::FillPaintProperties::PossiblyEvaluated properties {}; - static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + static const Properties<>::PossiblyEvaluated properties {}; + static const ClippingMaskProgram::PaintPropertyBinders paintAttributeData(properties, 0); for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) { - parameters.staticData.programs.fill.get(properties).draw( + parameters.staticData.programs.clippingMask.draw( parameters.context, gl::Triangles(), gl::DepthMode::disabled(), @@ -490,14 +508,13 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { gl::StencilMode::Replace }, gl::ColorMode::disabled(), - FillProgram::UniformValues { + ClippingMaskProgram::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, + paintAttributeData, properties, parameters.state.getZoom(), "clipping" @@ -714,12 +731,12 @@ std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sour return source->querySourceFeatures(options); } -void Renderer::Impl::onLowMemory() { +void Renderer::Impl::reduceMemoryUse() { assert(BackendScope::exists()); - backend.getContext().performCleanup(); for (const auto& entry : renderSources) { - entry.second->onLowMemory(); + entry.second->reduceMemoryUse(); } + backend.getContext().performCleanup(); observer->onInvalidate(); } @@ -768,6 +785,15 @@ bool Renderer::Impl::hasTransitions(TimePoint timePoint) const { return false; } +void Renderer::Impl::commitFeatureIndexes() { + for (auto& source : renderSources) { + for (auto& renderTile : source.second->getRenderTiles()) { + Tile& tile = renderTile.get().tile; + tile.commitFeatureIndex(); + } + } +} + void Renderer::Impl::updateFadingTiles() { fadingTiles = false; for (auto& source : renderSources) { diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 4f8139791c..4675ac79ea 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -38,7 +38,7 @@ class Renderer::Impl : public GlyphManagerObserver, public RenderSourceObserver{ public: Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode, - const optional<std::string> programCacheDir); + const optional<std::string> programCacheDir, const optional<std::string> localFontFamily); ~Impl() final; void markContextLost() { @@ -53,7 +53,7 @@ public: std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const; std::vector<Feature> queryShapeAnnotations(const ScreenLineString&) const; - void onLowMemory(); + void reduceMemoryUse(); void dumDebugLogs(); private: @@ -74,6 +74,7 @@ private: void onTileChanged(RenderSource&, const OverscaledTileID&) override; void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; + void commitFeatureIndexes(); void updateFadingTiles(); friend class Renderer; diff --git a/src/mbgl/renderer/renderer_observer.hpp b/src/mbgl/renderer/renderer_observer.hpp deleted file mode 100644 index 551b5c803e..0000000000 --- a/src/mbgl/renderer/renderer_observer.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#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 index 111f0234ed..057ad5a4a7 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -44,6 +44,7 @@ void RenderCustomGeometrySource::update(Immutable<style::Source::Impl> baseImpl_ SourceType::CustomVector, util::tileSize, impl().getZoomRange(), + {}, [&] (const OverscaledTileID& tileID) { return std::make_unique<CustomGeometryTile>(tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader); }); @@ -75,8 +76,8 @@ std::vector<Feature> RenderCustomGeometrySource::querySourceFeatures(const Sourc return tilePyramid.querySourceFeatures(options); } -void RenderCustomGeometrySource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderCustomGeometrySource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderCustomGeometrySource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp index 82e691d5c9..033d731029 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp @@ -33,7 +33,7 @@ public: std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index d07cfcdc41..cbf4db70b5 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -62,6 +62,7 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_, SourceType::GeoJSON, util::tileSize, impl().getZoomRange(), + optional<LatLngBounds>{}, [&] (const OverscaledTileID& tileID) { return std::make_unique<GeoJSONTile>(tileID, impl().id, parameters, data->getTile(tileID.canonical)); }); @@ -93,8 +94,8 @@ std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryO return tilePyramid.querySourceFeatures(options); } -void RenderGeoJSONSource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderGeoJSONSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderGeoJSONSource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index 55166ea901..72fccbd043 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -37,7 +37,7 @@ public: std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index b5c42584e0..31a5916a34 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -41,7 +41,7 @@ void RenderImageSource::startRender(PaintParameters& parameters) { mat4 matrix; matrix::identity(matrix); parameters.state.matrixFor(matrix, tileIds[i]); - matrix::multiply(matrix, parameters.projMatrix, matrix); + matrix::multiply(matrix, parameters.alignedProjMatrix, matrix); matrices.push_back(matrix); } @@ -56,7 +56,7 @@ void RenderImageSource::finishRender(PaintParameters& parameters) { } static const style::Properties<>::PossiblyEvaluated properties {}; - static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0); for (auto matrix : matrices) { parameters.programs.debug.draw( @@ -72,7 +72,7 @@ void RenderImageSource::finishRender(PaintParameters& parameters) { parameters.staticData.tileVertexBuffer, parameters.staticData.tileBorderIndexBuffer, parameters.staticData.tileBorderSegments, - paintAttibuteData, + paintAttributeData, properties, parameters.state.getZoom(), "debug" @@ -114,44 +114,43 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_, return; } - auto size = transformState.getSize(); - const double viewportHeight = size.height; - - // Compute the screen coordinates at wrap=0 for the given LatLng - ScreenCoordinate nePixel = { -INFINITY, -INFINITY }; - ScreenCoordinate swPixel = { INFINITY, INFINITY }; - + // 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) { - ScreenCoordinate pixel = transformState.latLngToScreenCoordinate(latLng); - swPixel.x = std::min(swPixel.x, pixel.x); - nePixel.x = std::max(nePixel.x, pixel.x); - swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y); - nePixel.y = std::max(nePixel.y, viewportHeight - pixel.y); - } - const double width = nePixel.x - swPixel.x; - const double height = nePixel.y - swPixel.y; + 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); + } - // Don't bother drawing the ImageSource unless it occupies >4 screen pixels - enabled = (width * height > 4); + // Calculate the optimum zoom level to determine the tile ids to use for transforms + 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; } - // Calculate the optimum zoom level to determine the tile ids to use for transforms - double minScale = INFINITY; - double scaleX = double(size.width) / width; - double scaleY = double(size.height) / height; - minScale = util::min(scaleX, scaleY); - double zoom = transformState.getZoom() + util::log2(minScale); - zoom = std::floor(util::clamp(zoom, transformState.getMinZoom(), transformState.getMaxZoom())); auto imageBounds = LatLngBounds::hull(coords[0], coords[1]); imageBounds.extend(coords[2]); imageBounds.extend(coords[3]); auto tileCover = util::tileCover(imageBounds, zoom); tileIds.clear(); tileIds.push_back(tileCover[0]); - bool hasVisibleTile = false; + bool hasVisibleTile = false; // Add additional wrapped tile ids if neccessary auto idealTiles = util::tileCover(transformState, transformState.getZoom()); for (auto tile : idealTiles) { @@ -177,9 +176,8 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_, // 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); } if (!bucket) { diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp index 72cf4cea61..85ee0ace11 100644 --- a/src/mbgl/renderer/sources/render_image_source.hpp +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -37,7 +37,7 @@ public: std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final { + void reduceMemoryUse() final { } void dumpDebugLogs() const final; diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp new file mode 100644 index 0000000000..b3153622c3 --- /dev/null +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -0,0 +1,166 @@ +#include <mbgl/renderer/sources/render_raster_dem_source.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/tile/raster_dem_tile.hpp> +#include <mbgl/algorithm/update_tile_masks.hpp> +#include <mbgl/geometry/dem_data.hpp> +#include <mbgl/renderer/buckets/hillshade_bucket.hpp> +#include <iostream> + +namespace mbgl { + +using namespace style; + +RenderRasterDEMSource::RenderRasterDEMSource(Immutable<style::RasterSource::Impl> impl_) + : RenderSource(impl_) { + tilePyramid.setObserver(this); +} + +const style::RasterSource::Impl& RenderRasterDEMSource::impl() const { + return static_cast<const style::RasterSource::Impl&>(*baseImpl); +} + +bool RenderRasterDEMSource::isLoaded() const { + return tilePyramid.isLoaded(); +} + +void RenderRasterDEMSource::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; + + optional<Tileset> _tileset = impl().getTileset(); + + if (tileset != _tileset) { + tileset = _tileset; + maxzoom = tileset->zoomRange.max; + // TODO: this removes existing buckets, and will cause flickering. + // Should instead refresh tile data in place. + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + tilePyramid.cache.clear(); + } + // Allow clearing the tile pyramid first, before the early return in case + // the new tileset is not yet available or has an error in loading + if (!_tileset) { + return; + } + + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::RasterDEM, + impl().getTileSize(), + tileset->zoomRange, + tileset->bounds, + [&] (const OverscaledTileID& tileID) { + return std::make_unique<RasterDEMTile>(tileID, parameters, *tileset); + }); +} + +void RenderRasterDEMSource::onTileChanged(Tile& tile){ + RasterDEMTile& demtile = static_cast<RasterDEMTile&>(tile); + + std::map<DEMTileNeighbors, DEMTileNeighbors> opposites = { + { DEMTileNeighbors::Left, DEMTileNeighbors::Right }, + { DEMTileNeighbors::Right, DEMTileNeighbors::Left }, + { DEMTileNeighbors::TopLeft, DEMTileNeighbors::BottomRight }, + { DEMTileNeighbors::TopCenter, DEMTileNeighbors::BottomCenter }, + { DEMTileNeighbors::TopRight, DEMTileNeighbors::BottomLeft }, + { DEMTileNeighbors::BottomRight, DEMTileNeighbors::TopLeft }, + { DEMTileNeighbors::BottomCenter, DEMTileNeighbors:: TopCenter }, + { DEMTileNeighbors::BottomLeft, DEMTileNeighbors::TopRight } + }; + + if (tile.isRenderable() && demtile.neighboringTiles != DEMTileNeighbors::Complete) { + const CanonicalTileID canonical = tile.id.canonical; + const uint32_t dim = std::pow(2, canonical.z); + const uint32_t px = (canonical.x - 1 + dim) % dim; + const int pxw = canonical.x == 0 ? tile.id.wrap - 1 : tile.id.wrap; + const uint32_t nx = (canonical.x + 1 + dim) % dim; + const int nxw = (canonical.x + 1 == dim) ? tile.id.wrap + 1 : tile.id.wrap; + + auto getNeighbor = [&] (DEMTileNeighbors mask){ + if (mask == DEMTileNeighbors::Left){ + return OverscaledTileID(tile.id.overscaledZ, pxw, canonical.z, px, canonical.y); + } else if (mask == DEMTileNeighbors::Right){ + return OverscaledTileID(tile.id.overscaledZ, nxw, canonical.z, nx, canonical.y); + } else if (mask == DEMTileNeighbors::TopLeft){ + return OverscaledTileID(tile.id.overscaledZ, pxw, canonical.z, px, canonical.y - 1); + } else if (mask == DEMTileNeighbors::TopCenter){ + return OverscaledTileID(tile.id.overscaledZ, tile.id.wrap, canonical.z, canonical.x, canonical.y - 1); + } else if (mask == DEMTileNeighbors::TopRight){ + return OverscaledTileID(tile.id.overscaledZ, nxw, canonical.z, nx, canonical.y - 1); + } else if (mask == DEMTileNeighbors::BottomLeft){ + return OverscaledTileID(tile.id.overscaledZ, pxw, canonical.z, px, canonical.y + 1); + } else if (mask == DEMTileNeighbors::BottomCenter){ + return OverscaledTileID(tile.id.overscaledZ, tile.id.wrap, canonical.z, canonical.x, canonical.y + 1); + } else if (mask == DEMTileNeighbors::BottomRight){ + return OverscaledTileID(tile.id.overscaledZ, nxw, canonical.z, nx, canonical.y + 1); + } else{ + throw std::runtime_error("mask is not a valid tile neighbor"); + } + }; + + for (uint8_t i = 0; i < 8; i++) { + DEMTileNeighbors mask = DEMTileNeighbors(std::pow(2,i)); + // only backfill if this neighbor has not been previously backfilled + if ((demtile.neighboringTiles & mask) != mask) { + OverscaledTileID neighborid = getNeighbor(mask); + Tile* renderableNeighbor = tilePyramid.getTile(neighborid); + if (renderableNeighbor != nullptr && renderableNeighbor->isRenderable()) { + RasterDEMTile& borderTile = static_cast<RasterDEMTile&>(*renderableNeighbor); + demtile.backfillBorder(borderTile, mask); + + // if the border tile has not been backfilled by a previous instance of the main + // tile, backfill its corresponding neighbor as well. + const DEMTileNeighbors& borderMask = opposites[mask]; + if ((borderTile.neighboringTiles & borderMask) != borderMask){ + borderTile.backfillBorder(demtile, borderMask); + } + } + } + } + } + RenderSource::onTileChanged(tile); +} + +void RenderRasterDEMSource::startRender(PaintParameters& parameters) { + algorithm::updateTileMasks(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); +} + +void RenderRasterDEMSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); +} + +std::vector<std::reference_wrapper<RenderTile>> RenderRasterDEMSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); +} + +std::unordered_map<std::string, std::vector<Feature>> +RenderRasterDEMSource::queryRenderedFeatures(const ScreenLineString&, + const TransformState&, + const std::vector<const RenderLayer*>&, + const RenderedQueryOptions&, + const CollisionIndex& ) const { + return std::unordered_map<std::string, std::vector<Feature>> {}; +} + +std::vector<Feature> RenderRasterDEMSource::querySourceFeatures(const SourceQueryOptions&) const { + return {}; +} + +void RenderRasterDEMSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); +} + +void RenderRasterDEMSource::dumpDebugLogs() const { + tilePyramid.dumpDebugLogs(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp new file mode 100644 index 0000000000..741214a14d --- /dev/null +++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include <mbgl/renderer/render_source.hpp> +#include <mbgl/renderer/tile_pyramid.hpp> +#include <mbgl/style/sources/raster_source_impl.hpp> + +namespace mbgl { + +class RenderRasterDEMSource : public RenderSource { +public: + RenderRasterDEMSource(Immutable<style::RasterSource::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 reduceMemoryUse() final; + void dumpDebugLogs() const final; + + uint8_t getMaxZoom() const { + return maxzoom; + }; + +private: + const style::RasterSource::Impl& impl() const; + + TilePyramid tilePyramid; + optional<Tileset> tileset; + uint8_t maxzoom = 15; + +protected: + void onTileChanged(Tile&) final; +}; + +template <> +inline bool RenderSource::is<RenderRasterDEMSource>() const { + return baseImpl->type == style::SourceType::RasterDEM; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index f11f9b7aed..60b3fa9a3b 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -29,14 +29,10 @@ void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_, enabled = needsRendering; - optional<Tileset> tileset = impl().getTileset(); + optional<Tileset> _tileset = impl().getTileset(); - if (!tileset) { - return; - } - - if (tileURLTemplates != tileset->tiles) { - tileURLTemplates = tileset->tiles; + if (tileset != _tileset) { + tileset = _tileset; // TODO: this removes existing buckets, and will cause flickering. // Should instead refresh tile data in place. @@ -44,6 +40,11 @@ void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_, tilePyramid.renderTiles.clear(); tilePyramid.cache.clear(); } + // Allow clearing the tile pyramid first, before the early return in case + // the new tileset is not yet available or has an error in loading + if (!_tileset) { + return; + } tilePyramid.update(layers, needsRendering, @@ -52,6 +53,7 @@ void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_, SourceType::Raster, impl().getTileSize(), tileset->zoomRange, + tileset->bounds, [&] (const OverscaledTileID& tileID) { return std::make_unique<RasterTile>(tileID, parameters, *tileset); }); @@ -83,8 +85,8 @@ std::vector<Feature> RenderRasterSource::querySourceFeatures(const SourceQueryOp return {}; } -void RenderRasterSource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderRasterSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderRasterSource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 25041fde43..78eda199ac 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -33,14 +33,14 @@ public: std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: const style::RasterSource::Impl& impl() const; TilePyramid tilePyramid; - optional<std::vector<std::string>> tileURLTemplates; + optional<Tileset> tileset; }; template <> diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index 49f8fdff2c..e87bea5dcd 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -32,14 +32,10 @@ void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_, enabled = needsRendering; - optional<Tileset> tileset = impl().getTileset(); + optional<Tileset> _tileset = impl().getTileset(); - if (!tileset) { - return; - } - - if (tileURLTemplates != tileset->tiles) { - tileURLTemplates = tileset->tiles; + if (tileset != _tileset) { + tileset = _tileset; // TODO: this removes existing buckets, and will cause flickering. // Should instead refresh tile data in place. @@ -47,6 +43,11 @@ void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_, tilePyramid.renderTiles.clear(); tilePyramid.cache.clear(); } + // Allow clearing the tile pyramid first, before the early return in case + // the new tileset is not yet available or has an error in loading + if (!_tileset) { + return; + } tilePyramid.update(layers, needsRendering, @@ -55,6 +56,7 @@ void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_, SourceType::Vector, util::tileSize, tileset->zoomRange, + tileset->bounds, [&] (const OverscaledTileID& tileID) { return std::make_unique<VectorTile>(tileID, impl().id, parameters, *tileset); }); @@ -86,8 +88,8 @@ std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOp return tilePyramid.querySourceFeatures(options); } -void RenderVectorSource::onLowMemory() { - tilePyramid.onLowMemory(); +void RenderVectorSource::reduceMemoryUse() { + tilePyramid.reduceMemoryUse(); } void RenderVectorSource::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 4a992e854f..592160dc16 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -33,14 +33,14 @@ public: std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void onLowMemory() final; + void reduceMemoryUse() final; void dumpDebugLogs() const final; private: const style::VectorSource::Impl& impl() const; TilePyramid tilePyramid; - optional<std::vector<std::string>> tileURLTemplates; + optional<Tileset> tileset; }; template <> diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index c1566d12a5..8f83a0f982 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -7,6 +7,7 @@ #include <mbgl/map/transform.hpp> #include <mbgl/math/clamp.hpp> #include <mbgl/util/tile_cover.hpp> +#include <mbgl/util/tile_range.hpp> #include <mbgl/util/enum.hpp> #include <mbgl/util/logging.hpp> @@ -14,6 +15,7 @@ #include <mapbox/geometry/envelope.hpp> +#include <cmath> #include <algorithm> namespace mbgl { @@ -54,6 +56,11 @@ std::vector<std::reference_wrapper<RenderTile>> TilePyramid::getRenderTiles() { return { renderTiles.begin(), renderTiles.end() }; } +Tile* TilePyramid::getTile(const OverscaledTileID& tileID){ + auto it = tiles.find(tileID); + return it == tiles.end() ? cache.get(tileID) : it->second.get(); +} + void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layers, const bool needsRendering, const bool needsRelayout, @@ -61,6 +68,7 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer const SourceType type, const uint16_t tileSize, const Range<uint8_t> zoomRange, + optional<LatLngBounds> bounds, std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) { // If we need a relayout, abandon any cached tiles; they're now stale. if (needsRelayout) { @@ -93,20 +101,21 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer if (overscaledZoom >= zoomRange.min) { int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom); + // Make sure we're not reparsing overzoomed raster tiles. if (type == SourceType::Raster) { tileZoom = idealZoom; + } - // FIXME: Prefetching is only enabled for raster - // tiles until we fix #7026. - - // Request lower zoom level tiles (if configure to do so) in an attempt + // Only attempt prefetching in continuous mode. + if (parameters.mode == MapMode::Continuous) { + // 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) { + if (panZoom < idealZoom) { panTiles = util::tileCover(parameters.transformState, panZoom); } } @@ -134,8 +143,19 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer auto it = tiles.find(tileID); return it == tiles.end() ? nullptr : it->second.get(); }; + + // The min and max zoom for TileRange are based on the updateRenderables algorithm. + // Tiles are created at the ideal tile zoom or at lower zoom levels. Child + // tiles are used from the cache, but not created. + optional<util::TileRange> tileRange = {}; + if (bounds) { + tileRange = util::TileRange::fromLatLngBounds(*bounds, zoomRange.min, std::min(tileZoom, (int32_t)zoomRange.max)); + } auto createTileFn = [&](const OverscaledTileID& tileID) -> Tile* { - std::unique_ptr<Tile> tile = cache.get(tileID); + if (tileRange && !tileRange->contains(tileID.canonical)) { + return nullptr; + } + std::unique_ptr<Tile> tile = cache.pop(tileID); if (!tile) { tile = createTile(tileID); if (tile) { @@ -198,13 +218,6 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer 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); @@ -293,7 +306,7 @@ void TilePyramid::setCacheSize(size_t size) { cache.setSize(size); } -void TilePyramid::onLowMemory() { +void TilePyramid::reduceMemoryUse() { cache.clear(); } diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index feab8a838c..2638599c38 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -40,12 +40,14 @@ public: style::SourceType type, uint16_t tileSize, Range<uint8_t> zoomRange, + optional<LatLngBounds> bounds, std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile); void startRender(PaintParameters&); void finishRender(PaintParameters&); std::vector<std::reference_wrapper<RenderTile>> getRenderTiles(); + Tile* getTile(const OverscaledTileID&); std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, @@ -57,7 +59,7 @@ public: std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const; void setCacheSize(size_t); - void onLowMemory(); + void reduceMemoryUse(); void setObserver(TileObserver*); void dumpDebugLogs() const; |