diff options
author | Molly Lloyd <molly@mapbox.com> | 2018-06-14 14:35:39 -0700 |
---|---|---|
committer | Molly Lloyd <molly@mapbox.com> | 2018-08-27 13:13:47 -0700 |
commit | b6f27e00311c1e0a17352a7e20cce596dc615dfe (patch) | |
tree | 8af7a11635a0c5be5d91774613cabdd52fdd261f | |
parent | f44d0dedc276a6ddc807eadc11d3f172e55ae21a (diff) | |
download | qtlocation-mapboxgl-b6f27e00311c1e0a17352a7e20cce596dc615dfe.tar.gz |
[core] add patterns to ImageAtlas and make their positions available in ppbinders
[core] convert line-pattern to a cross-faded ddp
[core] convert line-pattern draw code to use new shader uniforms
[core] use tile's iconAtlas for line-pattern properties
[core] fix cross-faded composite attribute bindings
[core] use intermediate PatternLayout object to populate pattern layer buckets
[core] revert addFeature to accept a reference
[core] pass pixelRatio as a parameter to LinePatternProgram
[core] try to fix compile errs
[core] add platform style code
[core] more compile errors :knife:
46 files changed, 823 insertions, 257 deletions
diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp index 9350b3d102..530f531d2f 100644 --- a/include/mbgl/style/layers/line_layer.hpp +++ b/include/mbgl/style/layers/line_layer.hpp @@ -114,9 +114,9 @@ public: void setLineDasharrayTransition(const TransitionOptions&); TransitionOptions getLineDasharrayTransition() const; - static PropertyValue<std::string> getDefaultLinePattern(); - PropertyValue<std::string> getLinePattern() const; - void setLinePattern(PropertyValue<std::string>); + static DataDrivenPropertyValue<std::string> getDefaultLinePattern(); + DataDrivenPropertyValue<std::string> getLinePattern() const; + void setLinePattern(DataDrivenPropertyValue<std::string>); void setLinePatternTransition(const TransitionOptions&); TransitionOptions getLinePatternTransition() const; diff --git a/include/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp index cd8c0fecbb..d1a9e226b1 100644 --- a/include/mbgl/util/indexed_tuple.hpp +++ b/include/mbgl/util/indexed_tuple.hpp @@ -28,8 +28,6 @@ class IndexedTuple<TypeList<Is...>, TypeList<Ts...>> : public tuple_polyfill<Ts. public: static_assert(sizeof...(Is) == sizeof...(Ts), "IndexedTuple size mismatch"); - using tuple_polyfill<Ts...>::tuple; - template <class I> auto& get() { return get_polyfill<TypeIndex<I, Is...>::value>(*this); @@ -40,6 +38,9 @@ public: return get_polyfill<TypeIndex<I, Is...>::value>(*this); } + template <class... Us> + IndexedTuple(Us&&... other) : tuple_polyfill<Ts...>(std::forward<Us>(other)...) {} + template <class... Js, class... Us> IndexedTuple<TypeList<Is..., Js...>, TypeList<Ts..., Us...>> concat(const IndexedTuple<TypeList<Js...>, TypeList<Us...>>& other) const { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java index e35f0edcc4..792dd84466 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java @@ -545,4 +545,20 @@ public class LineLayerTest extends BaseActivityTest { assertEquals((String) layer.getLinePattern().getValue(), (String) "pedestrian-polygon"); }); } + + @Test + public void testLinePatternAsExpression() { + validateTestSetup(); + setupLayer(); + Timber.i("line-pattern-expression"); + invoke(mapboxMap, (uiController, mapboxMap) -> { + assertNotNull(layer); + + // Set and Get + Expression expression = string(Expression.get("undefined")); + layer.setProperties(linePattern(expression)); + assertEquals(layer.getLinePattern().getExpression(), expression); + }); + } + } diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h index a7ac7f1462..28bb286049 100644 --- a/platform/darwin/src/MGLLineStyleLayer.h +++ b/platform/darwin/src/MGLLineStyleLayer.h @@ -473,11 +473,8 @@ MGL_EXPORT * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables - * Step functions applied to the `$zoomLevel` variable - - This property does not support applying interpolation functions to the - `$zoomLevel` variable or applying interpolation or step functions to feature - attributes. + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ @property (nonatomic, null_resettable) NSExpression *linePattern; diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm index 5cf0762475..0f749f90af 100644 --- a/platform/darwin/src/MGLLineStyleLayer.mm +++ b/platform/darwin/src/MGLLineStyleLayer.mm @@ -396,7 +396,7 @@ namespace mbgl { - (void)setLinePattern:(NSExpression *)linePattern { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(linePattern, false); + auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(linePattern, true); self.rawLayer->setLinePattern(mbglValue); } diff --git a/platform/darwin/test/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm index 4c0f91ba3b..3b00f02c0a 100644 --- a/platform/darwin/test/MGLLineStyleLayerTests.mm +++ b/platform/darwin/test/MGLLineStyleLayerTests.mm @@ -661,7 +661,7 @@ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Line Pattern'"]; layer.linePattern = constantExpression; - mbgl::style::PropertyValue<std::string> propertyValue = { "Line Pattern" }; + mbgl::style::DataDrivenPropertyValue<std::string> propertyValue = { "Line Pattern" }; XCTAssertEqual(rawLayer->getLinePattern(), propertyValue, @"Setting linePattern to a constant value expression should update line-pattern."); XCTAssertEqualObjects(layer.linePattern, constantExpression, @@ -689,12 +689,6 @@ @"Unsetting linePattern should return line-pattern to the default value."); XCTAssertEqualObjects(layer.linePattern, defaultExpression, @"linePattern should return the default value after being unset."); - - functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; - XCTAssertThrowsSpecificNamed(layer.linePattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); - functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}]; - functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}]; - XCTAssertThrowsSpecificNamed(layer.linePattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.linePatternTransition = transitionTest; auto toptions = rawLayer->getLinePatternTransition(); diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index f85c6d8fd0..3de7044ed3 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -56,21 +56,25 @@ global.evaluatedType = function (property) { function attributeUniformType(property, type) { const attributeNameExceptions = { - 'text-opacity': 'opacity', - 'icon-opacity': 'opacity', - 'text-color': 'fill_color', - 'icon-color': 'fill_color', - 'text-halo-color': 'halo_color', - 'icon-halo-color': 'halo_color', - 'text-halo-blur': 'halo_blur', - 'icon-halo-blur': 'halo_blur', - 'text-halo-width': 'halo_width', - 'icon-halo-width': 'halo_width', - 'line-gap-width': 'gapwidth' + 'text-opacity': ['opacity'], + 'icon-opacity': ['opacity'], + 'text-color': ['fill_color'], + 'icon-color': ['fill_color'], + 'text-halo-color': ['halo_color'], + 'icon-halo-color': ['halo_color'], + 'text-halo-blur': ['halo_blur'], + 'icon-halo-blur': ['halo_blur'], + 'text-halo-width': ['halo_width'], + 'icon-halo-width': ['halo_width'], + 'line-gap-width': ['gapwidth'], + 'line-pattern': ['pattern_to', 'pattern_from'] } - const name = attributeNameExceptions[property.name] || - property.name.replace(type + '-', '').replace(/-/g, '_'); - return `attributes::a_${name}${name === 'offset' ? '<1>' : ''}, uniforms::u_${name}`; + const names = attributeNameExceptions[property.name] || + [ property.name.replace(type + '-', '').replace(/-/g, '_') ]; + + return names.map(name => { + return `attributes::a_${name}${name === 'offset' ? '<1>' : ''}, uniforms::u_${name}` + }).join(', '); } global.layoutPropertyType = function (property) { @@ -86,8 +90,9 @@ global.layoutPropertyType = function (property) { global.paintPropertyType = function (property, type) { switch (property['property-type']) { case 'data-driven': - case 'cross-faded-data-driven': return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`; + case 'cross-faded-data-driven': + return `CrossFadedDataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`; case 'cross-faded': return `CrossFadedPaintProperty<${evaluatedType(property)}>`; default: diff --git a/src/mbgl/gl/uniform.cpp b/src/mbgl/gl/uniform.cpp index 2946980c19..3d8a8d53d9 100644 --- a/src/mbgl/gl/uniform.cpp +++ b/src/mbgl/gl/uniform.cpp @@ -79,6 +79,11 @@ void bindUniform<std::array<uint16_t, 2>>(UniformLocation location, const std::a bindUniform(location, util::convert<float>(t)); } +template <> +void bindUniform<std::array<uint16_t, 4>>(UniformLocation location, const std::array<uint16_t, 4>& t) { + bindUniform(location, util::convert<float>(t)); +} + // Add more as needed. #ifndef NDEBUG @@ -125,6 +130,12 @@ bool verifyUniform<std::array<float, 3>>(const ActiveUniform& uniform) { } template <> +bool verifyUniform<std::array<float, 4>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec4); + return true; +} + +template <> bool verifyUniform<std::array<double, 16>>(const ActiveUniform& uniform) { assert(uniform.size == 1 && uniform.type == UniformDataType::FloatMat4); return true; @@ -168,6 +179,14 @@ bool verifyUniform<std::array<uint16_t, 2>>(const ActiveUniform& uniform) { return true; } +template <> +bool verifyUniform<std::array<uint16_t, 4>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::IntVec4 || + uniform.type == UniformDataType::FloatVec4)); + return true; +} + // Add more as needed. #endif diff --git a/src/mbgl/gl/uniform.hpp b/src/mbgl/gl/uniform.hpp index d8a167c382..7b6489f34e 100644 --- a/src/mbgl/gl/uniform.hpp +++ b/src/mbgl/gl/uniform.hpp @@ -103,7 +103,7 @@ public: : false)... }); #endif - return State { { uniformLocation(id, Us::name()) }... }; + return State(uniformLocation(id, Us::name())...); } template <class Program> diff --git a/src/mbgl/layout/pattern_layout.cpp b/src/mbgl/layout/pattern_layout.cpp new file mode 100644 index 0000000000..82e29cfa4c --- /dev/null +++ b/src/mbgl/layout/pattern_layout.cpp @@ -0,0 +1,51 @@ +#include <mbgl/layout/pattern_layout.hpp> + + +namespace mbgl { + +using namespace style; + +PatternLayout::PatternLayout(const BucketParameters& parameters, + const std::vector<const RenderLayer*>& layers, + std::unique_ptr<GeometryTileLayer> sourceLayer_, + ImageDependencies& patternDependencies) + : bucketLeaderID(layers.at(0)->getID()), + sourceLayer(std::move(sourceLayer_)), + zoom(parameters.tileID.overscaledZ), + overscaling(parameters.tileID.overscaleFactor()) { + + const LineLayer::Impl& leader = layers.at(0)->as<RenderLineLayer>()->impl(); + layout = leader.layout.evaluate(PropertyEvaluationParameters(zoom)); + + for (const auto& layer : layers) { + const RenderLinePaintProperties::PossiblyEvaluated evaluatedProps = layer->as<RenderLineLayer>()->paintProperties(); + layerPaintProperties.emplace(layer->getID(), std::move(evaluatedProps)); + const auto patterns = evaluatedProps.get<LinePattern>().possibleOutputs(); + + for (auto& pattern : patterns) { + const auto patternString = pattern.value_or(""); + if (!patternString.empty()) { + patternDependencies.emplace(patternString, ImageType::Pattern); + } + } + } + const size_t featureCount = sourceLayer->featureCount(); + for (size_t i = 0; i < featureCount; ++i) { + auto feature = sourceLayer->getFeature(i); + if (!leader.filter(expression::EvaluationContext { this->zoom, feature.get() })) + continue; + features.push_back(std::move(feature)); + } +} + +std::unique_ptr<LineBucket> PatternLayout::createLayout(const ImagePositions& patternPositions) { + auto bucket = std::make_unique<LineBucket>(layout, layerPaintProperties, zoom, overscaling); + for (auto & feature : features) { + GeometryCollection geometries = feature->getGeometries(); + + bucket->addFeature(*feature, geometries, patternPositions); + } + return bucket; +}; + +} // namespace mbgl diff --git a/src/mbgl/layout/pattern_layout.hpp b/src/mbgl/layout/pattern_layout.hpp new file mode 100644 index 0000000000..d48f1bfc62 --- /dev/null +++ b/src/mbgl/layout/pattern_layout.hpp @@ -0,0 +1,29 @@ +#pragma once +#include <mbgl/renderer/buckets/line_bucket.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> + +namespace mbgl { + class LineBucket; + +class PatternLayout { +public: + PatternLayout(const BucketParameters&, + const std::vector<const RenderLayer*>&, + std::unique_ptr<GeometryTileLayer>, + ImageDependencies&); + + std::unique_ptr<LineBucket> createLayout(const ImagePositions&); + std::map<std::string, RenderLinePaintProperties::PossiblyEvaluated> layerPaintProperties; + + const std::string bucketLeaderID; +private: + const std::unique_ptr<GeometryTileLayer> sourceLayer; + std::vector<std::unique_ptr<GeometryTileFeature>> features; + style::LineLayoutProperties::PossiblyEvaluated layout; + + const float zoom; + const uint32_t overscaling; +}; +} // namespace mbgl + diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index ab718351ab..5ee2e3d188 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -136,7 +136,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasIcon) { ft.icon = layout.evaluate<IconImage>(zoom, ft); - imageDependencies.insert(*ft.icon); + imageDependencies.emplace(*ft.icon, ImageType::Icon); } if (ft.text || ft.icon) { @@ -462,8 +462,8 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(const bool showCollisionBoxes) } for (auto& pair : bucket->paintPropertyBinders) { - pair.second.first.populateVertexVectors(feature, bucket->icon.vertices.vertexSize()); - pair.second.second.populateVertexVectors(feature, bucket->text.vertices.vertexSize()); + pair.second.first.populateVertexVectors(feature, bucket->icon.vertices.vertexSize(), {}); + pair.second.second.populateVertexVectors(feature, bucket->text.vertices.vertexSize(), {}); } } diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index f1f1bdaa3f..ce3e7a7668 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -147,6 +147,16 @@ struct a_weight { using Type = gl::AttributeType<float, 1>; }; +struct a_pattern_to { + static auto name() { return "a_pattern_to"; } + using Type = gl::AttributeType<uint16_t, 4>; +}; + +struct a_pattern_from { + static auto name() { return "a_pattern_from"; } + using Type = gl::AttributeType<uint16_t, 4>; +}; + } // namespace attributes struct PositionOnlyLayoutAttributes : gl::Attributes< diff --git a/src/mbgl/programs/line_program.cpp b/src/mbgl/programs/line_program.cpp index 0533a13c35..87b09edf7d 100644 --- a/src/mbgl/programs/line_program.cpp +++ b/src/mbgl/programs/line_program.cpp @@ -87,31 +87,23 @@ LinePatternProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvalu const TransformState& state, const std::array<float, 2>& pixelsToGLUnits, const Size atlasSize, - const ImagePosition& posA, - const ImagePosition& posB) { - std::array<float, 2> sizeA {{ - tile.id.pixelsToTileUnits(posA.displaySize()[0] * properties.get<LinePattern>().fromScale, state.getIntegerZoom()), - posA.displaySize()[1] - }}; + const Faded<std::string> pattern, + const float pixelRatio) { + + const auto linepattern = properties.get<LinePattern>(); + const auto linePatternValue = linepattern.constantOr(mbgl::Faded<std::basic_string<char> >{ "", "", 2.0f, 1.0f, 0.5f}); - std::array<float, 2> sizeB {{ - tile.id.pixelsToTileUnits(posB.displaySize()[0] * properties.get<LinePattern>().toScale, state.getIntegerZoom()), - posB.displaySize()[1] - }}; + const auto tileRatio = 1 / tile.id.pixelsToTileUnits(1, state.getIntegerZoom()); return makeValues<LinePatternProgram::UniformValues>( properties, tile, state, pixelsToGLUnits, - uniforms::u_pattern_tl_a::Value{ posA.tl() }, - uniforms::u_pattern_br_a::Value{ posA.br() }, - uniforms::u_pattern_tl_b::Value{ posB.tl() }, - uniforms::u_pattern_br_b::Value{ posB.br() }, - uniforms::u_pattern_size_a::Value{ sizeA }, - uniforms::u_pattern_size_b::Value{ sizeB }, + // TODO get real pixel ratio + uniforms::u_scale::Value{ {{ pixelRatio, tileRatio, pattern.fromScale, pattern.toScale}} }, uniforms::u_texsize::Value{ atlasSize }, - uniforms::u_fade::Value{ properties.get<LinePattern>().t }, + uniforms::u_fade::Value{ linePatternValue.t }, uniforms::u_image::Value{ 0 } ); } diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp index 632dbe92b9..f6fe5c09c8 100644 --- a/src/mbgl/programs/line_program.hpp +++ b/src/mbgl/programs/line_program.hpp @@ -9,7 +9,7 @@ #include <mbgl/shaders/line_sdf.hpp> #include <mbgl/util/geometry.hpp> #include <mbgl/renderer/layers/render_line_layer.hpp> - +#include <mbgl/renderer/cross_faded_property_evaluator.hpp> #include <cmath> namespace mbgl { @@ -107,12 +107,7 @@ class LinePatternProgram : public Program< uniforms::u_matrix, uniforms::u_ratio, uniforms::u_gl_units_to_pixels, - uniforms::u_pattern_tl_a, - uniforms::u_pattern_br_a, - uniforms::u_pattern_tl_b, - uniforms::u_pattern_br_b, - uniforms::u_pattern_size_a, - uniforms::u_pattern_size_b, + uniforms::u_scale, uniforms::u_texsize, uniforms::u_fade, uniforms::u_image>, @@ -126,8 +121,8 @@ public: const TransformState&, const std::array<float, 2>& pixelsToGLUnits, Size atlasSize, - const ImagePosition& posA, - const ImagePosition& posB); + const Faded<std::string> pattern, + const float pixelRatio); }; class LineSDFProgram : public Program< diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp index 071a27c808..43d94df162 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -45,6 +45,9 @@ namespace heatmap { MBGL_DEFINE_UNIFORM_SCALAR(float, u_extrude_scale); } // namespace heatmap +MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 4, u_pattern_from); +MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 4, u_pattern_to); +MBGL_DEFINE_UNIFORM_VECTOR(float, 4, u_scale); MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_a); MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_br_a); MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_b); diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp index f48593ae49..62be423d66 100644 --- a/src/mbgl/renderer/bucket.hpp +++ b/src/mbgl/renderer/bucket.hpp @@ -3,7 +3,8 @@ #include <mbgl/util/noncopyable.hpp> #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/style/layer_type.hpp> - +#include <mbgl/style/image_impl.hpp> +#include <mbgl/renderer/image_atlas.hpp> #include <atomic> namespace mbgl { @@ -41,7 +42,11 @@ public: // 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&) {}; + const GeometryCollection&, + const mbgl::ImagePositions&) {}; + + virtual void populateFeatureBuffers(const ImagePositions&) {}; + virtual void addPatternDependencies(const std::vector<const RenderLayer*>&, ImageDependencies&) {}; // As long as this bucket has a Prepare render pass, this function is getting called. Typically, // this only happens once when the bucket is being rendered for the first time. diff --git a/src/mbgl/renderer/buckets/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp index d07c1f8dbe..e2f5c51a7b 100644 --- a/src/mbgl/renderer/buckets/circle_bucket.cpp +++ b/src/mbgl/renderer/buckets/circle_bucket.cpp @@ -39,7 +39,8 @@ bool CircleBucket::hasData() const { } void CircleBucket::addFeature(const GeometryTileFeature& feature, - const GeometryCollection& geometry) { + const GeometryCollection& geometry, + const ImagePositions&) { constexpr const uint16_t vertexLength = 4; for (auto& circle : geometry) { @@ -87,7 +88,7 @@ void CircleBucket::addFeature(const GeometryTileFeature& feature, } for (auto& pair : paintPropertyBinders) { - pair.second.populateVertexVectors(feature, vertices.vertexSize()); + pair.second.populateVertexVectors(feature, vertices.vertexSize(), {}); } } diff --git a/src/mbgl/renderer/buckets/circle_bucket.hpp b/src/mbgl/renderer/buckets/circle_bucket.hpp index 3c5f96fb15..b2bc008fb3 100644 --- a/src/mbgl/renderer/buckets/circle_bucket.hpp +++ b/src/mbgl/renderer/buckets/circle_bucket.hpp @@ -17,8 +17,9 @@ class CircleBucket : public Bucket { public: CircleBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); - void addFeature(const GeometryTileFeature&, - const GeometryCollection&) override; + virtual void addFeature(const GeometryTileFeature&, + const GeometryCollection&, + const mbgl::ImagePositions&) override; bool hasData() const override; void upload(gl::Context&) override; diff --git a/src/mbgl/renderer/buckets/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp index 14be98c3af..a4d8ea6f43 100644 --- a/src/mbgl/renderer/buckets/fill_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_bucket.cpp @@ -40,7 +40,8 @@ FillBucket::FillBucket(const BucketParameters& parameters, const std::vector<con } void FillBucket::addFeature(const GeometryTileFeature& feature, - const GeometryCollection& geometry) { + const GeometryCollection& geometry, + const ImagePositions&) { for (auto& polygon : classifyRings(geometry)) { // Optimize polygons with many interior rings for earcut tesselation. limitHoles(polygon, 500); @@ -105,7 +106,7 @@ void FillBucket::addFeature(const GeometryTileFeature& feature, } for (auto& pair : paintPropertyBinders) { - pair.second.populateVertexVectors(feature, vertices.vertexSize()); + pair.second.populateVertexVectors(feature, vertices.vertexSize(), {}); } } diff --git a/src/mbgl/renderer/buckets/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp index 20b65da39c..edfabd8eac 100644 --- a/src/mbgl/renderer/buckets/fill_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_bucket.hpp @@ -18,8 +18,9 @@ class FillBucket : public Bucket { public: FillBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); - void addFeature(const GeometryTileFeature&, - const GeometryCollection&) override; + virtual void addFeature(const GeometryTileFeature&, + const GeometryCollection&, + const mbgl::ImagePositions&) override; bool hasData() const override; void upload(gl::Context&) override; diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp index c6dba38db1..fc6e0df5f9 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -46,7 +46,8 @@ FillExtrusionBucket::FillExtrusionBucket(const BucketParameters& parameters, con } void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, - const GeometryCollection& geometry) { + const GeometryCollection& geometry, + const ImagePositions&) { for (auto& polygon : classifyRings(geometry)) { // Optimize polygons with many interior rings for earcut tesselation. limitHoles(polygon, 500); @@ -143,7 +144,7 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, } for (auto& pair : paintPropertyBinders) { - pair.second.populateVertexVectors(feature, vertices.vertexSize()); + pair.second.populateVertexVectors(feature, vertices.vertexSize(), {}); } } diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp index ed98e01292..7e874d58d3 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp @@ -16,8 +16,9 @@ class FillExtrusionBucket : public Bucket { public: FillExtrusionBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); - void addFeature(const GeometryTileFeature&, - const GeometryCollection&) override; + virtual void addFeature(const GeometryTileFeature&, + const GeometryCollection&, + const mbgl::ImagePositions&) override; bool hasData() const override; void upload(gl::Context&) override; diff --git a/src/mbgl/renderer/buckets/heatmap_bucket.cpp b/src/mbgl/renderer/buckets/heatmap_bucket.cpp index eff0c60280..1dc231bc59 100644 --- a/src/mbgl/renderer/buckets/heatmap_bucket.cpp +++ b/src/mbgl/renderer/buckets/heatmap_bucket.cpp @@ -39,7 +39,8 @@ bool HeatmapBucket::hasData() const { } void HeatmapBucket::addFeature(const GeometryTileFeature& feature, - const GeometryCollection& geometry) { + const GeometryCollection& geometry, + const ImagePositions&) { constexpr const uint16_t vertexLength = 4; for (auto& points : geometry) { @@ -87,7 +88,7 @@ void HeatmapBucket::addFeature(const GeometryTileFeature& feature, } for (auto& pair : paintPropertyBinders) { - pair.second.populateVertexVectors(feature, vertices.vertexSize()); + pair.second.populateVertexVectors(feature, vertices.vertexSize(), {}); } } diff --git a/src/mbgl/renderer/buckets/heatmap_bucket.hpp b/src/mbgl/renderer/buckets/heatmap_bucket.hpp index 86b6f10296..d42f4027c7 100644 --- a/src/mbgl/renderer/buckets/heatmap_bucket.hpp +++ b/src/mbgl/renderer/buckets/heatmap_bucket.hpp @@ -17,8 +17,9 @@ class HeatmapBucket : public Bucket { public: HeatmapBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); - void addFeature(const GeometryTileFeature&, - const GeometryCollection&) override; + virtual void addFeature(const GeometryTileFeature&, + const GeometryCollection&, + const mbgl::ImagePositions&) override; bool hasData() const override; void upload(gl::Context&) override; diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp index 80149edafe..f47f805d58 100644 --- a/src/mbgl/renderer/buckets/line_bucket.cpp +++ b/src/mbgl/renderer/buckets/line_bucket.cpp @@ -11,34 +11,39 @@ namespace mbgl { using namespace style; -LineBucket::LineBucket(const BucketParameters& parameters, - const std::vector<const RenderLayer*>& layers, - const style::LineLayoutProperties::Unevaluated& layout_) - : Bucket(LayerType::Line), - layout(layout_.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ))), - overscaling(parameters.tileID.overscaleFactor()), - zoom(parameters.tileID.overscaledZ) { - for (const auto& layer : layers) { +LineBucket::LineBucket(const style::LineLayoutProperties::PossiblyEvaluated layout_, + std::map<std::string, RenderLinePaintProperties::PossiblyEvaluated> layerPaintProperties, + const float zoom_, + const uint32_t overscaling_) + : Bucket(LayerType::Line) + layout(layout_), + zoom(zoom_), + overscaling(overscaling_) { + + for (const auto& pair : layerPaintProperties) { paintPropertyBinders.emplace( std::piecewise_construct, - std::forward_as_tuple(layer->getID()), + std::forward_as_tuple(pair.first), std::forward_as_tuple( - layer->as<RenderLineLayer>()->evaluated, - parameters.tileID.overscaledZ)); + pair.second, + zoom)); } } + void LineBucket::addFeature(const GeometryTileFeature& feature, - const GeometryCollection& geometryCollection) { + const GeometryCollection& geometryCollection, + const mbgl::ImagePositions& patternPositions) { for (auto& line : geometryCollection) { addGeometry(line, feature); } for (auto& pair : paintPropertyBinders) { - pair.second.populateVertexVectors(feature, vertices.vertexSize()); + pair.second.populateVertexVectors(feature, vertices.vertexSize(), patternPositions); } } + /* * Sharp corners cause dashed lines to tilt because the distance along the line * is the same at both the inner and outer corners. To improve the appearance of diff --git a/src/mbgl/renderer/buckets/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp index 23d30ca416..4a0990a062 100644 --- a/src/mbgl/renderer/buckets/line_bucket.hpp +++ b/src/mbgl/renderer/buckets/line_bucket.hpp @@ -7,6 +7,7 @@ #include <mbgl/programs/segment.hpp> #include <mbgl/programs/line_program.hpp> #include <mbgl/style/layers/line_layer_properties.hpp> +#include <mbgl/style/image_impl.hpp> #include <vector> @@ -17,12 +18,15 @@ class RenderLineLayer; class LineBucket : public Bucket { public: - LineBucket(const BucketParameters&, - const std::vector<const RenderLayer*>&, - const style::LineLayoutProperties::Unevaluated&); + LineBucket(const style::LineLayoutProperties::PossiblyEvaluated layout, + std::map<std::string, RenderLinePaintProperties::PossiblyEvaluated> layerPaintProperties, + const float zoom, + const uint32_t overscaling); void addFeature(const GeometryTileFeature&, - const GeometryCollection&) override; + const GeometryCollection&, + const mbgl::ImagePositions& patternPositions) override; + bool hasData() const override; void upload(gl::Context&) override; @@ -63,8 +67,8 @@ private: std::ptrdiff_t e2; std::ptrdiff_t e3; - const uint32_t overscaling; const float zoom; + const uint32_t overscaling; float getLineWidth(const RenderLineLayer& layer) const; }; diff --git a/src/mbgl/renderer/data_driven_property_evaluator.hpp b/src/mbgl/renderer/data_driven_property_evaluator.hpp index f9452cc572..a5618cfb6c 100644 --- a/src/mbgl/renderer/data_driven_property_evaluator.hpp +++ b/src/mbgl/renderer/data_driven_property_evaluator.hpp @@ -3,6 +3,7 @@ #include <mbgl/style/property_value.hpp> #include <mbgl/renderer/property_evaluation_parameters.hpp> #include <mbgl/renderer/possibly_evaluated_property_value.hpp> +#include <mbgl/renderer/cross_faded_property_evaluator.hpp> namespace mbgl { @@ -40,4 +41,51 @@ private: T defaultValue; }; +template <typename T> +class DataDrivenPropertyEvaluator<Faded<T>> { +public: + using ResultType = PossiblyEvaluatedPropertyValue<Faded<T>>; + + DataDrivenPropertyEvaluator(const PropertyEvaluationParameters& parameters_, T defaultValue_) + : parameters(parameters_), + defaultValue(std::move(defaultValue_)) {} + + ResultType operator()(const T& constant) const { + return ResultType(calculate(constant, constant, constant)); + } + + ResultType operator()(const style::Undefined& ) const { + return ResultType(calculate(defaultValue, defaultValue, defaultValue)); + } + + ResultType operator()(const style::CameraFunction<T>& function) const { + const T evaluated = parameters.useIntegerZoom ? function.evaluate(floor(parameters.z)) : function.evaluate(parameters.z); + return ResultType(calculate(evaluated, evaluated, evaluated)); + } + + template <class Function> + ResultType operator()(const Function& function) const { + return ResultType(function); + } + + +private: + Faded<T> calculate(const T& min, const T& mid, const T& max) const { + const float z = parameters.z; + const float fraction = z - std::floor(z); + const std::chrono::duration<float> d = parameters.defaultFadeDuration; + 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 } + : Faded<T> { max, mid, 0.5f, 1.0f, 1 - (1 - t) * fraction }; + }; + + const PropertyEvaluationParameters& parameters; + T defaultValue; +}; + } // namespace mbgl diff --git a/src/mbgl/renderer/image_atlas.cpp b/src/mbgl/renderer/image_atlas.cpp index 8eee7c2095..b39c788ced 100644 --- a/src/mbgl/renderer/image_atlas.cpp +++ b/src/mbgl/renderer/image_atlas.cpp @@ -16,36 +16,56 @@ ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& i ) { } -ImageAtlas makeImageAtlas(const ImageMap& images) { +const mapbox::Bin& _packImage(mapbox::ShelfPack& pack, const style::Image::Impl& image, ImageAtlas& resultImage, ImageType imageType) { + const mapbox::Bin& bin = *pack.packOne(-1, + image.image.size.width + 2 * padding, + image.image.size.height + 2 * padding); + + resultImage.image.resize({ + static_cast<uint32_t>(pack.width()), + static_cast<uint32_t>(pack.height()) + }); + + PremultipliedImage::copy(image.image, + resultImage.image, + { 0, 0 }, + { + bin.x + padding, + bin.y + padding + }, + image.image.size); + uint32_t x = bin.x + padding, + y = bin.y + padding, + w = image.image.size.width, + h = image.image.size.height; + + if (imageType == ImageType::Pattern) { + // Add 1 pixel wrapped padding on each side of the image. + PremultipliedImage::copy(image.image, resultImage.image, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T + PremultipliedImage::copy(image.image, resultImage.image, { 0, 0 }, { x, y + h }, { w, 1 }); // B + PremultipliedImage::copy(image.image, resultImage.image, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L + PremultipliedImage::copy(image.image, resultImage.image, { 0, 0 }, { x + w, y }, { 1, h }); // R + } + return bin; +} + +ImageAtlas makeImageAtlas(const ImageMap& icons, const ImageMap& patterns) { ImageAtlas result; mapbox::ShelfPack::ShelfPackOptions options; options.autoResize = true; mapbox::ShelfPack pack(0, 0, options); - for (const auto& entry : images) { + for (const auto& entry : icons) { const style::Image::Impl& image = *entry.second; + const mapbox::Bin& bin = _packImage(pack, image, result, ImageType::Icon); + result.iconPositions.emplace(image.id, ImagePosition { bin, image }); + } - const mapbox::Bin& bin = *pack.packOne(-1, - image.image.size.width + 2 * padding, - image.image.size.height + 2 * padding); - - result.image.resize({ - static_cast<uint32_t>(pack.width()), - static_cast<uint32_t>(pack.height()) - }); - - PremultipliedImage::copy(image.image, - result.image, - { 0, 0 }, - { - bin.x + padding, - bin.y + padding - }, - image.image.size); - - result.positions.emplace(image.id, - ImagePosition { bin, image }); + for (const auto& entry : patterns) { + const style::Image::Impl& image = *entry.second; + const mapbox::Bin& bin = _packImage(pack, image, result, ImageType::Pattern); + result.patternPositions.emplace(image.id, ImagePosition { bin, image }); } pack.shrink(); diff --git a/src/mbgl/renderer/image_atlas.hpp b/src/mbgl/renderer/image_atlas.hpp index b3cc166eff..56c47e19a0 100644 --- a/src/mbgl/renderer/image_atlas.hpp +++ b/src/mbgl/renderer/image_atlas.hpp @@ -43,9 +43,10 @@ using ImagePositions = std::map<std::string, ImagePosition>; class ImageAtlas { public: PremultipliedImage image; - ImagePositions positions; + ImagePositions iconPositions; + ImagePositions patternPositions; }; -ImageAtlas makeImageAtlas(const ImageMap&); +ImageAtlas makeImageAtlas(const ImageMap&, const ImageMap&); } // namespace mbgl diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp index 2ef6be0c4f..fc1f5bb167 100644 --- a/src/mbgl/renderer/image_manager.cpp +++ b/src/mbgl/renderer/image_manager.cpp @@ -67,7 +67,7 @@ void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair) bool hasAllDependencies = true; if (!isLoaded()) { for (const auto& dependency : pair.first) { - if (images.find(dependency) == images.end()) { + if (images.find(dependency.first) == images.end()) { hasAllDependencies = false; } } @@ -84,16 +84,17 @@ void ImageManager::removeRequestor(ImageRequestor& requestor) { } void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const { - ImageMap response; + ImageMap iconMap; + ImageMap patternMap; for (const auto& dependency : pair.first) { - auto it = images.find(dependency); + auto it = images.find(dependency.first); if (it != images.end()) { - response.emplace(*it); + dependency.second == ImageType::Pattern ? patternMap.emplace(*it) : iconMap.emplace(*it); } } - requestor.onImagesAvailable(response, pair.second); + requestor.onImagesAvailable(iconMap, patternMap, pair.second); } void ImageManager::dumpDebugLogs() const { diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp index f72ba9fb53..103491c58b 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, uint64_t imageCorrelationID) = 0; + virtual void onImagesAvailable(ImageMap icons, ImageMap patterns, uint64_t imageCorrelationID) = 0; }; /* diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index f204c909e1..1726b9192b 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -11,6 +11,8 @@ #include <mbgl/geometry/feature_index.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/intersection_tests.hpp> +#include <mbgl/tile/geometry_tile.hpp> +#include <mbgl/layout/pattern_layout.hpp> namespace mbgl { @@ -26,8 +28,19 @@ const style::LineLayer::Impl& RenderLineLayer::impl() const { return static_cast<const style::LineLayer::Impl&>(*baseImpl); } -std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<LineBucket>(parameters, layers, impl().layout); +std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); // Should be calling createLayout() instead. + return nullptr; +} + +std::unique_ptr<PatternLayout> RenderLineLayer::createLayout(const BucketParameters& parameters, + const std::vector<const RenderLayer*>& group, + std::unique_ptr<GeometryTileLayer> layer, + ImageDependencies& imageDependencies) const { + return std::make_unique<PatternLayout>(parameters, + group, + std::move(layer), + imageDependencies); } void RenderLineLayer::transition(const TransitionParameters& parameters) { @@ -65,11 +78,13 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { } LineBucket& bucket = *bucket_; - auto draw = [&] (auto& program, auto&& uniformValues) { + auto draw = [&] (auto& program, auto&& uniformValues, const optional<ImagePosition>& patternPositionA, const optional<ImagePosition>& patternPositionB) { auto& programInstance = program.get(evaluated); const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + paintPropertyBinders.setConstantPatternPositions(patternPositionA, patternPositionB); + const auto allUniformValues = programInstance.computeAllUniformValues( std::move(uniformValues), paintPropertyBinders, @@ -97,6 +112,9 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { getID() ); }; + const auto linepattern = evaluated.get<LinePattern>(); + // need a placeholder value that will trigget line pattern program if the line-pattern value is non-constant + const auto linePatternValue = linepattern.constantOr(mbgl::Faded<std::basic_string<char> >{ "temp", "temp", 2.0f, 1.0f, 0.5f}); if (!evaluated.get<LineDasharray>().from.empty()) { const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round @@ -115,16 +133,16 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { parameters.pixelsToGLUnits, posA, posB, - parameters.lineAtlas.getSize().width)); + 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); + } else if (!linePatternValue.from.empty()) { + assert(dynamic_cast<GeometryTile*>(&tile.tile)); + GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile); + parameters.context.bindTexture(*geometryTile.iconAtlasTexture, 0, gl::TextureFilter::Linear); + const Size texsize = geometryTile.iconAtlasTexture->size; - if (!posA || !posB) - return; - - parameters.imageManager.bind(parameters.context, 0); + optional<ImagePosition> posA = geometryTile.getPattern(linePatternValue.from); + optional<ImagePosition> posB = geometryTile.getPattern(linePatternValue.to); draw(parameters.programs.linePattern, LinePatternProgram::uniformValues( @@ -132,7 +150,9 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { tile, parameters.state, parameters.pixelsToGLUnits, - parameters.imageManager.getPixelSize(), + texsize, + linePatternValue, + parameters.pixelRatio), *posA, *posB)); } else if (!unevaluated.get<LineGradient>().getValue().isUndefined()) { @@ -146,14 +166,14 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { evaluated, tile, parameters.state, - parameters.pixelsToGLUnits)); + parameters.pixelsToGLUnits), {}, {}); } else { draw(parameters.programs.line, LineProgram::uniformValues( evaluated, tile, parameters.state, - parameters.pixelsToGLUnits)); + parameters.pixelsToGLUnits), {}, {}); } } } @@ -240,6 +260,22 @@ void RenderLineLayer::updateColorRamp() { } } +RenderLinePaintProperties::PossiblyEvaluated RenderLineLayer::paintProperties() const { + return RenderLinePaintProperties::PossiblyEvaluated { + evaluated.get<style::LineOpacity>(), + evaluated.get<style::LineColor>(), + evaluated.get<style::LineTranslate>(), + evaluated.get<style::LineTranslateAnchor>(), + evaluated.get<style::LineWidth>(), + evaluated.get<style::LineGapWidth>(), + evaluated.get<style::LineOffset>(), + evaluated.get<style::LineBlur>(), + evaluated.get<style::LineDasharray>(), + evaluated.get<style::LinePattern>(), + evaluated.get<LineFloorwidth>(), + }; +} + float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { float lineWidth = evaluated.get<style::LineWidth>() .evaluate(feature, zoom, style::LineWidth::defaultValue()); diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 1e0100548a..7694466e41 100644 --- a/src/mbgl/renderer/layers/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -4,10 +4,13 @@ #include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/style/layers/line_layer_properties.hpp> #include <mbgl/programs/uniforms.hpp> -#include <mbgl/util/image.hpp> +#include <mbgl/style/image_impl.hpp> +#include <mbgl/style/image_impl.hpp> namespace mbgl { +class PatternLayout; + struct LineFloorwidth : style::DataDrivenPaintProperty<float, attributes::a_floorwidth, uniforms::u_floorwidth> { static float defaultValue() { return 1; } }; @@ -26,6 +29,8 @@ public: bool hasTransition() const override; void render(PaintParameters&, RenderSource*) override; + RenderLinePaintProperties::PossiblyEvaluated paintProperties() const; + bool queryIntersectsFeature( const GeometryCoordinates&, const GeometryTileFeature&, @@ -37,7 +42,10 @@ public: void updateColorRamp(); std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; - + std::unique_ptr<PatternLayout> createLayout(const BucketParameters&, + const std::vector<const RenderLayer*>&, + std::unique_ptr<GeometryTileLayer>, + ImageDependencies&) const; // Paint properties style::LinePaintProperties::Unevaluated unevaluated; RenderLinePaintProperties::PossiblyEvaluated evaluated; diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp index 29eb716785..2a4105e5c8 100644 --- a/src/mbgl/renderer/paint_property_binder.hpp +++ b/src/mbgl/renderer/paint_property_binder.hpp @@ -7,6 +7,9 @@ #include <mbgl/util/type_list.hpp> #include <mbgl/renderer/possibly_evaluated_property_value.hpp> #include <mbgl/renderer/paint_property_statistics.hpp> +#include <mbgl/renderer/cross_faded_property_evaluator.hpp> +#include <mbgl/util/variant.hpp> +#include <mbgl/renderer/image_atlas.hpp> #include <bitset> @@ -72,54 +75,91 @@ std::array<float, N*2> zoomInterpolatedAttributeValue(const std::array<float, N> Note that the shader source varies depending on whether we're using a uniform or attribute. Like GL JS, we dynamically compile shaders at runtime to accomodate this. */ -template <class T, class... As> + +template <class T, class UniformValueType, class PossiblyEvaluatedType, class... As> class PaintPropertyBinder { public: virtual ~PaintPropertyBinder() = default; - virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) = 0; + virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&) = 0; virtual void upload(gl::Context& context) = 0; - virtual optional<gl::AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0; - virtual float interpolationFactor(float currentZoom) const = 0; - virtual T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0; + virtual void setConstantPatternPositions(const optional<ImagePosition>&, const optional<ImagePosition>&) = 0; + virtual std::tuple<ExpandToType<As, optional<gl::AttributeBinding>>...> attributeBinding(const PossiblyEvaluatedType& currentValue) const = 0; + virtual std::tuple<ExpandToType<As, float>...> interpolationFactor(float currentZoom) const = 0; + virtual std::tuple<ExpandToType<As, UniformValueType>...> uniformValue(const PossiblyEvaluatedType& currentValue) const = 0; - static std::unique_ptr<PaintPropertyBinder> create(const PossiblyEvaluatedPropertyValue<T>& value, float zoom, T defaultValue); + static std::unique_ptr<PaintPropertyBinder> create(const PossiblyEvaluatedType& value, float zoom, T defaultValue); PaintPropertyStatistics<T> statistics; }; template <class T, class A> -class ConstantPaintPropertyBinder : public PaintPropertyBinder<T, A> { +class ConstantPaintPropertyBinder : public PaintPropertyBinder<T, T, PossiblyEvaluatedPropertyValue<T>, A> { public: ConstantPaintPropertyBinder(T constant_) : constant(std::move(constant_)) { } - void populateVertexVector(const GeometryTileFeature&, std::size_t) override {} + void populateVertexVector(const GeometryTileFeature&, std::size_t, const ImagePositions&) override {} void upload(gl::Context&) override {} + void setConstantPatternPositions(const optional<ImagePosition>&, const optional<ImagePosition>&) override {}; - optional<gl::AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override { - return {}; + std::tuple<optional<gl::AttributeBinding>> attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override { + return std::tuple<optional<gl::AttributeBinding>> {}; } - float interpolationFactor(float) const override { - return 0.0f; + std::tuple<float> interpolationFactor(float) const override { + return std::tuple<float> { 0.0f }; } - T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { - return currentValue.constantOr(constant); + std::tuple<T> uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { + return std::tuple<T> { currentValue.constantOr(constant) }; } private: T constant; }; +template <class T, class... As> +class ConstantCrossFadedPaintPropertyBinder : public PaintPropertyBinder<T, std::array<uint16_t, 4>,PossiblyEvaluatedPropertyValue<Faded<T>>, As...> { +public: + ConstantCrossFadedPaintPropertyBinder(Faded<T> constant_) + : constant(std::move(constant_)), constantPatternPositions({}) { + } + + void populateVertexVector(const GeometryTileFeature&, std::size_t, const ImagePositions&) override {} + void upload(gl::Context&) override {} + + void setConstantPatternPositions(const optional<ImagePosition>& posA, const optional<ImagePosition>& posB) override { + if (!posA && !posB) { + return; + } else { + constantPatternPositions = std::tuple<std::array<uint16_t, 4>, std::array<uint16_t, 4>> { {{posB->tl()[0], posB->tl()[1], posB->br()[0], posB->br()[1]}}, {{posA->tl()[0], posA->tl()[1], posA->br()[0], posA->br()[1]}} }; + } + } + + std::tuple<optional<gl::AttributeBinding>, optional<gl::AttributeBinding>> attributeBinding(const PossiblyEvaluatedPropertyValue<Faded<T>>&) const override { + return std::tuple<ExpandToType<As, optional<gl::AttributeBinding>>...> {}; + } + + std::tuple<float, float> interpolationFactor(float) const override { + return std::tuple<float, float> { 0.0f, 0.0f }; + } + + std::tuple<std::array<uint16_t, 4>, std::array<uint16_t, 4>> uniformValue(const PossiblyEvaluatedPropertyValue<Faded<T>>&) const override { + return constantPatternPositions; + } + +private: + Faded<T> constant; + std::tuple<std::array<uint16_t, 4>, std::array<uint16_t, 4>> constantPatternPositions; +}; + template <class T, class A> -class SourceFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> { +class SourceFunctionPaintPropertyBinder : public PaintPropertyBinder<T, T, PossiblyEvaluatedPropertyValue<T>, A> { public: using BaseAttribute = A; - using BaseAttributeValue = typename BaseAttribute::Value; using BaseVertex = gl::detail::Vertex<BaseAttribute>; using AttributeType = ZoomInterpolatedAttributeType<A>; @@ -128,8 +168,8 @@ public: : expression(std::move(expression_)), defaultValue(std::move(defaultValue_)) { } - - void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override { + void setConstantPatternPositions(const optional<ImagePosition>&, const optional<ImagePosition>&) override {}; + void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&) override { auto evaluated = expression.evaluate(feature, defaultValue); this->statistics.add(evaluated); auto value = attributeValue(evaluated); @@ -142,21 +182,21 @@ public: vertexBuffer = context.createVertexBuffer(std::move(vertexVector)); } - optional<gl::AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { + std::tuple<optional<gl::AttributeBinding>> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { if (currentValue.isConstant()) { return {}; } else { - return AttributeType::binding(*vertexBuffer, 0, BaseAttribute::Dimensions); + return std::tuple<optional<gl::AttributeBinding>> { AttributeType::binding(*vertexBuffer, 0, BaseAttribute::Dimensions) }; } } - float interpolationFactor(float) const override { - return 0.0f; + std::tuple<float> interpolationFactor(float) const override { + return std::tuple<float> { 0.0f }; } - T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { + std::tuple<T> uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { if (currentValue.isConstant()) { - return *currentValue.constant(); + return std::tuple<T>{ *currentValue.constant() }; } else { // Uniform values for vertex attribute arrays are unused. return {}; @@ -171,7 +211,7 @@ private: }; template <class T, class A> -class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> { +class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder<T, T, PossiblyEvaluatedPropertyValue<T>, A> { public: using AttributeType = ZoomInterpolatedAttributeType<A>; @@ -183,8 +223,8 @@ public: defaultValue(std::move(defaultValue_)), zoomRange({zoom, zoom + 1}) { } - - void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override { + void setConstantPatternPositions(const optional<ImagePosition>&, const optional<ImagePosition>&) override {}; + void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&) override { Range<T> range = expression.evaluate(zoomRange, feature, defaultValue); this->statistics.add(range.min); this->statistics.add(range.max); @@ -200,25 +240,25 @@ public: vertexBuffer = context.createVertexBuffer(std::move(vertexVector)); } - optional<gl::AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { + std::tuple<optional<gl::AttributeBinding>> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { if (currentValue.isConstant()) { return {}; } else { - return AttributeType::binding(*vertexBuffer, 0); + return std::tuple<optional<gl::AttributeBinding>> { AttributeType::binding(*vertexBuffer, 0) }; } } - float interpolationFactor(float currentZoom) const override { - if (expression.useIntegerZoom) { - return expression.interpolationFactor(zoomRange, std::floor(currentZoom)); + std::tuple<float> interpolationFactor(float currentZoom) const override { + if (function.useIntegerZoom) { + return std::tuple<float> { expression.interpolationFactor(zoomRange, std::floor(currentZoom)) }; } else { - return expression.interpolationFactor(zoomRange, currentZoom); + return std::tuple<float> { expression.interpolationFactor(zoomRange, currentZoom) }; } } - T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { + std::tuple<T> uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { if (currentValue.isConstant()) { - return *currentValue.constant(); + return std::tuple<T> { *currentValue.constant() }; } else { // Uniform values for vertex attribute arrays are unused. return {}; @@ -233,21 +273,139 @@ private: optional<gl::VertexBuffer<Vertex>> vertexBuffer; }; -template <class T, class... As> -std::unique_ptr<PaintPropertyBinder<T, As...>> -PaintPropertyBinder<T, As...>::create(const PossiblyEvaluatedPropertyValue<T>& value, float zoom, T defaultValue) { - return value.match( - [&] (const T& constant) -> std::unique_ptr<PaintPropertyBinder<T, As...>> { - return std::make_unique<ConstantPaintPropertyBinder<T, As...>>(constant); - }, - [&] (const style::PropertyExpression<T>& expression) -> std::unique_ptr<PaintPropertyBinder<T, As...>> { - if (expression.isZoomConstant()) { - return std::make_unique<SourceFunctionPaintPropertyBinder<T, As...>>(expression, defaultValue); - } else { - return std::make_unique<CompositeFunctionPaintPropertyBinder<T, As...>>(expression, zoom, defaultValue); +template <class T, class A1, class A2> +class CompositeCrossFadedPaintPropertyBinder : public PaintPropertyBinder<T, std::array<uint16_t, 4>, PossiblyEvaluatedPropertyValue<Faded<T>>, A1, A2> { +public: + // to fix -- we will want to use both attributes + using AttributeType = ZoomInterpolatedAttributeType<A1>; + using AttributeType2 = ZoomInterpolatedAttributeType<A2>; + + using BaseAttribute = A1; + using BaseAttributeValue = typename BaseAttribute::Value; + + using BaseAttribute2 = A2; + using BaseAttributeValue2 = typename BaseAttribute2::Value; + + using Vertex = gl::detail::Vertex<BaseAttribute>; + using Vertex2 = gl::detail::Vertex<BaseAttribute2>; + + CompositeCrossFadedPaintPropertyBinder(style::PropertyExpression<T> expression_, float zoom, T defaultValue_) + : expression(std::move(expression_)), + defaultValue(std::move(defaultValue_)), + zoomRange({zoom, zoom + 1}) { + } + + void setConstantPatternPositions(const optional<ImagePosition>&, const optional<ImagePosition>&) override {}; + + void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions& patternPositions) override { + std::array<T, 3> range; + if (expression.isZoomConstant()) { + const T evaluatedValue = expression.evaluate(feature, defaultValue); + range = {{evaluatedValue, evaluatedValue, evaluatedValue}}; + } else { + range = {{expression.evaluate(zoomRange.min, feature, defaultValue), expression.evaluate(zoomRange.min, feature, defaultValue), expression.evaluate(zoomRange.min, feature, defaultValue)}}; + } + + if (!patternPositions.empty()) { + const auto min = patternPositions.find(range[0]); + const auto mid = patternPositions.find(range[1]); + const auto max = patternPositions.find(range[2]); + + const auto end = patternPositions.end(); + if (min == end || mid == end || max == end) return; + + const ImagePosition imageMin = min->second; + const ImagePosition imageMid = mid->second; + const ImagePosition imageMax = max->second; + + const BaseAttributeValue patternTo = {{ imageMid.tl()[0], imageMid.tl()[1], imageMid.br()[0], imageMid.br()[1] }}; + const BaseAttributeValue2 patternFromZoomIn = {{ imageMin.tl()[0], imageMin.tl()[1], imageMin.br()[0], imageMin.br()[1] }}; + const BaseAttributeValue2 patternFromZoomOut = {{ imageMax.tl()[0], imageMax.tl()[1], imageMax.br()[0], imageMax.br()[1] }}; + + for (std::size_t i = zoomInVertexVector.vertexSize(); i < length; ++i) { + patternToVertexVector.emplace_back(Vertex { patternTo }); + zoomInVertexVector.emplace_back(Vertex2 { patternFromZoomIn }); + zoomOutVertexVector.emplace_back(Vertex2 { patternFromZoomOut }); } } - ); + } + + void upload(gl::Context& context) override { + patternToVertexBuffer = context.createVertexBuffer(std::move(patternToVertexVector)); + zoomInVertexBuffer = context.createVertexBuffer(std::move(zoomInVertexVector)); + zoomOutVertexBuffer = context.createVertexBuffer(std::move(zoomOutVertexVector)); + } + + std::tuple<optional<gl::AttributeBinding>, optional<gl::AttributeBinding>> attributeBinding(const PossiblyEvaluatedPropertyValue<Faded<T>>& currentValue) const override { + if (currentValue.isConstant()) { + return {}; + } else { + return std::tuple<optional<gl::AttributeBinding>, optional<gl::AttributeBinding>> { AttributeType::binding(*patternToVertexBuffer, 0, BaseAttribute::Dimensions), AttributeType2::binding(*zoomInVertexBuffer, 0, BaseAttribute2::Dimensions) }; + } + } + + std::tuple<float, float> interpolationFactor(float) const override { + return std::tuple<float, float> { 0.0f, 0.0f }; + } + + std::tuple<std::array<uint16_t, 4>, std::array<uint16_t, 4>> uniformValue(const PossiblyEvaluatedPropertyValue<Faded<T>>& ) const override { + // Uniform values for vertex attribute arrays are unused. + return {}; + } + +private: + variant<style::CompositeFunction<T>, style::SourceFunction<T>> function; + T defaultValue; + Range<float> zoomRange; + gl::VertexVector<Vertex> patternToVertexVector; + gl::VertexVector<Vertex2> zoomInVertexVector; + gl::VertexVector<Vertex2> zoomOutVertexVector; + optional<gl::VertexBuffer<Vertex>> patternToVertexBuffer; + optional<gl::VertexBuffer<Vertex2>> zoomInVertexBuffer; + optional<gl::VertexBuffer<Vertex2>> zoomOutVertexBuffer; +}; + +template <class T, class PossiblyEvaluatedType> +struct CreateBinder { + template <class A> + static std::unique_ptr<PaintPropertyBinder<T, T, PossiblyEvaluatedType, A>> create(const PossiblyEvaluatedType& value, float zoom, T defaultValue) { + return value.match( + [&] (const T& constant) -> std::unique_ptr<PaintPropertyBinder<T, T, PossiblyEvaluatedType, A>> { + return std::make_unique<ConstantPaintPropertyBinder<T, A>>(constant); + }, + [&] (const style::PropertyExpression<T>& expression) -> std::unique_ptr<PaintPropertyBinder<T, A>> { + if (expression.isZoomConstant()) { + return std::make_unique<SourceFunctionPaintPropertyBinder<T, A>>(expression, defaultValue); + } else { + return std::make_unique<CompositeFunctionPaintPropertyBinder<T, A>>(expression, zoom, defaultValue); + } + } + ); + } +}; + +template <class T> +struct CreateBinder<T, PossiblyEvaluatedPropertyValue<Faded<T>>> { + template <class A1, class A2> + static std::unique_ptr<PaintPropertyBinder<T, std::array<uint16_t, 4>, PossiblyEvaluatedPropertyValue<Faded<T>>, A1, A2>> create(const PossiblyEvaluatedPropertyValue<Faded<T>>& value, float zoom, T defaultValue) { + return value.match( + [&] (const Faded<T>& constant) -> std::unique_ptr<PaintPropertyBinder<T, std::array<uint16_t, 4>, PossiblyEvaluatedPropertyValue<Faded<T>>, A1, A2>> { + return std::make_unique<ConstantCrossFadedPaintPropertyBinder<T, A1, A2>>(constant); + }, + [&] (const style::SourceFunction<T>& function) { + return std::make_unique<CompositeCrossFadedPaintPropertyBinder<T, A1, A2>>(expression, zoom, defaultValue); + }, + [&] (const style::CompositeFunction<T>& function) { + return std::make_unique<CompositeCrossFadedPaintPropertyBinder<T, A1, A2>>(expression, zoom, defaultValue); + } + ); + } +}; + +template <class T, class UniformValueType, class PossiblyEvaluatedType, class... As> +std::unique_ptr<PaintPropertyBinder<T, UniformValueType, PossiblyEvaluatedType, As... >> +PaintPropertyBinder<T, UniformValueType, PossiblyEvaluatedType, As...>::create(const PossiblyEvaluatedType& value, float zoom, T defaultValue) { + return CreateBinder<T, PossiblyEvaluatedType>::template create<As...>(value, zoom, defaultValue); } template <class Attr> @@ -270,17 +428,18 @@ class PaintPropertyBinders; template <class... Ps> class PaintPropertyBinders<TypeList<Ps...>> { private: - template <class T, class... As> + template <class T, class PossiblyEvaluatedType, class... As> struct Detail; - template <class T, class... As> - struct Detail<T, TypeList<As...>> { - using Binder = PaintPropertyBinder<T, typename As::Type...>; + template <class T, class UniformValueType, class PossiblyEvaluatedType, class... As> + struct Detail<T, UniformValueType, PossiblyEvaluatedType, TypeList<As...>> { + using Binder = PaintPropertyBinder<T, UniformValueType, PossiblyEvaluatedType, typename As::Type...>; using ZoomInterpolatedAttributeList = TypeList<ZoomInterpolatedAttribute<As>...>; + using InterpolationUniformList = TypeList<InterpolationUniform<As>...>; }; template <class P> - using Property = Detail<typename P::Type, typename P::Attributes>; + using Property = Detail<typename P::Type, typename P::Uniform::Type, typename P::PossiblyEvaluatedType, typename P::Attributes>; public: template <class P> @@ -299,9 +458,15 @@ public: PaintPropertyBinders(PaintPropertyBinders&&) = default; PaintPropertyBinders(const PaintPropertyBinders&) = delete; - void populateVertexVectors(const GeometryTileFeature& feature, std::size_t length) { + void populateVertexVectors(const GeometryTileFeature& feature, std::size_t length, const ImagePositions& patternPositions) { + util::ignore({ + (binders.template get<Ps>()->populateVertexVector(feature, length, patternPositions), 0)... + }); + } + + void setConstantPatternPositions(const optional<ImagePosition>& posA, const optional<ImagePosition>& posB) const { util::ignore({ - (binders.template get<Ps>()->populateVertexVector(feature, length), 0)... + (binders.template get<Ps>()->setConstantPatternPositions(posA, posB), 0)... }); } @@ -313,31 +478,32 @@ public: template <class P> using ZoomInterpolatedAttributeList = typename Property<P>::ZoomInterpolatedAttributeList; + template <class P> + using InterpolationUniformList = typename Property<P>::InterpolationUniformList; using Attributes = typename TypeListConcat<ZoomInterpolatedAttributeList<Ps>...>::template ExpandInto<gl::Attributes>; using AttributeBindings = typename Attributes::Bindings; template <class EvaluatedProperties> AttributeBindings attributeBindings(const EvaluatedProperties& currentProperties) const { - return AttributeBindings { - binders.template get<Ps>()->attributeBinding(currentProperties.template get<Ps>())... - }; + return AttributeBindings { std::tuple_cat( + binders.template get<Ps>()->attributeBinding(currentProperties.template get<Ps>())... + ) }; } - using Uniforms = gl::Uniforms<InterpolationUniform<typename Ps::Attribute>..., typename Ps::Uniform...>; + using Uniforms = typename TypeListConcat<InterpolationUniformList<Ps>..., typename Ps::Uniforms...>::template ExpandInto<gl::Uniforms>; using UniformValues = typename Uniforms::Values; template <class EvaluatedProperties> - UniformValues uniformValues(float currentZoom, const EvaluatedProperties& currentProperties) const { + UniformValues uniformValues(float currentZoom, EvaluatedProperties& currentProperties) const { (void)currentZoom; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958 - return UniformValues { - typename InterpolationUniform<typename Ps::Attribute>::Value { - binders.template get<Ps>()->interpolationFactor(currentZoom) - }..., - typename Ps::Uniform::Value { - binders.template get<Ps>()->uniformValue(currentProperties.template get<Ps>()) - }... - }; + return UniformValues ( + std::tuple_cat( + // interpolation uniform values + binders.template get<Ps>()->interpolationFactor(currentZoom)..., + // uniform values + binders.template get<Ps>()->uniformValue(currentProperties.template get<Ps>())...) + ); } template <class P> @@ -345,7 +511,6 @@ public: return binders.template get<P>()->statistics; } - using Bitset = std::bitset<sizeof...(Ps)>; template <class EvaluatedProperties> diff --git a/src/mbgl/renderer/possibly_evaluated_property_value.hpp b/src/mbgl/renderer/possibly_evaluated_property_value.hpp index f2d265f2f7..906df1d365 100644 --- a/src/mbgl/renderer/possibly_evaluated_property_value.hpp +++ b/src/mbgl/renderer/possibly_evaluated_property_value.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/style/property_expression.hpp> +#include <mbgl/renderer/cross_faded_property_evaluator.hpp> #include <mbgl/util/interpolate.hpp> #include <mbgl/util/variant.hpp> @@ -57,6 +58,74 @@ public: bool useIntegerZoom; }; +template <class T> +class PossiblyEvaluatedPropertyValue<Faded<T>> { +private: + using Value = variant< + Faded<T>, + style::PropertyExpression<T>>; + + Value value; + +public: + PossiblyEvaluatedPropertyValue() = default; + PossiblyEvaluatedPropertyValue(Value v, bool useIntegerZoom_ = false) + : value(std::move(v)), + useIntegerZoom(useIntegerZoom_) {} + + bool isConstant() const { + return value.template is<Faded<T>>(); + } + + optional<Faded<T>> constant() const { + return value.match( + [&] (const Faded<T>& t) { return optional<Faded<T>>(t); }, + [&] (const auto&) { return optional<Faded<T>>(); }); + } + + Faded<T> constantOr(const Faded<T>& t) const { + return constant().value_or(t); + } + + std::vector<optional<T>> possibleOutputs() const { + return this->match( + [&] (const Faded<T>& constant_) { + return std::vector<optional<T>>{ optional<T>(constant_.to), optional<T>(constant_.from) }; }, + [&] (const style::SourceFunction<T>& function) { + return function.possibleOutputs(); + }, + [&] (const style::CompositeFunction<T>& function) { + return function.possibleOutputs(); + } + ); + } + + template <class... Ts> + auto match(Ts&&... ts) const { + return value.match(std::forward<Ts>(ts)...); + } + + template <class Feature> + Faded<T> evaluate(const Feature& feature, float zoom, T defaultValue) const { + return this->match( + [&] (const Faded<T>& constant_) { return constant_; }, + [&] (const style::PropertyExpression<T>& expression) { + if (expression.isZoomDependent()) { + const T min = expression.evaluate(floor(zoom), feature, defaultValue); + const T max = expression.evaluate(floor(zoom) + 1, feature, defaultValue); + return Faded<T> {min, max, 0.0f, 0.0f, 0.0f}; + } else { + const T evaluated = expression.evaluate(feature, defaultValue); + return Faded<T> {evaluated, evaluated, 0.0f, 0.0f, 0.0f}; + } + } + ); + } + + bool useIntegerZoom; +}; + + namespace util { template <typename T> diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp index e439e42695..54b5e6487b 100644 --- a/src/mbgl/style/image_impl.hpp +++ b/src/mbgl/style/image_impl.hpp @@ -26,8 +26,13 @@ public: } // namespace style +enum class ImageType : bool { + Icon, + Pattern +}; + using ImageMap = std::unordered_map<std::string, Immutable<style::Image::Impl>>; -using ImageDependencies = std::set<std::string>; +using ImageDependencies = std::unordered_map<std::string, ImageType>; using ImageRequestPair = std::pair<ImageDependencies, uint64_t>; } // namespace mbgl diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp index 1ddc690cc7..6ca533b5fb 100644 --- a/src/mbgl/style/layers/line_layer.cpp +++ b/src/mbgl/style/layers/line_layer.cpp @@ -411,15 +411,15 @@ TransitionOptions LineLayer::getLineDasharrayTransition() const { return impl().paint.template get<LineDasharray>().options; } -PropertyValue<std::string> LineLayer::getDefaultLinePattern() { +DataDrivenPropertyValue<std::string> LineLayer::getDefaultLinePattern() { return { "" }; } -PropertyValue<std::string> LineLayer::getLinePattern() const { +DataDrivenPropertyValue<std::string> LineLayer::getLinePattern() const { return impl().paint.template get<LinePattern>().value; } -void LineLayer::setLinePattern(PropertyValue<std::string> value) { +void LineLayer::setLinePattern(DataDrivenPropertyValue<std::string> value) { if (value == getLinePattern()) return; auto impl_ = mutableImpl(); diff --git a/src/mbgl/style/layers/line_layer_properties.hpp b/src/mbgl/style/layers/line_layer_properties.hpp index 5fd349d38b..6f85eadafb 100644 --- a/src/mbgl/style/layers/line_layer_properties.hpp +++ b/src/mbgl/style/layers/line_layer_properties.hpp @@ -68,7 +68,7 @@ struct LineDasharray : CrossFadedPaintProperty<std::vector<float>> { static std::vector<float> defaultValue() { return { }; } }; -struct LinePattern : CrossFadedPaintProperty<std::string> { +struct LinePattern : CrossFadedDataDrivenPaintProperty<std::string, attributes::a_pattern_to, uniforms::u_pattern_to, attributes::a_pattern_from, uniforms::u_pattern_from> { static std::string defaultValue() { return ""; } }; diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp index 1afc1e5734..48d7609910 100644 --- a/src/mbgl/style/paint_property.hpp +++ b/src/mbgl/style/paint_property.hpp @@ -36,6 +36,23 @@ public: using Attribute = A; using Attributes = TypeList<A>; using Uniform = U; + using Uniforms = TypeList<U>; +}; + +template <class T, class A1, class U1, class A2, class U2> +class CrossFadedDataDrivenPaintProperty { +public: + using TransitionableType = Transitionable<DataDrivenPropertyValue<T>>; + using UnevaluatedType = Transitioning<DataDrivenPropertyValue<T>>; + using EvaluatorType = DataDrivenPropertyEvaluator<Faded<T>>; + using PossiblyEvaluatedType = PossiblyEvaluatedPropertyValue<Faded<T>>; + using Type = T; + static constexpr bool IsDataDriven = true; + + using Attribute = A1; + using Attributes = TypeList<A1, A2>; + using Uniforms = TypeList<U1, U2>; + using Uniform = U1; }; template <class T> diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 90d4d07895..22d35f26e9 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -137,8 +137,8 @@ void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelatio if (result.glyphAtlasImage) { glyphAtlasImage = std::move(*result.glyphAtlasImage); } - if (result.iconAtlasImage) { - iconAtlasImage = std::move(*result.iconAtlasImage); + if (result.iconAtlas.image.valid()) { + iconAtlas = std::move(result.iconAtlas); } observer->onTileChanged(*this); @@ -160,14 +160,22 @@ void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) { glyphManager.getGlyphs(*this, std::move(glyphDependencies)); } -void GeometryTile::onImagesAvailable(ImageMap images, uint64_t imageCorrelationID) { - worker.self().invoke(&GeometryTileWorker::onImagesAvailable, std::move(images), imageCorrelationID); +void GeometryTile::onImagesAvailable(ImageMap images, ImageMap patterns, uint64_t imageCorrelationID) { + worker.self().invoke(&GeometryTileWorker::onImagesAvailable, std::move(images), std::move(patterns), imageCorrelationID); } void GeometryTile::getImages(ImageRequestPair pair) { imageManager.getImages(*this, std::move(pair)); } +const optional<ImagePosition> GeometryTile::getPattern(const std::string& id) { + auto it = iconAtlas.patternPositions.find(id); + if (it != iconAtlas.patternPositions.end()) { + return it->second; + } + return {}; +} + void GeometryTile::upload(gl::Context& context) { auto uploadFn = [&] (Bucket& bucket) { if (bucket.needsUpload()) { @@ -184,9 +192,9 @@ void GeometryTile::upload(gl::Context& context) { glyphAtlasImage = {}; } - if (iconAtlasImage) { - iconAtlasTexture = context.createTexture(*iconAtlasImage, 0); - iconAtlasImage = {}; + if (iconAtlas.image.valid()) { + iconAtlasTexture = context.createTexture(iconAtlas.image, 0); + iconAtlas.image = {}; } } diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index af122474c2..e3632798cd 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -37,7 +37,7 @@ public: void setShowCollisionBoxes(const bool showCollisionBoxes) override; void onGlyphsAvailable(GlyphMap) override; - void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override; + void onImagesAvailable(ImageMap, ImageMap, uint64_t imageCorrelationID) override; void getGlyphs(GlyphDependencies); void getImages(ImageRequestPair); @@ -69,26 +69,26 @@ public: std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; std::unique_ptr<FeatureIndex> featureIndex; optional<AlphaImage> glyphAtlasImage; - optional<PremultipliedImage> iconAtlasImage; + ImageAtlas iconAtlas; LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets_, std::unique_ptr<FeatureIndex> featureIndex_, optional<AlphaImage> glyphAtlasImage_, - optional<PremultipliedImage> iconAtlasImage_) + ImageAtlas iconAtlas_) : buckets(std::move(buckets_)), featureIndex(std::move(featureIndex_)), glyphAtlasImage(std::move(glyphAtlasImage_)), - iconAtlasImage(std::move(iconAtlasImage_)) {} + iconAtlas(std::move(iconAtlas_)) {} }; void onLayout(LayoutResult, uint64_t correlationID); void onError(std::exception_ptr, uint64_t correlationID); - + bool holdForFade() const override; void markRenderedIdeal() override; void markRenderedPreviously() override; void performedFadePlacement() override; - + const optional<ImagePosition> getPattern(const std::string& id); const std::shared_ptr<FeatureIndex> getFeatureIndex() const { return latestFeatureIndex; } protected: @@ -118,6 +118,7 @@ private: optional<AlphaImage> glyphAtlasImage; optional<PremultipliedImage> iconAtlasImage; + ImageAtlas iconAtlas; const MapMode mode; diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 31f4b89801..3798d4f10b 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -2,12 +2,15 @@ #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/tile/geometry_tile.hpp> #include <mbgl/layout/symbol_layout.hpp> +#include <mbgl/layout/pattern_layout.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/renderer/group_by_layout.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/layers/render_line_layer.hpp> #include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/buckets/line_bucket.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/string.hpp> @@ -188,10 +191,10 @@ void GeometryTileWorker::symbolDependenciesChanged() { try { switch (state) { case Idle: - if (symbolLayoutsNeedPreparation) { - // symbolLayoutsNeedPreparation can only be set true by parsing + if (symbolLayoutsNeedPreparation || patternNeedsLayout) { + // symbolLayoutsNeedPreparation and patternNeedsLayout can only be set true by parsing // and the parse result can only be cleared by performSymbolLayout - // which also clears symbolLayoutsNeedPreparation + // which also clears symbolLayoutsNeedPreparation and patternNeedsLayout assert(hasPendingParseResult()); performSymbolLayout(); coalesce(); @@ -267,11 +270,12 @@ void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) { symbolDependenciesChanged(); } -void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap, uint64_t imageCorrelationID_) { +void GeometryTileWorker::onImagesAvailable(ImageMap newIconMap, ImageMap newPatternMap, uint64_t imageCorrelationID_) { if (imageCorrelationID != imageCorrelationID_) { return; // Ignore outdated image request replies. } - imageMap = std::move(newImageMap); + imageMap = std::move(newIconMap); + patternMap = std::move(newPatternMap); pendingImageDependencies.clear(); symbolDependenciesChanged(); } @@ -292,6 +296,7 @@ void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependen void GeometryTileWorker::requestNewImages(const ImageDependencies& imageDependencies) { pendingImageDependencies = imageDependencies; + if (!pendingImageDependencies.empty()) { parent.invoke(&GeometryTile::getImages, std::make_pair(pendingImageDependencies, ++imageCorrelationID)); } @@ -328,7 +333,16 @@ void GeometryTileWorker::parse() { } } + std::vector<std::string> patternOrder; + for (auto it = layers->rbegin(); it != layers->rend(); it++) { + if ((*it)->type == LayerType::Line) { + patternOrder.push_back((*it)->id); + } + } + std::unordered_map<std::string, std::unique_ptr<SymbolLayout>> symbolLayoutMap; + std::unordered_map<std::string, std::unique_ptr<PatternLayout>> patternLayoutMap; + buckets.clear(); featureIndex = std::make_unique<FeatureIndex>(*data ? (*data)->clone() : nullptr); BucketParameters parameters { id, mode, pixelRatio }; @@ -368,6 +382,11 @@ void GeometryTileWorker::parse() { parameters, group, std::move(geometryLayer), glyphDependencies, imageDependencies); symbolLayoutMap.emplace(leader.getID(), std::move(layout)); symbolLayoutsNeedPreparation = true; + } else if (leader.is<RenderLineLayer>()) { + auto layout = leader.as<RenderLineLayer>()->createLayout( + parameters, group, std::move(geometryLayer), imageDependencies); + patternLayoutMap.emplace(leader.getID(), std::move(layout)); + patternNeedsLayout = true; } else { const Filter& filter = leader.baseImpl->filter; const std::string& sourceLayerID = leader.baseImpl->sourceLayer; @@ -380,17 +399,17 @@ void GeometryTileWorker::parse() { continue; GeometryCollection geometries = feature->getGeometries(); - bucket->addFeature(*feature, geometries); + bucket->addFeature(*feature, geometries, {}); featureIndex->insert(geometries, i, sourceLayerID, leader.getID()); } if (!bucket->hasData()) { continue; } - for (const auto& layer : group) { buckets.emplace(layer->getID(), bucket); } + } } @@ -402,6 +421,14 @@ void GeometryTileWorker::parse() { } } + patternLayouts.clear(); + for (const auto& patternLayerID : patternOrder) { + auto it = patternLayoutMap.find(patternLayerID); + if (it != patternLayoutMap.end()) { + patternLayouts.push_back(std::move(it->second)); + } + } + requestNewGlyphs(glyphDependencies); requestNewImages(imageDependencies); @@ -434,13 +461,13 @@ void GeometryTileWorker::performSymbolLayout() { MBGL_TIMING_START(watch) optional<AlphaImage> glyphAtlasImage; optional<PremultipliedImage> iconAtlasImage; + ImageAtlas iconAtlas; if (symbolLayoutsNeedPreparation) { GlyphAtlas glyphAtlas = makeGlyphAtlas(glyphMap); - ImageAtlas imageAtlas = makeImageAtlas(imageMap); + iconAtlas = makeImageAtlas(imageMap, patternMap); glyphAtlasImage = std::move(glyphAtlas.image); - iconAtlasImage = std::move(imageAtlas.image); for (auto& symbolLayout : symbolLayouts) { if (obsolete) { @@ -448,12 +475,27 @@ void GeometryTileWorker::performSymbolLayout() { } symbolLayout->prepare(glyphMap, glyphAtlas.positions, - imageMap, imageAtlas.positions); + imageMap, iconAtlas.iconPositions); } symbolLayoutsNeedPreparation = false; } + if (!iconAtlas.image.valid()) { + iconAtlas = makeImageAtlas(imageMap, patternMap); + } + + for (auto& patternLayout : patternLayouts) { + if (obsolete) { + return; + } + + std::shared_ptr<LineBucket> bucket = patternLayout->createLayout(iconAtlas.patternPositions); + for (const auto& pair : patternLayout->layerPaintProperties) { + buckets.emplace(pair.first, bucket); + } + } + for (auto& symbolLayout : symbolLayouts) { if (obsolete) { return; @@ -483,7 +525,7 @@ void GeometryTileWorker::performSymbolLayout() { std::move(buckets), std::move(featureIndex), std::move(glyphAtlasImage), - std::move(iconAtlasImage) + std::move(iconAtlas) }, correlationID); } diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp index b5417c8114..b04feebc8f 100644 --- a/src/mbgl/tile/geometry_tile_worker.hpp +++ b/src/mbgl/tile/geometry_tile_worker.hpp @@ -19,6 +19,7 @@ namespace mbgl { class GeometryTile; class GeometryTileData; class SymbolLayout; +class PatternLayout; namespace style { class Layer; @@ -41,7 +42,7 @@ public: void setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_); void onGlyphsAvailable(GlyphMap glyphs); - void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID); + void onImagesAvailable(ImageMap icons, ImageMap patterns, uint64_t imageCorrelationID); private: void coalesced(); @@ -85,11 +86,16 @@ private: optional<std::unique_ptr<const GeometryTileData>> data; bool symbolLayoutsNeedPreparation = false; + bool patternNeedsLayout = false; + std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts; + std::vector<std::unique_ptr<PatternLayout>> patternLayouts; + GlyphDependencies pendingGlyphDependencies; ImageDependencies pendingImageDependencies; GlyphMap glyphMap; ImageMap imageMap; + ImageMap patternMap; bool showCollisionBoxes; bool firstLoad = true; diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp index 9b28b2e01a..bc7518fb33 100644 --- a/test/gl/bucket.test.cpp +++ b/test/gl/bucket.test.cpp @@ -52,7 +52,7 @@ TEST(Buckets, CircleBucket) { ASSERT_FALSE(bucket.needsUpload()); GeometryCollection point { { { 0, 0 } } }; - bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point); + bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point, {}); ASSERT_TRUE(bucket.hasData()); ASSERT_TRUE(bucket.needsUpload()); @@ -71,7 +71,7 @@ TEST(Buckets, FillBucket) { ASSERT_FALSE(bucket.needsUpload()); GeometryCollection polygon { { { 0, 0 }, { 0, 1 }, { 1, 1 } } }; - bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Polygon, polygon, properties }, polygon); + bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Polygon, polygon, properties }, polygon, {}); ASSERT_TRUE(bucket.hasData()); ASSERT_TRUE(bucket.needsUpload()); @@ -82,19 +82,20 @@ TEST(Buckets, FillBucket) { TEST(Buckets, LineBucket) { HeadlessBackend backend({ 512, 256 }); BackendScope scope { backend }; + style::LineLayoutProperties::PossiblyEvaluated layout; gl::Context context; - LineBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {}, {} }; + LineBucket bucket { layout, {}, 10.0f, 0 }; ASSERT_FALSE(bucket.hasData()); ASSERT_FALSE(bucket.needsUpload()); // Ignore invalid feature type. GeometryCollection point { { { 0, 0 } } }; - bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point); + bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point, {}); ASSERT_FALSE(bucket.hasData()); GeometryCollection line { { { 0, 0 }, { 1, 1 } } }; - bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::LineString, line, properties }, line); + bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::LineString, line, properties }, line, {}); ASSERT_TRUE(bucket.hasData()); ASSERT_TRUE(bucket.needsUpload()); @@ -123,7 +124,7 @@ TEST(Buckets, SymbolBucket) { // SymbolBucket::addFeature() is a no-op. GeometryCollection point { { { 0, 0 } } }; - bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point); + bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point, {}); ASSERT_FALSE(bucket.hasData()); ASSERT_FALSE(bucket.needsUpload()); diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp index ebe1bcd72f..4a838d0f9c 100644 --- a/test/renderer/image_manager.test.cpp +++ b/test/renderer/image_manager.test.cpp @@ -108,11 +108,11 @@ TEST(ImageManager, RemoveReleasesBinPackRect) { class StubImageRequestor : public ImageRequestor { public: - void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID_) final { - if (imagesAvailable && imageCorrelationID == imageCorrelationID_) imagesAvailable(images); + void onImagesAvailable(ImageMap icons, ImageMap patterns, uint64_t imageCorrelationID_) final { + if (imagesAvailable && imageCorrelationID == imageCorrelationID_) imagesAvailable(icons, patterns); } - std::function<void (ImageMap)> imagesAvailable; + std::function<void (ImageMap, ImageMap)> imagesAvailable; uint64_t imageCorrelationID = 0; }; @@ -121,12 +121,14 @@ TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) { StubImageRequestor requestor; bool notified = false; - requestor.imagesAvailable = [&] (ImageMap) { + requestor.imagesAvailable = [&] (ImageMap, ImageMap) { notified = true; }; uint64_t imageCorrelationID = 0; - imageManager.getImages(requestor, std::make_pair(std::set<std::string> {"one"}, imageCorrelationID)); + ImageDependencies dependencies; + dependencies.emplace("one", ImageType::Icon); + imageManager.getImages(requestor, std::make_pair(dependencies, imageCorrelationID)); ASSERT_FALSE(notified); imageManager.setLoaded(true); @@ -138,13 +140,15 @@ TEST(ImageManager, NotifiesRequestorImmediatelyIfDependenciesAreSatisfied) { StubImageRequestor requestor; bool notified = false; - requestor.imagesAvailable = [&] (ImageMap) { + requestor.imagesAvailable = [&] (ImageMap, ImageMap) { notified = true; }; uint64_t imageCorrelationID = 0; + ImageDependencies dependencies; + dependencies.emplace("one", ImageType::Icon); imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2)); - imageManager.getImages(requestor, std::make_pair(std::set<std::string> {"one"}, imageCorrelationID)); + imageManager.getImages(requestor, std::make_pair(dependencies, imageCorrelationID)); ASSERT_TRUE(notified); } |