[core] Polymorphic bucket creation

This commit is contained in:
John Firebaugh
2015-10-20 15:05:06 -07:00
parent 540c6eca15
commit ddc4251074
19 changed files with 247 additions and 162 deletions

View File

@@ -21,4 +21,8 @@ void BackgroundLayer::recalculate(const StyleCalculationParameters& parameters)
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
std::unique_ptr<Bucket> BackgroundLayer::createBucket(StyleBucketParameters&) const {
return nullptr;
}
}

View File

@@ -15,6 +15,8 @@ public:
void recalculate(const StyleCalculationParameters&) override;
std::unique_ptr<Bucket> createBucket(StyleBucketParameters&) const override;
BackgroundPaintProperties properties;
};

View File

@@ -1,5 +1,7 @@
#include <mbgl/layer/circle_layer.hpp>
#include <mbgl/style/property_parsing.hpp>
#include <mbgl/style/style_bucket_parameters.hpp>
#include <mbgl/renderer/circle_bucket.hpp>
namespace mbgl {
@@ -27,4 +29,14 @@ void CircleLayer::recalculate(const StyleCalculationParameters& parameters) {
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
std::unique_ptr<Bucket> CircleLayer::createBucket(StyleBucketParameters& parameters) const {
auto bucket = std::make_unique<CircleBucket>();
parameters.eachFilteredFeature(filter, [&] (const auto& feature) {
bucket->addGeometry(feature.getGeometries());
});
return std::move(bucket);
}
}

View File

@@ -15,6 +15,8 @@ public:
void recalculate(const StyleCalculationParameters&) override;
std::unique_ptr<Bucket> createBucket(StyleBucketParameters&) const override;
CirclePaintProperties properties;
};

View File

@@ -1,5 +1,7 @@
#include <mbgl/layer/fill_layer.hpp>
#include <mbgl/style/property_parsing.hpp>
#include <mbgl/style/style_bucket_parameters.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
namespace mbgl {
@@ -43,4 +45,14 @@ void FillLayer::recalculate(const StyleCalculationParameters& parameters) {
}
}
std::unique_ptr<Bucket> FillLayer::createBucket(StyleBucketParameters& parameters) const {
auto bucket = std::make_unique<FillBucket>();
parameters.eachFilteredFeature(filter, [&] (const auto& feature) {
bucket->addGeometry(feature.getGeometries());
});
return std::move(bucket);
}
}

View File

@@ -15,6 +15,8 @@ public:
void recalculate(const StyleCalculationParameters&) override;
std::unique_ptr<Bucket> createBucket(StyleBucketParameters&) const override;
FillPaintProperties properties;
};

View File

@@ -1,5 +1,7 @@
#include <mbgl/layer/line_layer.hpp>
#include <mbgl/style/property_parsing.hpp>
#include <mbgl/style/style_bucket_parameters.hpp>
#include <mbgl/renderer/line_bucket.hpp>
#include <mbgl/map/tile_id.hpp>
namespace mbgl {
@@ -52,4 +54,21 @@ void LineLayer::recalculate(const StyleCalculationParameters& parameters) {
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
std::unique_ptr<Bucket> LineLayer::createBucket(StyleBucketParameters& parameters) const {
auto bucket = std::make_unique<LineBucket>();
const float z = parameters.tileID.z;
layout.calculate(PropertyKey::LineCap, bucket->layout.cap, z);
layout.calculate(PropertyKey::LineJoin, bucket->layout.join, z);
layout.calculate(PropertyKey::LineMiterLimit, bucket->layout.miter_limit, z);
layout.calculate(PropertyKey::LineRoundLimit, bucket->layout.round_limit, z);
parameters.eachFilteredFeature(filter, [&] (const auto& feature) {
bucket->addGeometry(feature.getGeometries());
});
return std::move(bucket);
}
}

View File

@@ -15,6 +15,8 @@ public:
void recalculate(const StyleCalculationParameters&) override;
std::unique_ptr<Bucket> createBucket(StyleBucketParameters&) const override;
LinePaintProperties properties;
};

