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

@@ -31,6 +31,9 @@ public:
static Type GetType() { return Value(DefaultType); }
static bool getEnableGPUExpressionEval() { return enableGPUExpressionEval; }
static void setEnableGPUExpressionEval(bool value) { enableGPUExpressionEval = value; }
template <typename T, typename... Args>
static std::unique_ptr<T> Create(Args... args) {
#if MLN_RENDER_BACKEND_METAL
@@ -48,6 +51,8 @@ private:
static const Type type = value;
return type;
}
static bool enableGPUExpressionEval;
};
} // namespace gfx

View File

@@ -0,0 +1,108 @@
#pragma once
#include <mbgl/util/color.hpp>
#include <mbgl/util/variant.hpp>
#include <optional>
#if MLN_DRAWABLE_RENDERER
namespace mbgl {
namespace style {
namespace expression {
class Expression;
class Interpolate;
class Step;
} // namespace expression
using ZoomCurvePtr = variant<std::nullptr_t, const expression::Interpolate*, const expression::Step*>;
} // namespace style
namespace gfx {
enum class GPUInterpType : std::uint16_t {
Step,
Linear,
Exponential,
Bezier
};
enum class GPUOutputType : std::uint16_t {
Float,
Color,
};
enum class GPUOptions : std::uint16_t {
None = 0,
IntegerZoom = 1 << 0,
Transitioning = 1 << 1,
};
class GPUExpression;
using UniqueGPUExpression = std::unique_ptr<GPUExpression>;
class alignas(16) GPUExpression {
public:
static constexpr std::size_t maxStops = 16;
GPUExpression() = delete;
GPUExpression(GPUOutputType type, uint16_t count)
: outputType(type),
stopCount(count) {}
GPUExpression(GPUExpression&&) = default;
GPUExpression(const GPUExpression&) = default;
GPUExpression(const GPUExpression* ptr)
: GPUExpression(ptr ? *ptr : empty) {}
GPUExpression& operator=(GPUExpression&&) = delete;
GPUExpression& operator=(const GPUExpression&) = delete;
/* 0 */ const GPUOutputType outputType;
/* 2 */ const std::uint16_t stopCount;
/* 4 */ GPUOptions options;
/* 6 */ GPUInterpType interpolation;
/* 8 */ union InterpOptions {
struct Exponential {
float base;
} exponential;
struct Bezier {
float x1;
float y1;
float x2;
float y2;
} bezier;
} interpOptions;
/* 24 */ float inputs[maxStops];
/* 24 + (4 * maxStops) = 88 */ union Stops {
float floats[maxStops];
float colors[2 * maxStops];
} stops;
static UniqueGPUExpression create(GPUOutputType, std::uint16_t stopCount);
static UniqueGPUExpression create(const style::expression::Expression&, const style::ZoomCurvePtr&, bool intZoom);
float evaluateFloat(const float zoom) const;
Color evaluateColor(const float zoom) const;
template <typename T>
auto evaluate(const float zoom) const;
Color getColor(std::size_t index) const;
static const GPUExpression empty;
};
static_assert(sizeof(GPUExpression) == 32 + (4 + 8) * GPUExpression::maxStops);
static_assert(sizeof(GPUExpression) % 16 == 0);
template <>
inline auto GPUExpression::evaluate<Color>(const float zoom) const {
return evaluateColor(zoom);
}
template <>
inline auto GPUExpression::evaluate<float>(const float zoom) const {
return evaluateFloat(zoom);
}
} // namespace gfx
} // namespace mbgl
#endif

View File

@@ -44,7 +44,8 @@ struct alignas(16) GlobalPaintParamsUBO {
float symbol_fade_change;
float aspect_ratio;
float pixel_ratio;
float pad1, pad2;
float zoom;
float pad1;
};
static_assert(sizeof(GlobalPaintParamsUBO) == 3 * 16);

View File

