#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if MLN_RENDER_BACKEND_OPENGL #include #endif namespace mbgl { const uint16_t MAX_GLYPH_ICON_SIZE = 255; const uint16_t SIZE_PACK_FACTOR = 128; const uint16_t MAX_PACKED_SIZE = MAX_GLYPH_ICON_SIZE * SIZE_PACK_FACTOR; namespace style { class SymbolPropertyValues; } // namespace style class RenderTile; class TransformState; namespace uniforms { MBGL_DEFINE_UNIFORM_MATRIX(double, 4, coord_matrix); MBGL_DEFINE_UNIFORM_MATRIX(double, 4, label_plane_matrix); MBGL_DEFINE_UNIFORM_SCALAR(bool, is_halo); MBGL_DEFINE_UNIFORM_SCALAR(float, gamma_scale); MBGL_DEFINE_UNIFORM_SCALAR(bool, is_text); MBGL_DEFINE_UNIFORM_SCALAR(bool, is_size_zoom_constant); MBGL_DEFINE_UNIFORM_SCALAR(bool, is_size_feature_constant); MBGL_DEFINE_UNIFORM_SCALAR(float, size_t); MBGL_DEFINE_UNIFORM_SCALAR(float, size); MBGL_DEFINE_UNIFORM_SCALAR(bool, rotate_symbol); MBGL_DEFINE_UNIFORM_SCALAR(float, aspect_ratio); } // namespace uniforms using SymbolLayoutAttributes = TypeList, attributes::pixeloffset>; using SymbolDynamicLayoutAttributes = TypeList; using SymbolOpacityAttributes = TypeList; struct ZoomEvaluatedSize { bool isZoomConstant; bool isFeatureConstant; float sizeT; float size; float layoutSize; }; // Mimic the PaintPropertyBinder technique specifically for the {text,icon}-size // layout properties in order to provide a 'custom' scheme for encoding the // necessary attribute data. As with PaintPropertyBinder, SymbolSizeBinder is // an abstract class whose implementations handle the particular attribute & // uniform logic needed by each possible type of the {Text,Icon}Size properties. class SymbolSizeBinder { public: virtual ~SymbolSizeBinder() = default; using UniformList = TypeList; using UniformValues = gfx::UniformValues; static std::unique_ptr create(float tileZoom, const style::PropertyValue& sizeProperty, float defaultValue); virtual Range getVertexSizeData(const GeometryTileFeature& feature) = 0; virtual ZoomEvaluatedSize evaluateForZoom(float currentZoom) const = 0; UniformValues uniformValues(float currentZoom) const { const ZoomEvaluatedSize u = evaluateForZoom(currentZoom); return UniformValues{uniforms::is_size_zoom_constant::Value(u.isZoomConstant), uniforms::is_size_feature_constant::Value(u.isFeatureConstant), uniforms::size_t::Value(u.sizeT), uniforms::size::Value(u.size)}; } }; class ConstantSymbolSizeBinder final : public SymbolSizeBinder { public: ConstantSymbolSizeBinder(const float /*tileZoom*/, const float& size, const float /*defaultValue*/) noexcept : layoutSize(size) {} ConstantSymbolSizeBinder(const float /*tileZoom*/, const style::Undefined&, const float defaultValue) noexcept : layoutSize(defaultValue) {} ConstantSymbolSizeBinder(const float tileZoom, const style::PropertyExpression& expression_, const float /*defaultValue*/ ) : layoutSize(expression_.evaluate(tileZoom + 1)), expression(expression_) { const Range zoomLevels = expression_.getCoveringStops(tileZoom, tileZoom + 1); coveringRanges = std::make_tuple( zoomLevels, Range{expression_.evaluate(zoomLevels.min), expression_.evaluate(zoomLevels.max)}); } Range getVertexSizeData(const GeometryTileFeature&) noexcept override { return {0.0f, 0.0f}; }; ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override { float size = layoutSize; bool isZoomConstant = !(coveringRanges || expression); if (coveringRanges) { // Even though we could get the exact value of the camera function // at z = currentZoom, we intentionally do not: instead, we // interpolate between the camera function values at a pair of zoom // stops covering [tileZoom, tileZoom + 1] in order to be consistent // with this restriction on composite functions. const Range& zoomLevels = std::get<0>(*coveringRanges); const Range& sizeLevels = std::get<1>(*coveringRanges); float t = util::clamp(expression->interpolationFactor(zoomLevels, currentZoom), 0.0f, 1.0f); size = sizeLevels.min + t * (sizeLevels.max - sizeLevels.min); } else if (expression) { size = expression->evaluate(currentZoom); } const float unused = 0.0f; return {isZoomConstant, true, unused, size, layoutSize}; } float layoutSize; std::optional, Range>> coveringRanges; std::optional> expression; }; class SourceFunctionSymbolSizeBinder final : public SymbolSizeBinder { public: SourceFunctionSymbolSizeBinder(const float /*tileZoom*/, style::PropertyExpression expression_, const float defaultValue_) noexcept : expression(std::move(expression_)), defaultValue(defaultValue_) {} Range getVertexSizeData(const GeometryTileFeature& feature) override { const float size = expression.evaluate(feature, defaultValue); return {size, size}; }; ZoomEvaluatedSize evaluateForZoom(float) const noexcept override { const float unused = 0.0f; return {true, false, unused, unused, unused}; } style::PropertyExpression expression; const float defaultValue; }; class CompositeFunctionSymbolSizeBinder final : public SymbolSizeBinder { public: CompositeFunctionSymbolSizeBinder(const float tileZoom, style::PropertyExpression expression_, const float defaultValue_) noexcept : expression(std::move(expression_)), defaultValue(defaultValue_), layoutZoom(tileZoom + 1), coveringZoomStops(expression.getCoveringStops(tileZoom, tileZoom + 1)) {} Range getVertexSizeData(const GeometryTileFeature& feature) override { return {expression.evaluate(coveringZoomStops.min, feature, defaultValue), expression.evaluate(coveringZoomStops.max, feature, defaultValue)}; }; ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override { float sizeInterpolationT = util::clamp( expression.interpolationFactor(coveringZoomStops, currentZoom), 0.0f, 1.0f); const float unused = 0.0f; return {false, false, sizeInterpolationT, unused, unused}; } style::PropertyExpression expression; const float defaultValue; float layoutZoom; Range coveringZoomStops; }; class SymbolProgramBase : public gfx::Shader { public: static gfx::Vertex layoutVertex(Point labelAnchor, Point o, float glyphOffsetY, uint16_t tx, uint16_t ty, const Range& sizeData, bool isSDF, Point pixelOffset, Point minFontScale) { const uint16_t aSizeMin = (std::min(MAX_PACKED_SIZE, static_cast(sizeData.min * SIZE_PACK_FACTOR)) << 1) + uint16_t(isSDF); const uint16_t aSizeMax = std::min(MAX_PACKED_SIZE, static_cast(sizeData.max * SIZE_PACK_FACTOR)); return { // combining pos and offset to reduce number of vertex attributes // passed to shader (8 max for some devices) {{static_cast(labelAnchor.x), static_cast(labelAnchor.y), static_cast(std::round(o.x * 32)), // use 1/32 pixels for placement static_cast(std::round((o.y + glyphOffsetY) * 32))}}, {{tx, ty, aSizeMin, aSizeMax}}, {{static_cast(pixelOffset.x * 16), static_cast(pixelOffset.y * 16), static_cast(minFontScale.x * 256), static_cast(minFontScale.y * 256)}}, }; } static gfx::Vertex dynamicLayoutVertex(Point anchorPoint, float labelAngle) { return {{{anchorPoint.x, anchorPoint.y, labelAngle}}}; } static gfx::Vertex opacityVertex(bool placed, float opacity) { return {{{static_cast((static_cast(opacity * 127) << 1) | static_cast(placed))}}}; } }; template class SymbolProgram : public SymbolProgramBase { public: using LayoutVertex = gfx::Vertex; using LayoutAndSizeAttributeList = TypeListConcat; using PaintProperties = PaintProps; using Binders = PaintPropertyBinders; using PaintAttributeList = typename Binders::AttributeList; using AttributeList = TypeListConcat; using AttributeBindings = gfx::AttributeBindings; using LayoutUniformValues = gfx::UniformValues; using SizeUniformList = typename SymbolSizeBinder::UniformList; using PaintUniformList = typename Binders::UniformList; using UniformList = TypeListConcat; using UniformValues = gfx::UniformValues; using TextureList = Textures; using TextureBindings = gfx::TextureBindings; std::unique_ptr> program; SymbolProgram([[maybe_unused]] const ProgramParameters& programParameters) { switch (gfx::Backend::GetType()) { #if MLN_RENDER_BACKEND_OPENGL case gfx::Backend::Type::OpenGL: { program = std::make_unique>(programParameters.withDefaultSource( {gfx::Backend::Type::OpenGL, shaders::ShaderSource::vertex, shaders::ShaderSource::fragment})); break; } #endif default: { throw std::runtime_error("Unsupported rendering backend!"); } } } static UniformValues computeAllUniformValues(const LayoutUniformValues& layoutUniformValues, const SymbolSizeBinder& symbolSizeBinder, const Binders& paintPropertyBinders, const typename PaintProperties::PossiblyEvaluated& currentProperties, float currentZoom) { return layoutUniformValues.concat(symbolSizeBinder.uniformValues(currentZoom)) .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); } static AttributeBindings computeAllAttributeBindings( const gfx::VertexBuffer& layoutVertexBuffer, const gfx::VertexBuffer>& dynamicLayoutVertexBuffer, const gfx::VertexBuffer>& opacityVertexBuffer, const Binders& paintPropertyBinders, const typename PaintProperties::PossiblyEvaluated& currentProperties) { assert(layoutVertexBuffer.elements == dynamicLayoutVertexBuffer.elements && layoutVertexBuffer.elements == opacityVertexBuffer.elements); return gfx::AttributeBindings(layoutVertexBuffer) .concat(gfx::AttributeBindings(dynamicLayoutVertexBuffer)) .concat(gfx::AttributeBindings(opacityVertexBuffer)) .concat(paintPropertyBinders.attributeBindings(currentProperties)); } static uint32_t activeBindingCount(const AttributeBindings& allAttributeBindings) { return allAttributeBindings.activeCount(); } template void draw(gfx::Context& context, gfx::RenderPass& renderPass, const DrawMode& drawMode, const gfx::DepthMode& depthMode, const gfx::StencilMode& stencilMode, const gfx::ColorMode& colorMode, const gfx::CullFaceMode& cullFaceMode, const gfx::IndexBuffer& indexBuffer, const Segment& segment, const UniformValues& uniformValues, const AttributeBindings& allAttributeBindings, const TextureBindings& textureBindings, const std::string& layerID) { static_assert(Primitive == gfx::PrimitiveTypeOf::value, "incompatible draw mode"); if (!program) { return; } auto drawScopeIt = segment.drawScopes.find(layerID); if (drawScopeIt == segment.drawScopes.end()) { drawScopeIt = segment.drawScopes.emplace(layerID, context.createDrawScope()).first; } program->draw(context, renderPass, drawMode, depthMode, stencilMode, colorMode, cullFaceMode, uniformValues, drawScopeIt->second, allAttributeBindings.offset(segment.vertexOffset), textureBindings, indexBuffer, segment.indexOffset, segment.indexLength); } template void draw(gfx::Context& context, gfx::RenderPass& renderPass, const DrawMode& drawMode, const gfx::DepthMode& depthMode, const gfx::StencilMode& stencilMode, const gfx::ColorMode& colorMode, const gfx::CullFaceMode& cullFaceMode, const gfx::IndexBuffer& indexBuffer, const SegmentVector& segments, const UniformValues& uniformValues, const AttributeBindings& allAttributeBindings, const TextureBindings& textureBindings, const std::string& layerID) { static_assert(Primitive == gfx::PrimitiveTypeOf::value, "incompatible draw mode"); if (!program) { return; } for (const auto& segment : segments) { draw(context, renderPass, drawMode, depthMode, stencilMode, colorMode, cullFaceMode, indexBuffer, segment, uniformValues, allAttributeBindings, textureBindings, layerID); } } }; class SymbolIconProgram final : public SymbolProgram, TypeList, style::IconPaintProperties> { public: static constexpr std::string_view Name{"SymbolIconProgram"}; const std::string_view typeName() const noexcept override { return Name; } using SymbolProgram::SymbolProgram; static LayoutUniformValues layoutUniformValues(bool isText, bool hasVariablePacement, const style::SymbolPropertyValues&, const Size& texsize, const std::array& pixelsToGLUnits, bool alongLine, const RenderTile&, const TransformState&, float symbolFadeChange); }; enum class SymbolSDFPart { Fill = 1, Halo = 0 }; using SymbolSDFProgramUniforms = TypeList; template class SymbolSDFProgram : public SymbolProgram, PaintProperties> { public: using BaseProgram = SymbolProgram, PaintProperties>; using LayoutUniformValues = typename BaseProgram::LayoutUniformValues; using BaseProgram::BaseProgram; static LayoutUniformValues layoutUniformValues(bool isText, bool hasVariablePacement, const style::SymbolPropertyValues&, const Size& texsize, const std::array& pixelsToGLUnits, float pixelRatio, bool alongLine, const RenderTile&, const TransformState&, float SymbolFadeChange, SymbolSDFPart); }; using SymbolTextAndIconProgramUniforms = TypeList; class SymbolTextAndIconProgram final : public SymbolProgram, TypeList, style::TextPaintProperties> { public: static constexpr std::string_view Name{"SymbolTextAndIconProgram"}; const std::string_view typeName() const noexcept override { return Name; } using BaseProgram = SymbolProgram, TypeList, style::TextPaintProperties>; using LayoutUniformValues = typename BaseProgram::LayoutUniformValues; using BaseProgram::BaseProgram; static LayoutUniformValues layoutUniformValues(bool hasVariablePacement, const style::SymbolPropertyValues&, const Size& texsize, const Size& texsize_icon, const std::array& pixelsToGLUnits, float pixelRatio, bool alongLine, const RenderTile&, const TransformState&, float SymbolFadeChange, SymbolSDFPart); }; class SymbolSDFIconProgram final : public SymbolSDFProgram { public: static constexpr std::string_view Name{"SymbolSDFIconProgram"}; const std::string_view typeName() const noexcept override { return Name; } using SymbolSDFProgram::SymbolSDFProgram; }; class SymbolSDFTextProgram final : public SymbolSDFProgram { public: static constexpr std::string_view Name{"SymbolSDFTextProgram"}; const std::string_view typeName() const noexcept override { return Name; } using SymbolSDFProgram::SymbolSDFProgram; }; using SymbolLayoutVertex = gfx::Vertex; using SymbolIconAttributes = SymbolIconProgram::AttributeList; using SymbolTextAttributes = SymbolSDFTextProgram::AttributeList; } // namespace mbgl