View File

@@ -1,5 +1,6 @@
#include <mbgl/layer/raster_layer.hpp>
#include <mbgl/style/property_parsing.hpp>
#include <mbgl/renderer/bucket.hpp>
namespace mbgl {
@@ -35,4 +36,8 @@ void RasterLayer::recalculate(const StyleCalculationParameters& parameters) {
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
std::unique_ptr<Bucket> RasterLayer::createBucket(StyleBucketParameters&) const {
return nullptr;
}
}

View File

@@ -15,6 +15,8 @@ public:
void recalculate(const StyleCalculationParameters&) override;
std::unique_ptr<Bucket> createBucket(StyleBucketParameters&) const override;
RasterPaintProperties properties;
};

View File

@@ -1,6 +1,9 @@
#include <mbgl/layer/symbol_layer.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/style/property_evaluator.hpp>
#include <mbgl/style/property_parsing.hpp>
#include <mbgl/style/style_bucket_parameters.hpp>
namespace mbgl {
@@ -108,4 +111,70 @@ void SymbolLayer::recalculate(const StyleCalculationParameters& parameters) {
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
std::unique_ptr<Bucket> SymbolLayer::createBucket(StyleBucketParameters& parameters) const {
const float z = parameters.tileID.z;
auto bucket = std::make_unique<SymbolBucket>(parameters.tileID.overscaling, z);
layout.calculate(PropertyKey::SymbolPlacement, bucket->layout.placement, z);
if (bucket->layout.placement == PlacementType::Line) {
bucket->layout.icon.rotation_alignment = RotationAlignmentType::Map;
bucket->layout.text.rotation_alignment = RotationAlignmentType::Map;
};
layout.calculate(PropertyKey::SymbolSpacing, bucket->layout.spacing, z);
layout.calculate(PropertyKey::SymbolAvoidEdges, bucket->layout.avoid_edges, z);
layout.calculate(PropertyKey::IconAllowOverlap, bucket->layout.icon.allow_overlap, z);
layout.calculate(PropertyKey::IconIgnorePlacement, bucket->layout.icon.ignore_placement, z);
layout.calculate(PropertyKey::IconOptional, bucket->layout.icon.optional, z);
layout.calculate(PropertyKey::IconRotationAlignment, bucket->layout.icon.rotation_alignment, z);
layout.calculate(PropertyKey::IconImage, bucket->layout.icon.image, z);
layout.calculate(PropertyKey::IconPadding, bucket->layout.icon.padding, z);
layout.calculate(PropertyKey::IconRotate, bucket->layout.icon.rotate, z);
layout.calculate(PropertyKey::IconKeepUpright, bucket->layout.icon.keep_upright, z);
layout.calculate(PropertyKey::IconOffset, bucket->layout.icon.offset, z);
layout.calculate(PropertyKey::TextRotationAlignment, bucket->layout.text.rotation_alignment, z);
layout.calculate(PropertyKey::TextField, bucket->layout.text.field, z);
layout.calculate(PropertyKey::TextFont, bucket->layout.text.font, z);
layout.calculate(PropertyKey::TextMaxWidth, bucket->layout.text.max_width, z);
layout.calculate(PropertyKey::TextLineHeight, bucket->layout.text.line_height, z);
layout.calculate(PropertyKey::TextLetterSpacing, bucket->layout.text.letter_spacing, z);
layout.calculate(PropertyKey::TextMaxAngle, bucket->layout.text.max_angle, z);
layout.calculate(PropertyKey::TextRotate, bucket->layout.text.rotate, z);
layout.calculate(PropertyKey::TextPadding, bucket->layout.text.padding, z);
layout.calculate(PropertyKey::TextIgnorePlacement, bucket->layout.text.ignore_placement, z);
layout.calculate(PropertyKey::TextOptional, bucket->layout.text.optional, z);
layout.calculate(PropertyKey::TextJustify, bucket->layout.text.justify, z);
layout.calculate(PropertyKey::TextAnchor, bucket->layout.text.anchor, z);
layout.calculate(PropertyKey::TextKeepUpright, bucket->layout.text.keep_upright, z);
layout.calculate(PropertyKey::TextTransform, bucket->layout.text.transform, z);
layout.calculate(PropertyKey::TextOffset, bucket->layout.text.offset, z);
layout.calculate(PropertyKey::TextAllowOverlap, bucket->layout.text.allow_overlap, z);
layout.calculate(PropertyKey::IconSize, bucket->layout.icon.size, z + 1);
layout.calculate(PropertyKey::IconSize, bucket->layout.icon.max_size, 18);
layout.calculate(PropertyKey::TextSize, bucket->layout.text.size, z + 1);
layout.calculate(PropertyKey::TextSize, bucket->layout.text.max_size, 18);
bucket->parseFeatures(parameters.layer, filter);
if (bucket->needsDependencies(parameters.glyphStore, parameters.sprite)) {
parameters.partialParse = true;
}
// We do not add features if the parser is in a "partial" state because
// the layer ordering needs to be respected when calculating text
// collisions. Although, at this point, we requested all the resources
// needed by this tile.
if (!parameters.partialParse) {
bucket->addFeatures(parameters.tileUID,
parameters.spriteAtlas,
parameters.glyphAtlas,
parameters.glyphStore,
parameters.collisionTile);
}
return std::move(bucket);
}
}

View File

@@ -15,6 +15,8 @@ public:
void recalculate(const StyleCalculationParameters&) override;
std::unique_ptr<Bucket> createBucket(StyleBucketParameters&) const override;
SymbolPaintProperties properties;
};

