#pragma once #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::Attribute; 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: using Attribute = ZoomInterpolatedAttributeType; using AttributeBinding = typename Attribute::Binding; virtual ~PaintPropertyBinder() = default; virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) = 0; virtual void upload(gl::Context& context) = 0; virtual AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue& currentValue) const = 0; virtual float interpolationFactor(float currentZoom) const = 0; virtual T uniformValue(const PossiblyEvaluatedPropertyValue& currentValue) const = 0; static std::unique_ptr create(const PossiblyEvaluatedPropertyValue& value, float zoom, T defaultValue); PaintPropertyStatistics statistics; }; template class ConstantPaintPropertyBinder : public PaintPropertyBinder { public: using Attribute = ZoomInterpolatedAttributeType; using AttributeBinding = typename Attribute::Binding; ConstantPaintPropertyBinder(T constant_) : constant(std::move(constant_)) { } void populateVertexVector(const GeometryTileFeature&, std::size_t) override {} void upload(gl::Context&) override {} AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue&) const override { return gl::DisabledAttribute(); } float interpolationFactor(float) const override { return 0.0f; } T uniformValue(const PossiblyEvaluatedPropertyValue& currentValue) const override { return currentValue.constantOr(constant); } private: T constant; }; template class SourceFunctionPaintPropertyBinder : public PaintPropertyBinder { public: using BaseAttribute = A; using BaseAttributeValue = typename BaseAttribute::Value; using BaseVertex = gl::detail::Vertex; using Attribute = ZoomInterpolatedAttributeType; using AttributeBinding = typename Attribute::Binding; SourceFunctionPaintPropertyBinder(style::SourceFunction function_, T defaultValue_) : function(std::move(function_)), defaultValue(std::move(defaultValue_)) { } void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override { auto evaluated = function.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)); } AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue& currentValue) const override { if (currentValue.isConstant()) { return gl::DisabledAttribute(); } else { return Attribute::binding(*vertexBuffer, 0, BaseAttribute::Dimensions); } } float interpolationFactor(float) const override { return 0.0f; } T uniformValue(const PossiblyEvaluatedPropertyValue& currentValue) const override { if (currentValue.isConstant()) { return *currentValue.constant(); } else { // Uniform values for vertex attribute arrays are unused. return {}; } } private: style::SourceFunction function; T defaultValue; gl::VertexVector vertexVector; optional> vertexBuffer; }; template class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder { public: using BaseAttribute = A; using BaseAttributeValue = typename BaseAttribute::Value; using Attribute = ZoomInterpolatedAttributeType; using AttributeValue = typename Attribute::Value; using AttributeBinding = typename Attribute::Binding; using Vertex = gl::detail::Vertex; CompositeFunctionPaintPropertyBinder(style::CompositeFunction function_, float zoom, T defaultValue_) : function(std::move(function_)), defaultValue(std::move(defaultValue_)), rangeOfCoveringRanges(function.rangeOfCoveringRanges({zoom, zoom + 1})) { } void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override { Range range = function.evaluate(rangeOfCoveringRanges, 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)); } AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue& currentValue) const override { if (currentValue.isConstant()) { return gl::DisabledAttribute(); } else { return Attribute::binding(*vertexBuffer, 0); } } float interpolationFactor(float currentZoom) const override { if (function.useIntegerZoom) { return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, std::floor(currentZoom)); } else { return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom); } } T uniformValue(const PossiblyEvaluatedPropertyValue& currentValue) const override { if (currentValue.isConstant()) { return *currentValue.constant(); } else { // Uniform values for vertex attribute arrays are unused. return {}; } } private: style::CompositeFunction function; T defaultValue; using CoveringRanges = typename style::CompositeFunction::CoveringRanges; Range rangeOfCoveringRanges; gl::VertexVector vertexVector; optional> vertexBuffer; }; template std::unique_ptr> PaintPropertyBinder::create(const PossiblyEvaluatedPropertyValue& value, float zoom, T defaultValue) { return value.match( [&] (const T& constant) -> std::unique_ptr> { return std::make_unique>(constant); }, [&] (const style::SourceFunction& function) { return std::make_unique>(function, defaultValue); }, [&] (const style::CompositeFunction& function) { return std::make_unique>(function, 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> { public: template using Binder = PaintPropertyBinder; 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) { util::ignore({ (binders.template get()->populateVertexVector(feature, length), 0)... }); } void upload(gl::Context& context) { util::ignore({ (binders.template get()->upload(context), 0)... }); } template using Attribute = ZoomInterpolatedAttribute; using Attributes = gl::Attributes...>; using AttributeBindings = typename Attributes::Bindings; template AttributeBindings attributeBindings(const EvaluatedProperties& currentProperties) const { return AttributeBindings { binders.template get()->attributeBinding(currentProperties.template get())... }; } using Uniforms = gl::Uniforms..., typename Ps::Uniform...>; using UniformValues = typename Uniforms::Values; template UniformValues uniformValues(float currentZoom, const EvaluatedProperties& currentProperties) const { (void)currentZoom; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958 return UniformValues { typename InterpolationUniform::Value { binders.template get()->interpolationFactor(currentZoom) }..., typename Ps::Uniform::Value { 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 static std::vector defines(const EvaluatedProperties& currentProperties) { std::vector result; util::ignore({ (result.push_back(currentProperties.template get().isConstant() ? std::string("#define HAS_UNIFORM_") + Ps::Uniform::name() : std::string()), 0)... }); return result; } private: Binders binders; }; } // namespace mbgl