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