View File

@@ -1,13 +1,11 @@
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/map/tile_worker.hpp>
#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/property_evaluator.hpp>
#include <mbgl/style/style_bucket_parameters.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/renderer/line_bucket.hpp>
#include <mbgl/renderer/circle_bucket.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/constants.hpp>
@@ -21,7 +19,6 @@ TileWorker::TileWorker(TileID id_,
const std::atomic<TileData::State>& state_)
: id(id_),
sourceID(sourceID_),
parameters(id.z),
style(style_),
state(state_) {
assert(style.sprite);
@@ -36,6 +33,7 @@ TileParseResult TileWorker::parseAllLayers(std::vector<util::ptr<StyleLayer>> la
PlacementConfig config) {
// We're doing a fresh parse of the tile, because the underlying data has changed.
pending.clear();
partialParse = false;
// Reset the collision tile so we have a clean slate; we're placing all features anyway.
collisionTile = std::make_unique<CollisionTile>(config);
@@ -99,15 +97,6 @@ void TileWorker::redoPlacement(
}
}
template <typename T>
void applyLayoutProperty(PropertyKey key, const ClassProperties &classProperties, T &target, const StyleCalculationParameters& parameters) {
auto it = classProperties.properties.find(key);
if (it != classProperties.properties.end()) {
const PropertyEvaluator<T> evaluator(parameters);
target = mapbox::util::apply_visitor(evaluator, it->second);
}
}
void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometryTile) {
// Cancel early when parsing.
if (state == TileData::State::obsolete)
@@ -135,145 +124,23 @@ void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometr
return;
}
switch (layer.type) {
case StyleLayerType::Fill:
createFillBucket(*geometryLayer, layer);
break;
case StyleLayerType::Line:
createLineBucket(*geometryLayer, layer);
break;
case StyleLayerType::Circle:
createCircleBucket(*geometryLayer, layer);
break;
case StyleLayerType::Symbol:
createSymbolBucket(*geometryLayer, layer);
break;
case StyleLayerType::Raster:
break;
default:
Log::Warning(Event::ParseTile, "unknown bucket render type for layer '%s' (source layer '%s')",
layer.id.c_str(), layer.sourceLayer.c_str());
}
}
StyleBucketParameters parameters(id,
*geometryLayer,
state,
reinterpret_cast<uintptr_t>(this),
partialParse,
*style.spriteAtlas,
*style.sprite,
*style.glyphAtlas,
*style.glyphStore,
*collisionTile);
template <class Bucket>
void TileWorker::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& layer, const FilterExpression &filter) {
for (std::size_t i = 0; i < layer.featureCount(); i++) {
auto feature = layer.getFeature(i);
std::unique_ptr<Bucket> bucket = layer.createBucket(parameters);
if (state == TileData::State::obsolete)
return;
GeometryTileFeatureExtractor extractor(*feature);
if (!evaluate(filter, extractor))
continue;
bucket->addGeometry(feature->getGeometries());
}
}
void TileWorker::createFillBucket(const GeometryTileLayer& tileLayer,
const StyleLayer& layer) {
auto bucket = std::make_unique<FillBucket>();
// Fill does not have layout properties to apply.
addBucketGeometries(bucket, tileLayer, layer.filter);
insertBucket(layer.bucketName(), std::move(bucket));
}
void TileWorker::createLineBucket(const GeometryTileLayer& tileLayer,
const StyleLayer& layer) {
auto bucket = std::make_unique<LineBucket>();
const float z = id.z;
auto& layout = bucket->layout;
applyLayoutProperty(PropertyKey::LineCap, layer.layout, layout.cap, z);
applyLayoutProperty(PropertyKey::LineJoin, layer.layout, layout.join, z);
applyLayoutProperty(PropertyKey::LineMiterLimit, layer.layout, layout.miter_limit, z);
applyLayoutProperty(PropertyKey::LineRoundLimit, layer.layout, layout.round_limit, z);
addBucketGeometries(bucket, tileLayer, layer.filter);
insertBucket(layer.bucketName(), std::move(bucket));
}
void TileWorker::createCircleBucket(const GeometryTileLayer& tileLayer,
const StyleLayer& layer) {
auto bucket = std::make_unique<CircleBucket>();
// Circle does not have layout properties to apply.
addBucketGeometries(bucket, tileLayer, layer.filter);
insertBucket(layer.bucketName(), std::move(bucket));
}
void TileWorker::createSymbolBucket(const GeometryTileLayer& tileLayer,
const StyleLayer& layer) {
auto bucket = std::make_unique<SymbolBucket>(id.overscaling, id.z);
const float z = id.z;
auto& layout = bucket->layout;
applyLayoutProperty(PropertyKey::SymbolPlacement, layer.layout, layout.placement, z);
if (layout.placement == PlacementType::Line) {
layout.icon.rotation_alignment = RotationAlignmentType::Map;
layout.text.rotation_alignment = RotationAlignmentType::Map;
};
applyLayoutProperty(PropertyKey::SymbolSpacing, layer.layout, layout.spacing, z);
applyLayoutProperty(PropertyKey::SymbolAvoidEdges, layer.layout, layout.avoid_edges, z);
applyLayoutProperty(PropertyKey::IconAllowOverlap, layer.layout, layout.icon.allow_overlap, z);
applyLayoutProperty(PropertyKey::IconIgnorePlacement, layer.layout, layout.icon.ignore_placement, z);
applyLayoutProperty(PropertyKey::IconOptional, layer.layout, layout.icon.optional, z);
applyLayoutProperty(PropertyKey::IconRotationAlignment, layer.layout, layout.icon.rotation_alignment, z);
applyLayoutProperty(PropertyKey::IconImage, layer.layout, layout.icon.image, z);
applyLayoutProperty(PropertyKey::IconPadding, layer.layout, layout.icon.padding, z);
applyLayoutProperty(PropertyKey::IconRotate, layer.layout, layout.icon.rotate, z);
applyLayoutProperty(PropertyKey::IconKeepUpright, layer.layout, layout.icon.keep_upright, z);
applyLayoutProperty(PropertyKey::IconOffset, layer.layout, layout.icon.offset, z);
applyLayoutProperty(PropertyKey::TextRotationAlignment, layer.layout, layout.text.rotation_alignment, z);
applyLayoutProperty(PropertyKey::TextField, layer.layout, layout.text.field, z);
applyLayoutProperty(PropertyKey::TextFont, layer.layout, layout.text.font, z);
applyLayoutProperty(PropertyKey::TextMaxWidth, layer.layout, layout.text.max_width, z);
applyLayoutProperty(PropertyKey::TextLineHeight, layer.layout, layout.text.line_height, z);
applyLayoutProperty(PropertyKey::TextLetterSpacing, layer.layout, layout.text.letter_spacing, z);
applyLayoutProperty(PropertyKey::TextMaxAngle, layer.layout, layout.text.max_angle, z);
applyLayoutProperty(PropertyKey::TextRotate, layer.layout, layout.text.rotate, z);
applyLayoutProperty(PropertyKey::TextPadding, layer.layout, layout.text.padding, z);
applyLayoutProperty(PropertyKey::TextIgnorePlacement, layer.layout, layout.text.ignore_placement, z);
applyLayoutProperty(PropertyKey::TextOptional, layer.layout, layout.text.optional, z);
applyLayoutProperty(PropertyKey::TextJustify, layer.layout, layout.text.justify, z);
applyLayoutProperty(PropertyKey::TextAnchor, layer.layout, layout.text.anchor, z);
applyLayoutProperty(PropertyKey::TextKeepUpright, layer.layout, layout.text.keep_upright, z);
applyLayoutProperty(PropertyKey::TextTransform, layer.layout, layout.text.transform, z);
applyLayoutProperty(PropertyKey::TextOffset, layer.layout, layout.text.offset, z);
applyLayoutProperty(PropertyKey::TextAllowOverlap, layer.layout, layout.text.allow_overlap, z);
applyLayoutProperty(PropertyKey::IconSize, layer.layout, layout.icon.size, z + 1);
applyLayoutProperty(PropertyKey::IconSize, layer.layout, layout.icon.max_size, 18);
applyLayoutProperty(PropertyKey::TextSize, layer.layout, layout.text.size, z + 1);
applyLayoutProperty(PropertyKey::TextSize, layer.layout, layout.text.max_size, 18);
bucket->parseFeatures(tileLayer, layer.filter);
assert(style.glyphStore);
assert(style.sprite);
const bool needsDependencies = bucket->needsDependencies(*style.glyphStore, *style.sprite);
if (needsDependencies) {
if (layer.type == StyleLayerType::Symbol && partialParse) {
// We cannot parse this bucket yet. Instead, we're saving it for later.
pending.emplace_back(layer, std::move(bucket));
} else {
assert(style.spriteAtlas);
assert(style.glyphAtlas);
assert(style.glyphStore);
assert(collisionTile);
bucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas,
*style.glyphAtlas, *style.glyphStore, *collisionTile);
insertBucket(layer.bucketName(), std::move(bucket));
}
}

