#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { /* ZoomInterpolatedAttribute is a 'compound' attribute, representing two values of the the base attribute Attr. These two values are provided to the shader to allow interpolation between zoom levels, without the need to repopulate vertex buffers each frame as the map is being zoomed. */ template using ZoomInterpolatedAttributeType = gl::AttributeType; inline std::array attributeValue(float v) { return {{ v }}; } /* Encode a four-component color value into a pair of floats. Since csscolorparser uses 8-bit precision for each color component, for each float we use the upper 8 bits for one component (e.g. (color.r * 255) * 256), and the lower 8 for another. Also note that colors come in as floats 0..1, so we scale by 255. */ inline std::array attributeValue(const Color& color) { return {{ static_cast(mbgl::attributes::packUint8Pair(255 * color.r, 255 * color.g)), static_cast(mbgl::attributes::packUint8Pair(255 * color.b, 255 * color.a)) }}; } template std::array zoomInterpolatedAttributeValue(const std::array& min, const std::array& max) { std::array result; for (size_t i = 0; i < N; i++) { result[i] = min[i]; result[i+N] = max[i]; } return result; } /* PaintPropertyBinder is an abstract class serving as the interface definition for the strategy used for constructing, uploading, and binding paint property data as GLSL attributes. It has three concrete subclasses, one for each of the three strategies we use: * For _constant_ properties -- those whose value is a constant, or the constant result of evaluating a camera function at a particular camera position -- we don't need a vertex buffer, and instead use a uniform. * For source functions, we use a vertex buffer with a single attribute value, the evaluated result of the source function for the given feature. * For composite functions, we use a vertex buffer with two attributes: min and max values covering the range of zooms at which we expect the tile to be displayed. These values are calculated by evaluating the composite function for the given feature at strategically chosen zoom levels. In addition to this attribute data, we also use a uniform value which the shader uses to interpolate between the min and max value at the final displayed zoom level. The use of a uniform allows us to cheaply update the value on every frame. Note that the shader source varies depending on whether we're using a uniform or attribute. Like GL JS, we dynamically compile shaders at runtime to accomodate this. */ template class PaintPropertyBinder { public: virtual ~PaintPropertyBinder() = default; virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&, const optional&) = 0; virtual void upload(gl::Context& context) = 0; virtual void setPatternParameters(const optional&, const optional&, CrossfadeParameters&) = 0; virtual std::tuple>...> attributeBinding(const PossiblyEvaluatedType& currentValue) const = 0; virtual std::tuple...> interpolationFactor(float currentZoom) const = 0; virtual std::tuple...> uniformValue(const PossiblyEvaluatedType& currentValue) const = 0; static std::unique_ptr create(const PossiblyEvaluatedType& value, float zoom, T defaultValue); PaintPropertyStatistics statistics; }; template class ConstantPaintPropertyBinder : public PaintPropertyBinder, A> { public: ConstantPaintPropertyBinder(T constant_) : constant(std::move(constant_)) { } void populateVertexVector(const GeometryTileFeature&, std::size_t, const ImagePositions&, const optional&) override {} void upload(gl::Context&) override {} void setPatternParameters(const optional&, const optional&, CrossfadeParameters&) override {}; std::tuple> attributeBinding(const PossiblyEvaluatedPropertyValue&) const override { return std::tuple> {}; } std::tuple interpolationFactor(float) const override { return std::tuple { 0.0f }; } std::tuple uniformValue(const PossiblyEvaluatedPropertyValue& currentValue) const override { return std::tuple { currentValue.constantOr(constant) }; } private: T constant; }; template class ConstantCrossFadedPaintPropertyBinder : public PaintPropertyBinder,PossiblyEvaluatedPropertyValue>, As...> { public: ConstantCrossFadedPaintPropertyBinder(Faded constant_) : constant(std::move(constant_)), constantPatternPositions({}) { } void populateVertexVector(const GeometryTileFeature&, std::size_t, const ImagePositions&, const optional&) override {} void upload(gl::Context&) override {} void setPatternParameters(const optional& posA, const optional& posB, CrossfadeParameters&) override { if (!posA && !posB) { return; } else { constantPatternPositions = std::tuple, std::array> { posB->tlbr(), posA->tlbr() }; } } std::tuple, optional> attributeBinding(const PossiblyEvaluatedPropertyValue>&) const override { return std::tuple, optional> {}; } std::tuple interpolationFactor(float) const override { return std::tuple { 0.0f, 0.0f }; } std::tuple, std::array> uniformValue(const PossiblyEvaluatedPropertyValue>&) const override { return constantPatternPositions; } private: Faded constant; std::tuple, std::array> constantPatternPositions; }; template class SourceFunctionPaintPropertyBinder : public PaintPropertyBinder, A> { public: using BaseAttribute = A; using BaseVertex = gl::detail::Vertex; using AttributeType = ZoomInterpolatedAttributeType; SourceFunctionPaintPropertyBinder(style::PropertyExpression expression_, T defaultValue_) : expression(std::move(expression_)), defaultValue(std::move(defaultValue_)) { } void setPatternParameters(const optional&, const optional&, CrossfadeParameters&) override {}; void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&, const optional&) override { auto evaluated = expression.evaluate(feature, defaultValue); this->statistics.add(evaluated); auto value = attributeValue(evaluated); for (std::size_t i = vertexVector.vertexSize(); i < length; ++i) { vertexVector.emplace_back(BaseVertex { value }); } } void upload(gl::Context& context) override { vertexBuffer = context.createVertexBuffer(std::move(vertexVector)); } std::tuple> attributeBinding(const PossiblyEvaluatedPropertyValue& currentValue) const override { if (currentValue.isConstant()) { return {}; } else { return std::tuple> { AttributeType::binding(*vertexBuffer, 0, BaseAttribute::Dimensions) }; } } std::tuple interpolationFactor(float) const override { return std::tuple { 0.0f }; } std::tuple uniformValue(const PossiblyEvaluatedPropertyValue& currentValue) const override { if (currentValue.isConstant()) { return std::tuple{ *currentValue.constant() }; } else { // Uniform values for vertex attribute arrays are unused. return {}; } } private: style::PropertyExpression expression; T defaultValue; gl::VertexVector vertexVector; optional> vertexBuffer; }; template class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder, A> { public: using AttributeType = ZoomInterpolatedAttributeType; using AttributeValue = typename AttributeType::Value; using Vertex = gl::detail::Vertex; CompositeFunctionPaintPropertyBinder(style::PropertyExpression expression_, float zoom, T defaultValue_) : expression(std::move(expression_)), defaultValue(std::move(defaultValue_)), zoomRange({zoom, zoom + 1}) { } void setPatternParameters(const optional&, const optional&, CrossfadeParameters&) override {}; void populateVertexVector(const GeometryTileFeature& feature, std::size_t length, const ImagePositions&, const optional&) override { Range range = expression.evaluate(zoomRange, feature, defaultValue); this->statistics.add(range.min); this->statistics.add(range.max); AttributeValue value = zoomInterpolatedAttributeValue( attributeValue(range.min), attributeValue(range.max)); for (std::size_t i = vertexVector.vertexSize(); i < length; ++i) { vertexVector.emplace_back(Vertex { value }); } } void upload(gl::Context& context) override { vertexBuffer = context.createVertexBuffer(std::move(vertexVector)); } std::tuple> attributeBinding(const PossiblyEvaluatedPropertyValue& currentValue) const override { if (currentValue.isConstant()) { return {}; } else { return std::tuple> { AttributeType::binding(*vertexBuffer, 0) }; } } std::tuple interpolationFactor(float currentZoom) const override { if (expression.useIntegerZoom) { return std::tuple { expression.interpolationFactor(zoomRange, std::floor(currentZoom)) }; } else { return std::tuple { expression.interpolationFactor(zoomRange, currentZoom) }; } } std::tuple uniformValue(const PossiblyEvaluatedPropertyValue& currentValue) const override { if (currentValue.isConstant()) { return std::tuple { *currentValue.constant() }; } else { // Uniform values for vertex attribute arrays are unused. return {}; } } private: style::PropertyExpression expression; T defaultValue; Range zoomRange; gl::VertexVector vertexVector; optional> vertexBuffer; }; template class CompositeCrossFadedPaintPropertyBinder : public PaintPropertyBinder, PossiblyEvaluatedPropertyValue>, A1, A2> { public: using AttributeType = ZoomInterpolatedAttributeType; using AttributeType2 = ZoomInterpolatedAttributeType; using BaseAttribute = A1; using BaseAttributeValue = typename BaseAttribute::Value; using BaseAttribute2 = A2; using BaseAttributeValue2 = typename BaseAttribute2::Value; using Vertex = gl::detail::Vertex; using Vertex2 = gl::detail::Vertex; CompositeCrossFadedPaintPropertyBinder(style::PropertyExpression expression_, float zoom, T defaultValue_) : expression(std::move(expression_)), defaultValue(std::move(defaultValue_)), zoomRange({zoom, zoom + 1}) { } void setPatternParameters(const optional&, const optional&, CrossfadeParameters& crossfade_) override { crossfade = crossfade_; }; void populateVertexVector(const GeometryTileFeature&, std::size_t length, const ImagePositions& patternPositions, const optional& patternDependencies) override { if (!patternDependencies) return; if (!patternPositions.empty()) { const auto min = patternPositions.find(patternDependencies->min); const auto mid = patternPositions.find(patternDependencies->mid); const auto max = patternPositions.find(patternDependencies->max); const auto end = patternPositions.end(); if (min == end || mid == end || max == end) return; const ImagePosition imageMin = min->second; const ImagePosition imageMid = mid->second; const ImagePosition imageMax = max->second; for (std::size_t i = zoomInVertexVector.vertexSize(); i < length; ++i) { patternToVertexVector.emplace_back(Vertex { imageMid.tlbr() }); zoomInVertexVector.emplace_back(Vertex2 { imageMin.tlbr() }); zoomOutVertexVector.emplace_back(Vertex2 { imageMax.tlbr() }); } } } void upload(gl::Context& context) override { patternToVertexBuffer = context.createVertexBuffer(std::move(patternToVertexVector)); zoomInVertexBuffer = context.createVertexBuffer(std::move(zoomInVertexVector)); zoomOutVertexBuffer = context.createVertexBuffer(std::move(zoomOutVertexVector)); } std::tuple, optional> attributeBinding(const PossiblyEvaluatedPropertyValue>& currentValue) const override { if (currentValue.isConstant()) { return {}; } else { return std::tuple, optional> { AttributeType::binding(*patternToVertexBuffer, 0, BaseAttribute::Dimensions), AttributeType2::binding( crossfade.fromScale == 2 ? *zoomInVertexBuffer : *zoomOutVertexBuffer, 0, BaseAttribute2::Dimensions) }; } } std::tuple interpolationFactor(float) const override { return std::tuple { 0.0f, 0.0f }; } std::tuple, std::array> uniformValue(const PossiblyEvaluatedPropertyValue>& ) const override { // Uniform values for vertex attribute arrays are unused. return {}; } private: style::PropertyExpression expression; T defaultValue; Range zoomRange; gl::VertexVector patternToVertexVector; gl::VertexVector zoomInVertexVector; gl::VertexVector zoomOutVertexVector; optional> patternToVertexBuffer; optional> zoomInVertexBuffer; optional> zoomOutVertexBuffer; CrossfadeParameters crossfade; }; template struct CreateBinder { template static std::unique_ptr> create(const PossiblyEvaluatedType& value, float zoom, T defaultValue) { return value.match( [&] (const T& constant) -> std::unique_ptr> { return std::make_unique>(constant); }, [&] (const style::PropertyExpression& expression) -> std::unique_ptr> { if (expression.isZoomConstant()) { return std::make_unique>(expression, defaultValue); } else { return std::make_unique>(expression, zoom, defaultValue); } } ); } }; template struct CreateBinder>> { template static std::unique_ptr, PossiblyEvaluatedPropertyValue>, A1, A2>> create(const PossiblyEvaluatedPropertyValue>& value, float zoom, T defaultValue) { return value.match( [&] (const Faded& constant) -> std::unique_ptr, PossiblyEvaluatedPropertyValue>, A1, A2>> { return std::make_unique>(constant); }, [&] (const style::PropertyExpression& expression) -> std::unique_ptr, PossiblyEvaluatedPropertyValue>, A1, A2>> { return std::make_unique>(expression, zoom, defaultValue); } ); } }; template std::unique_ptr> PaintPropertyBinder::create(const PossiblyEvaluatedType& value, float zoom, T defaultValue) { return CreateBinder::template create(value, zoom, defaultValue); } template struct ZoomInterpolatedAttribute { static auto name() { return Attr::name(); } using Type = ZoomInterpolatedAttributeType; }; template struct InterpolationUniform : gl::UniformScalar, float> { static auto name() { static const std::string name = Attr::name() + std::string("_t"); return name.c_str(); } }; template class PaintPropertyBinders; template class PaintPropertyBinders> { private: template struct Detail; template struct Detail> { using Binder = PaintPropertyBinder; using ZoomInterpolatedAttributeList = TypeList...>; using InterpolationUniformList = TypeList...>; }; template using Property = Detail; public: template using Binder = typename Property

