GPU Expressions (#2202)

This commit is contained in:
Tim Sylvester
2024-05-17 07:30:55 -07:00
committed by GitHub
parent b812e97aaf
commit 33a1e162bc
64 changed files with 2336 additions and 318 deletions

View File

@@ -0,0 +1,183 @@
#include <mbgl/gfx/gpu_expression.hpp>
#include <mbgl/renderer/paint_property_binder.hpp>
#include <mbgl/style/expression/expression.hpp>
#include <mbgl/style/expression/interpolate.hpp>
#include <mbgl/style/expression/step.hpp>
#include <mbgl/style/expression/value.hpp>
namespace mbgl {
namespace gfx {
using namespace style::expression;
namespace {
inline GPUOutputType getOutputType(const Expression& expression) {
if (expression.getType().is<type::NumberType>()) {
return GPUOutputType::Float;
} else {
return GPUOutputType::Color;
}
}
inline GPUInterpType getInterpType(const Interpolator& interp) {
return interp.match(
[&](const ExponentialInterpolator& exp) {
return (exp.base == 1.0f) ? GPUInterpType::Linear : GPUInterpType::Exponential;
},
[&](const CubicBezierInterpolator&) { return GPUInterpType::Bezier; });
}
inline float getInterpBase(const Interpolator& interp) {
return interp.match([&](const ExponentialInterpolator& exp) { return static_cast<float>(exp.base); },
[&](const CubicBezierInterpolator&) { return 0.0f; });
}
// Produce a function for `eachStop` that extracts a single stop into the target expression
auto addStop(UniqueGPUExpression& expr, GPUOutputType outType, std::size_t& index) {
return [&, outType](double input, const Expression& output) {
if (expr) {
if (const auto result = output.evaluate(EvaluationContext{/*zoom=*/0.0f})) {
const style::expression::Value& value = *result;
expr->inputs[index] = static_cast<float>(input);
if (outType == GPUOutputType::Float) {
assert(value.is<double>());
if (value.is<double>()) {
expr->stops.floats[index++] = static_cast<float>(value.get<double>());
} else {
// Evaluation error, cancel
expr.reset();
}
} else if (outType == GPUOutputType::Color) {
assert(value.is<Color>());
if (value.is<Color>()) {
const auto& color = attributeValue(value.get<Color>());
std::copy(color.begin(), color.end(), &expr->stops.colors[2 * index++]);
} else {
// Evaluation error, cancel
expr.reset();
}
}
}
}
};
};
} // namespace
const GPUExpression GPUExpression::empty = {GPUOutputType::Float, 0};
UniqueGPUExpression GPUExpression::create(GPUOutputType type, std::uint16_t count) {
if (1 < count && count <= maxStops) {
return std::make_unique<GPUExpression>(type, count);
}
return {};
}
UniqueGPUExpression GPUExpression::create(const Expression& expression, const ZoomCurvePtr& zoomCurve, bool intZoom) {
std::size_t index = 0;
const auto outType = getOutputType(expression);
const auto options = (intZoom ? GPUOptions::IntegerZoom : GPUOptions::None);
return zoomCurve.match(
[&](const Step* step) {
if (step->getStopCount() > maxStops) {
return UniqueGPUExpression{};
}
auto expr = GPUExpression::create(outType, step->getStopCount());
expr->options = options;
expr->interpolation = GPUInterpType::Step;
step->eachStop(addStop(expr, outType, index));
return expr;
},
[&](const Interpolate* interp) {
if (interp->getStopCount() > maxStops) {
return UniqueGPUExpression{};
}
auto expr = GPUExpression::create(outType, interp->getStopCount());
expr->options = options;
expr->interpolation = getInterpType(interp->getInterpolator());
expr->interpOptions.exponential.base = getInterpBase(interp->getInterpolator());
interp->eachStop(addStop(expr, outType, index));
return expr;
},
[](std::nullptr_t) { return UniqueGPUExpression{}; });
}
float GPUExpression::evaluateFloat(const float zoom) const {
const auto index = std::distance(&inputs[0], std::upper_bound(&inputs[0], &inputs[stopCount], zoom));
if (index == 0) {
return stops.floats[0];
} else if (index == stopCount) {
return stops.floats[stopCount - 1];
}
switch (interpolation) {
case GPUInterpType::Step:
return stops.floats[index - 1];
default:
assert(false);
[[fallthrough]];
case GPUInterpType::Linear:
assert(interpOptions.exponential.base == 1.0f);
[[fallthrough]];
case GPUInterpType::Exponential: {
const Range<double> range{inputs[index - 1], inputs[index]};
const ExponentialInterpolator interpolator{interpOptions.exponential.base};
const auto t = interpolator.interpolationFactor(range, zoom);
return util::Interpolator<float>()(stops.floats[index - 1], stops.floats[index], t);
}
case GPUInterpType::Bezier:
assert(false);
return stops.floats[0];
}
}
namespace {
std::tuple<float, float> unpack_float(const float packedValue) {
const int packedIntValue = int(packedValue);
const int v0 = packedIntValue / 256;
return {static_cast<float>(v0) / 255, static_cast<float>(packedIntValue - v0 * 256) / 255};
}
std::tuple<float, float, float, float> decode_color(const float encoded[2]) {
return std::tuple_cat(unpack_float(encoded[0]), unpack_float(encoded[1]));
}
Color toColor(std::tuple<float, float, float, float> rgba) {
return {std::get<0>(rgba), std::get<1>(rgba), std::get<2>(rgba), std::get<3>(rgba)};
}
} // namespace
Color GPUExpression::getColor(std::size_t index) const {
return toColor(decode_color(&stops.colors[2 * index]));
}
Color GPUExpression::evaluateColor(const float zoom) const {
const auto index = std::distance(&inputs[0], std::upper_bound(&inputs[0], &inputs[stopCount], zoom));
if (index == 0) {
return getColor(0);
} else if (index == stopCount) {
return getColor(stopCount - 1);
}
switch (interpolation) {
case GPUInterpType::Step:
return getColor(index - 1);
default:
assert(false);
[[fallthrough]];
case GPUInterpType::Linear:
assert(interpOptions.exponential.base == 1.0f);
[[fallthrough]];
case GPUInterpType::Exponential: {
const Range<double> range{inputs[index - 1], inputs[index]};
const ExponentialInterpolator interpolator{interpOptions.exponential.base};
const auto t = interpolator.interpolationFactor(range, zoom);
return util::Interpolator<Color>()(getColor(index - 1), getColor(index), t);
break;
}
case GPUInterpType::Bezier:
assert(false);
return getColor(0);
}
}
} // namespace gfx
} // namespace mbgl

View File

@@ -57,23 +57,44 @@ RenderingStats& RenderingStats::operator+=(const RenderingStats& r) {
}
#if !defined(NDEBUG)
template <typename T>
std::ostream& optionalStatLine(std::ostream& stream, T value, std::string_view label, std::string_view sep) {
if (value) {
stream << label << " = " << value << sep;
}
return stream;
}
std::string RenderingStats::toString(std::string_view sep) const {
std::stringstream ss;
ss << "numFrames = " << numFrames << sep << "numDrawCalls = " << numDrawCalls << sep
<< "totalDrawCalls = " << totalDrawCalls << sep << "numCreatedTextures = " << numCreatedTextures << sep
<< "numActiveTextures = " << numActiveTextures << sep << "numTextureBindings = " << numTextureBindings << sep
<< "numTextureUpdates = " << numTextureUpdates << sep << "textureUpdateBytes = " << textureUpdateBytes << sep
<< "totalBuffers = " << totalBuffers << sep << "totalBufferObjs = " << totalBufferObjs << sep
<< "bufferUpdates = " << bufferUpdates << sep << "bufferObjUpdates = " << bufferObjUpdates << sep
<< "bufferUpdateBytes = " << bufferUpdateBytes << sep << "numBuffers = " << numBuffers << sep
<< "numFrameBuffers = " << numFrameBuffers << sep << "numIndexBuffers = " << numIndexBuffers << sep
<< "indexUpdateBytes = " << indexUpdateBytes << sep << "numVertexBuffers = " << numVertexBuffers << sep
<< "vertexUpdateBytes = " << vertexUpdateBytes << sep << "numUniformBuffers = " << numUniformBuffers << sep
<< "numUniformUpdates = " << numUniformUpdates << sep << "uniformUpdateBytes = " << uniformUpdateBytes << sep
<< "memTextures = " << memTextures << sep << "memBuffers = " << memBuffers << sep
<< "memIndexBuffers = " << memIndexBuffers << sep << "memVertexBuffers = " << memVertexBuffers << sep
<< "memUniformBuffers = " << memUniformBuffers << sep << "stencilClears = " << stencilClears << sep
<< "stencilUpdates = " << stencilUpdates << sep;
optionalStatLine(ss, numFrames, "numFrames", sep);
optionalStatLine(ss, numDrawCalls, "numDrawCalls", sep);
optionalStatLine(ss, totalDrawCalls, "totalDrawCalls", sep);
optionalStatLine(ss, numCreatedTextures, "numCreatedTextures", sep);
optionalStatLine(ss, numActiveTextures, "numActiveTextures", sep);
optionalStatLine(ss, numTextureBindings, "numTextureBindings", sep);
optionalStatLine(ss, numTextureUpdates, "numTextureUpdates", sep);
optionalStatLine(ss, textureUpdateBytes, "textureUpdateBytes", sep);
optionalStatLine(ss, totalBuffers, "totalBuffers", sep);
optionalStatLine(ss, totalBufferObjs, "totalBufferObjs", sep);
optionalStatLine(ss, bufferUpdates, "bufferUpdates", sep);
optionalStatLine(ss, bufferObjUpdates, "bufferObjUpdates", sep);
optionalStatLine(ss, bufferUpdateBytes, "bufferUpdateBytes", sep);
optionalStatLine(ss, numBuffers, "numBuffers", sep);
optionalStatLine(ss, numFrameBuffers, "numFrameBuffers", sep);
optionalStatLine(ss, numIndexBuffers, "numIndexBuffers", sep);
optionalStatLine(ss, indexUpdateBytes, "indexUpdateBytes", sep);
optionalStatLine(ss, numVertexBuffers, "numVertexBuffers", sep);
optionalStatLine(ss, vertexUpdateBytes, "vertexUpdateBytes", sep);
optionalStatLine(ss, numUniformBuffers, "numUniformBuffers", sep);
optionalStatLine(ss, numUniformUpdates, "numUniformUpdates", sep);
optionalStatLine(ss, uniformUpdateBytes, "uniformUpdateBytes", sep);
optionalStatLine(ss, memTextures, "memTextures", sep);
optionalStatLine(ss, memBuffers, "memBuffers", sep);
optionalStatLine(ss, memIndexBuffers, "memIndexBuffers", sep);
optionalStatLine(ss, memVertexBuffers, "memVertexBuffers", sep);
optionalStatLine(ss, memUniformBuffers, "memUniformBuffers", sep);
optionalStatLine(ss, stencilClears, "stencilClears", sep);
optionalStatLine(ss, stencilUpdates, "stencilUpdates", sep);
return ss.str();
}
#endif

View File

@@ -22,8 +22,7 @@ void logStyleDependencies(EventSeverity severity, Event event, const style::Styl
counts[0]++;
} else {
for (size_t i = 0; i < maskCount; ++i) {
const auto mask = Dependency{1u << i};
if ((deps & mask) == mask) {
if (deps & Dependency{1u << i}) {
counts[i + 1]++;
}
}
@@ -33,7 +32,9 @@ void logStyleDependencies(EventSeverity severity, Event event, const style::Styl
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";
if (counts[i + 1]) {
ss << " " << Dependency{1u << i} << ": " << counts[i + 1] << "\n";
}
}
Log::Record(severity, event, ss.str());
}

View File

@@ -17,6 +17,7 @@ template <typename T>
class CrossFadedPropertyEvaluator {
public:
using ResultType = Faded<T>;
static constexpr bool useIntegerZoom = false;
CrossFadedPropertyEvaluator(const PropertyEvaluationParameters& parameters_, T defaultValue_)
: parameters(parameters_),

View File

@@ -9,10 +9,11 @@
namespace mbgl {
template <typename T, bool useIntegerZoom = false>
template <typename T, bool useIntegerZoom_ = false>
class DataDrivenPropertyEvaluator {
public:
using ResultType = PossiblyEvaluatedPropertyValue<T>;
static constexpr bool useIntegerZoom = useIntegerZoom_;
DataDrivenPropertyEvaluator(const PropertyEvaluationParameters& parameters_, T defaultValue_)
: parameters(parameters_),
@@ -26,7 +27,7 @@ public:
if constexpr (useIntegerZoom) { // Compiler will optimize out the unused branch.
if (!expression.isFeatureConstant() || !expression.isRuntimeConstant()) {
auto returnExpression = expression;
returnExpression.useIntegerZoom = true;
returnExpression.setUseIntegerZoom(true);
return ResultType(returnExpression);
}
return ResultType(expression.evaluate(std::floor(parameters.z)));
@@ -47,6 +48,7 @@ template <typename T>
class DataDrivenPropertyEvaluator<Faded<T>> {
public:
using ResultType = PossiblyEvaluatedPropertyValue<Faded<T>>;
static constexpr bool useIntegerZoom = false;
DataDrivenPropertyEvaluator(const PropertyEvaluationParameters& parameters_, T defaultValue_)
: parameters(parameters_),

View File

@@ -15,6 +15,8 @@ ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& i
stretchY(image.stretchY),
content(image.content) {}
namespace {
const mapbox::Bin& _packImage(mapbox::ShelfPack& pack,
const style::Image::Impl& image,
ImageAtlas& resultImage,
@@ -26,12 +28,13 @@ const mapbox::Bin& _packImage(mapbox::ShelfPack& pack,
PremultipliedImage::copy(
image.image, resultImage.image, {0, 0}, {bin.x + padding, bin.y + padding}, image.image.size);
uint32_t x = bin.x + padding;
uint32_t y = bin.y + padding;
uint32_t w = image.image.size.width;
uint32_t h = image.image.size.height;
if (imageType == ImageType::Pattern) {
const uint32_t x = bin.x + padding;
const uint32_t y = bin.y + padding;
const uint32_t w = image.image.size.width;
const uint32_t h = image.image.size.height;
// Add 1 pixel wrapped padding on each side of the image.
PremultipliedImage::copy(image.image, resultImage.image, {0, h - 1}, {x, y - 1}, {w, 1}); // T
PremultipliedImage::copy(image.image, resultImage.image, {0, 0}, {x, y + h}, {w, 1}); // B
@@ -41,8 +44,6 @@ const mapbox::Bin& _packImage(mapbox::ShelfPack& pack,
return bin;
}
namespace {
void populateImagePatches(ImagePositions& imagePositions,
const ImageManager& imageManager,
std::vector<ImagePatch>& /*out*/ patches) {

View File

@@ -10,7 +10,9 @@
namespace mbgl {
static ImageManagerObserver nullObserver;
namespace {
ImageManagerObserver nullObserver;
} // namespace
ImageManager::ImageManager() = default;

View File

@@ -23,30 +23,135 @@ namespace mbgl {
using namespace style;
using namespace shaders;
#if MLN_RENDER_BACKEND_METAL && !defined(NDEBUG)
constexpr bool diff(float actual, float expected, float e = 1.0e-6) {
return actual != expected && (expected == 0 || std::fabs((actual - expected) / expected) > e);
}
constexpr bool diff(Color actual, Color expected, float e = 1.0e-6) {
return diff(actual.r, expected.r, e) || diff(actual.g, expected.g, e) || diff(actual.b, expected.b, e) ||
diff(actual.a, expected.a, e);
}
#endif // MLN_RENDER_BACKEND_METAL
#if MLN_RENDER_BACKEND_METAL && !defined(NDEBUG)
template <typename Result>
std::optional<Result> LineLayerTweaker::gpuEvaluate(const LinePaintProperties::PossiblyEvaluated& evaluated,
const PaintParameters& parameters,
const std::size_t index) const {
if (const auto& gpu = gpuExpressions[index]) {
const float effectiveZoom = (gpu->options & gfx::GPUOptions::IntegerZoom)
? parameters.state.getIntegerZoom()
: static_cast<float>(parameters.state.getZoom());
return gpu->template evaluate<Result>(effectiveZoom);
}
return {};
}
#endif // MLN_RENDER_BACKEND_METAL
template <typename Property>
auto LineLayerTweaker::evaluate([[maybe_unused]] const PaintParameters& parameters) const {
const auto& evaluated = static_cast<const LineLayerProperties&>(*evaluatedProperties).evaluated;
#if MLN_RENDER_BACKEND_METAL && !defined(NDEBUG)
constexpr auto index = propertyIndex<Property>();
if (auto gpuValue = gpuEvaluate<typename Property::Type>(evaluated, parameters, index)) {
assert(!diff(*gpuValue, evaluated.get<Property>().constantOr(Property::defaultValue())));
return *gpuValue;
}
#endif // MLN_RENDER_BACKEND_METAL
return evaluated.get<Property>().constantOr(Property::defaultValue());
}
void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters& parameters) {
auto& context = parameters.context;
const auto zoom = parameters.state.getZoom();
const auto& evaluated = static_cast<const LineLayerProperties&>(*evaluatedProperties).evaluated;
const auto& crossfade = static_cast<const LineLayerProperties&>(*evaluatedProperties).crossfade;
// Each property UBO is updated at most once if new evaluated properties were set
const auto zoom = static_cast<float>(parameters.state.getZoom());
const auto intZoom = parameters.state.getIntegerZoom();
#if MLN_RENDER_BACKEND_METAL
const auto getExpressionBuffer = [&]() {
const bool enableEval = gfx::Backend::getEnableGPUExpressionEval();
if (!expressionUniformBuffer || (gpuExpressionsUpdated && enableEval)) {
LineExpressionUBO exprUBO = {
/* color = */ enableEval ? gpuExpressions[propertyIndex<LineColor>()].get() : nullptr,
/* blur = */ enableEval ? gpuExpressions[propertyIndex<LineBlur>()].get() : nullptr,
/* opacity = */ enableEval ? gpuExpressions[propertyIndex<LineOpacity>()].get() : nullptr,
/* gapwidth = */ enableEval ? gpuExpressions[propertyIndex<LineGapWidth>()].get() : nullptr,
/* offset = */ enableEval ? gpuExpressions[propertyIndex<LineOffset>()].get() : nullptr,
/* width = */ enableEval ? gpuExpressions[propertyIndex<LineWidth>()].get() : nullptr,
/* floorWidth = */ enableEval ? gpuExpressions[propertyIndex<LineFloorWidth>()].get() : nullptr,
};
context.emplaceOrUpdateUniformBuffer(expressionUniformBuffer, &exprUBO);
gpuExpressionsUpdated = false;
}
return expressionUniformBuffer;
};
#endif // MLN_RENDER_BACKEND_METAL
if (!evaluatedPropsUniformBuffer || propertiesUpdated) {
const LineEvaluatedPropsUBO propsUBO = {
/*color =*/evaluated.get<LineColor>().constantOr(LineColor::defaultValue()),
/*blur =*/evaluated.get<LineBlur>().constantOr(LineBlur::defaultValue()),
/*opacity =*/evaluated.get<LineOpacity>().constantOr(LineOpacity::defaultValue()),
/*gapwidth =*/evaluated.get<LineGapWidth>().constantOr(LineGapWidth::defaultValue()),
/*offset =*/evaluated.get<LineOffset>().constantOr(LineOffset::defaultValue()),
/*width =*/evaluated.get<LineWidth>().constantOr(LineWidth::defaultValue()),
/*floorwidth =*/evaluated.get<LineFloorWidth>().constantOr(LineFloorWidth::defaultValue()),
0,
#if MLN_RENDER_BACKEND_METAL
expressionMask =
!gfx::Backend::getEnableGPUExpressionEval()
? LineExpressionMask::None
: ((gpuExpressions[propertyIndex<LineColor>()] ? LineExpressionMask::Color : LineExpressionMask::None) |
(gpuExpressions[propertyIndex<LineBlur>()] ? LineExpressionMask::Blur : LineExpressionMask::None) |
(gpuExpressions[propertyIndex<LineOpacity>()] ? LineExpressionMask::Opacity
: LineExpressionMask::None) |
(gpuExpressions[propertyIndex<LineGapWidth>()] ? LineExpressionMask::GapWidth
: LineExpressionMask::None) |
(gpuExpressions[propertyIndex<LineOffset>()] ? LineExpressionMask::Offset
: LineExpressionMask::None) |
(gpuExpressions[propertyIndex<LineWidth>()] ? LineExpressionMask::Width : LineExpressionMask::None) |
(gpuExpressions[propertyIndex<LineFloorWidth>()] ? LineExpressionMask::FloorWidth
: LineExpressionMask::None));
const LineEvaluatedPropsUBO propsUBO{
/*color =*/(expressionMask & LineExpressionMask::Color) ? LineColor::defaultValue()
: evaluate<LineColor>(parameters),
/*blur =*/
(expressionMask & LineExpressionMask::Blur) ? LineBlur::defaultValue() : evaluate<LineBlur>(parameters),
/*opacity =*/
(expressionMask & LineExpressionMask::Opacity) ? LineOpacity::defaultValue()
: evaluate<LineOpacity>(parameters),
/*gapwidth =*/
(expressionMask & LineExpressionMask::GapWidth) ? LineGapWidth::defaultValue()
: evaluate<LineGapWidth>(parameters),
/*offset =*/
(expressionMask & LineExpressionMask::Offset) ? LineOffset::defaultValue()
: evaluate<LineOffset>(parameters),
/*width =*/
(expressionMask & LineExpressionMask::Width) ? LineWidth::defaultValue() : evaluate<LineWidth>(parameters),
/*floorwidth =*/
(expressionMask & LineExpressionMask::FloorWidth) ? LineFloorWidth::defaultValue()
: evaluate<LineFloorWidth>(parameters),
expressionMask,
0};
#else
const LineEvaluatedPropsUBO propsUBO{/*color =*/evaluate<LineColor>(parameters),
/*blur =*/evaluate<LineBlur>(parameters),
/*opacity =*/evaluate<LineOpacity>(parameters),
/*gapwidth =*/evaluate<LineGapWidth>(parameters),
/*offset =*/evaluate<LineOffset>(parameters),
/*width =*/evaluate<LineWidth>(parameters),
/*floorwidth =*/evaluate<LineFloorWidth>(parameters),
LineExpressionMask::None,
0};
#endif // MLN_RENDER_BACKEND_METAL
context.emplaceOrUpdateUniformBuffer(evaluatedPropsUniformBuffer, &propsUBO);
propertiesUpdated = false;
}
auto& layerUniforms = layerGroup.mutableUniformBuffers();
layerUniforms.set(idLineEvaluatedPropsUBO, evaluatedPropsUniformBuffer);
#if MLN_RENDER_BACKEND_METAL
// GPU Expressions
layerUniforms.set(idLineExpressionUBO, getExpressionBuffer());
#endif // MLN_RENDER_BACKEND_METAL
visitLayerGroupDrawables(layerGroup, [&](gfx::Drawable& drawable) {
const auto shader = drawable.getShader();
if (!drawable.getTileID() || !shader || !checkTweakDrawable(drawable)) {
@@ -83,7 +188,6 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters
0,
0};
drawableUniforms.createOrUpdate(idLineDrawableUBO, &drawableUBO, context);
;
} break;
case LineType::Pattern: {
@@ -95,7 +199,7 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters
/*matrix =*/util::cast<float>(matrix),
/*scale =*/
{parameters.pixelRatio,
1 / tileID.pixelsToTileUnits(1, parameters.state.getIntegerZoom()),
1 / tileID.pixelsToTileUnits(1, intZoom),
crossfade.fromScale,
crossfade.toScale},
/*texsize =*/{static_cast<float>(textureSize.width), static_cast<float>(textureSize.height)},
@@ -128,12 +232,10 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters
const LineSDFDrawableUBO drawableUBO{
/* matrix = */ util::cast<float>(matrix),
/* patternscale_a = */
{1.0f / tileID.pixelsToTileUnits(widthA, parameters.state.getIntegerZoom()),
-posA.height / 2.0f},
{1.0f / tileID.pixelsToTileUnits(widthA, intZoom), -posA.height / 2.0f},
/* patternscale_b = */
{1.0f / tileID.pixelsToTileUnits(widthB, parameters.state.getIntegerZoom()),
-posB.height / 2.0f},
/* ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, static_cast<float>(zoom)),
{1.0f / tileID.pixelsToTileUnits(widthB, intZoom), -posB.height / 2.0f},
/* ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom),
/* tex_y_a = */ posA.y,
/* tex_y_b = */ posB.y,
/* sdfgamma = */ static_cast<float>(dashPatternTexture.getSize().width) /
@@ -155,4 +257,18 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters
});
}
#if MLN_RENDER_BACKEND_METAL
void LineLayerTweaker::updateGPUExpressions(const Unevaluated& unevaluated, TimePoint now) {
if (gfx::Backend::getEnableGPUExpressionEval()) {
if (unevaluated.updateGPUExpressions(gpuExpressions, now)) {
gpuExpressionsUpdated = true;
// Masks also need to be updated
propertiesUpdated = true;
}
}
}
#endif // MLN_RENDER_BACKEND_METAL
} // namespace mbgl

View File

@@ -1,8 +1,13 @@
#pragma once
#include <mbgl/renderer/layer_tweaker.hpp>
#include <mbgl/style/layers/line_layer_properties.hpp>
#include <string_view>
#if MLN_RENDER_BACKEND_METAL
#include <mbgl/shaders/line_layer_ubo.hpp>
#endif // MLN_RENDER_BACKEND_METAL
#include <string>
namespace mbgl {
@@ -30,8 +35,37 @@ public:
void execute(LayerGroupBase&, const PaintParameters&) override;
#if MLN_RENDER_BACKEND_METAL
using LinePaintProperties = style::LinePaintProperties;
using Unevaluated = LinePaintProperties::Unevaluated;
void updateGPUExpressions(const Unevaluated&, TimePoint now);
template <typename T>
static constexpr std::size_t propertyIndex() {
return LinePaintProperties::Tuple<LinePaintProperties::PropertyTypes>::getIndex<T>();
}
#endif // MLN_RENDER_BACKEND_METAL
private:
template <typename Property>
auto evaluate(const PaintParameters& parameters) const;
#if MLN_RENDER_BACKEND_METAL
template <typename Result>
std::optional<Result> gpuEvaluate(const LinePaintProperties::PossiblyEvaluated&,
const PaintParameters&,
const std::size_t index) const;
#endif // MLN_RENDER_BACKEND_METAL
protected:
gfx::UniformBufferPtr evaluatedPropsUniformBuffer;
#if MLN_RENDER_BACKEND_METAL
gfx::UniformBufferPtr expressionUniformBuffer;
Unevaluated::GPUExpressions gpuExpressions;
shaders::LineExpressionMask expressionMask = shaders::LineExpressionMask::None;
bool gpuExpressionsUpdated = true;
#endif // MLN_RENDER_BACKEND_METAL
};
} // namespace mbgl

View File

@@ -53,7 +53,8 @@ void RenderBackgroundLayer::transition(const TransitionParameters& parameters) {
}
void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters& parameters) {
auto evaluated = unevaluated.evaluate(parameters);
const auto previousProperties = staticImmutableCast<BackgroundLayerProperties>(evaluatedProperties);
auto evaluated = unevaluated.evaluate(parameters, previousProperties->evaluated);
auto properties = makeMutable<BackgroundLayerProperties>(
staticImmutableCast<BackgroundLayer::Impl>(baseImpl), parameters.getCrossfadeParameters(), evaluated);

View File

@@ -71,8 +71,10 @@ void RenderCircleLayer::transition(const TransitionParameters& parameters) {
}
void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) {
auto properties = makeMutable<CircleLayerProperties>(staticImmutableCast<CircleLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters));
const auto previousProperties = staticImmutableCast<CircleLayerProperties>(evaluatedProperties);
auto properties = makeMutable<CircleLayerProperties>(
staticImmutableCast<CircleLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters, previousProperties->evaluated));
const auto& evaluated = properties->evaluated;
passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 ||

View File

@@ -58,9 +58,11 @@ void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters
}
void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) {
auto properties = makeMutable<FillExtrusionLayerProperties>(staticImmutableCast<FillExtrusionLayer::Impl>(baseImpl),
parameters.getCrossfadeParameters(),
unevaluated.evaluate(parameters));
const auto previousProperties = staticImmutableCast<FillExtrusionLayerProperties>(evaluatedProperties);
auto properties = makeMutable<FillExtrusionLayerProperties>(
staticImmutableCast<FillExtrusionLayer::Impl>(baseImpl),
parameters.getCrossfadeParameters(),
unevaluated.evaluate(parameters, previousProperties->evaluated));
passes = (properties->evaluated.get<style::FillExtrusionOpacity>() > 0)
? (RenderPass::Translucent | RenderPass::Pass3D)

View File

@@ -69,9 +69,10 @@ void RenderFillLayer::transition(const TransitionParameters& parameters) {
}
void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) {
const auto previousProperties = staticImmutableCast<FillLayerProperties>(evaluatedProperties);
auto properties = makeMutable<FillLayerProperties>(staticImmutableCast<FillLayer::Impl>(baseImpl),
parameters.getCrossfadeParameters(),
unevaluated.evaluate(parameters));
unevaluated.evaluate(parameters, previousProperties->evaluated));
auto& evaluated = properties->evaluated;
if (unevaluated.get<style::FillOutlineColor>().isUndefined()) {

View File

@@ -63,8 +63,10 @@ void RenderHeatmapLayer::layerChanged(const TransitionParameters& parameters,
#endif
void RenderHeatmapLayer::evaluate(const PropertyEvaluationParameters& parameters) {
auto properties = makeMutable<HeatmapLayerProperties>(staticImmutableCast<HeatmapLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters));
const auto previousProperties = staticImmutableCast<HeatmapLayerProperties>(evaluatedProperties);
auto properties = makeMutable<HeatmapLayerProperties>(
staticImmutableCast<HeatmapLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters, previousProperties->evaluated));
passes = (properties->evaluated.get<style::HeatmapOpacity>() > 0) ? (RenderPass::Translucent | RenderPass::Pass3D)
: RenderPass::None;

View File

@@ -77,8 +77,11 @@ void RenderHillshadeLayer::layerChanged(const TransitionParameters& parameters,
#endif
void RenderHillshadeLayer::evaluate(const PropertyEvaluationParameters& parameters) {
auto properties = makeMutable<HillshadeLayerProperties>(staticImmutableCast<HillshadeLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters));
const auto previousProperties = staticImmutableCast<HillshadeLayerProperties>(evaluatedProperties);
auto properties = makeMutable<HillshadeLayerProperties>(
staticImmutableCast<HillshadeLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters, previousProperties->evaluated));
passes = (properties->evaluated.get<style::HillshadeExaggeration>() > 0)
? (RenderPass::Translucent | RenderPass::Pass3D)
: RenderPass::None;

View File

@@ -64,12 +64,19 @@ RenderLineLayer::~RenderLineLayer() = default;
void RenderLineLayer::transition(const TransitionParameters& parameters) {
unevaluated = impl_cast(baseImpl).paint.transitioned(parameters, std::move(unevaluated));
updateColorRamp();
#if MLN_RENDER_BACKEND_METAL
if (auto* tweaker = static_cast<LineLayerTweaker*>(layerTweaker.get())) {
tweaker->updateGPUExpressions(unevaluated, parameters.now);
}
#endif // MLN_RENDER_BACKEND_METAL
}
void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) {
const auto previousProperties = staticImmutableCast<LineLayerProperties>(evaluatedProperties);
auto properties = makeMutable<LineLayerProperties>(staticImmutableCast<LineLayer::Impl>(baseImpl),
parameters.getCrossfadeParameters(),
unevaluated.evaluate(parameters));
unevaluated.evaluate(parameters, previousProperties->evaluated));
auto& evaluated = properties->evaluated;
passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0 &&
@@ -81,8 +88,11 @@ void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) {
evaluatedProperties = std::move(properties);
#if MLN_DRAWABLE_RENDERER
if (layerTweaker) {
layerTweaker->updateProperties(evaluatedProperties);
if (auto* tweaker = static_cast<LineLayerTweaker*>(layerTweaker.get())) {
tweaker->updateProperties(evaluatedProperties);
#if MLN_RENDER_BACKEND_METAL
tweaker->updateGPUExpressions(unevaluated, parameters.now);
#endif // MLN_RENDER_BACKEND_METAL
}
#endif
}
@@ -348,7 +358,7 @@ float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature,
void RenderLineLayer::update(gfx::ShaderRegistry& shaders,
gfx::Context& context,
const TransformState& state,
const std::shared_ptr<UpdateParameters>&,
[[maybe_unused]] const std::shared_ptr<UpdateParameters>& parameters,
[[maybe_unused]] const RenderTree& renderTree,
[[maybe_unused]] UniqueChangeRequestVec& changes) {
if (!renderTiles || renderTiles->empty()) {
@@ -367,7 +377,12 @@ void RenderLineLayer::update(gfx::ShaderRegistry& shaders,
auto* tileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
if (!layerTweaker) {
layerTweaker = std::make_shared<LineLayerTweaker>(getID(), evaluatedProperties);
auto tweaker = std::make_shared<LineLayerTweaker>(getID(), evaluatedProperties);
#if MLN_RENDER_BACKEND_METAL
tweaker->updateGPUExpressions(unevaluated, parameters->timePoint);
#endif // MLN_RENDER_BACKEND_METAL
layerTweaker = std::move(tweaker);
layerGroup->addLayerTweaker(layerTweaker);
}

View File

@@ -50,8 +50,11 @@ void RenderRasterLayer::transition(const TransitionParameters& parameters) {
}
void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) {
auto properties = makeMutable<RasterLayerProperties>(staticImmutableCast<RasterLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters));
const auto previousProperties = staticImmutableCast<RasterLayerProperties>(evaluatedProperties);
auto properties = makeMutable<RasterLayerProperties>(
staticImmutableCast<RasterLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters, previousProperties->evaluated));
passes = properties->evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None;
properties->renderPasses = mbgl::underlying_type(passes);
evaluatedProperties = std::move(properties);

View File

@@ -371,8 +371,11 @@ void RenderSymbolLayer::transition(const TransitionParameters& parameters) {
}
void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) {
auto properties = makeMutable<SymbolLayerProperties>(staticImmutableCast<SymbolLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters));
const auto previousProperties = staticImmutableCast<SymbolLayerProperties>(evaluatedProperties);
auto properties = makeMutable<SymbolLayerProperties>(
staticImmutableCast<SymbolLayer::Impl>(baseImpl),
unevaluated.evaluate(parameters, previousProperties->evaluated));
auto& evaluated = properties->evaluated;
const auto& layout = impl_cast(baseImpl).layout;

View File

@@ -476,7 +476,7 @@ public:
#endif // MLN_LEGACY_RENDERER
std::tuple<float> interpolationFactor(float currentZoom) const override {
const float possiblyRoundedZoom = expression.useIntegerZoom ? std::floor(currentZoom) : currentZoom;
const float possiblyRoundedZoom = expression.getUseIntegerZoom() ? std::floor(currentZoom) : currentZoom;
return std::tuple<float>{
std::fmax(0.0f, std::fmin(1.0f, expression.interpolationFactor(zoomRange, possiblyRoundedZoom)))};

View File

@@ -34,6 +34,11 @@ public:
return value.match(std::forward<Ts>(ts)...);
}
T evaluate(float zoom) const {
return this->match([&](const T& constant_) { return constant_; },
[&](const style::PropertyExpression<T>& expression) { return expression.evaluate(zoom); });
}
template <class Feature>
T evaluate(const Feature& feature, float zoom, T defaultValue) const {
return this->match([&](const T& constant_) { return constant_; },

View File

@@ -41,6 +41,10 @@ public:
TimePoint now;
ZoomHistory zoomHistory;
Duration defaultFadeDuration;
bool zoomChanged = true;
bool layerChanged = false;
bool hasCrossfade = true;
};
} // namespace mbgl

View File

@@ -9,6 +9,7 @@ template <typename T>
class PropertyEvaluator {
public:
using ResultType = T;
static constexpr bool useIntegerZoom = false;
PropertyEvaluator(const PropertyEvaluationParameters& parameters_, T defaultValue_)
: parameters(parameters_),

View File

@@ -175,12 +175,11 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
: TransitionOptions();
const TransitionParameters transitionParameters{updateParameters->timePoint, transitionOptions};
const auto transitionDuration = transitionOptions.duration.value_or(
isMapModeContinuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero());
const PropertyEvaluationParameters evaluationParameters{
zoomHistory,
updateParameters->timePoint,
transitionOptions.duration.value_or(isMapModeContinuous ? util::DEFAULT_TRANSITION_DURATION
: Duration::zero())};
PropertyEvaluationParameters evaluationParameters{zoomHistory, updateParameters->timePoint, transitionDuration};
evaluationParameters.zoomChanged = zoomChanged;
const TileParameters tileParameters{updateParameters->pixelRatio,
updateParameters->debugOptions,
@@ -300,13 +299,9 @@ 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);
// 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()) {
evaluationParameters.layerChanged = layerAddedOrChanged;
evaluationParameters.hasCrossfade = layer.hasCrossfade();
if (layerAddedOrChanged || zoomChanged || evaluationParameters.hasCrossfade || layer.hasTransition()) {
const auto previousMask = layer.evaluatedProperties->constantsMask();
layer.evaluate(evaluationParameters);
if (previousMask != layer.evaluatedProperties->constantsMask()) {
@@ -907,8 +902,12 @@ void RenderOrchestrator::updateLayers(gfx::ShaderRegistry& shaders,
transitionOptions.duration.value_or(defDuration),
};
const auto& items = renderTree.getLayerRenderItemMap();
std::vector<std::unique_ptr<ChangeRequest>> changes;
for (const auto& item : renderTree.getLayerRenderItemMap()) {
changes.reserve(items.size() * 3);
for (const auto& item : items) {
auto& renderLayer = item.layer.get();
renderLayer.update(shaders, context, state, updateParameters, renderTree, changes);
}

View File

@@ -258,8 +258,9 @@ void Renderer::Impl::render(const RenderTree& renderTree,
/* .symbol_fade_change = */ parameters.symbolFadeChange,
/* .aspect_ratio = */ parameters.state.getSize().aspectRatio(),
/* .pixel_ratio = */ parameters.pixelRatio,
/* .pad1/2 = */ 0,
0};
/* .zoom = */ static_cast<float>(parameters.state.getZoom()),
/* .pad1 = */ 0,
};
auto& globalUniforms = context.mutableGlobalUniformBuffers();
globalUniforms.createOrUpdate(shaders::idGlobalPaintParamsUBO, &globalPaintParamsUBO, context);
#endif

View File

@@ -188,7 +188,7 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr
: linePropertiesUBO(properties) {}
~PolylineDrawableTweaker() override = default;
void init(gfx::Drawable&) override {};
void init(gfx::Drawable&) override {}
void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override {
if (!drawable.getTileID().has_value()) {
@@ -220,6 +220,9 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr
drawableUniforms.createOrUpdate(idLineDrawableUBO, &drawableUBO, parameters.context);
drawableUniforms.createOrUpdate(idLineInterpolationUBO, &lineInterpolationUBO, parameters.context);
drawableUniforms.createOrUpdate(idLineEvaluatedPropsUBO, &linePropertiesUBO, parameters.context);
// We would need to set up `idLineExpressionUBO` if the expression mask isn't empty
assert(linePropertiesUBO.expressionMask == LineExpressionMask::None);
};
private:
@@ -244,7 +247,7 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr
/*offset*/ 0.f,
/*width*/ 4.f,
/*floorwidth*/ 0,
0,
LineExpressionMask::None,
0};
auto tweaker = std::make_shared<PolylineDrawableTweaker>(linePropertiesUBO);

View File

@@ -3,11 +3,12 @@
namespace mbgl {
namespace shaders {
const std::array<UniformBlockInfo, 4> ShaderSource<BuiltIn::LineShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{true, false, sizeof(GlobalPaintParamsUBO), idGlobalPaintParamsUBO},
const std::array<UniformBlockInfo, 5> ShaderSource<BuiltIn::LineShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{true, true, sizeof(GlobalPaintParamsUBO), idGlobalPaintParamsUBO},
UniformBlockInfo{true, false, sizeof(idLineDrawableUBO), idLineDrawableUBO},
UniformBlockInfo{true, false, sizeof(LineInterpolationUBO), idLineInterpolationUBO},
UniformBlockInfo{true, true, sizeof(LineEvaluatedPropsUBO), idLineEvaluatedPropsUBO},
UniformBlockInfo{true, true, sizeof(LineExpressionUBO), idLineExpressionUBO},
};
const std::array<AttributeInfo, 8> ShaderSource<BuiltIn::LineShader, gfx::Backend::Type::Metal>::attributes = {
AttributeInfo{lineUBOCount + 0, gfx::AttributeDataType::Short2, idLinePosNormalVertexAttribute},
@@ -22,7 +23,7 @@ const std::array<AttributeInfo, 8> ShaderSource<BuiltIn::LineShader, gfx::Backen
const std::array<TextureInfo, 0> ShaderSource<BuiltIn::LineShader, gfx::Backend::Type::Metal>::textures = {};
const std::array<UniformBlockInfo, 4> ShaderSource<BuiltIn::LineGradientShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{true, false, sizeof(GlobalPaintParamsUBO), idGlobalPaintParamsUBO},
UniformBlockInfo{true, true, sizeof(GlobalPaintParamsUBO), idGlobalPaintParamsUBO},
UniformBlockInfo{true, false, sizeof(LineGradientDrawableUBO), idLineDrawableUBO},
UniformBlockInfo{true, false, sizeof(LineGradientInterpolationUBO), idLineInterpolationUBO},
UniformBlockInfo{true, true, sizeof(LineEvaluatedPropsUBO), idLineEvaluatedPropsUBO},
@@ -40,12 +41,13 @@ const std::array<TextureInfo, 1> ShaderSource<BuiltIn::LineGradientShader, gfx::
TextureInfo{0, idLineImageTexture},
};
const std::array<UniformBlockInfo, 5> ShaderSource<BuiltIn::LinePatternShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{true, false, sizeof(GlobalPaintParamsUBO), idGlobalPaintParamsUBO},
const std::array<UniformBlockInfo, 6> ShaderSource<BuiltIn::LinePatternShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{true, true, sizeof(GlobalPaintParamsUBO), idGlobalPaintParamsUBO},
UniformBlockInfo{true, true, sizeof(LinePatternDrawableUBO), idLineDrawableUBO},
UniformBlockInfo{true, false, sizeof(LinePatternInterpolationUBO), idLineInterpolationUBO},
UniformBlockInfo{true, true, sizeof(LinePatternTilePropertiesUBO), idLineTilePropertiesUBO},
UniformBlockInfo{true, true, sizeof(LineEvaluatedPropsUBO), idLineEvaluatedPropsUBO},
UniformBlockInfo{true, true, sizeof(LineExpressionUBO), idLineExpressionUBO},
};
const std::array<AttributeInfo, 9> ShaderSource<BuiltIn::LinePatternShader, gfx::Backend::Type::Metal>::attributes = {
AttributeInfo{lineUBOCount + 0, gfx::AttributeDataType::Short2, idLinePosNormalVertexAttribute},
@@ -62,11 +64,12 @@ const std::array<TextureInfo, 1> ShaderSource<BuiltIn::LinePatternShader, gfx::B
TextureInfo{0, idLineImageTexture},
};
const std::array<UniformBlockInfo, 4> ShaderSource<BuiltIn::LineSDFShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{true, false, sizeof(GlobalPaintParamsUBO), idGlobalPaintParamsUBO},
const std::array<UniformBlockInfo, 5> ShaderSource<BuiltIn::LineSDFShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{true, true, sizeof(GlobalPaintParamsUBO), idGlobalPaintParamsUBO},
UniformBlockInfo{true, true, sizeof(LineSDFDrawableUBO), idLineDrawableUBO},
UniformBlockInfo{true, false, sizeof(LineSDFInterpolationUBO), idLineInterpolationUBO},
UniformBlockInfo{true, true, sizeof(LineEvaluatedPropsUBO), idLineEvaluatedPropsUBO},
UniformBlockInfo{true, true, sizeof(LineExpressionUBO), idLineExpressionUBO},
};
const std::array<AttributeInfo, 9> ShaderSource<BuiltIn::LineSDFShader, gfx::Backend::Type::Metal>::attributes = {
AttributeInfo{lineUBOCount + 0, gfx::AttributeDataType::Short2, idLinePosNormalVertexAttribute},

View File

@@ -102,8 +102,9 @@ CoerceFunction getCoerceFunction(const type::Type& t) {
return toStringValue;
}
Dependency extraDependency(const type::Type& t) {
return t.is<type::ImageType>() ? Dependency::Image : Dependency::None;
/// `isRuntimeConstant` does not consider values coerced to `Type` image to be of `Kind` image, so we don't either.
constexpr Dependency extraDependency(const type::Type& /*t*/) {
return /*t.is<type::ImageType>() ? Dependency::Image :*/ Dependency::None;
}
} // namespace

View File

@@ -8,14 +8,17 @@ namespace mbgl {
namespace style {
namespace expression {
namespace {
/// Collators are considered feature-dependent, see `isFeatureConstant`
constexpr auto extraDependency = Dependency::Feature;
} // namespace
CollatorExpression::CollatorExpression(std::unique_ptr<Expression> caseSensitive_,
std::unique_ptr<Expression> diacriticSensitive_,
std::optional<std::unique_ptr<Expression>> locale_)
: Expression(Kind::CollatorExpression,
type::Collator,
depsOf(caseSensitive_) | depsOf(diacriticSensitive_) | depsOf(locale_) |
// Collations are treated as feature-dependent. See `style::expression::isFeatureConstant`
Dependency::Feature),
depsOf(caseSensitive_) | depsOf(diacriticSensitive_) | depsOf(locale_) | extraDependency),
caseSensitive(std::move(caseSensitive_)),
diacriticSensitive(std::move(diacriticSensitive_)),
locale(std::move(locale_)) {}

View File

@@ -17,7 +17,7 @@ std::ostream& operator<<(std::ostream& os, const Dependency deps) {
bool any = false;
const auto add = [&](Dependency dep, std::string_view name) {
if ((deps & dep) != Dependency::None) {
if (deps & dep) {
os << (any ? "|" : std::string_view{}) << name;
any = true;
}

View File

@@ -12,42 +12,46 @@ 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>> result;
ZoomCurveOrError findZoomCurve(const expression::Expression& e) {
ZoomCurveOrError result;
switch (e->getKind()) {
switch (e.getKind()) {
case Kind::Let: {
auto let = static_cast<const Let*>(e);
result = findZoomCurve(let->getResult());
const auto& let = static_cast<const Let&>(e);
if (const auto& child = let.getResult()) {
result = findZoomCurve(*child);
}
break;
}
case Kind::Coalesce: {
auto coalesce = static_cast<const Coalesce*>(e);
std::size_t length = coalesce->getLength();
const auto& coalesce = static_cast<const Coalesce&>(e);
const std::size_t length = coalesce.getLength();
for (std::size_t i = 0; i < length; i++) {
result = findZoomCurve(coalesce->getChild(i));
if (result) {
break;
if (const auto* child = coalesce.getChild(i)) {
result = findZoomCurve(*child);
if (result) {
break;
}
}
}
break;
}
case Kind::Interpolate: {
auto curve = static_cast<const Interpolate*>(e);
if (curve->getInput()->getKind() == Kind::CompoundExpression) {
auto z = static_cast<CompoundExpression*>(curve->getInput().get());
const auto& curve = static_cast<const Interpolate&>(e);
if (curve.getInput()->getKind() == Kind::CompoundExpression) {
const auto* z = static_cast<CompoundExpression*>(curve.getInput().get());
if (z && z->getOperator() == "zoom") {
result = {curve};
result = {&curve};
}
}
break;
}
case Kind::Step: {
auto step = static_cast<const Step*>(e);
if (step->getInput()->getKind() == Kind::CompoundExpression) {
auto z = static_cast<CompoundExpression*>(step->getInput().get());
const auto& step = static_cast<const Step&>(e);
if (step.getInput()->getKind() == Kind::CompoundExpression) {
const auto* z = static_cast<CompoundExpression*>(step.getInput().get());
if (z && z->getOperator() == "zoom") {
result = {step};
result = {&step};
}
}
break;
@@ -60,9 +64,8 @@ std::optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCu
return result;
}
e->eachChild([&](const Expression& child) {
std::optional<variant<const Interpolate*, const Step*, ParsingError>> childResult(findZoomCurve(&child));
if (childResult) {
e.eachChild([&](const Expression& child) {
if (const ZoomCurveOrError childResult = findZoomCurve(child)) {
if (childResult->is<ParsingError>()) {
result = childResult;
} else if (!result && childResult) {
@@ -79,16 +82,16 @@ std::optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCu
return result;
}
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 {
ZoomCurvePtr findZoomCurveChecked(const expression::Expression& e) {
if (!e.has(Dependency::Zoom)) {
return nullptr;
}
return findZoomCurve(e)->match(
[](const ParsingError& err) -> ZoomCurvePtr {
Log::Error(Event::Style, "Invalid Expression: " + err.message);
return nullptr;
},
[](auto zoomCurve) -> ZoomCurveOrNull { return zoomCurve; });
[](auto zoomCurve) -> ZoomCurvePtr { return zoomCurve; });
}
} // namespace expression

View File

@@ -248,19 +248,22 @@ ParseResult ParsingContext::parseExpression(const Convertible& value,
}
ParseResult ParsingContext::parseLayerPropertyExpression(const Convertible& value) {
std::optional<TypeAnnotationOption> typeAnnotationOption;
if (expected && *expected == type::String) {
typeAnnotationOption = TypeAnnotationOption::coerce;
}
const auto typeAnnotationOption = (expected && *expected == type::String)
? std::make_optional(TypeAnnotationOption::coerce)
: std::nullopt;
ParseResult parsed = parse(value, typeAnnotationOption);
if (parsed && !isZoomConstant(**parsed)) {
std::optional<variant<const Interpolate*, const Step*, ParsingError>> zoomCurve = findZoomCurve(parsed->get());
if (!zoomCurve) {
// If the expression is zoom-dependent, validate that we can resolve a zoom curve or treat it as an error
assert(!parsed || !*parsed || !((*parsed)->dependencies & Dependency::Zoom) == isZoomConstant(**parsed));
if (parsed && (*parsed)->has(Dependency::Zoom)) {
if (const auto zoomCurve = findZoomCurve(**parsed)) {
if (zoomCurve->is<ParsingError>()) {
error(zoomCurve->get<ParsingError>().message);
return ParseResult();
}
} else {
error(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)");
return ParseResult();
} else if (zoomCurve->is<ParsingError>()) {
error(zoomCurve->get<ParsingError>().message);
return ParseResult();
}
}
return parsed;

View File

@@ -91,8 +91,9 @@ class LineDrawableTweaker : public gfx::DrawableTweaker {
public:
LineDrawableTweaker(const shaders::LineEvaluatedPropsUBO& properties)
: linePropertiesUBO(properties) {}
~LineDrawableTweaker() override = default;
void init(gfx::Drawable&) override {};
void init(gfx::Drawable&) override {}
void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override {
if (!drawable.getTileID().has_value()) {
@@ -124,6 +125,9 @@ public:
drawableUniforms.createOrUpdate(idLineDrawableUBO, &drawableUBO, parameters.context);
drawableUniforms.createOrUpdate(idLineInterpolationUBO, &lineInterpolationUBO, parameters.context);
drawableUniforms.createOrUpdate(idLineEvaluatedPropsUBO, &linePropertiesUBO, parameters.context);
// We would need to set up `idLineExpressionUBO` if the expression mask isn't empty
assert(linePropertiesUBO.expressionMask == LineExpressionMask::None);
};
private:
@@ -135,7 +139,7 @@ public:
WideVectorDrawableTweaker(const CustomDrawableLayerHost::Interface::LineOptions& options_)
: options(options_) {}
void init(gfx::Drawable&) override {};
void init(gfx::Drawable&) override {}
void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override {
if (!drawable.getTileID().has_value()) {
@@ -198,8 +202,9 @@ public:
FillDrawableTweaker(const Color& color_, float opacity_)
: color(color_),
opacity(opacity_) {}
~FillDrawableTweaker() override = default;
void init(gfx::Drawable&) override {};
void init(gfx::Drawable&) override {}
void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override {
if (!drawable.getTileID().has_value()) {
@@ -244,8 +249,9 @@ class SymbolDrawableTweaker : public gfx::DrawableTweaker {
public:
SymbolDrawableTweaker(const CustomDrawableLayerHost::Interface::SymbolOptions& options_)
: options(options_) {}
~SymbolDrawableTweaker() override = default;
void init(gfx::Drawable&) override {};
void init(gfx::Drawable&) override {}
void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override {
if (!drawable.getTileID().has_value()) {
@@ -536,8 +542,8 @@ void CustomDrawableLayerHost::Interface::finish() {
lineOptions.gapWidth,
lineOptions.offset,
lineOptions.width,
0,
0,
/*floorwidth=*/0,
LineExpressionMask::None,
0};
auto tweaker = std::make_shared<LineDrawableTweaker>(linePropertiesUBO);

View File

@@ -6,10 +6,13 @@
#include <mbgl/style/color_ramp_property_value.hpp>
#include <mbgl/style/conversion/stringify.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/util/convert.hpp>
#include <mbgl/util/indexed_tuple.hpp>
#include <mbgl/util/ignore.hpp>
#include <mbgl/util/logging.hpp>
#include <bitset>
#include <tuple>
namespace mbgl {
@@ -65,8 +68,13 @@ public:
bool hasTransition() const noexcept { return bool(prior); }
bool isTransitioning(TimePoint now) const {
return hasTransition() && (begin <= now) && (now < end) && !value.isDataDriven();
}
bool isUndefined() const noexcept { return value.isUndefined(); }
Value& getValue() noexcept { return value; }
const Value& getValue() const noexcept { return value; }
private:
@@ -296,6 +304,24 @@ public:
return PossiblyEvaluated{evaluate<Ps>(parameters)...};
}
/// Evaluate the property if necessary, or produce a copy of the previous value if appropriate
template <class P>
auto maybeEvaluate(const PropertyEvaluationParameters& parameters,
const typename P::EvaluatorType::ResultType& oldResult) const {
using Evaluator = typename P::EvaluatorType;
const auto& property = this->template get<P>();
const bool needEvaluate = parameters.layerChanged || parameters.hasCrossfade || property.hasTransition() ||
(parameters.zoomChanged && (getDependencies(property) & Dependency::Zoom));
return needEvaluate ? property.evaluate(Evaluator(parameters, P::defaultValue()), parameters.now)
: oldResult;
}
/// Optionally evaluate each property or produce a copy of the previous value, if appropriate.
PossiblyEvaluated evaluate(const PropertyEvaluationParameters& parameters,
const PossiblyEvaluated& previous) const {
return PossiblyEvaluated{maybeEvaluate<Ps>(parameters, previous.template get<Ps>())...};
}
template <class Writer>
void stringify(Writer& writer) const {
writer.StartObject();
@@ -303,6 +329,8 @@ public:
writer.EndObject();
}
unsigned long constantsMask() const { return ConstantsMask<DataDrivenProperties>::getMask(*this); }
/// Get the combined dependencies of any contained expressions
constexpr Dependency getDependencies() const noexcept {
Dependency result = Dependency::None;
@@ -310,9 +338,20 @@ public:
return result;
}
unsigned long constantsMask() const { return ConstantsMask<DataDrivenProperties>::getMask(*this); }
#if MLN_DRAWABLE_RENDERER
using GPUExpressions = std::array<gfx::UniqueGPUExpression, UnevaluatedTypes::TypeCount>;
/// Update the GPU expressions, if applicable, for each item in the tuple.
/// @return true if any are updated (including being cleared)
bool updateGPUExpressions(Unevaluated::GPUExpressions& exprs, TimePoint now) const {
const auto results = util::to_array(std::make_tuple(updateGPUExpression<Ps>(exprs, now)...));
return std::any_of(results.begin(), results.end(), [](bool x) { return x; });
}
#endif // MLN_DRAWABLE_RENDERER
protected:
// gather dependencies for each type that can appear in this tuple
template <class P>
Dependency getDependencies(const PropertyValue<P>& v) const noexcept {
return v.getDependencies();
@@ -322,6 +361,53 @@ public:
Dependency getDependencies(const Transitioning<P>& v) const noexcept {
return v.getValue().getDependencies();
}
#if MLN_DRAWABLE_RENDERER
template <typename P>
bool updateGPUExpression(Unevaluated::GPUExpressions& exprs, TimePoint now) const {
constexpr auto index = TypeIndex<P, Ps...>::value;
constexpr bool forceIntZoom = P::EvaluatorType::useIntegerZoom;
return updateGPUExpression(exprs[index], this->template get<P>(), now, forceIntZoom);
}
template <class P>
static bool updateGPUExpression(gfx::UniqueGPUExpression& expr,
const PropertyValue<P>& val,
TimePoint /*now*/,
bool intZoom) {
if (val.isExpression() && val.asExpression().isGPUCapable()) {
if (!expr) {
expr = val.asExpression().getGPUExpression(intZoom);
return true;
}
} else if (expr) {
// Previously set and shouldn't be
expr.reset();
return true;
}
return false;
}
template <class P>
static bool updateGPUExpression(gfx::UniqueGPUExpression& expr,
const Transitioning<P>& val,
TimePoint now,
bool intZoom) {
if (val.isTransitioning(now)) {
if (expr) {
expr.reset();
return true;
}
return false;
}
return updateGPUExpression(expr, val.getValue(), now, intZoom);
}
static bool updateGPUExpression(gfx::UniqueGPUExpression&,
const style::ColorRampPropertyValue&,
TimePoint,
bool) {
return false;
}
#endif // MLN_DRAWABLE_RENDERER
};
class Transitionable : public Tuple<TransitionableTypes> {

View File

@@ -1,14 +1,84 @@
#include <mbgl/style/property_expression.hpp>
#include <mbgl/renderer/paint_property_binder.hpp>
#include <mbgl/util/convert.hpp>
#if MLN_DRAWABLE_RENDERER
#include <mbgl/gfx/gpu_expression.hpp>
#endif // MLN_DRAWABLE_RENDERER
namespace mbgl {
namespace style {
PropertyExpressionBase::PropertyExpressionBase(std::unique_ptr<expression::Expression> expression_) noexcept
namespace {
using namespace expression;
bool checkGPUCapable(const Expression& expression, const ZoomCurvePtr& zoomCurve) {
return (expression.dependencies == Dependency::Zoom) && !zoomCurve.is<std::nullptr_t>() &&
(expression.getType().is<type::NumberType>() || expression.getType().is<type::ColorType>());
}
} // namespace
PropertyExpressionBase::PropertyExpressionBase(std::unique_ptr<expression::Expression> expression_)
: expression(std::move(expression_)),
isZoomConstant_(expression::isZoomConstant(*expression)),
isFeatureConstant_(expression::isFeatureConstant(*expression)),
isRuntimeConstant_(expression::isRuntimeConstant(*expression)),
zoomCurve(isZoomConstant_ ? nullptr : expression::findZoomCurveChecked(*expression)) {}
zoomCurve(expression->has(Dependency::Zoom) ? expression::findZoomCurveChecked(*expression) : nullptr),
useIntegerZoom_(false),
isZoomConstant_(!expression->has(Dependency::Zoom)),
isFeatureConstant_(!expression->has(Dependency::Feature)),
isRuntimeConstant_(!expression->has(Dependency::Image)),
isGPUCapable_(checkGPUCapable(*expression, zoomCurve)) {
assert(isZoomConstant_ == expression::isZoomConstant(*expression));
assert(isFeatureConstant_ == expression::isFeatureConstant(*expression));
assert(isRuntimeConstant_ == expression::isRuntimeConstant(*expression));
}
PropertyExpressionBase::PropertyExpressionBase(PropertyExpressionBase&& other)
: expression(std::move(other.expression)),
zoomCurve(std::move(other.zoomCurve)),
useIntegerZoom_(other.useIntegerZoom_),
isZoomConstant_(other.isZoomConstant_),
isFeatureConstant_(other.isFeatureConstant_),
isRuntimeConstant_(other.isRuntimeConstant_),
isGPUCapable_(other.isGPUCapable_) {}
PropertyExpressionBase::PropertyExpressionBase(const PropertyExpressionBase& other)
: expression(other.expression),
zoomCurve(other.zoomCurve),
useIntegerZoom_(other.useIntegerZoom_),
isZoomConstant_(other.isZoomConstant_),
isFeatureConstant_(other.isFeatureConstant_),
isRuntimeConstant_(other.isRuntimeConstant_),
isGPUCapable_(other.isGPUCapable_) {}
PropertyExpressionBase& PropertyExpressionBase::operator=(PropertyExpressionBase&& other) {
expression = std::move(other.expression);
zoomCurve = other.zoomCurve;
useIntegerZoom_ = other.useIntegerZoom_;
isZoomConstant_ = other.isZoomConstant_;
isFeatureConstant_ = other.isFeatureConstant_;
isRuntimeConstant_ = other.isRuntimeConstant_;
isGPUCapable_ = other.isGPUCapable_;
return *this;
}
PropertyExpressionBase& PropertyExpressionBase::operator=(const PropertyExpressionBase& other) {
expression = other.expression;
zoomCurve = other.zoomCurve;
useIntegerZoom_ = other.useIntegerZoom_;
isZoomConstant_ = other.isZoomConstant_;
isFeatureConstant_ = other.isFeatureConstant_;
isRuntimeConstant_ = other.isRuntimeConstant_;
isGPUCapable_ = other.isGPUCapable_;
return *this;
}
#if MLN_DRAWABLE_RENDERER
gfx::UniqueGPUExpression PropertyExpressionBase::getGPUExpression(bool intZoom) const {
return isGPUCapable_ ? gfx::GPUExpression::create(*expression, zoomCurve, useIntegerZoom_ || intZoom)
: gfx::UniqueGPUExpression{};
}
#endif // MLN_DRAWABLE_RENDERER
float PropertyExpressionBase::interpolationFactor(const Range<float>& inputLevels,
const float inputValue) const noexcept {
return zoomCurve.match(