View File

@@ -6,8 +6,6 @@
#include <mbgl/map/tile_data.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/style/filter_expression.hpp>
#include <mbgl/style/style_calculation_parameters.hpp>
#include <mbgl/text/placement_config.hpp>
#include <string>
@@ -23,7 +21,6 @@ class GeometryTile;
class Style;
class Bucket;
class StyleLayer;
class GeometryTileLayer;
// We're using this class to shuttle the resulting buckets from the worker thread to the MapContext
// thread. This class is movable-only because the vector contains movable-only value elements.
@@ -56,24 +53,16 @@ public:
private:
void parseLayer(const StyleLayer&, const GeometryTile&);
void createFillBucket(const GeometryTileLayer&, const StyleLayer&);
void createLineBucket(const GeometryTileLayer&, const StyleLayer&);
void createCircleBucket(const GeometryTileLayer&, const StyleLayer&);
void createSymbolBucket(const GeometryTileLayer&, const StyleLayer&);
void insertBucket(const std::string& name, std::unique_ptr<Bucket>);
template <class Bucket>
void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&);
const TileID id;
const std::string sourceID;
const StyleCalculationParameters parameters;
Style& style;
const std::atomic<TileData::State>& state;
bool partialParse = false;
std::unique_ptr<CollisionTile> collisionTile;
// Contains buckets that we couldn't parse so far due to missing resources.

