1 #pragma once
2 
3 #include <mbgl/programs/attributes.hpp>
4 #include <mbgl/gl/attribute.hpp>
5 #include <mbgl/gl/uniform.hpp>
6 #include <mbgl/gl/context.hpp>
7 #include <mbgl/util/type_list.hpp>
8 #include <mbgl/renderer/possibly_evaluated_property_value.hpp>
9 #include <mbgl/renderer/paint_property_statistics.hpp>
10 
11 #include <bitset>
12 
13 namespace mbgl {
14 
15 /*
16    ZoomInterpolatedAttribute<Attr> is a 'compound' attribute, representing two values of the
17    the base attribute Attr.  These two values are provided to the shader to allow interpolation
18    between zoom levels, without the need to repopulate vertex buffers each frame as the map is
19    being zoomed.
20 */
21 template <class A>
22 using ZoomInterpolatedAttributeType = gl::Attribute<typename A::ValueType, A::Dimensions * 2>;
23 
attributeValue(float v)24 inline std::array<float, 1> attributeValue(float v) {
25     return {{ v }};
26 }
27 
28 /*
29     Encode a four-component color value into a pair of floats.  Since csscolorparser
30     uses 8-bit precision for each color component, for each float we use the upper 8
31     bits for one component (e.g. (color.r * 255) * 256), and the lower 8 for another.
32 
33     Also note that colors come in as floats 0..1, so we scale by 255.
34 */
attributeValue(const Color & color)35 inline std::array<float, 2> attributeValue(const Color& color) {
36     return {{
37         static_cast<float>(mbgl::attributes::packUint8Pair(255 * color.r, 255 * color.g)),
38         static_cast<float>(mbgl::attributes::packUint8Pair(255 * color.b, 255 * color.a))
39     }};
40 }
41 
42 template <size_t N>
zoomInterpolatedAttributeValue(const std::array<float,N> & min,const std::array<float,N> & max)43 std::array<float, N*2> zoomInterpolatedAttributeValue(const std::array<float, N>& min, const std::array<float, N>& max) {
44     std::array<float, N*2> result;
45     for (size_t i = 0; i < N; i++) {
46         result[i]   = min[i];
47         result[i+N] = max[i];
48     }
49     return result;
50 }
51 
52 /*
53    PaintPropertyBinder is an abstract class serving as the interface definition for
54    the strategy used for constructing, uploading, and binding paint property data as
55    GLSL attributes.
56 
57    It has three concrete subclasses, one for each of the three strategies we use:
58 
59    * For _constant_ properties -- those whose value is a constant, or the constant
60      result of evaluating a camera function at a particular camera position -- we
61      don't need a vertex buffer, and instead use a uniform.
62    * For source functions, we use a vertex buffer with a single attribute value,
63      the evaluated result of the source function for the given feature.
64    * For composite functions, we use a vertex buffer with two attributes: min and
65      max values covering the range of zooms at which we expect the tile to be
66      displayed. These values are calculated by evaluating the composite function for
67      the given feature at strategically chosen zoom levels. In addition to this
68      attribute data, we also use a uniform value which the shader uses to interpolate
69      between the min and max value at the final displayed zoom level. The use of a
70      uniform allows us to cheaply update the value on every frame.
71 
72    Note that the shader source varies depending on whether we're using a uniform or
73    attribute. Like GL JS, we dynamically compile shaders at runtime to accomodate this.
74 */
75 template <class T, class A>
76 class PaintPropertyBinder {
77 public:
78     using Attribute = ZoomInterpolatedAttributeType<A>;
79     using AttributeBinding = typename Attribute::Binding;
80 
81     virtual ~PaintPropertyBinder() = default;
82 
83     virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) = 0;
84     virtual void upload(gl::Context& context) = 0;
85     virtual optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0;
86     virtual float interpolationFactor(float currentZoom) const = 0;
87     virtual T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0;
88 
89     static std::unique_ptr<PaintPropertyBinder> create(const PossiblyEvaluatedPropertyValue<T>& value, float zoom, T defaultValue);
90 
91     PaintPropertyStatistics<T> statistics;
92 };
93 
94 template <class T, class A>
95 class ConstantPaintPropertyBinder : public PaintPropertyBinder<T, A> {
96 public:
97     using Attribute = ZoomInterpolatedAttributeType<A>;
98     using AttributeBinding = typename Attribute::Binding;
99 
ConstantPaintPropertyBinder(T constant_)100     ConstantPaintPropertyBinder(T constant_)
101         : constant(std::move(constant_)) {
102     }
103 
populateVertexVector(const GeometryTileFeature &,std::size_t)104     void populateVertexVector(const GeometryTileFeature&, std::size_t) override {}
upload(gl::Context &)105     void upload(gl::Context&) override {}
106 
attributeBinding(const PossiblyEvaluatedPropertyValue<T> &) const107     optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override {
108         return {};
109     }
110 
interpolationFactor(float) const111     float interpolationFactor(float) const override {
112         return 0.0f;
113     }
114 
uniformValue(const PossiblyEvaluatedPropertyValue<T> & currentValue) const115     T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
116         return currentValue.constantOr(constant);
117     }
118 
119 private:
120     T constant;
121 };
122 
123 template <class T, class A>
124 class SourceFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> {
125 public:
126     using BaseAttribute = A;
127     using BaseAttributeValue = typename BaseAttribute::Value;
128     using BaseVertex = gl::detail::Vertex<BaseAttribute>;
129 
130     using Attribute = ZoomInterpolatedAttributeType<A>;
131     using AttributeBinding = typename Attribute::Binding;
132 
SourceFunctionPaintPropertyBinder(style::PropertyExpression<T> expression_,T defaultValue_)133     SourceFunctionPaintPropertyBinder(style::PropertyExpression<T> expression_, T defaultValue_)
134         : expression(std::move(expression_)),
135           defaultValue(std::move(defaultValue_)) {
136     }
137 
populateVertexVector(const GeometryTileFeature & feature,std::size_t length)138     void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override {
139         auto evaluated = expression.evaluate(feature, defaultValue);
140         this->statistics.add(evaluated);
141         auto value = attributeValue(evaluated);
142         for (std::size_t i = vertexVector.vertexSize(); i < length; ++i) {
143             vertexVector.emplace_back(BaseVertex { value });
144         }
145     }
146 
upload(gl::Context & context)147     void upload(gl::Context& context) override {
148         vertexBuffer = context.createVertexBuffer(std::move(vertexVector));
149     }
150 
attributeBinding(const PossiblyEvaluatedPropertyValue<T> & currentValue) const151     optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
152         if (currentValue.isConstant()) {
153             return {};
154         } else {
155             return Attribute::binding(*vertexBuffer, 0, BaseAttribute::Dimensions);
156         }
157     }
158 
interpolationFactor(float) const159     float interpolationFactor(float) const override {
160         return 0.0f;
161     }
162 
uniformValue(const PossiblyEvaluatedPropertyValue<T> & currentValue) const163     T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
164         if (currentValue.isConstant()) {
165             return *currentValue.constant();
166         } else {
167             // Uniform values for vertex attribute arrays are unused.
168             return {};
169         }
170     }
171 
172 private:
173     style::PropertyExpression<T> expression;
174     T defaultValue;
175     gl::VertexVector<BaseVertex> vertexVector;
176     optional<gl::VertexBuffer<BaseVertex>> vertexBuffer;
177 };
178 
179 template <class T, class A>
180 class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> {
181 public:
182     using BaseAttribute = A;
183     using BaseAttributeValue = typename BaseAttribute::Value;
184 
185     using Attribute = ZoomInterpolatedAttributeType<A>;
186     using AttributeValue = typename Attribute::Value;
187     using AttributeBinding = typename Attribute::Binding;
188     using Vertex = gl::detail::Vertex<Attribute>;
189 
CompositeFunctionPaintPropertyBinder(style::PropertyExpression<T> expression_,float zoom,T defaultValue_)190     CompositeFunctionPaintPropertyBinder(style::PropertyExpression<T> expression_, float zoom, T defaultValue_)
191         : expression(std::move(expression_)),
192           defaultValue(std::move(defaultValue_)),
193           zoomRange({zoom, zoom + 1}) {
194     }
195 
populateVertexVector(const GeometryTileFeature & feature,std::size_t length)196     void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override {
197         Range<T> range = expression.evaluate(zoomRange, feature, defaultValue);
198         this->statistics.add(range.min);
199         this->statistics.add(range.max);
200         AttributeValue value = zoomInterpolatedAttributeValue(
201             attributeValue(range.min),
202             attributeValue(range.max));
203         for (std::size_t i = vertexVector.vertexSize(); i < length; ++i) {
204             vertexVector.emplace_back(Vertex { value });
205         }
206     }
207 
upload(gl::Context & context)208     void upload(gl::Context& context) override {
209         vertexBuffer = context.createVertexBuffer(std::move(vertexVector));
210     }
211 
attributeBinding(const PossiblyEvaluatedPropertyValue<T> & currentValue) const212     optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
213         if (currentValue.isConstant()) {
214             return {};
215         } else {
216             return Attribute::binding(*vertexBuffer, 0);
217         }
218     }
219 
interpolationFactor(float currentZoom) const220     float interpolationFactor(float currentZoom) const override {
221         if (expression.useIntegerZoom) {
222             return expression.interpolationFactor(zoomRange, std::floor(currentZoom));
223         } else {
224             return expression.interpolationFactor(zoomRange, currentZoom);
225         }
226     }
227 
uniformValue(const PossiblyEvaluatedPropertyValue<T> & currentValue) const228     T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
229         if (currentValue.isConstant()) {
230             return *currentValue.constant();
231         } else {
232             // Uniform values for vertex attribute arrays are unused.
233             return {};
234         }
235     }
236 
237 private:
238     style::PropertyExpression<T> expression;
239     T defaultValue;
240     Range<float> zoomRange;
241     gl::VertexVector<Vertex> vertexVector;
242     optional<gl::VertexBuffer<Vertex>> vertexBuffer;
243 };
244 
245 template <class T, class A>
246 std::unique_ptr<PaintPropertyBinder<T, A>>
create(const PossiblyEvaluatedPropertyValue<T> & value,float zoom,T defaultValue)247 PaintPropertyBinder<T, A>::create(const PossiblyEvaluatedPropertyValue<T>& value, float zoom, T defaultValue) {
248     return value.match(
249         [&] (const T& constant) -> std::unique_ptr<PaintPropertyBinder<T, A>> {
250             return std::make_unique<ConstantPaintPropertyBinder<T, A>>(constant);
251         },
252         [&] (const style::PropertyExpression<T>& expression) -> std::unique_ptr<PaintPropertyBinder<T, A>> {
253             if (expression.isZoomConstant()) {
254                 return std::make_unique<SourceFunctionPaintPropertyBinder<T, A>>(expression, defaultValue);
255             } else {
256                 return std::make_unique<CompositeFunctionPaintPropertyBinder<T, A>>(expression, zoom, defaultValue);
257             }
258         }
259     );
260 }
261 
262 template <class Attr>
263 struct ZoomInterpolatedAttribute {
namembgl::ZoomInterpolatedAttribute264     static auto name() { return Attr::name(); }
265     using Type = ZoomInterpolatedAttributeType<typename Attr::Type>;
266 };
267 
268 template <class Attr>
269 struct InterpolationUniform : gl::UniformScalar<InterpolationUniform<Attr>, float> {
namembgl::InterpolationUniform270     static auto name() {
271         static const std::string name = Attr::name() + std::string("_t");
272         return name.c_str();
273     }
274 };
275 
276 template <class Ps>
277 class PaintPropertyBinders;
278 
279 template <class... Ps>
280 class PaintPropertyBinders<TypeList<Ps...>> {
281 public:
282     template <class P>
283     using Binder = PaintPropertyBinder<typename P::Type, typename P::Attribute::Type>;
284 
285     using Binders = IndexedTuple<
286         TypeList<Ps...>,
287         TypeList<std::unique_ptr<Binder<Ps>>...>>;
288 
289     template <class EvaluatedProperties>
PaintPropertyBinders(const EvaluatedProperties & properties,float z)290     PaintPropertyBinders(const EvaluatedProperties& properties, float z)
291         : binders(Binder<Ps>::create(properties.template get<Ps>(), z, Ps::defaultValue())...) {
292         (void)z; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958
293     }
294 
295     PaintPropertyBinders(PaintPropertyBinders&&) = default;
296     PaintPropertyBinders(const PaintPropertyBinders&) = delete;
297 
populateVertexVectors(const GeometryTileFeature & feature,std::size_t length)298     void populateVertexVectors(const GeometryTileFeature& feature, std::size_t length) {
299         util::ignore({
300             (binders.template get<Ps>()->populateVertexVector(feature, length), 0)...
301         });
302     }
303 
upload(gl::Context & context)304     void upload(gl::Context& context) {
305         util::ignore({
306             (binders.template get<Ps>()->upload(context), 0)...
307         });
308     }
309 
310     template <class P>
311     using Attribute = ZoomInterpolatedAttribute<typename P::Attribute>;
312 
313     using Attributes = gl::Attributes<Attribute<Ps>...>;
314     using AttributeBindings = typename Attributes::Bindings;
315 
316     template <class EvaluatedProperties>
attributeBindings(const EvaluatedProperties & currentProperties) const317     AttributeBindings attributeBindings(const EvaluatedProperties& currentProperties) const {
318         return AttributeBindings {
319             binders.template get<Ps>()->attributeBinding(currentProperties.template get<Ps>())...
320         };
321     }
322 
323     using Uniforms = gl::Uniforms<InterpolationUniform<typename Ps::Attribute>..., typename Ps::Uniform...>;
324     using UniformValues = typename Uniforms::Values;
325 
326     template <class EvaluatedProperties>
uniformValues(float currentZoom,const EvaluatedProperties & currentProperties) const327     UniformValues uniformValues(float currentZoom, const EvaluatedProperties& currentProperties) const {
328         (void)currentZoom; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958
329         return UniformValues {
330             typename InterpolationUniform<typename Ps::Attribute>::Value {
331                 binders.template get<Ps>()->interpolationFactor(currentZoom)
332             }...,
333             typename Ps::Uniform::Value {
334                 binders.template get<Ps>()->uniformValue(currentProperties.template get<Ps>())
335             }...
336         };
337     }
338 
339     template <class P>
statistics() const340     const auto& statistics() const {
341         return binders.template get<P>()->statistics;
342     }
343 
344 
345     using Bitset = std::bitset<sizeof...(Ps)>;
346 
347     template <class EvaluatedProperties>
constants(const EvaluatedProperties & currentProperties)348     static Bitset constants(const EvaluatedProperties& currentProperties) {
349         Bitset result;
350         util::ignore({
351             result.set(TypeIndex<Ps, Ps...>::value,
352                        currentProperties.template get<Ps>().isConstant())...
353         });
354         return result;
355     }
356 
357     template <class EvaluatedProperties>
defines(const EvaluatedProperties & currentProperties)358     static std::vector<std::string> defines(const EvaluatedProperties& currentProperties) {
359         std::vector<std::string> result;
360         util::ignore({
361             (result.push_back(currentProperties.template get<Ps>().isConstant()
362                 ? std::string("#define HAS_UNIFORM_") + Ps::Uniform::name()
363                 : std::string()), 0)...
364         });
365         return result;
366     }
367 
368 private:
369     Binders binders;
370 };
371 
372 } // namespace mbgl
373