@@ -1,10 +1,27 @@
#pragma once
#include <mbgl/shaders/layer_ubo.hpp>
#include <mbgl/style/property_expression.hpp>
#include <mbgl/util/bitmask_operations.hpp>
#if MLN_DRAWABLE_RENDERER
#include <mbgl/gfx/gpu_expression.hpp>
#endif // MLN_DRAWABLE_RENDERER
namespace mbgl {
namespace shaders {
enum class LineExpressionMask : uint32_t {
None = 0,
Color = 1 << 0,
Opacity = 1 << 1,
Blur = 1 << 2,
Width = 1 << 3,
GapWidth = 1 << 4,
FloorWidth = 1 << 5,
Offset = 1 << 6,
};
//
// Line
@@ -26,6 +43,17 @@ struct alignas(16) LineInterpolationUBO {
};
static_assert(sizeof(LineInterpolationUBO) % 16 == 0);
struct alignas(16) LineExpressionUBO {
gfx::GPUExpression color;
gfx::GPUExpression blur;
gfx::GPUExpression opacity;
gfx::GPUExpression gapwidth;
gfx::GPUExpression offset;
gfx::GPUExpression width;
gfx::GPUExpression floorWidth;
};
static_assert(sizeof(LineExpressionUBO) % 16 == 0);
//
// Line gradient
@@ -110,7 +138,8 @@ struct alignas(16) LineEvaluatedPropsUBO {
float offset;
float width;
float floorwidth;
float pad1, pad2;
LineExpressionMask expressionMask;
float pad1;
};
static_assert(sizeof(LineEvaluatedPropsUBO) % 16 == 0);
@@ -119,6 +148,7 @@ enum {
idLineInterpolationUBO,
idLineTilePropertiesUBO,
idLineEvaluatedPropsUBO,
idLineExpressionUBO,
lineUBOCount
};

View File

@@ -54,6 +54,191 @@ float4 unpack_mix_color(const float4 packedColors, const float t) {
decode_color(float2(packedColors[2], packedColors[3])), t);
}
template<class ForwardIt, class T>
ForwardIt upper_bound(ForwardIt first, ForwardIt last, thread const T& value)
{
size_t count = last - first;
while (count > 0)
{
ForwardIt it = first;
const size_t step = count / 2;
it += step;
if (!(value < *it))
{
first = ++it;
count -= step + 1;
}
else {
count = step;
}
}
return first;
}
float interpolationFactor(float base, float rangeMin, float rangeMax, float z) {
const float zoomDiff = rangeMax - rangeMin;
const float zoomProgress = z - rangeMin;
if (zoomDiff == 0) {
return 0;
} else if (base == 1.0f) {
return zoomProgress / zoomDiff;
} else {
return (pow(base, zoomProgress) - 1) / (pow(base, zoomDiff) - 1);
}
}
enum class GPUInterpType : uint16_t {
Step,
Linear,
Exponential,
Bezier
};
enum class GPUOutputType : uint16_t {
Float,
Color,
};
enum class GPUOptions : uint16_t {
None = 0,
IntegerZoom = 1 << 0,
Transitioning = 1 << 1,
};
bool operator&(GPUOptions a, GPUOptions b) { return (uint16_t)a & (uint16_t)b; }
constant const int maxExprStops = 16;
struct alignas(16) GPUExpression {
GPUOutputType outputType;
uint16_t stopCount;
GPUOptions options;
GPUInterpType interpolation;
union InterpOptions {
struct Exponential {
float base;
} exponential;
struct Bezier {
float x1;
float y1;
float x2;
float y2;
} bezier;
} interpOptions;
float inputs[maxExprStops];
union Stops {
float floats[maxExprStops];
float2 colors[maxExprStops];
} stops;
float eval(float zoom) device const {
const auto effectiveZoom = (options & GPUOptions::IntegerZoom) ? floor(zoom) : zoom;
const auto index = find(effectiveZoom);
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 float rangeBeg = inputs[index - 1];
const float rangeEnd = inputs[index];
const auto t = interpolationFactor(interpOptions.exponential.base, rangeBeg, rangeEnd, effectiveZoom);
return mix(stops.floats[index - 1], stops.floats[index], t);
}
case GPUInterpType::Bezier:
assert(false);
return stops.floats[0];
}
}
float4 evalColor(float zoom) device const {
const auto effectiveZoom = (options & GPUOptions::IntegerZoom) ? floor(zoom) : zoom;
const auto index = find(effectiveZoom);
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 float rangeBeg = inputs[index - 1];
const float rangeEnd = inputs[index];
const auto t = interpolationFactor(interpOptions.exponential.base, rangeBeg, rangeEnd, effectiveZoom);
return mix(getColor(index - 1), getColor(index), clamp(t, 0.0, 1.0));
}
case GPUInterpType::Bezier:
assert(false);
return getColor(0);
}
}
/// Get the index of the entry to use from the zoom level
size_t find(float zoom) device const {
return upper_bound(&inputs[0], &inputs[stopCount], zoom) - &inputs[0];
}
float4 getColor(size_t index) device const { return decode_color(stops.colors[index]); }
};
static_assert(sizeof(GPUExpression) == 32 + (4 + 8) * maxExprStops, "wrong alignment");
static_assert(sizeof(GPUExpression) % 16 == 0, "wrong alignment");
enum {
idGlobalPaintParamsUBO,
globalUBOCount
};
enum {
idLineDrawableUBO = globalUBOCount,
idLineInterpolationUBO,
idLineTilePropertiesUBO,
idLineEvaluatedPropsUBO,
idLineExpressionUBO,
lineUBOCount
};
enum class LineExpressionMask : uint32_t {
None = 0,
Color = 1 << 0,
Opacity = 1 << 1,
Blur = 1 << 2,
Width = 1 << 3,
GapWidth = 1 << 4,
FloorWidth = 1 << 5,
Offset = 1 << 6,
};
bool operator&(LineExpressionMask a, LineExpressionMask b) { return (uint32_t)a & (uint32_t)b; }
struct alignas(16) LineExpressionUBO {
GPUExpression color;
GPUExpression blur;
GPUExpression opacity;
GPUExpression gapwidth;
GPUExpression offset;
GPUExpression width;
GPUExpression floorwidth;
};
static_assert(sizeof(LineExpressionUBO) % 16 == 0, "wrong alignment");
struct alignas(16) GlobalPaintParamsUBO {
/* 0 */ float2 pattern_atlas_texsize;
/* 8 */ float2 units_to_pixels;
@@ -62,7 +247,8 @@ struct alignas(16) GlobalPaintParamsUBO {
/* 28 */ float symbol_fade_change;
/* 32 */ float aspect_ratio;
/* 36 */ float pixel_ratio;
/* 40 */ float pad1, pad2;
/* 40 */ float zoom;
/* 44 */ float pad1;
/* 48 */
};
static_assert(sizeof(GlobalPaintParamsUBO) == 3 * 16, "unexpected padding");
@@ -129,7 +315,8 @@ struct alignas(16) LineEvaluatedPropsUBO {
float offset;
float width;
float floorwidth;
float pad1, pad2;
LineExpressionMask expressionMask;
float pad1;
};
struct alignas(16) SymbolDrawableUBO {

View File

@@ -14,33 +14,33 @@ struct ShaderSource<BuiltIn::LineShader, gfx::Backend::Type::Metal> {
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<UniformBlockInfo, 4> uniforms;
static const std::array<UniformBlockInfo, 5> uniforms;
static const std::array<AttributeInfo, 8> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto source = R"(
struct VertexStage {
short2 pos_normal [[attribute(5)]];
uchar4 data [[attribute(6)]];
short2 pos_normal [[attribute(lineUBOCount + 0)]];
uchar4 data [[attribute(lineUBOCount + 1)]];
#if !defined(HAS_UNIFORM_u_color)
float4 color [[attribute(7)]];
float4 color [[attribute(lineUBOCount + 2)]];
#endif
#if !defined(HAS_UNIFORM_u_blur)
float2 blur [[attribute(8)]];
float2 blur [[attribute(lineUBOCount + 3)]];
#endif
#if !defined(HAS_UNIFORM_u_opacity)
float2 opacity [[attribute(9)]];
float2 opacity [[attribute(lineUBOCount + 4)]];
#endif
#if !defined(HAS_UNIFORM_u_gapwidth)
float2 gapwidth [[attribute(10)]];
float2 gapwidth [[attribute(lineUBOCount + 5)]];
#endif
#if !defined(HAS_UNIFORM_u_offset)
float2 offset [[attribute(11)]];
float2 offset [[attribute(lineUBOCount + 6)]];
#endif
#if !defined(HAS_UNIFORM_u_width)
float2 width [[attribute(12)]];
float2 width [[attribute(lineUBOCount + 7)]];
#endif
};
@@ -78,23 +78,29 @@ struct alignas(16) LineInterpolationUBO {
};
FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
device const GlobalPaintParamsUBO& paintParams [[buffer(0)]],
device const LineDrawableUBO& drawable [[buffer(1)]],
device const LineInterpolationUBO& interp [[buffer(2)]],
device const LineEvaluatedPropsUBO& props [[buffer(4)]]) {
device const GlobalPaintParamsUBO& paintParams [[buffer(idGlobalPaintParamsUBO)]],
device const LineDrawableUBO& drawable [[buffer(idLineDrawableUBO)]],
device const LineInterpolationUBO& interp [[buffer(idLineInterpolationUBO)]],
device const LineEvaluatedPropsUBO& props [[buffer(idLineEvaluatedPropsUBO)]],
device const LineExpressionUBO& expr [[buffer(idLineExpressionUBO)]]) {
#if defined(HAS_UNIFORM_u_gapwidth)
const auto gapwidth = props.gapwidth / 2;
const auto exprGapWidth = (props.expressionMask & LineExpressionMask::GapWidth);
const auto gapwidth = (exprGapWidth ? expr.gapwidth.eval(paintParams.zoom) : props.gapwidth) / 2;
#else
const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2;
#endif
#if defined(HAS_UNIFORM_u_offset)
const auto offset = props.offset * -1;
const auto exprOffset = (props.expressionMask & LineExpressionMask::Offset);
const auto offset = (exprOffset ? expr.offset.eval(paintParams.zoom) : props.offset) * -1;
#else
const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1;
#endif
#if defined(HAS_UNIFORM_u_width)
const auto width = props.width;
const auto exprWidth = (props.expressionMask & LineExpressionMask::Width);
const auto width = exprWidth ? expr.width.eval(paintParams.zoom) : props.width;
#else
const auto width = unpack_mix_float(vertx.width, interp.width_t);
#endif
@@ -154,23 +160,30 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
}
half4 fragment fragmentMain(FragmentStage in [[stage_in]],
device const LineEvaluatedPropsUBO& props [[buffer(4)]]) {
device const GlobalPaintParamsUBO& paintParams [[buffer(idGlobalPaintParamsUBO)]],
device const LineEvaluatedPropsUBO& props [[buffer(idLineEvaluatedPropsUBO)]],
device const LineExpressionUBO& expr [[buffer(idLineExpressionUBO)]]) {
#if defined(OVERDRAW_INSPECTOR)
return half4(1.0);
#endif
#if defined(HAS_UNIFORM_u_color)
const float4 color = props.color;
const auto exprColor = (props.expressionMask & LineExpressionMask::Color);
const auto color = exprColor ? expr.color.evalColor(paintParams.zoom) : props.color;
#else
const float4 color = in.color;
#endif
#if defined(HAS_UNIFORM_u_blur)
const float blur = props.blur;
const auto exprBlur = (props.expressionMask & LineExpressionMask::Blur);
const float blur = exprBlur ? expr.blur.eval(paintParams.zoom) : props.blur;
#else
const float blur = in.blur;
#endif
#if defined(HAS_UNIFORM_u_opacity)
const float opacity = props.opacity;
const auto exprOpacity = (props.expressionMask & LineExpressionMask::Opacity);
const float opacity = exprOpacity ? expr.opacity.eval(paintParams.zoom) : props.opacity;
#else
const float opacity = in.opacity;
#endif
@@ -201,13 +214,23 @@ struct ShaderSource<BuiltIn::LineGradientShader, gfx::Backend::Type::Metal> {
static constexpr auto source = R"(
struct VertexStage {
short2 pos_normal [[attribute(5)]];
uchar4 data [[attribute(6)]];
float2 blur [[attribute(7)]];
float2 opacity [[attribute(8)]];
float2 gapwidth [[attribute(9)]];
float2 offset [[attribute(10)]];
float2 width [[attribute(11)]];
short2 pos_normal [[attribute(lineUBOCount + 0)]];
uchar4 data [[attribute(lineUBOCount + 1)]];
#if !defined(HAS_UNIFORM_u_blur)
float2 blur [[attribute(lineUBOCount + 2)]];
#endif
#if !defined(HAS_UNIFORM_u_opacity)
float2 opacity [[attribute(lineUBOCount + 3)]];
#endif
#if !defined(HAS_UNIFORM_u_gapwidth)
float2 gapwidth [[attribute(lineUBOCount + 4)]];
#endif
#if !defined(HAS_UNIFORM_u_offset)
float2 offset [[attribute(lineUBOCount + 5)]];
#endif
#if !defined(HAS_UNIFORM_u_width)
float2 width [[attribute(lineUBOCount + 6)]];
#endif
};
struct FragmentStage {
@@ -241,10 +264,11 @@ struct alignas(16) LineGradientInterpolationUBO {
};
FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
device const GlobalPaintParamsUBO& paintParams [[buffer(0)]],
device const LineGradientDrawableUBO& drawable [[buffer(1)]],
device const LineGradientInterpolationUBO& interp [[buffer(2)]],
device const LineEvaluatedPropsUBO& props [[buffer(4)]]) {
device const GlobalPaintParamsUBO& paintParams [[buffer(idGlobalPaintParamsUBO)]],
device const LineGradientDrawableUBO& drawable [[buffer(idLineDrawableUBO)]],
device const LineGradientInterpolationUBO& interp [[buffer(idLineInterpolationUBO)]],
device const LineEvaluatedPropsUBO& props [[buffer(idLineEvaluatedPropsUBO)]],
device const LineExpressionUBO& expr [[buffer(idLineExpressionUBO)]]) {
#if !defined(HAS_UNIFORM_u_blur)
const auto blur = unpack_mix_float(vertx.blur, interp.blur_t);
@@ -253,12 +277,12 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const auto opacity = unpack_mix_float(vertx.opacity, interp.opacity_t);
#endif
#if defined(HAS_UNIFORM_u_gapwidth)
const auto gapwidth = props.gapwidth;
const auto gapwidth = props.gapwidth / 2;
#else
const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2;
#endif
#if defined(HAS_UNIFORM_u_offset)
const auto offset = props.offset;
const auto offset = props.offset * -1;
#else
const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1;
#endif
@@ -322,7 +346,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
}
half4 fragment fragmentMain(FragmentStage in [[stage_in]],
device const LineEvaluatedPropsUBO& props [[buffer(4)]],
device const LineEvaluatedPropsUBO& props [[buffer(idLineEvaluatedPropsUBO)]],
device const LineExpressionUBO& expr [[buffer(idLineExpressionUBO)]],
texture2d<float, access::sample> gradientTexture [[texture(0)]]) {
#if defined(OVERDRAW_INSPECTOR)
return half4(1.0);
@@ -363,36 +388,36 @@ struct ShaderSource<BuiltIn::LinePatternShader, gfx::Backend::Type::Metal> {
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<UniformBlockInfo, 5> uniforms;
static const std::array<UniformBlockInfo, 6> uniforms;
static const std::array<AttributeInfo, 9> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto source = R"(
struct VertexStage {
short2 pos_normal [[attribute(5)]];
uchar4 data [[attribute(6)]];
short2 pos_normal [[attribute(lineUBOCount + 0)]];
uchar4 data [[attribute(lineUBOCount + 1)]];
#if !defined(HAS_UNIFORM_u_blur)
float2 blur [[attribute(7)]];
float2 blur [[attribute(lineUBOCount + 2)]];
#endif
#if !defined(HAS_UNIFORM_u_opacity)
float2 opacity [[attribute(8)]];
float2 opacity [[attribute(lineUBOCount + 3)]];
#endif
#if !defined(HAS_UNIFORM_u_gapwidth)
float2 gapwidth [[attribute(9)]];
float2 gapwidth [[attribute(lineUBOCount + 4)]];
#endif
#if !defined(HAS_UNIFORM_u_offset)
float2 offset [[attribute(10)]];
float2 offset [[attribute(lineUBOCount + 5)]];
#endif
#if !defined(HAS_UNIFORM_u_width)
float2 width [[attribute(11)]];
float2 width [[attribute(lineUBOCount + 6)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_from)
ushort4 pattern_from [[attribute(12)]];
ushort4 pattern_from [[attribute(lineUBOCount + 7)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
ushort4 pattern_to [[attribute(13)]];
ushort4 pattern_to [[attribute(lineUBOCount + 8)]];
#endif
};
@@ -442,26 +467,32 @@ struct alignas(16) LinePatternTilePropertiesUBO {
};
FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
device const GlobalPaintParamsUBO& paintParams [[buffer(0)]],
device const LinePatternDrawableUBO& drawable [[buffer(1)]],
device const LinePatternInterpolationUBO& interp [[buffer(2)]],
device const LinePatternTilePropertiesUBO& tileProps [[buffer(3)]],
device const LineEvaluatedPropsUBO& props [[buffer(4)]]) {
device const GlobalPaintParamsUBO& paintParams [[buffer(idGlobalPaintParamsUBO)]],
device const LinePatternDrawableUBO& drawable [[buffer(idLineDrawableUBO)]],
device const LinePatternInterpolationUBO& interp [[buffer(idLineInterpolationUBO)]],
device const LinePatternTilePropertiesUBO& tileProps [[buffer(idLineTilePropertiesUBO)]],
device const LineEvaluatedPropsUBO& props [[buffer(idLineEvaluatedPropsUBO)]],
device const LineExpressionUBO& expr [[buffer(idLineExpressionUBO)]]) {
#if defined(HAS_UNIFORM_u_gapwidth)
const auto gapwidth = props.gapwidth / 2;
const auto exprGapWidth = (props.expressionMask & LineExpressionMask::GapWidth);
const auto gapwidth = (exprGapWidth ? expr.gapwidth.eval(paintParams.zoom) : props.gapwidth) / 2;
#else
const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2;
#endif
#if defined(HAS_UNIFORM_u_offset)
const auto offset = props.offset * -1;
const auto exprOffset = (props.expressionMask & LineExpressionMask::Offset);
const auto offset = (exprOffset ? expr.offset.eval(paintParams.zoom) : props.offset) * -1;
#else
const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1;
const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1;
#endif
#if defined(HAS_UNIFORM_u_width)
const auto width = props.width;
const auto exprWidth = (props.expressionMask & LineExpressionMask::Width);
const auto width = exprWidth ? expr.width.eval(paintParams.zoom) : props.width;
#else
const auto width = unpack_mix_float(vertx.width, interp.width_t);
const auto width = unpack_mix_float(vertx.width, interp.width_t);
#endif
// the distance over which the line edge fades out.
@@ -525,9 +556,11 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
}
half4 fragment fragmentMain(FragmentStage in [[stage_in]],
device const LinePatternDrawableUBO& drawable [[buffer(1)]],
device const LinePatternTilePropertiesUBO& tileProps [[buffer(3)]],
device const LineEvaluatedPropsUBO& props [[buffer(4)]],
device const GlobalPaintParamsUBO& paintParams [[buffer(idGlobalPaintParamsUBO)]],
device const LinePatternDrawableUBO& drawable [[buffer(idLineDrawableUBO)]],
device const LinePatternTilePropertiesUBO& tileProps [[buffer(idLineTilePropertiesUBO)]],
device const LineEvaluatedPropsUBO& props [[buffer(idLineEvaluatedPropsUBO)]],
device const LineExpressionUBO& expr [[buffer(idLineExpressionUBO)]],
texture2d<float, access::sample> image0 [[texture(0)]],
sampler image0_sampler [[sampler(0)]]) {
#if defined(OVERDRAW_INSPECTOR)
@@ -535,20 +568,25 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
#endif
#if defined(HAS_UNIFORM_u_blur)
const half blur = props.blur;
const auto exprBlur = (props.expressionMask & LineExpressionMask::Blur);
const float blur = exprBlur ? expr.blur.eval(paintParams.zoom) : props.blur;
#else
const half blur = in.blur;
const float blur = in.blur;
#endif
#if defined(HAS_UNIFORM_u_opacity)
const half opacity = props.opacity;
const auto exprOpacity = (props.expressionMask & LineExpressionMask::Opacity);
const float opacity = exprOpacity ? expr.opacity.eval(paintParams.zoom) : props.opacity;
#else
const half opacity = in.opacity;
const float opacity = in.opacity;
#endif
#if defined(HAS_UNIFORM_u_pattern_from)
const auto pattern_from = float4(tileProps.pattern_from);
#else
const auto pattern_from = float4(in.pattern_from);
#endif
#if defined(HAS_UNIFORM_u_pattern_to)
const auto pattern_to = float4(tileProps.pattern_to);
#else
@@ -606,36 +644,36 @@ struct ShaderSource<BuiltIn::LineSDFShader, gfx::Backend::Type::Metal> {
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<UniformBlockInfo, 4> uniforms;
static const std::array<UniformBlockInfo, 5> uniforms;
static const std::array<AttributeInfo, 9> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto source = R"(
struct VertexStage {
short2 pos_normal [[attribute(5)]];
uchar4 data [[attribute(6)]];
short2 pos_normal [[attribute(lineUBOCount + 0)]];
uchar4 data [[attribute(lineUBOCount + 1)]];
#if !defined(HAS_UNIFORM_u_color)
float4 color [[attribute(7)]];
float4 color [[attribute(lineUBOCount + 2)]];
#endif
#if !defined(HAS_UNIFORM_u_blur)
float2 blur [[attribute(8)]];
float2 blur [[attribute(lineUBOCount + 3)]];
#endif
#if !defined(HAS_UNIFORM_u_opacity)
float2 opacity [[attribute(9)]];
float2 opacity [[attribute(lineUBOCount + 4)]];
#endif
#if !defined(HAS_UNIFORM_u_gapwidth)
float2 gapwidth [[attribute(10)]];
float2 gapwidth [[attribute(lineUBOCount + 5)]];
#endif
#if !defined(HAS_UNIFORM_u_offset)
float2 offset [[attribute(11)]];
float2 offset [[attribute(lineUBOCount + 6)]];
#endif
#if !defined(HAS_UNIFORM_u_width)
float2 width [[attribute(12)]];
float2 width [[attribute(lineUBOCount + 7)]];
#endif
#if !defined(HAS_UNIFORM_u_floorwidth)
float2 floorwidth [[attribute(13)]];
float2 floorwidth [[attribute(lineUBOCount + 8)]];
#endif
};
@@ -685,28 +723,36 @@ struct alignas(16) LineSDFInterpolationUBO {
};
FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
device const GlobalPaintParamsUBO& paintParams [[buffer(0)]],
device const LineSDFDrawableUBO& drawable [[buffer(1)]],
device const LineSDFInterpolationUBO& interp [[buffer(2)]],
device const LineEvaluatedPropsUBO& props [[buffer(4)]]) {
device const GlobalPaintParamsUBO& paintParams [[buffer(idGlobalPaintParamsUBO)]],
device const LineSDFDrawableUBO& drawable [[buffer(idLineDrawableUBO)]],
device const LineSDFInterpolationUBO& interp [[buffer(idLineInterpolationUBO)]],
device const LineEvaluatedPropsUBO& props [[buffer(idLineEvaluatedPropsUBO)]],
device const LineExpressionUBO& expr [[buffer(idLineExpressionUBO)]]) {
#if defined(HAS_UNIFORM_u_gapwidth)
const auto gapwidth = props.gapwidth / 2;
const auto exprGapWidth = (props.expressionMask & LineExpressionMask::GapWidth);
const auto gapwidth = (exprGapWidth ? expr.gapwidth.eval(paintParams.zoom) : props.gapwidth) / 2;
#else
const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2;
const auto gapwidth = unpack_mix_float(vertx.gapwidth, interp.gapwidth_t) / 2;
#endif
#if defined(HAS_UNIFORM_u_offset)
const auto offset = props.offset * -1;
const auto exprOffset = (props.expressionMask & LineExpressionMask::Offset);
const auto offset = (exprOffset ? expr.offset.eval(paintParams.zoom) : props.offset) * -1;
#else
const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1;
const auto offset = unpack_mix_float(vertx.offset, interp.offset_t) * -1;
#endif
#if defined(HAS_UNIFORM_u_width)
const auto width = props.width;
const auto exprWidth = (props.expressionMask & LineExpressionMask::Width);
const auto width = exprWidth ? expr.width.eval(paintParams.zoom) : props.width;
#else
const auto width = unpack_mix_float(vertx.width, interp.width_t);
const auto width = unpack_mix_float(vertx.width, interp.width_t);
#endif
#if defined(HAS_UNIFORM_u_floorwidth)
const auto floorwidth = props.floorwidth;
const auto exprFloorWidth = (props.expressionMask & LineExpressionMask::FloorWidth);
const auto floorwidth = exprFloorWidth ? expr.floorwidth.eval(paintParams.zoom) : props.floorwidth;
#else
const auto floorwidth = unpack_mix_float(vertx.floorwidth, interp.floorwidth_t);
#endif
@@ -773,8 +819,10 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
}
half4 fragment fragmentMain(FragmentStage in [[stage_in]],
device const LineSDFDrawableUBO& drawable [[buffer(1)]],
device const LineEvaluatedPropsUBO& props [[buffer(4)]],
device const GlobalPaintParamsUBO& paintParams [[buffer(idGlobalPaintParamsUBO)]],
device const LineSDFDrawableUBO& drawable [[buffer(idLineDrawableUBO)]],
device const LineEvaluatedPropsUBO& props [[buffer(idLineEvaluatedPropsUBO)]],
device const LineExpressionUBO& expr [[buffer(idLineExpressionUBO)]],
texture2d<float, access::sample> image0 [[texture(0)]],
sampler image0_sampler [[sampler(0)]]) {
#if defined(OVERDRAW_INSPECTOR)
@@ -782,24 +830,31 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
#endif
#if defined(HAS_UNIFORM_u_color)
const float4 color = props.color;
const auto exprColor = (props.expressionMask & LineExpressionMask::Color);
const auto color = exprColor ? expr.color.evalColor(paintParams.zoom) : props.color;
#else
const float4 color = in.color;
#endif
#if defined(HAS_UNIFORM_u_blur)
const float blur = props.blur;
const auto exprBlur = (props.expressionMask & LineExpressionMask::Blur);
const float blur = exprBlur ? expr.blur.eval(paintParams.zoom) : props.blur;
#else
const float blur = in.blur;
#endif
#if defined(HAS_UNIFORM_u_opacity)
const float opacity = props.opacity;
const auto exprOpacity = (props.expressionMask & LineExpressionMask::Opacity);
const float opacity = exprOpacity ? expr.opacity.eval(paintParams.zoom) : props.opacity;
#else
const float opacity = in.opacity;
#endif
#if defined(HAS_UNIFORM_u_floorwidth)
const float floorwidth = props.floorwidth;
const auto exprFloorWidth = (props.expressionMask & LineExpressionMask::FloorWidth);
const auto floorwidth = exprFloorWidth ? expr.floorwidth.eval(paintParams.zoom) : props.floorwidth;
#else
const float floorwidth = in.floorwidth;
const auto floorwidth = in.floorwidth;
#endif
// Calculate the antialiasing fade factor. This is either when fading in the

View File

@@ -4,6 +4,7 @@
#include <mbgl/style/expression/type.hpp>
#include <mbgl/style/expression/value.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/bitmask_operations.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/traits.hpp>
#include <mbgl/util/variant.hpp>
@@ -202,17 +203,14 @@ enum class Dependency : uint32_t {
Var = 1 << 5, // Use variable binding
Override = 1 << 6, // Property override
MaskCount = 7,
All = (1 << MaskCount) - 1,
};
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 Interpolate;
class Step;
using ZoomCurveOrError = std::optional<variant<const Interpolate*, const Step*, ParsingError>>;
using ZoomCurvePtr = variant<std::nullptr_t, const Interpolate*, const Step*>;
class Expression {
public:
@@ -264,6 +262,9 @@ public:
const Dependency dependencies;
/// Test the expression's dependencies for any of one or more values
bool has(Dependency dep) const { return (dependencies & dep); }
protected:
template <typename T>
static bool childrenEqual(const T& lhs, const T& rhs) noexcept {

View File

@@ -11,9 +11,18 @@ namespace mbgl {
namespace style {
namespace expression {
std::optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCurve(const expression::Expression*);
using ZoomCurveOrError = std::optional<variant<const Interpolate*, const Step*, ParsingError>>;
using ZoomCurvePtr = variant<std::nullptr_t, const Interpolate*, const Step*>;
variant<std::nullptr_t, const Interpolate*, const Step*> findZoomCurveChecked(const expression::Expression&);
/// Find a zoom curve in the expression tree.
/// @param expr Expression root
/// @return The relevant step or interpolate item, or the error if the expression represents an error
ZoomCurveOrError findZoomCurve(const expression::Expression&);
/// Find a zoom curve in the expression tree.
/// @param expr Expression root
/// @return The relevant step or interpolate item, or null if the expression represents an error
ZoomCurvePtr findZoomCurveChecked(const expression::Expression& expr);
} // namespace expression
} // namespace style

View File

@@ -34,6 +34,8 @@ public:
}
}
std::size_t getStopCount() const { return stops.size(); }
void eachStop(const std::function<void(double, const Expression&)>& visit) const {
for (const auto& stop : stops) {
visit(stop.first, *stop.second);

View File

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

View File

@@ -20,6 +20,8 @@ public:
EvaluationResult evaluate(const EvaluationContext& params) const override;
void eachChild(const std::function<void(const Expression&)>& visit) const override;
std::size_t getStopCount() const { return stops.size(); }
void eachStop(const std::function<void(double, const Expression&)>& visit) const;
const std::unique_ptr<Expression>& getInput() const noexcept { return input; }

View File

@@ -5,45 +5,75 @@
#include <mbgl/style/expression/interpolate.hpp>
#include <mbgl/style/expression/step.hpp>
#include <mbgl/style/expression/find_zoom_curve.hpp>
#include <mbgl/util/bitmask_operations.hpp>
#include <mbgl/util/range.hpp>
#if MLN_DRAWABLE_RENDERER
#include <mbgl/gfx/gpu_expression.hpp>
#endif // MLN_DRAWABLE_RENDERER
#include <optional>
namespace mbgl {
namespace gfx {
class GPUExpression;
using UniqueGPUExpression = std::unique_ptr<GPUExpression>;
} // namespace gfx
namespace style {
class PropertyExpressionBase {
public:
explicit PropertyExpressionBase(std::unique_ptr<expression::Expression>) noexcept;
using Expression = expression::Expression;
using Dependency = expression::Dependency;
using ZoomCurvePtr = expression::ZoomCurvePtr;
PropertyExpressionBase(PropertyExpressionBase&&);
PropertyExpressionBase(const PropertyExpressionBase&);
explicit PropertyExpressionBase(std::unique_ptr<Expression>);
virtual ~PropertyExpressionBase() = default;
PropertyExpressionBase& operator=(PropertyExpressionBase&&);
PropertyExpressionBase& operator=(const PropertyExpressionBase&);
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;
const Expression& getExpression() const noexcept;
/// Can be used for aggregating property expressions from multiple
/// properties(layers) into single match / case expression. Method may
/// be removed if a better way of aggregation is found.
std::shared_ptr<const expression::Expression> getSharedExpression() const noexcept;
bool isGPUCapable() const { return isGPUCapable_; }
bool useIntegerZoom = false;
bool getUseIntegerZoom() const { return useIntegerZoom_; }
void setUseIntegerZoom(bool value) { useIntegerZoom_ = value; }
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;
}
/// Can be used for aggregating property expressions from multiple properties(layers) into single match / case
/// expression. May be removed if a better way of aggregation is found.
std::shared_ptr<const Expression> getSharedExpression() const noexcept;
#if MLN_DRAWABLE_RENDERER
/// Build a cached GPU representation of the expression, with the same lifetime as this object.
gfx::UniqueGPUExpression getGPUExpression(bool intZoom) const;
#endif // MLN_DRAWABLE_RENDERER
Dependency getDependencies() const noexcept { return expression ? expression->dependencies : Dependency::None; }
const ZoomCurvePtr& getZoomCurve() const { return zoomCurve; }
protected:
std::shared_ptr<const expression::Expression> expression;
std::shared_ptr<const Expression> expression;
ZoomCurvePtr zoomCurve;
bool useIntegerZoom_ = false;
bool isZoomConstant_;
bool isFeatureConstant_;
bool isRuntimeConstant_;
variant<std::nullptr_t, const expression::Interpolate*, const expression::Step*> zoomCurve;
// If the expression depends on zoom and nothing else, and produces
// a number or color, we can potentially evaluate it on the GPU
bool isGPUCapable_;
};
template <class T>
@@ -52,8 +82,7 @@ class PropertyExpression final : public PropertyExpressionBase {
public:
// Second parameter to be used only for conversions from legacy functions.
PropertyExpression(std::unique_ptr<expression::Expression> expression_,
std::optional<T> defaultValue_ = std::nullopt) noexcept
PropertyExpression(std::unique_ptr<Expression> expression_, std::optional<T> defaultValue_ = std::nullopt)
: PropertyExpressionBase(std::move(expression_)),
defaultValue(std::move(defaultValue_)) {}

View File

@@ -49,6 +49,7 @@ public:
const T& asConstant() const noexcept { return value.template get<T>(); }
PropertyExpression<T>& asExpression() noexcept { return value.template get<PropertyExpression<T>>(); }
const PropertyExpression<T>& asExpression() const noexcept { return value.template get<PropertyExpression<T>>(); }
template <class... Ts>

View File

@@ -27,6 +27,8 @@ public:
assert(a_ >= 0.0f);
assert(a_ <= 1.0f);
}
Color(const float rgba[4])
: Color(rgba[0], rgba[1], rgba[2], rgba[3]) {}
float r = 0.0f;
float g = 0.0f;

View File

@@ -5,6 +5,7 @@
#include <algorithm>
#include <array>
#include <cstddef>
#include <tuple>
#include <type_traits>
namespace mbgl {
@@ -24,5 +25,12 @@ constexpr std::array<To, Size> cast(const std::array<From, Size>& from) {
return to;
}
/// Convert an `N`-tuple of all `T`s to `array<T,N>`
template <typename... Ts>
inline constexpr auto to_array(std::tuple<Ts...>&& tuple) {
return std::apply([](auto&&... x) { return std::array{std::forward<decltype(x)>(x)...}; },
std::forward<std::tuple<Ts...>>(tuple));
}
} // namespace util
} // namespace mbgl

View File

@@ -1,5 +1,7 @@
#pragma once
#include <mbgl/util/traits.hpp>
#include <algorithm>
#include <cassert>
#include <string>

View File

@@ -30,31 +30,36 @@ public:
static_assert(sizeof...(Is) == sizeof...(Ts), "IndexedTuple size mismatch");
template <class I>
auto& get() {
return std::get<TypeIndex<I, Is...>::value>(*this);
static constexpr std::size_t getIndex() {
return TypeIndex<I, Is...>::value;
}
template <class I>
const auto& get() const {
return std::get<TypeIndex<I, Is...>::value>(*this);
constexpr auto& get() {
return std::get<getIndex<I>()>(*this);
}
template <class I>
constexpr const auto& get() const {
return std::get<getIndex<I>()>(*this);
}
template <class... Us>
IndexedTuple(Us&&... other)
constexpr IndexedTuple(Us&&... other)
: std::tuple<Ts...>(std::forward<Us>(other)...) {}
template <class... Js, class... Us>
IndexedTuple<TypeList<Is..., Js...>, TypeList<Ts..., Us...>> concat(
constexpr IndexedTuple<TypeList<Is..., Js...>, TypeList<Ts..., Us...>> concat(
const IndexedTuple<TypeList<Js...>, TypeList<Us...>>& other) const {
return IndexedTuple<TypeList<Is..., Js...>, TypeList<Ts..., Us...>>{get<Is>()..., other.template get<Js>()...};
}
// Help out MSVC++
bool operator==(const IndexedTuple<TypeList<Is...>, TypeList<Ts...>>& other) const {
constexpr bool operator==(const IndexedTuple<TypeList<Is...>, TypeList<Ts...>>& other) const {
return static_cast<const std::tuple<Ts...>&>(*this) == static_cast<const std::tuple<Ts...>&>(other);
}
bool operator!=(const IndexedTuple<TypeList<Is...>, TypeList<Ts...>>& other) const noexcept {
constexpr bool operator!=(const IndexedTuple<TypeList<Is...>, TypeList<Ts...>>& other) const {
return !(*this == other);
}
};

View File

@@ -11,10 +11,18 @@ struct SuppressCopies {
SuppressCopies(SuppressCopies const&) noexcept {}
SuppressCopies(SuppressCopies&& other)
: value(std::forward<T>(other.value)) noexcept {}
SuppressCopies& operator=(SuppressCopies o) noexcept {
SuppressCopies& operator=(T&& o) {
value = std::move(o);
return *this;
}
SuppressCopies& operator=(SuppressCopies o) {
std::swap(value, o.value);
return *this;
}
T& operator->() noexcept { return value; }
const T& operator->() const noexcept { return value; }
operator T&() noexcept { return value; }
T& get() noexcept { return value; }

View File

@@ -9,6 +9,7 @@ template <class... Ts>
struct TypeList {
template <template <class...> class T, class... Ps>
using ExpandInto = T<Ps..., Ts...>;
static constexpr std::size_t TypeCount = sizeof...(Ts);
};
namespace detail {