View File

@@ -21,7 +21,7 @@ class CollisionTile;
class Bucket : private util::noncopyable {
public:
Bucket() : uploaded(false) {}
// 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.
virtual void upload() = 0;

View File

@@ -3,6 +3,7 @@
#include <mbgl/style/property_key.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/property_evaluator.hpp>
#include <mbgl/style/property_transition.hpp>
#include <map>
@@ -29,6 +30,15 @@ public:
return properties.end();
}
template <typename T>
void calculate(PropertyKey key, T& target, const float z) const {
auto it = properties.find(key);
if (it != properties.end()) {
const PropertyEvaluator<T> evaluator(z);
target = mapbox::util::apply_visitor(evaluator, it->second);
}
}
public:
std::map<PropertyKey, PropertyValue> properties;
std::map<PropertyKey, PropertyTransition> transitions;

View File

@@ -0,0 +1,19 @@
#include <mbgl/style/style_bucket_parameters.hpp>
#include <mbgl/map/geometry_tile.hpp>
namespace mbgl {
void StyleBucketParameters::eachFilteredFeature(const FilterExpression& filter,
std::function<void (const GeometryTileFeature&)> function) {
for (std::size_t i = 0; !cancelled() && i < layer.featureCount(); i++) {
auto feature = layer.getFeature(i);
GeometryTileFeatureExtractor extractor(*feature);
if (!evaluate(filter, extractor))
continue;
function(*feature);
}
}
}

