summaryrefslogtreecommitdiff
path: root/src/mbgl/renderer/paint_property_binder.hpp
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2017-04-27 14:33:00 -0700
committerJohn Firebaugh <john.firebaugh@gmail.com>2017-05-03 08:30:17 -0700
commitd5f4d0f05fcb8490984649d78d6c026f87a77f4e (patch)
treedea3b08a7c1d6f29a0f75d37cb2ee54a2429e6b4 /src/mbgl/renderer/paint_property_binder.hpp
parent6c7730ee7c2d9e63df526965bb511b2f43ee82b7 (diff)
downloadqtlocation-mapboxgl-d5f4d0f05fcb8490984649d78d6c026f87a77f4e.tar.gz
[core] Move render-related sources out of style directory/namespace
Moves the following to the renderer directory and out of the style namespace: * CascadeParameters * PropertyEvaluationParameters * UpdateParameters * PropertyEvaluator * DataDrivenPropertyEvaluator * CrossFadedPropertyEvaluator * PaintPropertyBinder * PaintProperyStatistics * PossiblyEvaluatedPropertyValue * TransitioningLight * EvaluatedLight
Diffstat (limited to 'src/mbgl/renderer/paint_property_binder.hpp')
-rw-r--r--src/mbgl/renderer/paint_property_binder.hpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp
new file mode 100644
index 0000000000..3e53bcc884
--- /dev/null
+++ b/src/mbgl/renderer/paint_property_binder.hpp
@@ -0,0 +1,330 @@
+#pragma once
+
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/gl/attribute.hpp>
+#include <mbgl/gl/uniform.hpp>
+#include <mbgl/util/type_list.hpp>
+#include <mbgl/renderer/paint_property_statistics.hpp>
+
+namespace mbgl {
+
+/*
+ ZoomInterpolatedAttribute<Attr> 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 <class A>
+using ZoomInterpolatedAttributeType = gl::Attribute<typename A::ValueType, A::Dimensions * 2>;
+
+inline std::array<float, 1> 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<float, 2> attributeValue(const Color& color) {
+ return {{
+ static_cast<float>(mbgl::attributes::packUint8Pair(255 * color.r, 255 * color.g)),
+ static_cast<float>(mbgl::attributes::packUint8Pair(255 * color.b, 255 * color.a))
+ }};
+}
+
+template <size_t N>
+std::array<float, N*2> zoomInterpolatedAttributeValue(const std::array<float, N>& min, const std::array<float, N>& max) {
+ std::array<float, N*2> 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 can instead use a constant attribute binding
+ via the `glVertexAttrib*` family of functions.
+ * 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 is the same regardless of the strategy used to bind
+ the attribute -- in all cases the attribute is declared as a vec2, in order to
+ support composite min and max values (color attributes use a vec4 with special
+ packing). When the constant or source function strategies are used, the
+ interpolation uniform value is set to zero, and the second attribute element is
+ unused. This differs from the GL JS implementation, which dynamically generates
+ shader source based on the strategy used. We found that in WebGL, using
+ `glVertexAttrib*` was unnacceptably slow. Additionally, in GL Native we have
+ implemented binary shader caching, which works better if the shaders are constant.
+*/
+template <class T, class A>
+class PaintPropertyBinder {
+public:
+ using Attribute = ZoomInterpolatedAttributeType<A>;
+ 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<T>& currentValue) const = 0;
+ virtual float interpolationFactor(float currentZoom) const = 0;
+
+ static std::unique_ptr<PaintPropertyBinder> create(const PossiblyEvaluatedPropertyValue<T>& value, float zoom, T defaultValue);
+
+ PaintPropertyStatistics<T> statistics;
+};
+
+template <class T, class A>
+class ConstantPaintPropertyBinder : public PaintPropertyBinder<T, A> {
+public:
+ using Attribute = ZoomInterpolatedAttributeType<A>;
+ 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<T>& currentValue) const override {
+ auto value = attributeValue(currentValue.constantOr(constant));
+ return typename Attribute::ConstantBinding {
+ zoomInterpolatedAttributeValue(value, value)
+ };
+ }
+
+ float interpolationFactor(float) const override {
+ return 0.0f;
+ }
+
+private:
+ T constant;
+};
+
+template <class T, class A>
+class SourceFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> {
+public:
+ using BaseAttribute = A;
+ using BaseAttributeValue = typename BaseAttribute::Value;
+ using BaseVertex = gl::detail::Vertex<BaseAttribute>;
+
+ using Attribute = ZoomInterpolatedAttributeType<A>;
+ using AttributeBinding = typename Attribute::Binding;
+
+ SourceFunctionPaintPropertyBinder(style::SourceFunction<T> 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<T>& currentValue) const override {
+ if (currentValue.isConstant()) {
+ BaseAttributeValue value = attributeValue(*currentValue.constant());
+ return typename Attribute::ConstantBinding {
+ zoomInterpolatedAttributeValue(value, value)
+ };
+ } else {
+ return Attribute::variableBinding(*vertexBuffer, 0, BaseAttribute::Dimensions);
+ }
+ }
+
+ float interpolationFactor(float) const override {
+ return 0.0f;
+ }
+
+private:
+ style::SourceFunction<T> function;
+ T defaultValue;
+ gl::VertexVector<BaseVertex> vertexVector;
+ optional<gl::VertexBuffer<BaseVertex>> vertexBuffer;
+};
+
+template <class T, class A>
+class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> {
+public:
+ using BaseAttribute = A;
+ using BaseAttributeValue = typename BaseAttribute::Value;
+
+ using Attribute = ZoomInterpolatedAttributeType<A>;
+ using AttributeValue = typename Attribute::Value;
+ using AttributeBinding = typename Attribute::Binding;
+ using Vertex = gl::detail::Vertex<Attribute>;
+
+ CompositeFunctionPaintPropertyBinder(style::CompositeFunction<T> function_, float zoom, T defaultValue_)
+ : function(std::move(function_)),
+ defaultValue(std::move(defaultValue_)),
+ coveringRanges(function.coveringRanges(zoom)) {
+ }
+
+ void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override {
+ Range<T> range = function.evaluate(std::get<1>(coveringRanges), 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<T>& currentValue) const override {
+ if (currentValue.isConstant()) {
+ BaseAttributeValue value = attributeValue(*currentValue.constant());
+ return typename Attribute::ConstantBinding {
+ zoomInterpolatedAttributeValue(value, value)
+ };
+ } else {
+ return Attribute::variableBinding(*vertexBuffer, 0);
+ }
+ }
+
+ float interpolationFactor(float currentZoom) const override {
+ return util::interpolationFactor(1.0f, std::get<0>(coveringRanges), currentZoom);
+ }
+
+private:
+ using InnerStops = typename style::CompositeFunction<T>::InnerStops;
+ style::CompositeFunction<T> function;
+ T defaultValue;
+ std::tuple<Range<float>, Range<InnerStops>> coveringRanges;
+ gl::VertexVector<Vertex> vertexVector;
+ optional<gl::VertexBuffer<Vertex>> vertexBuffer;
+};
+
+template <class T, class A>
+std::unique_ptr<PaintPropertyBinder<T, A>>
+PaintPropertyBinder<T, A>::create(const PossiblyEvaluatedPropertyValue<T>& value, float zoom, T defaultValue) {
+ return value.match(
+ [&] (const T& constant) -> std::unique_ptr<PaintPropertyBinder<T, A>> {
+ return std::make_unique<ConstantPaintPropertyBinder<T, A>>(constant);
+ },
+ [&] (const style::SourceFunction<T>& function) {
+ return std::make_unique<SourceFunctionPaintPropertyBinder<T, A>>(function, defaultValue);
+ },
+ [&] (const style::CompositeFunction<T>& function) {
+ return std::make_unique<CompositeFunctionPaintPropertyBinder<T, A>>(function, zoom, defaultValue);
+ }
+ );
+}
+
+template <class Attr>
+struct ZoomInterpolatedAttribute {
+ static auto name() { return Attr::name(); }
+ using Type = ZoomInterpolatedAttributeType<typename Attr::Type>;
+};
+
+template <class Attr>
+struct InterpolationUniform : gl::UniformScalar<InterpolationUniform<Attr>, float> {
+ static auto name() {
+ static const std::string name = Attr::name() + std::string("_t");
+ return name.c_str();
+ }
+};
+
+template <class Ps>
+class PaintPropertyBinders;
+
+template <class... Ps>
+class PaintPropertyBinders<TypeList<Ps...>> {
+public:
+ template <class P>
+ using Binder = PaintPropertyBinder<typename P::Type, typename P::Attribute::Type>;
+
+ using Binders = IndexedTuple<
+ TypeList<Ps...>,
+ TypeList<std::unique_ptr<Binder<Ps>>...>>;
+
+ template <class EvaluatedProperties>
+ PaintPropertyBinders(const EvaluatedProperties& properties, float z)
+ : binders(Binder<Ps>::create(properties.template get<Ps>(), 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<Ps>()->populateVertexVector(feature, length), 0)...
+ });
+ }
+
+ void upload(gl::Context& context) {
+ util::ignore({
+ (binders.template get<Ps>()->upload(context), 0)...
+ });
+ }
+
+ template <class P>
+ using Attribute = ZoomInterpolatedAttribute<typename P::Attribute>;
+
+ using Attributes = gl::Attributes<Attribute<Ps>...>;
+ using AttributeBindings = typename Attributes::Bindings;
+
+ template <class EvaluatedProperties>
+ AttributeBindings attributeBindings(const EvaluatedProperties& currentProperties) const {
+ return AttributeBindings {
+ binders.template get<Ps>()->attributeBinding(currentProperties.template get<Ps>())...
+ };
+ }
+
+ using Uniforms = gl::Uniforms<InterpolationUniform<typename Ps::Attribute>...>;
+ using UniformValues = typename Uniforms::Values;
+
+ UniformValues uniformValues(float currentZoom) const {
+ (void)currentZoom; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958
+ return UniformValues {
+ typename InterpolationUniform<typename Ps::Attribute>::Value {
+ binders.template get<Ps>()->interpolationFactor(currentZoom)
+ }...
+ };
+ }
+
+ template <class P>
+ const auto& statistics() const {
+ return binders.template get<P>()->statistics;
+ }
+
+private:
+ Binders binders;
+};
+
+} // namespace mbgl