1 #pragma once 2 3 #include <mbgl/gl/context.hpp> 4 #include <mbgl/gl/program.hpp> 5 #include <mbgl/math/clamp.hpp> 6 #include <mbgl/util/interpolate.hpp> 7 8 #include <mbgl/programs/attributes.hpp> 9 #include <mbgl/programs/uniforms.hpp> 10 #include <mbgl/programs/segment.hpp> 11 #include <mbgl/shaders/symbol_icon.hpp> 12 #include <mbgl/shaders/symbol_sdf.hpp> 13 #include <mbgl/util/geometry.hpp> 14 #include <mbgl/util/size.hpp> 15 #include <mbgl/style/layers/symbol_layer_properties.hpp> 16 #include <mbgl/style/layers/symbol_layer_impl.hpp> 17 #include <mbgl/renderer/layers/render_symbol_layer.hpp> 18 19 20 #include <cmath> 21 #include <array> 22 23 namespace mbgl { 24 25 namespace style { 26 class SymbolPropertyValues; 27 } // namespace style 28 29 class RenderTile; 30 class TransformState; 31 32 namespace uniforms { 33 MBGL_DEFINE_UNIFORM_MATRIX(double, 4, u_gl_coord_matrix); 34 MBGL_DEFINE_UNIFORM_MATRIX(double, 4, u_label_plane_matrix); 35 MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture); 36 MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo); 37 MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale); 38 39 MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_text); 40 MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_zoom_constant); 41 MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_feature_constant); 42 MBGL_DEFINE_UNIFORM_SCALAR(float, u_size_t); 43 MBGL_DEFINE_UNIFORM_SCALAR(float, u_size); 44 MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_symbol); 45 MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); 46 } // namespace uniforms 47 48 struct SymbolLayoutAttributes : gl::Attributes< 49 attributes::a_pos_offset, 50 attributes::a_data<uint16_t, 4>> 51 { vertexmbgl::SymbolLayoutAttributes52 static Vertex vertex(Point<float> labelAnchor, 53 Point<float> o, 54 float glyphOffsetY, 55 uint16_t tx, 56 uint16_t ty, 57 const Range<float>& sizeData) { 58 return Vertex { 59 // combining pos and offset to reduce number of vertex attributes passed to shader (8 max for some devices) 60 {{ 61 static_cast<int16_t>(labelAnchor.x), 62 static_cast<int16_t>(labelAnchor.y), 63 static_cast<int16_t>(::round(o.x * 32)), // use 1/32 pixels for placement 64 static_cast<int16_t>(::round((o.y + glyphOffsetY) * 32)) 65 }}, 66 {{ 67 tx, 68 ty, 69 static_cast<uint16_t>(sizeData.min * 10), 70 static_cast<uint16_t>(sizeData.max * 10) 71 }} 72 }; 73 } 74 }; 75 76 struct SymbolDynamicLayoutAttributes : gl::Attributes<attributes::a_projected_pos> { vertexmbgl::SymbolDynamicLayoutAttributes77 static Vertex vertex(Point<float> anchorPoint, float labelAngle) { 78 return Vertex { 79 {{ 80 anchorPoint.x, 81 anchorPoint.y, 82 labelAngle 83 }} 84 }; 85 } 86 }; 87 88 struct SymbolOpacityAttributes : gl::Attributes<attributes::a_fade_opacity> { vertexmbgl::SymbolOpacityAttributes89 static Vertex vertex(bool placed, float opacity) { 90 return Vertex { 91 {{ static_cast<uint8_t>((static_cast<uint8_t>(opacity * 127) << 1) | static_cast<uint8_t>(placed)) }} 92 }; 93 } 94 }; 95 96 struct ZoomEvaluatedSize { 97 bool isZoomConstant; 98 bool isFeatureConstant; 99 float sizeT; 100 float size; 101 float layoutSize; 102 }; 103 // Mimic the PaintPropertyBinder technique specifically for the {text,icon}-size layout properties 104 // in order to provide a 'custom' scheme for encoding the necessary attribute data. As with 105 // PaintPropertyBinder, SymbolSizeBinder is an abstract class whose implementations handle the 106 // particular attribute & uniform logic needed by each possible type of the {Text,Icon}Size properties. 107 class SymbolSizeBinder { 108 public: 109 virtual ~SymbolSizeBinder() = default; 110 111 using Uniforms = gl::Uniforms< 112 uniforms::u_is_size_zoom_constant, 113 uniforms::u_is_size_feature_constant, 114 uniforms::u_size_t, 115 uniforms::u_size>; 116 using UniformValues = Uniforms::Values; 117 118 static std::unique_ptr<SymbolSizeBinder> create(const float tileZoom, 119 const style::DataDrivenPropertyValue<float>& sizeProperty, 120 const float defaultValue); 121 122 virtual Range<float> getVertexSizeData(const GeometryTileFeature& feature) = 0; 123 virtual ZoomEvaluatedSize evaluateForZoom(float currentZoom) const = 0; 124 uniformValues(float currentZoom) const125 UniformValues uniformValues(float currentZoom) const { 126 const ZoomEvaluatedSize u = evaluateForZoom(currentZoom); 127 return UniformValues { 128 uniforms::u_is_size_zoom_constant::Value{ u.isZoomConstant }, 129 uniforms::u_is_size_feature_constant::Value{ u.isFeatureConstant}, 130 uniforms::u_size_t::Value{ u.sizeT }, 131 uniforms::u_size::Value{ u.size } 132 }; 133 } 134 }; 135 136 137 class ConstantSymbolSizeBinder final : public SymbolSizeBinder { 138 public: ConstantSymbolSizeBinder(const float,const float & size,const float)139 ConstantSymbolSizeBinder(const float /*tileZoom*/, const float& size, const float /*defaultValue*/) 140 : layoutSize(size) {} 141 ConstantSymbolSizeBinder(const float,const style::Undefined &,const float defaultValue)142 ConstantSymbolSizeBinder(const float /*tileZoom*/, const style::Undefined&, const float defaultValue) 143 : layoutSize(defaultValue) {} 144 ConstantSymbolSizeBinder(const float tileZoom,const style::PropertyExpression<float> & expression_,const float)145 ConstantSymbolSizeBinder(const float tileZoom, const style::PropertyExpression<float>& expression_, const float /*defaultValue*/) 146 : layoutSize(expression_.evaluate(tileZoom + 1)), 147 expression(expression_) { 148 const Range<float> zoomLevels = expression_.getCoveringStops(tileZoom, tileZoom + 1); 149 coveringRanges = std::make_tuple( 150 zoomLevels, 151 Range<float> { expression_.evaluate(zoomLevels.min), expression_.evaluate(zoomLevels.max) } 152 ); 153 } 154 getVertexSizeData(const GeometryTileFeature &)155 Range<float> getVertexSizeData(const GeometryTileFeature&) override { return { 0.0f, 0.0f }; }; 156 evaluateForZoom(float currentZoom) const157 ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override { 158 float size = layoutSize; 159 bool isZoomConstant = !(coveringRanges || expression); 160 if (coveringRanges) { 161 // Even though we could get the exact value of the camera function 162 // at z = currentZoom, we intentionally do not: instead, we interpolate 163 // between the camera function values at a pair of zoom stops covering 164 // [tileZoom, tileZoom + 1] in order to be consistent with this 165 // restriction on composite functions. 166 const Range<float>& zoomLevels = std::get<0>(*coveringRanges); 167 const Range<float>& sizeLevels = std::get<1>(*coveringRanges); 168 float t = util::clamp( 169 expression->interpolationFactor(zoomLevels, currentZoom), 170 0.0f, 1.0f 171 ); 172 size = sizeLevels.min + t * (sizeLevels.max - sizeLevels.min); 173 } else if (expression) { 174 size = expression->evaluate(currentZoom); 175 } 176 177 const float unused = 0.0f; 178 return { isZoomConstant, true, unused, size, layoutSize }; 179 } 180 181 float layoutSize; 182 optional<std::tuple<Range<float>, Range<float>>> coveringRanges; 183 optional<style::PropertyExpression<float>> expression; 184 }; 185 186 class SourceFunctionSymbolSizeBinder final : public SymbolSizeBinder { 187 public: 188 using Vertex = gl::detail::Vertex<gl::Attribute<uint16_t, 1>>; 189 using VertexVector = gl::VertexVector<Vertex>; 190 using VertexBuffer = gl::VertexBuffer<Vertex>; 191 SourceFunctionSymbolSizeBinder(const float,style::PropertyExpression<float> expression_,const float defaultValue_)192 SourceFunctionSymbolSizeBinder(const float /*tileZoom*/, style::PropertyExpression<float> expression_, const float defaultValue_) 193 : expression(std::move(expression_)), 194 defaultValue(defaultValue_) { 195 } 196 getVertexSizeData(const GeometryTileFeature & feature)197 Range<float> getVertexSizeData(const GeometryTileFeature& feature) override { 198 const float size = expression.evaluate(feature, defaultValue); 199 return { size, size }; 200 }; 201 evaluateForZoom(float) const202 ZoomEvaluatedSize evaluateForZoom(float) const override { 203 const float unused = 0.0f; 204 return { true, false, unused, unused, unused }; 205 } 206 207 style::PropertyExpression<float> expression; 208 const float defaultValue; 209 }; 210 211 class CompositeFunctionSymbolSizeBinder final : public SymbolSizeBinder { 212 public: 213 CompositeFunctionSymbolSizeBinder(const float tileZoom,style::PropertyExpression<float> expression_,const float defaultValue_)214 CompositeFunctionSymbolSizeBinder(const float tileZoom, style::PropertyExpression<float> expression_, const float defaultValue_) 215 : expression(std::move(expression_)), 216 defaultValue(defaultValue_), 217 layoutZoom(tileZoom + 1), 218 coveringZoomStops(expression.getCoveringStops(tileZoom, tileZoom + 1)) 219 {} 220 getVertexSizeData(const GeometryTileFeature & feature)221 Range<float> getVertexSizeData(const GeometryTileFeature& feature) override { 222 return { 223 expression.evaluate(coveringZoomStops.min, feature, defaultValue), 224 expression.evaluate(coveringZoomStops.max, feature, defaultValue) 225 }; 226 }; 227 evaluateForZoom(float currentZoom) const228 ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override { 229 float sizeInterpolationT = util::clamp( 230 expression.interpolationFactor(coveringZoomStops, currentZoom), 231 0.0f, 1.0f 232 ); 233 234 const float unused = 0.0f; 235 return { false, false, sizeInterpolationT, unused, unused }; 236 } 237 238 style::PropertyExpression<float> expression; 239 const float defaultValue; 240 float layoutZoom; 241 Range<float> coveringZoomStops; 242 }; 243 244 245 template <class Shaders, 246 class Primitive, 247 class LayoutAttrs, 248 class Uniforms, 249 class PaintProps> 250 class SymbolProgram { 251 public: 252 using LayoutAttributes = LayoutAttrs; 253 using LayoutVertex = typename LayoutAttributes::Vertex; 254 255 using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, gl::ConcatenateAttributes<SymbolDynamicLayoutAttributes, SymbolOpacityAttributes>>; 256 257 using PaintProperties = PaintProps; 258 using PaintPropertyBinders = typename PaintProperties::Binders; 259 using PaintAttributes = typename PaintPropertyBinders::Attributes; 260 using Attributes = gl::ConcatenateAttributes<LayoutAndSizeAttributes, PaintAttributes>; 261 262 using UniformValues = typename Uniforms::Values; 263 using SizeUniforms = typename SymbolSizeBinder::Uniforms; 264 using PaintUniforms = typename PaintPropertyBinders::Uniforms; 265 using AllUniforms = gl::ConcatenateUniforms<Uniforms, gl::ConcatenateUniforms<SizeUniforms, PaintUniforms>>; 266 267 using ProgramType = gl::Program<Primitive, Attributes, AllUniforms>; 268 269 ProgramType program; 270 SymbolProgram(gl::Context & context,const ProgramParameters & programParameters)271 SymbolProgram(gl::Context& context, const ProgramParameters& programParameters) 272 : program(ProgramType::createProgram( 273 context, 274 programParameters, 275 Shaders::name, 276 Shaders::vertexSource, 277 Shaders::fragmentSource)) { 278 } 279 computeAllUniformValues(const UniformValues & uniformValues,const SymbolSizeBinder & symbolSizeBinder,const PaintPropertyBinders & paintPropertyBinders,const typename PaintProperties::PossiblyEvaluated & currentProperties,float currentZoom)280 static typename AllUniforms::Values computeAllUniformValues( 281 const UniformValues& uniformValues, 282 const SymbolSizeBinder& symbolSizeBinder, 283 const PaintPropertyBinders& paintPropertyBinders, 284 const typename PaintProperties::PossiblyEvaluated& currentProperties, 285 float currentZoom) { 286 return uniformValues.concat(symbolSizeBinder.uniformValues(currentZoom)) 287 .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); 288 } 289 computeAllAttributeBindings(const gl::VertexBuffer<LayoutVertex> & layoutVertexBuffer,const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex> & dynamicLayoutVertexBuffer,const gl::VertexBuffer<SymbolOpacityAttributes::Vertex> & opacityVertexBuffer,const PaintPropertyBinders & paintPropertyBinders,const typename PaintProperties::PossiblyEvaluated & currentProperties)290 static typename Attributes::Bindings computeAllAttributeBindings( 291 const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer, 292 const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>& dynamicLayoutVertexBuffer, 293 const gl::VertexBuffer<SymbolOpacityAttributes::Vertex>& opacityVertexBuffer, 294 const PaintPropertyBinders& paintPropertyBinders, 295 const typename PaintProperties::PossiblyEvaluated& currentProperties) { 296 assert(layoutVertexBuffer.vertexCount == dynamicLayoutVertexBuffer.vertexCount && 297 layoutVertexBuffer.vertexCount == opacityVertexBuffer.vertexCount); 298 return LayoutAttributes::bindings(layoutVertexBuffer) 299 .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer)) 300 .concat(SymbolOpacityAttributes::bindings(opacityVertexBuffer)) 301 .concat(paintPropertyBinders.attributeBindings(currentProperties)); 302 } 303 activeBindingCount(const typename Attributes::Bindings & allAttributeBindings)304 static uint32_t activeBindingCount(const typename Attributes::Bindings& allAttributeBindings) { 305 return Attributes::activeBindingCount(allAttributeBindings); 306 } 307 308 template <class DrawMode> draw(gl::Context & context,DrawMode drawMode,gl::DepthMode depthMode,gl::StencilMode stencilMode,gl::ColorMode colorMode,const gl::IndexBuffer<DrawMode> & indexBuffer,const SegmentVector<Attributes> & segments,const typename AllUniforms::Values & allUniformValues,const typename Attributes::Bindings & allAttributeBindings,const std::string & layerID)309 void draw(gl::Context& context, 310 DrawMode drawMode, 311 gl::DepthMode depthMode, 312 gl::StencilMode stencilMode, 313 gl::ColorMode colorMode, 314 const gl::IndexBuffer<DrawMode>& indexBuffer, 315 const SegmentVector<Attributes>& segments, 316 const typename AllUniforms::Values& allUniformValues, 317 const typename Attributes::Bindings& allAttributeBindings, 318 const std::string& layerID) { 319 for (auto& segment : segments) { 320 auto vertexArrayIt = segment.vertexArrays.find(layerID); 321 322 if (vertexArrayIt == segment.vertexArrays.end()) { 323 vertexArrayIt = segment.vertexArrays.emplace(layerID, context.createVertexArray()).first; 324 } 325 326 program.draw( 327 context, 328 std::move(drawMode), 329 std::move(depthMode), 330 std::move(stencilMode), 331 std::move(colorMode), 332 allUniformValues, 333 vertexArrayIt->second, 334 Attributes::offsetBindings(allAttributeBindings, segment.vertexOffset), 335 indexBuffer, 336 segment.indexOffset, 337 segment.indexLength); 338 } 339 } 340 }; 341 342 class SymbolIconProgram : public SymbolProgram< 343 shaders::symbol_icon, 344 gl::Triangle, 345 SymbolLayoutAttributes, 346 gl::Uniforms< 347 uniforms::u_matrix, 348 uniforms::u_label_plane_matrix, 349 uniforms::u_gl_coord_matrix, 350 uniforms::u_extrude_scale, 351 uniforms::u_texsize, 352 uniforms::u_texture, 353 uniforms::u_fade_change, 354 uniforms::u_is_text, 355 uniforms::u_camera_to_center_distance, 356 uniforms::u_pitch, 357 uniforms::u_pitch_with_map, 358 uniforms::u_rotate_symbol, 359 uniforms::u_aspect_ratio>, 360 style::IconPaintProperties> 361 { 362 public: 363 using SymbolProgram::SymbolProgram; 364 365 static UniformValues uniformValues(const bool isText, 366 const style::SymbolPropertyValues&, 367 const Size& texsize, 368 const std::array<float, 2>& pixelsToGLUnits, 369 const bool alongLine, 370 const RenderTile&, 371 const TransformState&, 372 const float symbolFadeChange); 373 }; 374 375 enum class SymbolSDFPart { 376 Fill = 1, 377 Halo = 0 378 }; 379 380 template <class PaintProperties> 381 class SymbolSDFProgram : public SymbolProgram< 382 shaders::symbol_sdf, 383 gl::Triangle, 384 SymbolLayoutAttributes, 385 gl::Uniforms< 386 uniforms::u_matrix, 387 uniforms::u_label_plane_matrix, 388 uniforms::u_gl_coord_matrix, 389 uniforms::u_extrude_scale, 390 uniforms::u_texsize, 391 uniforms::u_texture, 392 uniforms::u_fade_change, 393 uniforms::u_is_text, 394 uniforms::u_camera_to_center_distance, 395 uniforms::u_pitch, 396 uniforms::u_pitch_with_map, 397 uniforms::u_rotate_symbol, 398 uniforms::u_aspect_ratio, 399 uniforms::u_gamma_scale, 400 uniforms::u_is_halo>, 401 PaintProperties> 402 { 403 public: 404 using BaseProgram = SymbolProgram<shaders::symbol_sdf, 405 gl::Triangle, 406 SymbolLayoutAttributes, 407 gl::Uniforms< 408 uniforms::u_matrix, 409 uniforms::u_label_plane_matrix, 410 uniforms::u_gl_coord_matrix, 411 uniforms::u_extrude_scale, 412 uniforms::u_texsize, 413 uniforms::u_texture, 414 uniforms::u_fade_change, 415 uniforms::u_is_text, 416 uniforms::u_camera_to_center_distance, 417 uniforms::u_pitch, 418 uniforms::u_pitch_with_map, 419 uniforms::u_rotate_symbol, 420 uniforms::u_aspect_ratio, 421 uniforms::u_gamma_scale, 422 uniforms::u_is_halo>, 423 PaintProperties>; 424 425 using UniformValues = typename BaseProgram::UniformValues; 426 427 428 429 using BaseProgram::BaseProgram; 430 431 static UniformValues uniformValues(const bool isText, 432 const style::SymbolPropertyValues&, 433 const Size& texsize, 434 const std::array<float, 2>& pixelsToGLUnits, 435 const bool alongLine, 436 const RenderTile&, 437 const TransformState&, 438 const float SymbolFadeChange, 439 const SymbolSDFPart); 440 }; 441 442 using SymbolSDFIconProgram = SymbolSDFProgram<style::IconPaintProperties>; 443 using SymbolSDFTextProgram = SymbolSDFProgram<style::TextPaintProperties>; 444 445 using SymbolLayoutVertex = SymbolLayoutAttributes::Vertex; 446 using SymbolIconAttributes = SymbolIconProgram::Attributes; 447 using SymbolTextAttributes = SymbolSDFTextProgram::Attributes; 448 449 } // namespace mbgl 450