View File

@@ -0,0 +1,63 @@
#ifndef STYLE_BUCKET_PARAMETERS
#define STYLE_BUCKET_PARAMETERS
#include <mbgl/style/filter_expression.hpp>
#include <mbgl/map/tile_data.hpp>
#include <functional>
namespace mbgl {
class TileID;
class GeometryTileLayer;
class GeometryTileFeature;
class SpriteAtlas;
class Sprite;
class GlyphAtlas;
class GlyphStore;
class CollisionTile;
class StyleBucketParameters {
public:
StyleBucketParameters(const TileID& tileID_,
const GeometryTileLayer& layer_,
const std::atomic<TileData::State>& state_,
uintptr_t tileUID_,
bool& partialParse_,
SpriteAtlas& spriteAtlas_,
Sprite& sprite_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
CollisionTile& collisionTile_)
: tileID(tileID_),
layer(layer_),
state(state_),
tileUID(tileUID_),
partialParse(partialParse_),
spriteAtlas(spriteAtlas_),
sprite(sprite_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
collisionTile(collisionTile_) {}
bool cancelled() const {
return state == TileData::State::obsolete;
}
void eachFilteredFeature(const FilterExpression&, std::function<void (const GeometryTileFeature&)>);
const TileID& tileID;
const GeometryTileLayer& layer;
const std::atomic<TileData::State>& state;
uintptr_t tileUID;
bool& partialParse;
SpriteAtlas& spriteAtlas;
Sprite& sprite;
GlyphAtlas& glyphAtlas;
GlyphStore& glyphStore;
CollisionTile& collisionTile;
};
}
#endif

View File

@@ -7,6 +7,7 @@
#include <mbgl/style/paint_properties_map.hpp>
#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/map/tile_data.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/chrono.hpp>
@@ -20,6 +21,7 @@
namespace mbgl {
class StyleCalculationParameters;
class StyleBucketParameters;
class PropertyTransition;
using JSVal = rapidjson::Value;
@@ -44,6 +46,8 @@ public:
// Fully evaluate cascaded paint properties based on a zoom level.
virtual void recalculate(const StyleCalculationParameters&) = 0;
virtual std::unique_ptr<Bucket> createBucket(StyleBucketParameters&) const = 0;
// Checks whether this layer has any active paint properties with transitions.
bool hasTransitions() const;