::Binder; using Binders = IndexedTuple< TypeList, TypeList>...>>; template PaintPropertyBinders(const EvaluatedProperties& properties, float z) : binders(Binder::create(properties.template get(), z, Ps::defaultValue())...) { (void)z; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958 } PaintPropertyBinders(PaintPropertyBinders&&) = default; PaintPropertyBinders(const PaintPropertyBinders&) = delete; void populateVertexVectors(const GeometryTileFeature& feature, std::size_t length, const ImagePositions& patternPositions, const optional& patternDependencies) { util::ignore({ (binders.template get()->populateVertexVector(feature, length, patternPositions, patternDependencies), 0)... }); } void setPatternParameters(const optional& posA, const optional& posB, CrossfadeParameters& crossfade) const { util::ignore({ (binders.template get()->setPatternParameters(posA, posB, crossfade), 0)... }); } void upload(gl::Context& context) { util::ignore({ (binders.template get()->upload(context), 0)... }); } template using ZoomInterpolatedAttributeList = typename Property

::ZoomInterpolatedAttributeList; template using InterpolationUniformList = typename Property

::InterpolationUniformList; using Attributes = typename TypeListConcat...>::template ExpandInto; using AttributeBindings = typename Attributes::Bindings; template AttributeBindings attributeBindings(const EvaluatedProperties& currentProperties) const { return AttributeBindings { std::tuple_cat( binders.template get()->attributeBinding(currentProperties.template get())... ) }; } using Uniforms = typename TypeListConcat..., typename Ps::Uniforms...>::template ExpandInto; using UniformValues = typename Uniforms::Values; template UniformValues uniformValues(float currentZoom, EvaluatedProperties& currentProperties) const { (void)currentZoom; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958 return UniformValues ( std::tuple_cat( // interpolation uniform values binders.template get()->interpolationFactor(currentZoom)..., // uniform values binders.template get()->uniformValue(currentProperties.template get())...) ); } template const auto& statistics() const { return binders.template get

()->statistics; } using Bitset = std::bitset; template static Bitset constants(const EvaluatedProperties& currentProperties) { Bitset result; util::ignore({ result.set(TypeIndex::value, currentProperties.template get().isConstant())... }); return result; } template struct UniformDefines; template struct UniformDefines> { static void appendDefines(std::vector& defines) { util::ignore({ (defines.push_back(std::string("#define HAS_UNIFORM_") + Us::name()), 0)... }); } }; template static std::vector defines(const EvaluatedProperties& currentProperties) { std::vector result; util::ignore({ (currentProperties.template get().isConstant() ? UniformDefines::appendDefines(result) : (void) 0, 0)... }); return result; } private: Binders binders; }; } // namespace mbgl