Track expression dependencies (#2113)

This commit is contained in:
Tim Sylvester
2024-03-07 10:36:50 -08:00
committed by GitHub
parent 9614b566d8
commit 25678a4d99
105 changed files with 1082 additions and 339 deletions

View File

@@ -24,8 +24,7 @@ private:
}
public:
ColorRampPropertyValue()
: value(nullptr) {}
ColorRampPropertyValue() noexcept = default;
ColorRampPropertyValue(std::shared_ptr<expression::Expression> value_)
: value(std::move(value_)) {}
@@ -46,6 +45,9 @@ public:
bool hasDataDrivenPropertyDifference(const ColorRampPropertyValue&) const { return false; }
const expression::Expression& getExpression() const { return *value; }
using Dependency = style::expression::Dependency;
Dependency getDependencies() const noexcept { return value ? value->dependencies : Dependency::None; }
};
} // namespace style

View File

@@ -11,7 +11,7 @@ namespace expression {
class At : public Expression {
public:
At(std::unique_ptr<Expression> index_, std::unique_ptr<Expression> input_)
: Expression(Kind::At, input_->getType().get<type::Array>().itemType),
: Expression(Kind::At, input_->getType().get<type::Array>().itemType, depsOf(index_) | depsOf(input_)),
index(std::move(index_)),
input(std::move(input_)) {}

View File

@@ -12,7 +12,7 @@ namespace expression {
class Any : public Expression {
public:
Any(std::vector<std::unique_ptr<Expression>> inputs_)
: Expression(Kind::Any, type::Boolean),
: Expression(Kind::Any, type::Boolean, collectDependencies(inputs_)),
inputs(std::move(inputs_)) {}
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
@@ -31,7 +31,7 @@ private:
class All : public Expression {
public:
All(std::vector<std::unique_ptr<Expression>> inputs_)
: Expression(Kind::All, type::Boolean),
: Expression(Kind::All, type::Boolean, collectDependencies(inputs_)),
inputs(std::move(inputs_)) {}
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);

View File

@@ -17,7 +17,7 @@ public:
using Branch = std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>;
Case(type::Type type_, std::vector<Branch> branches_, std::unique_ptr<Expression> otherwise_)
: Expression(Kind::Case, std::move(type_)),
: Expression(Kind::Case, std::move(type_), collectDependencies(branches_) | depsOf(otherwise_)),
branches(std::move(branches_)),
otherwise(std::move(otherwise_)) {}
@@ -32,6 +32,12 @@ public:
std::string getOperator() const override { return "case"; }
protected:
using Expression::collectDependencies;
static Dependency collectDependencies(const std::vector<Branch>& branches) {
return collectDependencies(branches, [](const Branch& v) { return depsOf(v.first) | depsOf(v.second); });
}
private:
std::vector<Branch> branches;
std::unique_ptr<Expression> otherwise;

View File

@@ -15,7 +15,7 @@ class Coalesce : public Expression {
public:
using Args = std::vector<std::unique_ptr<Expression>>;
Coalesce(const type::Type& type_, Args args_)
: Expression(Kind::Coalesce, type_),
: Expression(Kind::Coalesce, type_, collectDependencies(args_)),
args(std::move(args_)) {}
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);

View File

@@ -12,7 +12,7 @@ namespace expression {
class Coercion : public Expression {
public:
Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_);
Coercion(const type::Type& type_, std::vector<std::unique_ptr<Expression>> inputs_);
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);

View File

@@ -21,7 +21,7 @@ public:
void eachChild(const std::function<void(const Expression&)>&) const override;
bool operator==(const Expression& e) const override;
bool operator==(const Expression&) const override;
std::vector<std::optional<Value>> possibleOutputs() const override {
// Technically the set of possible outputs is the combinatoric set of

View File

@@ -11,7 +11,7 @@ namespace expression {
class Error : public Expression {
public:
Error(std::string message_)
: Expression(Kind::Error, type::Error),
: Expression(Kind::Error, type::Error, Dependency::None),
message(std::move(message_)) {}
void eachChild(const std::function<void(const Expression&)>&) const override {}

View File

@@ -5,12 +5,14 @@
#include <mbgl/style/expression/value.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/traits.hpp>
#include <mbgl/util/variant.hpp>
#include <array>
#include <vector>
#include <memory>
#include <numeric>
#include <optional>
#include <vector>
namespace mbgl {
@@ -135,23 +137,23 @@ public:
};
/**
Expression is an abstract class that serves as an interface and base class
for particular expression implementations.
Expression is an abstract class that serves as an interface and base class
for particular expression implementations.
CompoundExpression implements the majority of expressions in the spec by
inferring the argument and output from a simple function (const T0& arg0,
const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are member types of
mbgl::style::expression::Value.
CompoundExpression implements the majority of expressions in the spec by
inferring the argument and output from a simple function (const T0& arg0,
const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are member types of
mbgl::style::expression::Value.
The other Expression subclasses (Let, Curve, Match, etc.) exist in order to
implement expressions that need specialized parsing, type checking, or
evaluation logic that can't be handled by CompoundExpression's inference
mechanism.
The other Expression subclasses (Let, Curve, Match, etc.) exist in order to
implement expressions that need specialized parsing, type checking, or
evaluation logic that can't be handled by CompoundExpression's inference
mechanism.
Each Expression subclass also provides a static
ParseResult ExpressionClass::parse(const V&, ParsingContext),
which handles parsing a style-spec JSON representation of the expression.
*/
Each Expression subclass also provides a static
ParseResult ExpressionClass::parse(const V&, ParsingContext),
which handles parsing a style-spec JSON representation of the expression.
*/
enum class Kind : int32_t {
Coalesce,
@@ -183,10 +185,33 @@ enum class Kind : int32_t {
Slice
};
enum class Dependency : uint32_t {
None = 0,
Feature = 1 << 0, // Data reference
Image = 1 << 1, // Image reference (equivalent to not "runtime constant")
Zoom = 1 << 2, // Zoom level
Location = 1 << 3, // Not used yet, "distance-from-center" not supported
Bind = 1 << 4, // Create variable binding ("let")
Var = 1 << 5, // Use variable binding
Override = 1 << 6, // Property override
MaskCount = 7,
};
inline constexpr static Dependency operator|(Dependency x, Dependency y) noexcept {
return Dependency{mbgl::underlying_type(x) | mbgl::underlying_type(y)};
}
inline constexpr static Dependency operator&(Dependency x, Dependency y) noexcept {
return Dependency{mbgl::underlying_type(x) & mbgl::underlying_type(y)};
}
inline static Dependency& operator|=(Dependency& target, Dependency source) noexcept {
target = target | source;
return target;
}
class Expression {
public:
Expression(Kind kind_, type::Type type_)
: kind(kind_),
Expression(Kind kind_, type::Type type_, Dependency dependencies_)
: dependencies(dependencies_),
kind(kind_),
type(std::move(type_)) {}
virtual ~Expression() = default;
@@ -195,8 +220,8 @@ public:
virtual bool operator==(const Expression&) const = 0;
bool operator!=(const Expression& rhs) const { return !operator==(rhs); }
Kind getKind() const { return kind; };
type::Type getType() const { return type; };
Kind getKind() const { return kind; }
const type::Type& getType() const { return type; }
EvaluationResult evaluate(std::optional<float> zoom,
const Feature& feature,
@@ -229,6 +254,8 @@ public:
virtual std::string getOperator() const = 0;
const Dependency dependencies;
protected:
template <typename T>
static bool childrenEqual(const T& lhs, const T& rhs) {
@@ -261,6 +288,43 @@ protected:
return *(lhs.first) == *(rhs.first) && *(lhs.second) == *(rhs.second);
}
static Dependency depsOf(const std::shared_ptr<Expression>& ex) { return ex ? ex->dependencies : Dependency::None; }
static Dependency depsOf(const std::unique_ptr<Expression>& ex) { return ex ? ex->dependencies : Dependency::None; }
template <typename T>
static Dependency depsOf(const std::optional<T>& ex) {
return ex ? depsOf(*ex) : Dependency::None;
}
/// Combine the dependencies of all the expressions in a container of shared- or unique-pointers to expressions
template <typename T>
static constexpr Dependency collectDependencies(const std::vector<T>& container) {
return std::accumulate(container.begin(), container.end(), Dependency::None, [](auto a, const auto& ex) {
return a | depsOf(ex);
});
}
template <typename T, typename F>
static constexpr Dependency collectDependencies(const std::vector<T>& container, F op) {
return std::accumulate(container.begin(), container.end(), Dependency::None, [&](auto a, const auto& item) {
return a | op(item);
});
}
template <typename K, typename T>
static constexpr Dependency collectDependencies(const std::map<K, T>& container) {
return std::accumulate(container.begin(), container.end(), Dependency::None, [](auto a, const auto& v) {
return a | depsOf(v.second);
});
}
template <typename K, typename T>
static constexpr Dependency collectDependencies(const std::unordered_map<K, T>& container) {
return std::accumulate(container.begin(), container.end(), Dependency::None, [](auto a, const auto& kv) {
return a | depsOf(kv.second);
});
}
private:
Kind kind;
type::Type type;
@@ -268,4 +332,11 @@ private:
} // namespace expression
} // namespace style
namespace util {
std::string toString(style::expression::Dependency);
} // namespace util
std::ostream& operator<<(std::ostream&, style::expression::Dependency);
} // namespace mbgl

View File

@@ -1,9 +1,8 @@
#pragma once
#include <mbgl/style/expression/parsing_context.hpp>
#include <mbgl/style/expression/interpolate.hpp>
#include <mbgl/style/expression/parsing_context.hpp>
#include <mbgl/style/expression/step.hpp>
#include <mbgl/util/variant.hpp>
#include <optional>
@@ -12,9 +11,9 @@ namespace mbgl {
namespace style {
namespace expression {
std::optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCurve(const expression::Expression* e);
std::optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCurve(const expression::Expression*);
variant<std::nullptr_t, const Interpolate*, const Step*> findZoomCurveChecked(const expression::Expression* e);
variant<std::nullptr_t, const Interpolate*, const Step*> findZoomCurveChecked(const expression::Expression&);
} // namespace expression
} // namespace style

View File

@@ -10,17 +10,17 @@ namespace expression {
struct FormatExpressionSection {
explicit FormatExpressionSection(std::unique_ptr<Expression> content_);
void setTextSectionOptions(std::optional<std::unique_ptr<Expression>> fontScale_,
std::optional<std::unique_ptr<Expression>> textFont_,
std::optional<std::unique_ptr<Expression>> textColor_);
void setTextSectionOptions(std::unique_ptr<Expression>&& fontScale_,
std::unique_ptr<Expression>&& textFont_,
std::unique_ptr<Expression>&& textColor_);
// Content can be expression that evaluates to String or Image.
std::shared_ptr<Expression> content;
// Text related section options.
std::optional<std::shared_ptr<Expression>> fontScale;
std::optional<std::shared_ptr<Expression>> textFont;
std::optional<std::shared_ptr<Expression>> textColor;
std::shared_ptr<Expression> fontScale;
std::shared_ptr<Expression> textFont;
std::shared_ptr<Expression> textColor;
};
class FormatExpression final : public Expression {
@@ -45,6 +45,11 @@ public:
mbgl::Value serialize() const override;
std::string getOperator() const override { return "format"; }
private:
static Dependency depsOfSection(const FormatExpressionSection& s) {
return depsOf(s.content) | depsOf(s.fontScale) | depsOf(s.textFont) | depsOf(s.textColor);
}
private:
std::vector<FormatExpressionSection> sections;
};

View File

@@ -13,7 +13,9 @@ public:
FormatSectionOverride(const type::Type& type_,
PossiblyEvaluatedPropertyValue<T> defaultValue_,
std::string propertyName_)
: Expression(Kind::FormatSectionOverride, type_),
: Expression(Kind::FormatSectionOverride,
type_,
defaultValue_.getDependencies() | Dependency::Override | Dependency::Feature),
defaultValue(std::move(defaultValue_)),
propertyName(std::move(propertyName_)) {}

View File

@@ -17,7 +17,7 @@ public:
using Bindings = std::map<std::string, std::shared_ptr<Expression>>;
Let(Bindings bindings_, std::unique_ptr<Expression> result_)
: Expression(Kind::Let, result_->getType()),
: Expression(Kind::Let, result_->getType(), collectDependencies(bindings_) | Dependency::Bind),
bindings(std::move(bindings_)),
result(std::move(result_)) {}
@@ -49,7 +49,7 @@ private:
class Var : public Expression {
public:
Var(std::string name_, std::shared_ptr<Expression> value_)
: Expression(Kind::Var, value_->getType()),
: Expression(Kind::Var, value_->getType(), depsOf(value_) | Dependency::Var),
name(std::move(name_)),
value(std::move(value_)) {}

View File

@@ -13,11 +13,11 @@ namespace expression {
class Literal : public Expression {
public:
Literal(const Value& value_)
: Expression(Kind::Literal, typeOf(value_)),
: Expression(Kind::Literal, typeOf(value_), Dependency::None),
value(value_) {}
Literal(const type::Array& type_, std::vector<Value> value_)
: Expression(Kind::Literal, type_),
: Expression(Kind::Literal, type_, Dependency::None),
value(std::move(value_)) {}
EvaluationResult evaluate(const EvaluationContext&) const override { return value; }

View File

@@ -19,7 +19,7 @@ public:
std::unique_ptr<Expression> input_,
Branches branches_,
std::unique_ptr<Expression> otherwise_)
: Expression(Kind::Match, type_),
: Expression(Kind::Match, type_, depsOf(input_) | depsOf(otherwise_) | collectDependencies(branches_)),
input(std::move(input_)),
branches(std::move(branches_)),
otherwise(std::move(otherwise_)) {}

View File

@@ -16,6 +16,9 @@
namespace mbgl {
namespace style {
namespace expression {
enum class Dependency : uint32_t;
} // namespace expression
class LayerObserver;
class Filter;
@@ -159,6 +162,9 @@ public:
mapbox::base::WeakPtr<Layer> makeWeakPtr() { return weakFactory.makeWeakPtr(); }
/// Collect dependencies
expression::Dependency getDependencies() const noexcept;
protected:
virtual Mutable<Impl> mutableBaseImpl() const = 0;
void serializeProperty(Value&, const StyleProperty&, const char* propertyName, bool isPaint) const;

View File

@@ -20,18 +20,20 @@ public:
/// Contains render passes used by the renderer, see `mbgl::RenderPass`.
uint8_t renderPasses = 0u;
virtual expression::Dependency getDependencies() const noexcept = 0;
protected:
LayerProperties(Immutable<Layer::Impl> impl)
: baseImpl(std::move(impl)) {}
};
template <class Derived>
inline const auto& getEvaluated(const Immutable<LayerProperties>& properties) {
inline const auto& getEvaluated(const Immutable<LayerProperties>& properties) noexcept {
return static_cast<const Derived&>(*properties).evaluated;
}
template <class Derived>
inline const auto& getCrossfade(const Immutable<LayerProperties>& properties) {
inline const auto& getCrossfade(const Immutable<LayerProperties>& properties) noexcept {
return static_cast<const Derived&>(*properties).crossfade;
}

View File

@@ -16,9 +16,9 @@ class PropertyExpressionBase {
public:
explicit PropertyExpressionBase(std::unique_ptr<expression::Expression>);
bool isZoomConstant() const noexcept;
bool isFeatureConstant() const noexcept;
bool isRuntimeConstant() const noexcept;
bool isZoomConstant() const noexcept { return isZoomConstant_; }
bool isFeatureConstant() const noexcept { return isFeatureConstant_; }
bool isRuntimeConstant() const noexcept { return isRuntimeConstant_; }
float interpolationFactor(const Range<float>&, float) const noexcept;
Range<float> getCoveringStops(float, float) const noexcept;
const expression::Expression& getExpression() const noexcept;
@@ -30,12 +30,20 @@ public:
bool useIntegerZoom = false;
using Dependency = expression::Dependency;
Dependency getDependencies() const noexcept {
auto v = expression ? expression->dependencies : Dependency::None;
assert(isZoomConstant_ == !(underlying_type(v) & underlying_type(Dependency::Zoom)));
assert(isFeatureConstant_ == !(underlying_type(v) & underlying_type(Dependency::Feature)));
return v;
}
protected:
std::shared_ptr<const expression::Expression> expression;
variant<std::nullptr_t, const expression::Interpolate*, const expression::Step*> zoomCurve;
bool isZoomConstant_;
bool isFeatureConstant_;
bool isRuntimeConstant_;
variant<std::nullptr_t, const expression::Interpolate*, const expression::Step*> zoomCurve;
};
template <class T>

View File

@@ -62,6 +62,13 @@ public:
bool hasDataDrivenPropertyDifference(const PropertyValue<T>& other) const {
return *this != other && (isDataDriven() || other.isDataDriven());
}
using Dependency = style::expression::Dependency;
Dependency getDependencies() const noexcept {
return value.match([](const Undefined&) noexcept { return Dependency::None; },
[](const T&) noexcept { return Dependency::None; },
[](const PropertyExpression<T>& ex) noexcept { return ex.getDependencies(); });
}
};
} // namespace style

View File

@@ -248,14 +248,14 @@ namespace mbgl {
double totalFrameEncodingTime = 0;
double totalFrameRenderingTime = 0;
for (const auto& row : result) {
NSLog(@"| %-*s | %4.1f ms | %4.1f ms |", int(colWidth), row.first.c_str(), 1e3 * row.second.first, 1e3 * row.second.second);
NSLog(@"| %-*s | %4.2f ms | %4.2f ms |", int(colWidth), row.first.c_str(), 1e3 * row.second.first, 1e3 * row.second.second);
totalFrameEncodingTime += row.second.first;
totalFrameRenderingTime += row.second.second;
}
NSLog(@"Average frame encoding time: %4.1f ms", totalFrameEncodingTime * 1e3 / result.size());
NSLog(@"Average frame rendering time: %4.1f ms", totalFrameRenderingTime * 1e3 / result.size());
NSLog(@"Average frame encoding time: %4.2f ms", totalFrameEncodingTime * 1e3 / result.size());
NSLog(@"Average frame rendering time: %4.2f ms", totalFrameRenderingTime * 1e3 / result.size());
// NSLog(@"Total uploads: %zu", mbgl::uploadCount);
// NSLog(@"Total uploads with dirty vattr: %zu", mbgl::uploadVertextAttrsDirty);
// NSLog(@"Total uploads with invalid segs: %zu", mbgl::uploadInvalidSegments);

View File

@@ -23,7 +23,6 @@
self.testStatus = NO;
self.runner = new TestRunner();
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *xcTestBundleRoot = [[NSBundle bundleForClass:[self class]] resourcePath];

View File

@@ -4,9 +4,42 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/style/style_impl.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/traits.hpp>
namespace mbgl {
#if !defined(NDEBUG)
namespace {
void logStyleDependencies(EventSeverity severity, Event event, const style::Style& style) {
using Dependency = style::expression::Dependency;
constexpr auto maskCount = underlying_type(Dependency::MaskCount);
std::array<std::size_t, maskCount + 1> counts = {0};
const auto layers = style.getLayers();
for (const auto& layer : layers) {
const auto deps = layer->getDependencies();
if (deps == Dependency::None) {
counts[0]++;
} else {
for (size_t i = 0; i < maskCount; ++i) {
const auto mask = Dependency{1u << i};
if ((deps & mask) == mask) {
counts[i + 1]++;
}
}
}
}
std::ostringstream ss;
ss << "Style '" << style.getName() << "' has " << layers.size() << " layers:\n";
ss << " " << Dependency::None << ": " << counts[0] << "\n";
for (size_t i = 0; i < maskCount; ++i) {
ss << " " << Dependency{1u << i} << ": " << counts[i + 1] << "\n";
}
Log::Record(severity, event, ss.str());
}
} // namespace
#endif
Map::Impl::Impl(RendererFrontend& frontend_,
MapObserver& observer_,
std::shared_ptr<FileSource> fileSource_,
@@ -84,6 +117,10 @@ void Map::Impl::onStyleLoaded() {
annotationManager.onStyleLoaded();
}
observer.onDidFinishLoadingStyle();
#if !defined(NDEBUG)
logStyleDependencies(EventSeverity::Info, Event::Style, *style);
#endif
}
void Map::Impl::onStyleError(std::exception_ptr error) {

View File

@@ -23,7 +23,7 @@ public:
ResultType operator()(const T& constant) const { return ResultType(constant); }
ResultType operator()(const style::PropertyExpression<T>& expression) const {
if (useIntegerZoom) { // Compiler will optimize out the unused branch.
if constexpr (useIntegerZoom) { // Compiler will optimize out the unused branch.
if (!expression.isFeatureConstant() || !expression.isRuntimeConstant()) {
auto returnExpression = expression;
returnExpression.useIntegerZoom = true;

View File

@@ -42,7 +42,9 @@ inline const BackgroundLayer::Impl& impl_cast(const Immutable<style::Layer::Impl
RenderBackgroundLayer::RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl> _impl)
: RenderLayer(makeMutable<BackgroundLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {}
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {
styleDependencies = unevaluated.getDependencies();
}
RenderBackgroundLayer::~RenderBackgroundLayer() = default;

View File

@@ -62,7 +62,9 @@ inline const style::CircleLayer::Impl& impl_cast(const Immutable<style::Layer::I
RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl)
: RenderLayer(makeMutable<CircleLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {}
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {
styleDependencies = unevaluated.getDependencies();
}
void RenderCircleLayer::transition(const TransitionParameters& parameters) {
unevaluated = impl_cast(baseImpl).paint.transitioned(parameters, std::move(unevaluated));

View File

@@ -47,7 +47,9 @@ inline const FillExtrusionLayer::Impl& impl_cast(const Immutable<style::Layer::I
RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl)
: RenderLayer(makeMutable<FillExtrusionLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {}
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {
styleDependencies = unevaluated.getDependencies();
}
RenderFillExtrusionLayer::~RenderFillExtrusionLayer() = default;

View File

@@ -59,7 +59,9 @@ inline const FillLayer::Impl& impl_cast(const Immutable<style::Layer::Impl>& imp
RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl)
: RenderLayer(makeMutable<FillLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {}
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {
styleDependencies = unevaluated.getDependencies();
}
RenderFillLayer::~RenderFillLayer() = default;

View File

@@ -42,6 +42,7 @@ inline const HeatmapLayer::Impl& impl_cast(const Immutable<Layer::Impl>& impl) {
RenderHeatmapLayer::RenderHeatmapLayer(Immutable<HeatmapLayer::Impl> _impl)
: RenderLayer(makeMutable<HeatmapLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {
styleDependencies = unevaluated.getDependencies();
colorRamp = std::make_shared<PremultipliedImage>(Size(256, 1));
}

View File

@@ -43,7 +43,9 @@ inline const HillshadeLayer::Impl& impl_cast(const Immutable<style::Layer::Impl>
RenderHillshadeLayer::RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl> _impl)
: RenderLayer(makeMutable<HillshadeLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {}
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {
styleDependencies = unevaluated.getDependencies();
}
RenderHillshadeLayer::~RenderHillshadeLayer() = default;

View File

@@ -55,7 +55,9 @@ const auto posNormalAttribName = "a_pos_normal";
RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl)
: RenderLayer(makeMutable<LineLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()),
colorRamp(std::make_shared<PremultipliedImage>(Size(256, 1))) {}
colorRamp(std::make_shared<PremultipliedImage>(Size(256, 1))) {
styleDependencies = unevaluated.getDependencies();
}
RenderLineLayer::~RenderLineLayer() = default;

View File

@@ -39,7 +39,9 @@ inline const RasterLayer::Impl& impl_cast(const Immutable<style::Layer::Impl>& i
RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl)
: RenderLayer(makeMutable<RasterLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {}
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {
styleDependencies = unevaluated.getDependencies();
}
RenderRasterLayer::~RenderRasterLayer() = default;

View File

@@ -358,7 +358,9 @@ inline const SymbolLayer::Impl& impl_cast(const Immutable<style::Layer::Impl>& i
RenderSymbolLayer::RenderSymbolLayer(Immutable<style::SymbolLayer::Impl> _impl)
: RenderLayer(makeMutable<SymbolLayerProperties>(std::move(_impl))),
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {}
unevaluated(impl_cast(baseImpl).paint.untransitioned()) {
styleDependencies = unevaluated.getDependencies();
}
RenderSymbolLayer::~RenderSymbolLayer() = default;

View File

@@ -57,6 +57,13 @@ public:
return expression.evaluate(zoom, feature, featureState, defaultValue);
});
}
using Dependency = style::expression::Dependency;
Dependency getDependencies() const noexcept {
return value.match(
[](const T&) noexcept { return Dependency::None; },
[](const style::PropertyExpression<T>& expression) noexcept { return expression.getDependencies(); });
}
};
template <class T>
@@ -105,6 +112,13 @@ public:
}
});
}
using Dependency = style::expression::Dependency;
Dependency getDependencies() const noexcept {
return value.match(
[](const Faded<T>&) noexcept { return Dependency::None; },
[](const style::PropertyExpression<T>& expression) noexcept { return expression.getDependencies(); });
}
};
namespace util {

View File

@@ -194,6 +194,9 @@ public:
bool isLayerRenderable() const noexcept { return isRenderable; }
#endif
using Dependency = style::expression::Dependency;
Dependency getStyleDependencies() const { return styleDependencies; }
protected:
// Checks whether the current hardware can render this layer. If it can't,
// we'll show a warning in the console to inform the developer.
@@ -299,6 +302,8 @@ protected:
// Current layer index as specified by the layerIndexChanged event
int32_t layerIndex{0};
Dependency styleDependencies = Dependency::None;
// Current renderable status as specified by the markLayerRenderable event
bool isRenderable{false};

View File

@@ -300,8 +300,14 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
for (RenderLayer& layer : orderedLayers) {
const std::string& id = layer.getID();
const bool layerAddedOrChanged = layerDiff.added.count(id) || layerDiff.changed.count(id);
if (layerAddedOrChanged || zoomChanged || layer.hasTransition() || layer.hasCrossfade()) {
auto previousMask = layer.evaluatedProperties->constantsMask();
// Only re-evaluate on change of zoom if the style has some reference to it
using Dependency = expression::Dependency;
const bool zoomChangedAndMatters = zoomChanged && !layerAddedOrChanged &&
(layer.getStyleDependencies() & Dependency::Zoom) != Dependency::None;
if (layerAddedOrChanged || zoomChangedAndMatters || layer.hasTransition() || layer.hasCrossfade()) {
const auto previousMask = layer.evaluatedProperties->constantsMask();
layer.evaluate(evaluationParameters);
if (previousMask != layer.evaluatedProperties->constantsMask()) {
constantsMaskChanged.insert(id);

View File

@@ -10,7 +10,7 @@ namespace expression {
using namespace mbgl::style::conversion;
Assertion::Assertion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_)
: Expression(Kind::Assertion, std::move(type_)),
: Expression(Kind::Assertion, std::move(type_), collectDependencies(inputs_)),
inputs(std::move(inputs_)) {
assert(!inputs.empty());
}

View File

@@ -8,6 +8,10 @@ namespace mbgl {
namespace style {
namespace expression {
using CoerceFunction = EvaluationResult (*)(const Value&);
namespace {
EvaluationResult toBoolean(const Value& v) {
return v.match([&](double f) { return static_cast<bool>(f); },
[&](const std::string& s) { return s.length() > 0; },
@@ -76,28 +80,39 @@ EvaluationResult toImage(const Value& imageValue) {
return Image(toString(imageValue).c_str());
}
Coercion::Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_)
: Expression(Kind::Coercion, std::move(type_)),
EvaluationResult toStringValue(const Value& imageValue) {
return toString(imageValue);
}
CoerceFunction getCoerceFunction(const type::Type& t) {
if (t.is<type::BooleanType>()) {
return toBoolean;
} else if (t.is<type::ColorType>()) {
return toColor;
} else if (t.is<type::NumberType>()) {
return toNumber;
} else if (t.is<type::StringType>()) {
return toStringValue;
} else if (t.is<type::FormattedType>()) {
return toFormatted;
} else if (t.is<type::ImageType>()) {
return toImage;
}
assert(false);
return toStringValue;
}
Dependency extraDependency(const type::Type& t) {
return t.is<type::ImageType>() ? Dependency::Image : Dependency::None;
}
} // namespace
Coercion::Coercion(const type::Type& type_, std::vector<std::unique_ptr<Expression>> inputs_)
: Expression(Kind::Coercion, type_, collectDependencies(inputs_) | extraDependency(type_)),
coerceSingleValue(getCoerceFunction(getType())),
inputs(std::move(inputs_)) {
assert(!inputs.empty());
type::Type t = getType();
if (t.is<type::BooleanType>()) {
coerceSingleValue = toBoolean;
} else if (t.is<type::ColorType>()) {
coerceSingleValue = toColor;
} else if (t.is<type::NumberType>()) {
coerceSingleValue = toNumber;
} else if (t.is<type::StringType>()) {
coerceSingleValue = [](const Value& v) -> EvaluationResult {
return toString(v);
};
} else if (t.is<type::FormattedType>()) {
coerceSingleValue = toFormatted;
} else if (t.is<type::ImageType>()) {
coerceSingleValue = toImage;
} else {
assert(false);
}
}
mbgl::Value Coercion::serialize() const {

View File

@@ -11,7 +11,9 @@ namespace expression {
CollatorExpression::CollatorExpression(std::unique_ptr<Expression> caseSensitive_,
std::unique_ptr<Expression> diacriticSensitive_,
std::optional<std::unique_ptr<Expression>> locale_)
: Expression(Kind::CollatorExpression, type::Collator),
: Expression(Kind::CollatorExpression,
type::Collator,
depsOf(caseSensitive_) | depsOf(diacriticSensitive_) | depsOf(locale_)),
caseSensitive(std::move(caseSensitive_)),
diacriticSensitive(std::move(diacriticSensitive_)),
locale(std::move(locale_)) {}
@@ -56,7 +58,7 @@ ParseResult CollatorExpression::parse(const Convertible& value, ParsingContext&
ParseResult locale;
if (localeOption) {
locale = ctx.parse(*localeOption, 1, {type::String});
if (!locale) {
if (!locale || !*locale) {
return ParseResult();
}
}
@@ -68,15 +70,17 @@ ParseResult CollatorExpression::parse(const Convertible& value, ParsingContext&
void CollatorExpression::eachChild(const std::function<void(const Expression&)>& fn) const {
fn(*caseSensitive);
fn(*diacriticSensitive);
if (locale) {
if (locale && *locale) {
fn(**locale);
}
}
bool CollatorExpression::operator==(const Expression& e) const {
if (e.getKind() == Kind::CollatorExpression) {
auto rhs = static_cast<const CollatorExpression*>(&e);
if ((locale && (!rhs->locale || **locale != **(rhs->locale))) || (!locale && rhs->locale)) {
const auto* rhs = static_cast<const CollatorExpression*>(&e);
const bool lLocSet = locale && *locale;
const bool rLocSet = rhs->locale && *rhs->locale;
if ((!lLocSet && rLocSet) || (lLocSet && (!rLocSet || **locale != **rhs->locale))) {
return false;
}
return *caseSensitive == *(rhs->caseSensitive) && *diacriticSensitive == *(rhs->diacriticSensitive);
@@ -88,7 +92,7 @@ mbgl::Value CollatorExpression::serialize() const {
std::unordered_map<std::string, mbgl::Value> options;
options["case-sensitive"] = caseSensitive->serialize();
options["diacritic-sensitive"] = diacriticSensitive->serialize();
if (locale) {
if (locale && *locale) {
options["locale"] = (*locale)->serialize();
}
return std::vector<mbgl::Value>{{std::string("collator"), options}};
@@ -105,12 +109,13 @@ EvaluationResult CollatorExpression::evaluate(const EvaluationContext& params) c
}
if (locale) {
auto localeResult = (*locale)->evaluate(params);
if (!localeResult) {
if (auto localeResult = (*locale)->evaluate(params)) {
return Collator(caseSensitiveResult->get<bool>(),
diacriticSensitiveResult->get<bool>(),
localeResult->get<std::string>());
} else {
return localeResult.error();
}
return Collator(
caseSensitiveResult->get<bool>(), diacriticSensitiveResult->get<bool>(), localeResult->get<std::string>());
} else {
return Collator(caseSensitiveResult->get<bool>(), diacriticSensitiveResult->get<bool>());
}

View File

@@ -110,7 +110,7 @@ static CollatorComparison::CompareFunctionType getCollatorComparisonFunction(con
}
BasicComparison::BasicComparison(std::string op_, std::unique_ptr<Expression> lhs_, std::unique_ptr<Expression> rhs_)
: Expression(Kind::Comparison, type::Boolean),
: Expression(Kind::Comparison, type::Boolean, depsOf(lhs_) | depsOf(rhs_)),
op(std::move(op_)),
compare(getBasicCompareFunction(op)),
lhs(std::move(lhs_)),
@@ -168,7 +168,7 @@ CollatorComparison::CollatorComparison(std::string op_,
std::unique_ptr<Expression> lhs_,
std::unique_ptr<Expression> rhs_,
std::unique_ptr<Expression> collator_)
: Expression(Kind::Comparison, type::Boolean),
: Expression(Kind::Comparison, type::Boolean, depsOf(lhs_) | depsOf(rhs_) | depsOf(collator_)),
op(std::move(op_)),
compare(getCollatorComparisonFunction(op)),
lhs(std::move(lhs_)),

View File

@@ -47,17 +47,22 @@ namespace detail {
struct SignatureBase {
using Args = std::vector<std::unique_ptr<Expression>>;
SignatureBase(type::Type result_, variant<std::vector<type::Type>, VarargsType> params_, std::string name_)
SignatureBase(type::Type result_,
variant<std::vector<type::Type>, VarargsType> params_,
std::string name_,
Dependency dependencies_)
: result(std::move(result_)),
params(std::move(params_)),
name(std::move(name_)) {}
name(std::move(name_)),
dependencies(dependencies_) {}
virtual ~SignatureBase() = default;
virtual EvaluationResult apply(const EvaluationContext&, const Args&) const = 0;
type::Type result;
variant<std::vector<type::Type>, VarargsType> params;
std::string name;
const type::Type result;
const variant<std::vector<type::Type>, VarargsType> params;
const std::string name;
const Dependency dependencies;
};
/*
@@ -87,10 +92,11 @@ struct Signature;
// Simple evaluate function (const T0&, const T1&, ...) -> Result<U>
template <class R, class... Params>
struct Signature<R(Params...)> : SignatureBase {
Signature(R (*evaluate_)(Params...), const std::string& name_)
Signature(R (*evaluate_)(Params...), const std::string& name_, Dependency dependencies_)
: SignatureBase(valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
std::vector<type::Type>{valueTypeToExpressionType<std::decay_t<Params>>()...},
name_),
name_,
dependencies_),
evaluate(evaluate_) {}
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override {
@@ -119,10 +125,11 @@ private:
// Varargs evaluate function (const Varargs<T>&) -> Result<U>
template <class R, typename T>
struct Signature<R(const Varargs<T>&)> : SignatureBase {
Signature(R (*evaluate_)(const Varargs<T>&), const std::string& name_)
Signature(R (*evaluate_)(const Varargs<T>&), const std::string& name_, Dependency dependencies_)
: SignatureBase(valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
VarargsType{valueTypeToExpressionType<T>()},
name_),
name_,
dependencies_),
evaluate(evaluate_) {}
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override {
@@ -145,10 +152,11 @@ struct Signature<R(const Varargs<T>&)> : SignatureBase {
// (const EvaluationParams&, const T0&, const T1&, ...) -> Result<U>
template <class R, class... Params>
struct Signature<R(const EvaluationContext&, Params...)> : SignatureBase {
Signature(R (*evaluate_)(const EvaluationContext&, Params...), const std::string& name_)
Signature(R (*evaluate_)(const EvaluationContext&, Params...), const std::string& name_, Dependency dependencies_)
: SignatureBase(valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
std::vector<type::Type>{valueTypeToExpressionType<std::decay_t<Params>>()...},
name_),
name_,
dependencies_),
evaluate(evaluate_) {}
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override {
@@ -178,10 +186,13 @@ private:
// (const EvaluationContext&, const Varargs<T>&) -> Result<U>
template <class R, typename T>
struct Signature<R(const EvaluationContext&, const Varargs<T>&)> : SignatureBase {
Signature(R (*evaluate_)(const EvaluationContext&, const Varargs<T>&), const std::string& name_)
Signature(R (*evaluate_)(const EvaluationContext&, const Varargs<T>&),
const std::string& name_,
Dependency dependencies_)
: SignatureBase(valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
VarargsType{valueTypeToExpressionType<T>()},
name_),
name_,
dependencies_),
evaluate(evaluate_) {}
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override {
@@ -215,8 +226,11 @@ struct SignatureType<Lambda, std::enable_if_t<std::is_class_v<Lambda>>> {
};
template <typename Fn>
static std::unique_ptr<detail::SignatureBase> makeSignature(std::string name, Fn evaluateFunction) {
return std::make_unique<Signature<typename SignatureType<Fn>::Type>>(evaluateFunction, std::move(name));
static std::unique_ptr<detail::SignatureBase> makeSignature(std::string name,
Fn evaluateFunction,
Dependency dependencies = Dependency::None) {
return std::make_unique<Signature<typename SignatureType<Fn>::Type>>(
evaluateFunction, std::move(name), dependencies);
}
} // namespace detail
@@ -322,14 +336,17 @@ const auto& rgbCompoundExpression() {
}
const auto& zoomCompoundExpression() {
static auto signature = detail::makeSignature("zoom", [](const EvaluationContext& params) -> Result<double> {
if (!params.zoom) {
return EvaluationError{
"The 'zoom' expression is unavailable in the current "
"evaluation context."};
}
return *(params.zoom);
});
static auto signature = detail::makeSignature(
"zoom",
[](const EvaluationContext& params) -> Result<double> {
if (!params.zoom) {
return EvaluationError{
"The 'zoom' expression is unavailable in the current "
"evaluation context."};
}
return *(params.zoom);
},
Dependency::Zoom);
return signature;
}
@@ -374,7 +391,8 @@ const auto& accumulatedCompoundExpression() {
const auto& hasContextCompoundExpression() {
static auto signature = detail::makeSignature(
"has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> {
"has",
[](const EvaluationContext& params, const std::string& key) -> Result<bool> {
if (!params.feature) {
return EvaluationError{
"Feature data is unavailable in the current evaluation "
@@ -382,21 +400,25 @@ const auto& hasContextCompoundExpression() {
}
return static_cast<bool>(params.feature->getValue(key));
});
},
Dependency::Feature);
return signature;
}
const auto& hasObjectCompoundExpression() {
static auto signature = detail::makeSignature(
"has", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<bool> {
"has",
[](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<bool> {
return object.find(key) != object.end();
});
},
Dependency::Feature);
return signature;
}
const auto& getContextCompoundExpression() {
static auto signature = detail::makeSignature(
"get", [](const EvaluationContext& params, const std::string& key) -> Result<Value> {
"get",
[](const EvaluationContext& params, const std::string& key) -> Result<Value> {
if (!params.feature) {
return EvaluationError{
"Feature data is unavailable in the current evaluation "
@@ -408,28 +430,30 @@ const auto& getContextCompoundExpression() {
return Null;
}
return Value(toExpressionValue(*propertyValue));
});
},
Dependency::Feature);
return signature;
}
const auto& getObjectCompoundExpression() {
static auto signature = detail::makeSignature(
"get", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<Value> {
"get",
[](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<Value> {
if (object.find(key) == object.end()) {
return Null;
}
return object.at(key);
});
},
Dependency::Feature);
return signature;
}
const auto& propertiesCompoundExpression() {
static auto signature = detail::makeSignature(
"properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> {
"properties",
[](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> {
if (!params.feature) {
return EvaluationError{
"Feature data is unavailable in the current evaluation "
"context."};
return EvaluationError{"Feature data is unavailable in the current evaluation context."};
}
std::unordered_map<std::string, Value> result;
const PropertyMap& properties = params.feature->getProperties();
@@ -438,44 +462,50 @@ const auto& propertiesCompoundExpression() {
result[entry.first] = toExpressionValue(entry.second);
}
return result;
});
},
Dependency::Feature);
return signature;
}
const auto& geometryTypeCompoundExpression() {
static auto signature = detail::makeSignature("geometry-type",
[](const EvaluationContext& params) -> Result<std::string> {
if (!params.feature) {
return EvaluationError{
"Feature data is unavailable in the current evaluation "
"context."};
}
static auto signature = detail::makeSignature(
"geometry-type",
[](const EvaluationContext& params) -> Result<std::string> {
if (!params.feature) {
return EvaluationError{
"Feature data is unavailable in the current evaluation "
"context."};
}
auto type = params.feature->getType();
if (type == FeatureType::Point) {
return "Point";
} else if (type == FeatureType::LineString) {
return "LineString";
} else if (type == FeatureType::Polygon) {
return "Polygon";
} else {
return "Unknown";
}
});
auto type = params.feature->getType();
if (type == FeatureType::Point) {
return "Point";
} else if (type == FeatureType::LineString) {
return "LineString";
} else if (type == FeatureType::Polygon) {
return "Polygon";
} else {
return "Unknown";
}
},
Dependency::Feature);
return signature;
}
const auto& idCompoundExpression() {
static auto signature = detail::makeSignature("id", [](const EvaluationContext& params) -> Result<Value> {
if (!params.feature) {
return EvaluationError{
"Feature data is unavailable in the current evaluation "
"context."};
}
static auto signature = detail::makeSignature(
"id",
[](const EvaluationContext& params) -> Result<Value> {
if (!params.feature) {
return EvaluationError{
"Feature data is unavailable in the current evaluation "
"context."};
}
auto id = params.feature->getID();
return id.match([](const auto& idValue) { return toExpressionValue(mbgl::Value(idValue)); });
});
auto id = params.feature->getID();
return id.match([](const auto& idValue) { return toExpressionValue(mbgl::Value(idValue)); });
},
Dependency::Feature);
return signature;
}
@@ -675,7 +705,8 @@ const auto& errorCompoundExpression() {
const auto& featureStateCompoundExpression() {
static auto signature = detail::makeSignature(
"feature-state", [](const EvaluationContext& params, const std::string& key) -> Result<Value> {
"feature-state",
[](const EvaluationContext& params, const std::string& key) -> Result<Value> {
mbgl::Value state;
if (params.featureState != nullptr) {
auto it = params.featureState->find(key);
@@ -684,43 +715,52 @@ const auto& featureStateCompoundExpression() {
}
}
return toExpressionValue(state);
});
},
Dependency::Feature);
return signature;
}
// Legacy Filters
const auto& filterEqualsCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-==", [](const EvaluationContext& params, const std::string& key, const Value& lhs) -> Result<bool> {
"filter-==",
[](const EvaluationContext& params, const std::string& key, const Value& lhs) -> Result<bool> {
const auto rhs = featurePropertyAsExpressionValue(params, key);
return rhs ? lhs == *rhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterIdEqualsCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-id-==", [](const EvaluationContext& params, const Value& lhs) -> Result<bool> {
"filter-id-==",
[](const EvaluationContext& params, const Value& lhs) -> Result<bool> {
return lhs == featureIdAsExpressionValue(params);
});
},
Dependency::Feature);
return signature;
}
const auto& filterTypeEqualsCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-type-==", [](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
"filter-type-==",
[](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
if (!params.feature) return false;
return featureTypeAsString(params.feature->getType()) == lhs;
});
},
Dependency::Feature);
return signature;
}
const auto& filterLessThanNumberCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-<", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
"filter-<",
[](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
auto rhs = featurePropertyAsDouble(params, key);
return rhs ? rhs < lhs : false;
});
},
Dependency::Feature);
return signature;
}
@@ -730,34 +770,41 @@ const auto& filterLessThanStringCompoundExpression() {
[](const EvaluationContext& params, const std::string& key, const std::string& lhs) -> Result<bool> {
auto rhs = featurePropertyAsString(params, key);
return rhs ? rhs < lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterIdLessThanNumberCompoundExpression() {
static auto signature = detail::makeSignature("filter-id-<",
[](const EvaluationContext& params, double lhs) -> Result<bool> {
auto rhs = featureIdAsDouble(params);
return rhs ? rhs < lhs : false;
});
static auto signature = detail::makeSignature(
"filter-id-<",
[](const EvaluationContext& params, double lhs) -> Result<bool> {
auto rhs = featureIdAsDouble(params);
return rhs ? rhs < lhs : false;
},
Dependency::Feature);
return signature;
}
const auto& filterIdLessThanStringCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-id-<", [](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
"filter-id-<",
[](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
auto rhs = featureIdAsString(params);
return rhs ? rhs < lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterMoreThanNumberCompoundExpression() {
static auto signature = detail::makeSignature(
"filter->", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
"filter->",
[](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
auto rhs = featurePropertyAsDouble(params, key);
return rhs ? rhs > lhs : false;
});
},
Dependency::Feature);
return signature;
}
@@ -767,34 +814,41 @@ const auto& filterMoreThanStringCompoundExpression() {
[](const EvaluationContext& params, const std::string& key, const std::string& lhs) -> Result<bool> {
auto rhs = featurePropertyAsString(params, key);
return rhs ? rhs > lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterIdMoreThanNumberCompoundExpression() {
static auto signature = detail::makeSignature("filter-id->",
[](const EvaluationContext& params, double lhs) -> Result<bool> {
auto rhs = featureIdAsDouble(params);
return rhs ? rhs > lhs : false;
});
static auto signature = detail::makeSignature(
"filter-id->",
[](const EvaluationContext& params, double lhs) -> Result<bool> {
auto rhs = featureIdAsDouble(params);
return rhs ? rhs > lhs : false;
},
Dependency::Feature);
return signature;
}
const auto& filterIdMoreThanStringCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-id->", [](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
"filter-id->",
[](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
auto rhs = featureIdAsString(params);
return rhs ? rhs > lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterLessOrEqualThanNumberCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-<=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
"filter-<=",
[](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
auto rhs = featurePropertyAsDouble(params, key);
return rhs ? rhs <= lhs : false;
});
},
Dependency::Feature);
return signature;
}
@@ -804,34 +858,41 @@ const auto& filterLessOrEqualThanStringCompoundExpression() {
[](const EvaluationContext& params, const std::string& key, const std::string& lhs) -> Result<bool> {
auto rhs = featurePropertyAsString(params, key);
return rhs ? rhs <= lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterIdLessOrEqualThanNumberCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-id-<=", [](const EvaluationContext& params, double lhs) -> Result<bool> {
"filter-id-<=",
[](const EvaluationContext& params, double lhs) -> Result<bool> {
auto rhs = featureIdAsDouble(params);
return rhs ? rhs <= lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterIdLessOrEqualThanStringCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-id-<=", [](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
"filter-id-<=",
[](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
auto rhs = featureIdAsString(params);
return rhs ? rhs <= lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterGreaterOrEqualThanNumberCompoundExpression() {
static auto signature = detail::makeSignature(
"filter->=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
"filter->=",
[](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
auto rhs = featurePropertyAsDouble(params, key);
return rhs ? rhs >= lhs : false;
});
},
Dependency::Feature);
return signature;
}
@@ -841,72 +902,88 @@ const auto& filterGreaterOrEqualThanStringCompoundExpression() {
[](const EvaluationContext& params, const std::string& key, const std::string& lhs) -> Result<bool> {
auto rhs = featurePropertyAsString(params, key);
return rhs ? rhs >= lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterIdGreaterOrEqualThanNumberCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-id->=", [](const EvaluationContext& params, double lhs) -> Result<bool> {
"filter-id->=",
[](const EvaluationContext& params, double lhs) -> Result<bool> {
auto rhs = featureIdAsDouble(params);
return rhs ? rhs >= lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterIdGreaterOrEqualThanStringCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-id->=", [](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
"filter-id->=",
[](const EvaluationContext& params, const std::string& lhs) -> Result<bool> {
auto rhs = featureIdAsString(params);
return rhs ? rhs >= lhs : false;
});
},
Dependency::Feature);
return signature;
}
const auto& filterHasCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> {
"filter-has",
[](const EvaluationContext& params, const std::string& key) -> Result<bool> {
assert(params.feature);
return bool(params.feature->getValue(key));
});
},
Dependency::Feature);
return signature;
}
const auto& filterHasIdCompoundExpression() {
static auto signature = detail::makeSignature("filter-has-id", [](const EvaluationContext& params) -> Result<bool> {
assert(params.feature);
return !params.feature->getID().is<NullValue>();
});
static auto signature = detail::makeSignature(
"filter-has-id",
[](const EvaluationContext& params) -> Result<bool> {
assert(params.feature);
return !params.feature->getID().is<NullValue>();
},
Dependency::Feature);
return signature;
}
const auto& filterTypeInCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-type-in", [](const EvaluationContext& params, const Varargs<std::string>& types) -> Result<bool> {
"filter-type-in",
[](const EvaluationContext& params, const Varargs<std::string>& types) -> Result<bool> {
assert(params.feature);
std::optional<std::string> type = featureTypeAsString(params.feature->getType());
return std::find(types.begin(), types.end(), type) != types.end();
});
},
Dependency::Feature);
return signature;
}
const auto& filterIdInCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-id-in", [](const EvaluationContext& params, const Varargs<Value>& ids) -> Result<bool> {
"filter-id-in",
[](const EvaluationContext& params, const Varargs<Value>& ids) -> Result<bool> {
auto id = featureIdAsExpressionValue(params);
return std::find(ids.begin(), ids.end(), id) != ids.end();
});
},
Dependency::Feature);
return signature;
}
const auto& filterInCompoundExpression() {
static auto signature = detail::makeSignature(
"filter-in", [](const EvaluationContext& params, const Varargs<Value>& varargs) -> Result<bool> {
"filter-in",
[](const EvaluationContext& params, const Varargs<Value>& varargs) -> Result<bool> {
if (varargs.size() < 2) return false;
assert(varargs[0].is<std::string>());
auto value = featurePropertyAsExpressionValue(params, varargs[0].get<std::string>());
return value ? std::find(varargs.begin() + 1, varargs.end(), *value) != varargs.end() : false;
});
},
Dependency::Feature);
return signature;
}
@@ -1158,7 +1235,7 @@ ParseResult createCompoundExpression(const std::string& name,
CompoundExpression::CompoundExpression(const detail::SignatureBase& signature_,
std::vector<std::unique_ptr<Expression>> args_)
: Expression(Kind::CompoundExpression, signature_.result),
: Expression(Kind::CompoundExpression, signature_.result, signature_.dependencies | collectDependencies(args_)),
signature(signature_),
args(std::move(args_)) {}

View File

@@ -770,7 +770,7 @@ namespace style {
namespace expression {
Distance::Distance(GeoJSON geojson, Feature::geometry_type geometries_)
: Expression(Kind::Distance, type::Number),
: Expression(Kind::Distance, type::Number, Dependency::Feature),
geoJSONSource(std::move(geojson)),
geometries(std::move(geometries_)) {}

View File

@@ -1,9 +1,49 @@
#include <mbgl/style/expression/compound_expression.hpp>
#include <mbgl/style/expression/expression.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <sstream>
#include <utility>
namespace mbgl {
using Dependency = style::expression::Dependency;
std::ostream& operator<<(std::ostream& os, const Dependency deps) {
if (deps == Dependency::None) {
os << "None";
return os;
}
bool any = false;
const auto add = [&](Dependency dep, std::string_view name) {
if ((deps & dep) != Dependency::None) {
os << (any ? "|" : std::string_view{}) << name;
any = true;
}
};
add(Dependency::Feature, "Feature");
add(Dependency::Image, "Image");
add(Dependency::Zoom, "Zoom");
add(Dependency::Location, "Location");
add(Dependency::Bind, "Bind");
add(Dependency::Var, "Var");
add(Dependency::Override, "Override");
return os;
}
namespace util {
std::string toString(const style::expression::Dependency deps) {
std::ostringstream ss;
ss << deps;
return ss.str();
}
} // namespace util
namespace style {
namespace expression {

View File

@@ -3,7 +3,7 @@
#include <mbgl/style/expression/let.hpp>
#include <mbgl/style/expression/coalesce.hpp>
#include <mbgl/style/expression/is_constant.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/variant.hpp>
#include <optional>
@@ -79,16 +79,16 @@ std::optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCu
return result;
}
variant<std::nullptr_t, const Interpolate*, const Step*> findZoomCurveChecked(const expression::Expression* e) {
if (isZoomConstant(*e)) {
return nullptr;
}
return findZoomCurve(e)->match(
[](const ParsingError&) -> variant<std::nullptr_t, const Interpolate*, const Step*> {
assert(false);
using ZoomCurveOrNull = variant<std::nullptr_t, const Interpolate*, const Step*>;
ZoomCurveOrNull findZoomCurveChecked(const expression::Expression& e) {
assert(!expression::isZoomConstant(e));
return findZoomCurve(&e)->match(
[](const ParsingError& err) -> ZoomCurveOrNull {
Log::Error(Event::Style, "Invalid Expression: " + err.message);
return nullptr;
},
[](auto zoomCurve) -> variant<std::nullptr_t, const Interpolate*, const Step*> { return zoomCurve; });
[](auto zoomCurve) -> ZoomCurveOrNull { return zoomCurve; });
}
} // namespace expression

View File

@@ -9,27 +9,24 @@ namespace expression {
FormatExpressionSection::FormatExpressionSection(std::unique_ptr<Expression> content_)
: content(std::move(content_)) {}
void FormatExpressionSection::setTextSectionOptions(std::optional<std::unique_ptr<Expression>> fontScale_,
std::optional<std::unique_ptr<Expression>> textFont_,
std::optional<std::unique_ptr<Expression>> textColor_) {
void FormatExpressionSection::setTextSectionOptions(std::unique_ptr<Expression>&& fontScale_,
std::unique_ptr<Expression>&& textFont_,
std::unique_ptr<Expression>&& textColor_) {
if (fontScale_) {
assert(*fontScale_);
fontScale = std::shared_ptr<Expression>(std::move(*fontScale_));
fontScale = std::shared_ptr<Expression>(std::move(fontScale_));
}
if (textFont_) {
assert(*textFont_);
textFont = std::shared_ptr<Expression>(std::move(*textFont_));
textFont = std::shared_ptr<Expression>(std::move(textFont_));
}
if (textColor_) {
assert(*textColor_);
textColor = std::shared_ptr<Expression>(std::move(*textColor_));
textColor = std::shared_ptr<Expression>(std::move(textColor_));
}
}
FormatExpression::FormatExpression(std::vector<FormatExpressionSection> sections_)
: Expression(Kind::FormatExpression, type::Formatted),
: Expression(Kind::FormatExpression, type::Formatted, collectDependencies(sections_, &depsOfSection)),
sections(std::move(sections_)) {}
using namespace mbgl::style::conversion;
@@ -80,7 +77,9 @@ ParseResult FormatExpression::parse(const Convertible& value, ParsingContext& ct
}
}
sections.back().setTextSectionOptions(std::move(fontScale), std::move(textFont), std::move(textColor));
sections.back().setTextSectionOptions(fontScale ? std::move(*fontScale) : std::unique_ptr<Expression>{},
textFont ? std::move(*textFont) : std::unique_ptr<Expression>{},
textColor ? std::move(*textColor) : std::unique_ptr<Expression>{});
} else {
ParseResult parsedArg = ctx.parse(arg, 1, {type::Value});
if (!parsedArg) {
@@ -100,13 +99,13 @@ void FormatExpression::eachChild(const std::function<void(const Expression&)>& f
for (auto& section : sections) {
fn(*section.content);
if (section.fontScale) {
fn(**section.fontScale);
fn(*section.fontScale);
}
if (section.textFont) {
fn(**section.textFont);
fn(*section.textFont);
}
if (section.textColor) {
fn(**section.textColor);
fn(*section.textColor);
}
}
}
@@ -123,15 +122,15 @@ bool FormatExpression::operator==(const Expression& e) const {
if (*lhsSection.content != *rhsSection.content) {
return false;
}
if ((lhsSection.fontScale && (!rhsSection.fontScale || **lhsSection.fontScale != **rhsSection.fontScale)) ||
if ((lhsSection.fontScale && (!rhsSection.fontScale || *lhsSection.fontScale != *rhsSection.fontScale)) ||
(!lhsSection.fontScale && rhsSection.fontScale)) {
return false;
}
if ((lhsSection.textFont && (!rhsSection.textFont || **lhsSection.textFont != **rhsSection.textFont)) ||
if ((lhsSection.textFont && (!rhsSection.textFont || *lhsSection.textFont != *rhsSection.textFont)) ||
(!lhsSection.textFont && rhsSection.textFont)) {
return false;
}
if ((lhsSection.textColor && (!rhsSection.textColor || **lhsSection.textColor != **rhsSection.textColor)) ||
if ((lhsSection.textColor && (!rhsSection.textColor || *lhsSection.textColor != *rhsSection.textColor)) ||
(!lhsSection.textColor && rhsSection.textColor)) {
return false;
}
@@ -147,13 +146,13 @@ mbgl::Value FormatExpression::serialize() const {
serialized.push_back(section.content->serialize());
std::unordered_map<std::string, mbgl::Value> options;
if (section.fontScale) {
options.emplace(kFormattedSectionFontScale, (*section.fontScale)->serialize());
options.emplace(kFormattedSectionFontScale, section.fontScale->serialize());
}
if (section.textFont) {
options.emplace(kFormattedSectionTextFont, (*section.textFont)->serialize());
options.emplace(kFormattedSectionTextFont, section.textFont->serialize());
}
if (section.textColor) {
options.emplace(kFormattedSectionTextColor, (*section.textColor)->serialize());
options.emplace(kFormattedSectionTextColor, section.textColor->serialize());
}
serialized.emplace_back(options);
}
@@ -188,7 +187,7 @@ EvaluationResult FormatExpression::evaluate(const EvaluationContext& params) con
std::optional<double> evaluatedFontScale;
if (section.fontScale) {
auto fontScaleResult = (*section.fontScale)->evaluate(params);
auto fontScaleResult = section.fontScale->evaluate(params);
if (!fontScaleResult) {
return fontScaleResult.error();
}
@@ -197,7 +196,7 @@ EvaluationResult FormatExpression::evaluate(const EvaluationContext& params) con
std::optional<FontStack> evaluatedTextFont;
if (section.textFont) {
auto textFontResult = (*section.textFont)->evaluate(params);
auto textFontResult = section.textFont->evaluate(params);
if (!textFontResult) {
return textFontResult.error();
}
@@ -212,7 +211,7 @@ EvaluationResult FormatExpression::evaluate(const EvaluationContext& params) con
std::optional<Color> evaluatedTextColor;
if (section.textColor) {
auto textColorResult = (*section.textColor)->evaluate(params);
auto textColorResult = section.textColor->evaluate(params);
if (!textColorResult) {
return textColorResult.error();
}

View File

@@ -7,7 +7,7 @@ namespace style {
namespace expression {
ImageExpression::ImageExpression(std::unique_ptr<Expression> imageID_)
: Expression(Kind::ImageExpression, type::Image),
: Expression(Kind::ImageExpression, type::Image, depsOf(imageID_) | Dependency::Image),
imageID(std::move(imageID_)) {
assert(imageID);
}

View File

@@ -29,7 +29,7 @@ bool isSearchableRuntimeType(const type::Type& type) {
} // namespace
In::In(std::unique_ptr<Expression> needle_, std::unique_ptr<Expression> haystack_)
: Expression(Kind::In, type::Boolean),
: Expression(Kind::In, type::Boolean, depsOf(needle_) | depsOf(haystack_)),
needle(std::move(needle_)),
haystack(std::move(haystack_)) {
assert(isComparableType(needle->getType()));

View File

@@ -10,7 +10,7 @@ namespace expression {
IndexOf::IndexOf(std::unique_ptr<Expression> keyword_,
std::unique_ptr<Expression> input_,
std::unique_ptr<Expression> fromIndex_)
: Expression(Kind::IndexOf, type::Number),
: Expression(Kind::IndexOf, type::Number, depsOf(keyword_) | depsOf(input_) | depsOf(fromIndex_)),
keyword(std::move(keyword_)),
input(std::move(input_)),
fromIndex(std::move(fromIndex_)) {}

View File

@@ -250,7 +250,7 @@ Interpolate::Interpolate(const type::Type& type_,
Interpolator interpolator_,
std::unique_ptr<Expression> input_,
std::map<double, std::unique_ptr<Expression>> stops_)
: Expression(Kind::Interpolate, type_),
: Expression(Kind::Interpolate, type_, depsOf(input_) | collectDependencies(stops_)),
interpolator(std::move(interpolator_)),
input(std::move(input_)),
stops(std::move(stops_)) {

View File

@@ -7,7 +7,7 @@ namespace style {
namespace expression {
Length::Length(std::unique_ptr<Expression> input_)
: Expression(Kind::Length, type::Number),
: Expression(Kind::Length, type::Number, depsOf(input_)),
input(std::move(input_)) {}
EvaluationResult Length::evaluate(const EvaluationContext& params) const {

View File

@@ -16,7 +16,10 @@ NumberFormat::NumberFormat(std::unique_ptr<Expression> number_,
std::unique_ptr<Expression> currency_,
std::unique_ptr<Expression> minFractionDigits_,
std::unique_ptr<Expression> maxFractionDigits_)
: Expression(Kind::NumberFormat, type::String),
: Expression(Kind::NumberFormat,
type::String,
depsOf(number_) | depsOf(locale_) | depsOf(currency_) | depsOf(minFractionDigits_) |
depsOf(maxFractionDigits_)),
number(std::move(number_)),
locale(std::move(locale_)),
currency(std::move(currency_)),

View File

@@ -22,7 +22,7 @@ int normalizeIndex(int index, int length) {
Slice::Slice(std::unique_ptr<Expression> input_,
std::unique_ptr<Expression> fromIndex_,
std::unique_ptr<Expression> toIndex_)
: Expression(Kind::Slice, input_->getType()),
: Expression(Kind::Slice, input_->getType(), depsOf(input_) | depsOf(fromIndex_) | depsOf(toIndex_)),
input(std::move(input_)),
fromIndex(std::move(fromIndex_)),
toIndex(std::move(toIndex_)) {}

View File

@@ -12,7 +12,7 @@ namespace expression {
Step::Step(const type::Type& type_,
std::unique_ptr<Expression> input_,
std::map<double, std::unique_ptr<Expression>> stops_)
: Expression(Kind::Step, type_),
: Expression(Kind::Step, type_, depsOf(input_) | collectDependencies(stops_)),
input(std::move(input_)),
stops(std::move(stops_)) {
assert(input->getType() == type::Number);

View File

@@ -206,7 +206,7 @@ namespace style {
namespace expression {
Within::Within(GeoJSON geojson, Feature::geometry_type geometries_)
: Expression(Kind::Within, type::Boolean),
: Expression(Kind::Within, type::Boolean, Dependency::Feature),
geoJSONSource(std::move(geojson)),
geometries(std::move(geometries_)) {}

View File

@@ -223,5 +223,10 @@ const LayerTypeInfo* Layer::getTypeInfo() const noexcept {
return baseImpl->getTypeInfo();
}
/// Collect dependencies
expression::Dependency Layer::getDependencies() const noexcept {
return baseImpl->getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -48,6 +48,9 @@ public:
// Populates the given \a fontStack with fonts being used by the layer.
virtual void populateFontStack(std::set<FontStack>& fontStack) const;
/// Collect the style dependencies for this layer
virtual expression::Dependency getDependencies() const noexcept { return expression::Dependency::None; }
std::string id;
std::string source;
std::string sourceLayer;

View File

@@ -31,6 +31,10 @@ const BackgroundLayer::Impl& BackgroundLayerProperties::layerImpl() const {
return static_cast<const BackgroundLayer::Impl&>(*baseImpl);
}
expression::Dependency BackgroundLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -45,6 +45,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const BackgroundLayer::Impl& layerImpl() const;
// Data members.
CrossfadeParameters crossfade;

View File

@@ -29,6 +29,10 @@ const CircleLayer::Impl& CircleLayerProperties::layerImpl() const {
return static_cast<const CircleLayer::Impl&>(*baseImpl);
}
expression::Dependency CircleLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies() | layerImpl().layout.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -93,6 +93,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const CircleLayer::Impl& layerImpl() const;
// Data members.
CirclePaintProperties::PossiblyEvaluated evaluated;

View File

@@ -28,6 +28,8 @@ class CustomDrawableLayerProperties final : public LayerProperties {
public:
explicit CustomDrawableLayerProperties(Immutable<CustomDrawableLayer::Impl> impl)
: LayerProperties(std::move(impl)) {}
expression::Dependency getDependencies() const noexcept override { return expression::Dependency::None; }
};
} // namespace style

View File

@@ -28,6 +28,8 @@ class CustomLayerProperties final : public LayerProperties {
public:
explicit CustomLayerProperties(Immutable<CustomLayer::Impl> impl)
: LayerProperties(std::move(impl)) {}
expression::Dependency getDependencies() const noexcept override { return expression::Dependency::None; }
};
} // namespace style

View File

@@ -31,6 +31,10 @@ const FillExtrusionLayer::Impl& FillExtrusionLayerProperties::layerImpl() const
return static_cast<const FillExtrusionLayer::Impl&>(*baseImpl);
}
expression::Dependency FillExtrusionLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -70,6 +70,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const FillExtrusionLayer::Impl& layerImpl() const;
// Data members.
CrossfadeParameters crossfade;

View File

@@ -31,6 +31,10 @@ const FillLayer::Impl& FillLayerProperties::layerImpl() const {
return static_cast<const FillLayer::Impl&>(*baseImpl);
}
expression::Dependency FillLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies() | layerImpl().layout.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -74,6 +74,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const FillLayer::Impl& layerImpl() const;
// Data members.
CrossfadeParameters crossfade;

View File

@@ -29,6 +29,10 @@ const HeatmapLayer::Impl& HeatmapLayerProperties::layerImpl() const {
return static_cast<const HeatmapLayer::Impl&>(*baseImpl);
}
expression::Dependency HeatmapLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -53,6 +53,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const HeatmapLayer::Impl& layerImpl() const;
// Data members.
HeatmapPaintProperties::PossiblyEvaluated evaluated;

View File

@@ -29,6 +29,10 @@ const HillshadeLayer::Impl& HillshadeLayerProperties::layerImpl() const {
return static_cast<const HillshadeLayer::Impl&>(*baseImpl);
}
expression::Dependency HillshadeLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -59,6 +59,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const HillshadeLayer::Impl& layerImpl() const;
// Data members.
HillshadePaintProperties::PossiblyEvaluated evaluated;

View File

@@ -40,6 +40,10 @@ const <%- camelize(type) %>Layer::Impl& <%- camelize(type) %>LayerProperties::la
return static_cast<const <%- camelize(type) %>Layer::Impl&>(*baseImpl);
}
expression::Dependency <%- camelize(type) %>LayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies()<% if (layoutProperties.length) { -%> | layerImpl().layout.getDependencies()<% } -%>;
}
} // namespace style
} // namespace mbgl

View File

@@ -76,6 +76,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const <%- camelize(type) %>Layer::Impl& layerImpl() const;
// Data members.
<% if (type === 'background' || type === 'fill' || type === 'line' || type === 'fill-extrusion') { -%>

View File

@@ -14,6 +14,10 @@ public:
bool hasLayoutDifference(const Layer::Impl&) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
expression::Dependency getDependencies() const noexcept override {
return layout.getDependencies() | paint.getDependencies();
}
LineLayoutProperties::Unevaluated layout;
LinePaintProperties::Transitionable paint;

View File

@@ -31,6 +31,10 @@ const LineLayer::Impl& LineLayerProperties::layerImpl() const {
return static_cast<const LineLayer::Impl&>(*baseImpl);
}
expression::Dependency LineLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies() | layerImpl().layout.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -123,6 +123,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const LineLayer::Impl& layerImpl() const;
// Data members.
CrossfadeParameters crossfade;

View File

@@ -29,6 +29,10 @@ const LocationIndicatorLayer::Impl& LocationIndicatorLayerProperties::layerImpl(
return static_cast<const LocationIndicatorLayer::Impl&>(*baseImpl);
}
expression::Dependency LocationIndicatorLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies() | layerImpl().layout.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -100,6 +100,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const LocationIndicatorLayer::Impl& layerImpl() const;
// Data members.
LocationIndicatorPaintProperties::PossiblyEvaluated evaluated;

View File

@@ -29,6 +29,10 @@ const RasterLayer::Impl& RasterLayerProperties::layerImpl() const {
return static_cast<const RasterLayer::Impl&>(*baseImpl);
}
expression::Dependency RasterLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -69,6 +69,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const RasterLayer::Impl& layerImpl() const;
// Data members.
RasterPaintProperties::PossiblyEvaluated evaluated;

View File

@@ -29,6 +29,10 @@ const SymbolLayer::Impl& SymbolLayerProperties::layerImpl() const {
return static_cast<const SymbolLayer::Impl&>(*baseImpl);
}
expression::Dependency SymbolLayerProperties::getDependencies() const noexcept {
return layerImpl().paint.getDependencies() | layerImpl().layout.getDependencies();
}
} // namespace style
} // namespace mbgl

View File

@@ -351,6 +351,8 @@ public:
unsigned long constantsMask() const override;
expression::Dependency getDependencies() const noexcept override;
const SymbolLayer::Impl& layerImpl() const;
// Data members.
SymbolPaintProperties::PossiblyEvaluated evaluated;

View File

@@ -1,10 +1,11 @@
#pragma once
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/conversion/stringify.hpp>
#include <mbgl/renderer/transition_parameters.hpp>
#include <mbgl/renderer/possibly_evaluated_property_value.hpp>
#include <mbgl/renderer/property_evaluation_parameters.hpp>
#include <mbgl/renderer/transition_parameters.hpp>
#include <mbgl/style/color_ramp_property_value.hpp>
#include <mbgl/style/conversion/stringify.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/util/indexed_tuple.hpp>
#include <mbgl/util/ignore.hpp>
@@ -133,6 +134,8 @@ public:
using DataDrivenProperties = FilteredTypeList<PropertyTypes, IsDataDriven>;
using OverridableProperties = FilteredTypeList<PropertyTypes, IsOverridable>;
using Dependency = expression::Dependency;
template <class TypeList>
using Tuple = IndexedTuple<PropertyTypes, TypeList>;
@@ -245,6 +248,26 @@ public:
return Evaluated{evaluate<Ps>(z, feature)...};
}
/// Extract dependencies from a possibly-evaluated property which may have an expression.
template <class P>
Dependency getDependencies(const P&) const noexcept {
return Dependency::None;
}
template <class P>
Dependency getDependencies(const PossiblyEvaluatedPropertyValue<P>& v) const noexcept {
return v.getDependencies();
}
template <class P>
Dependency getDependencies(const PossiblyEvaluatedPropertyValue<Faded<P>>& v) const noexcept {
return v.getDependencies();
}
Dependency getDependencies() const noexcept {
Dependency result = Dependency::None;
util::ignore({(result |= getDependencies(this->template get<Ps>()))...});
return result;
}
unsigned long constantsMask() const { return ConstantsMask<DataDrivenProperties>::getMask(*this); }
};
@@ -276,6 +299,26 @@ public:
util::ignore({(conversion::stringify<Ps>(writer, this->template get<Ps>()), 0)...});
writer.EndObject();
}
/// Get the combined dependencies of any contained expressions
constexpr Dependency getDependencies() const noexcept {
Dependency result = Dependency::None;
util::ignore({(result |= getDependencies(this->template get<Ps>()))...});
return result;
}
unsigned long constantsMask() const { return ConstantsMask<DataDrivenProperties>::getMask(*this); }
protected:
template <class P>
Dependency getDependencies(const PropertyValue<P>& v) const noexcept {
return v.getDependencies();
}
template <class P>
Dependency getDependencies(const Transitioning<P>& v) const noexcept {
return v.getValue().getDependencies();
}
};
class Transitionable : public Tuple<TransitionableTypes> {
@@ -298,6 +341,24 @@ public:
other.template get<Ps>().value))...});
return result;
}
Dependency getDependencies() const noexcept {
Dependency result = Dependency::None;
util::ignore({(result |= getDependencies(this->template get<Ps>()))...});
return result;
}
protected:
template <typename P>
Dependency getDependencies(const style::Transitionable<PropertyValue<P>>& v) const noexcept {
return v.value.getDependencies();
}
Dependency getDependencies(const style::ColorRampPropertyValue& v) const noexcept {
return v.getDependencies();
}
Dependency getDependencies(const style::Transitionable<ColorRampPropertyValue>& v) const noexcept {
return v.value.getDependencies();
}
};
};

View File

@@ -5,23 +5,10 @@ namespace style {
PropertyExpressionBase::PropertyExpressionBase(std::unique_ptr<expression::Expression> expression_)
: expression(std::move(expression_)),
zoomCurve(expression::findZoomCurveChecked(expression.get())) {
isZoomConstant_ = expression::isZoomConstant(*expression);
isFeatureConstant_ = expression::isFeatureConstant(*expression);
isRuntimeConstant_ = expression::isRuntimeConstant(*expression);
}
bool PropertyExpressionBase::isZoomConstant() const noexcept {
return isZoomConstant_;
}
bool PropertyExpressionBase::isFeatureConstant() const noexcept {
return isFeatureConstant_;
}
bool PropertyExpressionBase::isRuntimeConstant() const noexcept {
return isRuntimeConstant_;
}
isZoomConstant_(expression::isZoomConstant(*expression)),
isFeatureConstant_(expression::isFeatureConstant(*expression)),
isRuntimeConstant_(expression::isRuntimeConstant(*expression)),
zoomCurve(isZoomConstant_ ? nullptr : expression::findZoomCurveChecked(*expression)) {}
float PropertyExpressionBase::interpolationFactor(const Range<float>& inputLevels,
const float inputValue) const noexcept {

View File

@@ -51,6 +51,7 @@ add_library(
${PROJECT_SOURCE_DIR}/test/style/conversion/property_value.test.cpp
${PROJECT_SOURCE_DIR}/test/style/conversion/stringify.test.cpp
${PROJECT_SOURCE_DIR}/test/style/conversion/tileset.test.cpp
${PROJECT_SOURCE_DIR}/test/style/expression/dependency.test.cpp
${PROJECT_SOURCE_DIR}/test/style/expression/expression.test.cpp
${PROJECT_SOURCE_DIR}/test/style/expression/util.test.cpp
${PROJECT_SOURCE_DIR}/test/style/filter.test.cpp

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": true, "locale": "de"} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": false, "diacritic-sensitive": true, "locale": "de"} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": true, "locale": "de"} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": false, "locale": "de"} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": true, "locale": "de"} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": true, "locale": "en"} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": true} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": false, "locale": "de"} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": true, "locale": "de"} ]

View File

@@ -0,0 +1 @@
[ "collator", {"case-sensitive": true, "diacritic-sensitive": false} ]

View File

@@ -4,7 +4,7 @@
[1, "WARNING", "ParseStyle", "Expected color but found number instead."],
[1, "WARNING", "ParseStyle", "Expected arguments of type (number, number, number, number), but found (number, string, number, number) instead."],
[1, "WARNING", "ParseStyle", "\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression."],
[1, "WARNING", "ParseStyle", "value must be a string"],
[3, "WARNING", "ParseStyle", "value must be a string"],
[1, "WARNING", "ParseStyle", "data expressions not supported"],
[1, "WARNING", "ParseStyle", "Type array<number> is not interpolatable."]
]

View File

@@ -52,6 +52,24 @@
"visibility": ["literal", true]
}
},
{
"id": "invalid value - must be a string",
"type": "fill",
"source": "source",
"source-layer": 42069,
"layout": {
"visibility": "visible"
}
},
{
"id": "invalid value - must be a string (enum)",
"type": "fill",
"source": "source",
"source-layer": "layer",
"layout": {
"visibility": [42069, true]
}
},
{
"id": "invalid expression - not a DDS property",
"type": "fill-extrusion",

View File

@@ -72,9 +72,14 @@ size_t FixtureLog::uncheckedCount() const {
return observer ? observer->uncheckedCount() : 0;
}
std::vector<FixtureLog::Message> FixtureLog::unchecked() const {
return observer ? observer->unchecked() : std::vector<FixtureLog::Message>{};
}
FixtureLog::~FixtureLog() {
if (observer) {
Log::removeObserver();
Log::removeObserver(); // destroys `observer`
observer = nullptr;
}
}

View File

@@ -51,6 +51,7 @@ public:
bool empty() const;
size_t uncheckedCount() const;
size_t count(const Message& message, bool substring = false) const;
std::vector<Message> unchecked() const;
~FixtureLog();

View File

@@ -4,6 +4,7 @@
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/property_value.hpp>
#include <mbgl/style/expression/dsl.hpp>
#include <mbgl/util/logging.hpp>
using namespace mbgl;
using namespace mbgl::style;
@@ -52,33 +53,37 @@ TEST(StyleConversion, Function) {
ASSERT_EQ("function base must be a number", error.message);
}
TEST(StyleConversion, CompositeFunctionExpression) {
namespace {
template <typename T>
void parseCheck(const std::string& json, std::string expectedError = {}) {
using namespace std::string_literals;
SCOPED_TRACE("Expression: "s + json);
Error error;
const auto expr = convertJSON<PropertyValue<T>>(json, error, true, false);
if (expectedError.empty()) {
ASSERT_TRUE(expr);
} else {
ASSERT_FALSE(expr);
ASSERT_EQ(expectedError, error.message);
}
}
} // namespace
auto parseFunction = [&](const std::string& json) {
return convertJSON<PropertyValue<float>>(json, error, true, false);
};
auto fn1 = parseFunction(R"(["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10])");
ASSERT_TRUE(fn1);
auto fn2 = parseFunction(
R"(["coalesce", ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10], 0])");
ASSERT_TRUE(fn2);
auto fn3 = parseFunction(
TEST(StyleConversion, CompositeFunctionExpression) {
parseCheck<float>(R"(["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10])");
parseCheck<float>(R"(["coalesce", ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10], 0])");
parseCheck<float>(
R"(["let", "a", 0, ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10] ])");
ASSERT_TRUE(fn3);
auto fn4 = parseFunction(
parseCheck<float>(
R"(["coalesce", ["let", "a", 0, ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10]], 0])");
ASSERT_TRUE(fn4);
auto fn5 = parseFunction(
R"(["coalesce", ["interpolate", ["linear"], ["number", ["get", "x"]], 0, ["zoom"], 10, 10], 0])");
ASSERT_FALSE(fn5);
ASSERT_EQ(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)",
error.message);
parseCheck<float>(
R"(["coalesce", ["interpolate", ["linear"], ["number", ["get", "x"]], 0, ["zoom"], 10, 10], 0])",
R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)");
parseCheck<float>(R"(["number", ["get", "x", ["literal", {"x": 0}]]])");
parseCheck<bool>(R"(["has", "x", ["literal", {"x": 0}]])");
parseCheck<bool>(R"(["has", "x", ["properties"]])");
}
TEST(StyleConversion, TokenStrings) {

View File

@@ -0,0 +1,79 @@
#include <mbgl/style/expression/dsl.hpp>
#include <mbgl/style/expression/format_section_override.hpp>
#include <mbgl/style/layers/custom_layer_impl.hpp>
#include <mbgl/test/util.hpp>
using namespace mbgl;
using namespace mbgl::style;
using namespace mbgl::style::expression;
using namespace mbgl::style::expression::dsl;
using namespace std::string_literals;
namespace std {
using ::mbgl::operator<<;
}
template <typename T>
auto createOverride(expression::type::Type exprType,
PossiblyEvaluatedPropertyValue<T> propValue,
std::string propName) {
return std::make_unique<FormatSectionOverride<T>>(std::move(exprType), std::move(propValue), std::move(propName));
}
TEST(ExpressionDependencies, Get) {
EXPECT_EQ(Dependency::Feature, number(get("property"))->dependencies);
}
TEST(ExpressionDependencies, Id) {
EXPECT_EQ(Dependency::Feature, id()->dependencies);
}
TEST(ExpressionDependencies, Zoom) {
EXPECT_EQ(Dependency::Zoom, zoom()->dependencies);
}
TEST(ExpressionDependencies, Convert) {
EXPECT_EQ(Dependency::Feature, toColor(string(get("color")))->dependencies);
EXPECT_EQ(Dependency::Feature, toString(number(string(get("color"))))->dependencies);
EXPECT_EQ(Dependency::Feature, boolean(string(get("color")))->dependencies);
EXPECT_EQ(Dependency::Feature, toFormatted(toColor(string(get("color"))))->dependencies);
EXPECT_EQ(Dependency::Feature | Dependency::Image, toImage(string(get("color")))->dependencies);
}
TEST(ExpressionDependencies, Compare) {
EXPECT_EQ(Dependency::Feature, eq(string(get("color")), string(get("color")))->dependencies);
EXPECT_EQ(Dependency::Feature, lt(string(get("color")), string(get("color")))->dependencies);
EXPECT_EQ(Dependency::Feature, gt(string(get("color")), string(get("color")))->dependencies);
EXPECT_EQ(Dependency::Feature, eq(string(get("color")), string(get("color")))->dependencies);
}
TEST(ExpressionDependencies, Interpolate) {
EXPECT_EQ(Dependency::Feature, step(number(id()), literal(0.0), 0.0, literal(0.0))->dependencies);
EXPECT_EQ(Dependency::Feature, step(literal(0.0), number(id()), 0.0, literal(0.0))->dependencies);
EXPECT_EQ(Dependency::Feature, step(literal(0.0), literal(0.0), 0.0, number(id()))->dependencies);
interpolate(linear(), number(id()), 0.0, literal(0.0), 0.0, literal(0.0), 0.0, literal(0.0));
interpolate(linear(), literal(0.0), 0.0, number(id()), 0.0, literal(0.0), 0.0, literal(0.0));
interpolate(linear(), literal(0.0), 0.0, literal(0.0), 0.0, number(id()), 0.0, literal(0.0));
interpolate(linear(), literal(0.0), 0.0, literal(0.0), 0.0, literal(0.0), 0.0, number(id()));
}
TEST(ExpressionDependencies, Image) {
EXPECT_EQ(Dependency::Image, image(literal("airport-11"))->dependencies);
EXPECT_EQ(Dependency::Image | Dependency::Feature, image(get(literal("image_name"s)))->dependencies);
}
TEST(ExpressionDependencies, Distance) {
const std::string pointGeoSource = R"({
"type": "Point",
"coordinates":[24.938492774963375,60.16980226227959]})";
const auto exprStr = R"(["distance", )" + pointGeoSource + R"( ])";
const auto expression = createExpression(exprStr.c_str());
EXPECT_EQ(Dependency::Feature, expression->dependencies);
}
TEST(ExpressionDependencies, CustomLayer) {
auto impl = makeMutable<CustomLayer::Impl>("", nullptr);
EXPECT_EQ(Dependency::None, CustomLayerProperties{std::move(impl)}.getDependencies());
}

View File

@@ -1,9 +1,10 @@
#include <mbgl/style/conversion_impl.hpp>
#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/expression/dsl.hpp>
#include <mbgl/style/expression/is_expression.hpp>
#include <mbgl/test/util.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/style/conversion_impl.hpp>
#include <mbgl/util/rapidjson.hpp>
#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/expression/is_expression.hpp>
#include <rapidjson/document.h>
@@ -81,6 +82,10 @@ TEST_P(ExpressionEqualityTest, ExpressionEquality) {
EXPECT_TRUE(*expression_a1 == *expression_a2);
EXPECT_TRUE(*expression_a1 != *expression_b);
// Exercise the type-not-equal branches
using namespace expression;
EXPECT_FALSE(*expression_b == *((expression_b->getKind() == Kind::Literal) ? dsl::id() : dsl::literal(0.0)));
}
static void populateNames(std::vector<std::string>& names) {
@@ -127,6 +132,7 @@ static void populateNames(std::vector<std::string>& names) {
INSTANTIATE_TEST_SUITE_P(Expression, ExpressionEqualityTest, ::testing::ValuesIn([] {
std::vector<std::string> names;
populateNames(names);
std::sort(names.begin(), names.end());
EXPECT_GT(names.size(), 0u);
return names;
}()));

Some files were not shown because too many files have changed in this diff Show More