From 32f95a9368ea58652d429f95ca0ebba6597ea438 Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Thu, 30 Mar 2017 23:39:19 -0400 Subject: [PATCH] Draft implementation of DDS for {text,icon}-size Ports https://github.com/mapbox/mapbox-gl-js/pull/4455 --- .../mbgl/style/data_driven_property_value.hpp | 9 + .../mbgl/style/function/camera_function.hpp | 18 ++ .../style/function/composite_function.hpp | 17 ++ src/mbgl/layout/symbol_instance.cpp | 8 +- src/mbgl/layout/symbol_instance.hpp | 1 + src/mbgl/layout/symbol_layout.cpp | 75 ++++-- src/mbgl/layout/symbol_layout.hpp | 8 +- src/mbgl/programs/attributes.hpp | 9 +- src/mbgl/programs/symbol_program.cpp | 56 +++- src/mbgl/programs/symbol_program.hpp | 245 +++++++++++++++++- src/mbgl/renderer/painter_symbol.cpp | 17 +- src/mbgl/renderer/symbol_bucket.cpp | 9 +- src/mbgl/renderer/symbol_bucket.hpp | 9 +- src/mbgl/style/layers/symbol_layer_impl.cpp | 6 - src/mbgl/style/layers/symbol_layer_impl.hpp | 1 - src/mbgl/text/quads.cpp | 3 +- src/mbgl/text/quads.hpp | 1 + 17 files changed, 433 insertions(+), 59 deletions(-) diff --git a/include/mbgl/style/data_driven_property_value.hpp b/include/mbgl/style/data_driven_property_value.hpp index 3f9ac694363..5acf800840f 100644 --- a/include/mbgl/style/data_driven_property_value.hpp +++ b/include/mbgl/style/data_driven_property_value.hpp @@ -45,6 +45,15 @@ class DataDrivenPropertyValue { bool isDataDriven() const { return value.template is>() || value.template is>(); } + + bool isZoomConstant() const { + return !value.template is>() && !value.template is>(); + } + + template + auto match(Ts&&... ts) const { + return value.match(std::forward(ts)...); + } template auto evaluate(const Evaluator& evaluator) const { diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp index 5636b1663c9..0bb5edc873c 100644 --- a/include/mbgl/style/function/camera_function.hpp +++ b/include/mbgl/style/function/camera_function.hpp @@ -28,6 +28,24 @@ class CameraFunction { return s.evaluate(Value(double(zoom))).value_or(T()); }); } + + // TODO: this is duped from composite function; dedupe it. + Range coveringZoomStops(float lowerZoom, float upperZoom) const { + return stops.match( + [&] (const auto& s) { + assert(!s.stops.empty()); + auto minIt = s.stops.lower_bound(lowerZoom); + auto maxIt = s.stops.upper_bound(upperZoom); + if (minIt != s.stops.begin()) { + minIt--; + } + return Range { + minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first, + maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first + }; + } + ); + } friend bool operator==(const CameraFunction& lhs, const CameraFunction& rhs) { diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp index b31329b75e8..2e524a02b27 100644 --- a/include/mbgl/style/function/composite_function.hpp +++ b/include/mbgl/style/function/composite_function.hpp @@ -72,6 +72,23 @@ class CompositeFunction { } ); } + + Range coveringZoomStops(float lowerZoom, float upperZoom) const { + return stops.match( + [&] (const auto& s) { + assert(!s.stops.empty()); + auto minIt = s.stops.lower_bound(lowerZoom); + auto maxIt = s.stops.upper_bound(upperZoom); + if (minIt != s.stops.begin()) { + minIt--; + } + return Range { + minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first, + maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first + }; + } + ); + } template Range evaluate(Range coveringStops, diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 8bdc528bbbb..2a3bf068c17 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -10,6 +10,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const std::pair& shapedTextOrientations, const PositionedIcon& shapedIcon, const SymbolLayoutProperties::Evaluated& layout, + const float layoutTextSize, const bool addToBuffers, const uint32_t index_, const float textBoxScale, @@ -26,15 +27,18 @@ SymbolInstance::SymbolInstance(Anchor& anchor, hasText(shapedTextOrientations.first || shapedTextOrientations.second), hasIcon(shapedIcon), + // Create the collision features that will be used to check whether this symbol instance can be placed textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), featureIndex(featureIndex_) { + + // Create the quads used for rendering the icon and glyphs. if (addToBuffers) { if (shapedIcon) { - iconQuad = getIconQuad(anchor, shapedIcon, line, layout, iconPlacement, shapedTextOrientations.first); + iconQuad = getIconQuad(anchor, shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first); } if (shapedTextOrientations.first) { auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, face); @@ -55,6 +59,8 @@ SymbolInstance::SymbolInstance(Anchor& anchor, } else { writingModes = WritingModeType::None; } + + } } // namespace mbgl diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index 70ebfeefa29..efe6cb05aa2 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -17,6 +17,7 @@ class SymbolInstance { const std::pair& shapedTextOrientations, const PositionedIcon& shapedIcon, const style::SymbolLayoutProperties::Evaluated&, + const float layoutTextSize, const bool inside, const uint32_t index, const float textBoxScale, diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 8e36bb84ea0..34803124f09 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -41,7 +41,10 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, mode(parameters.mode), spriteAtlas(spriteAtlas_), tileSize(util::tileSize * overscaling), - tilePixelRatio(float(util::EXTENT) / tileSize) { + tilePixelRatio(float(util::EXTENT) / tileSize), + textSize(layers.at(0)->as()->impl->layout.unevaluated.get()), + iconSize(layers.at(0)->as()->impl->layout.unevaluated.get()) + { const SymbolLayer::Impl& leader = *layers.at(0)->as()->impl; @@ -67,12 +70,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (layout.get() == AlignmentType::Auto) { layout.get() = layout.get(); } - - textMaxSize = leader.layout.evaluate(PropertyEvaluationParameters(18)); - - layout.get() = leader.layout.evaluate(PropertyEvaluationParameters(zoom + 1)); - layout.get() = leader.layout.evaluate(PropertyEvaluationParameters(zoom + 1)); - + const bool hasTextField = layout.get().match( [&] (const std::string& s) { return !s.empty(); }, [&] (const auto&) { return true; } @@ -92,7 +90,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, layer->as()->impl->textPaintProperties() )); } - + // Determine and load glyph ranges const size_t featureCount = sourceLayer.featureCount(); for (size_t i = 0; i < featureCount; ++i) { @@ -307,11 +305,20 @@ void SymbolLayout::addFeature(const std::size_t index, const GlyphPositions& face) { const float minScale = 0.5f; const float glyphSize = 24.0f; - - const float fontScale = layout.get() / glyphSize; + + const float layoutTextSize = layout.evaluate(zoom + 1, feature); + const float layoutIconSize = layout.evaluate(zoom + 1, feature); + + // To reduce the number of labels that jump around when zooming we need + // to use a text-size value that is the same for all zoom levels. + // This calculates text-size at a high zoom level so that all tiles can + // use the same value when calculating anchor positions. + const float textMaxSize = layout.evaluate(18, feature); + + const float fontScale = layoutTextSize / glyphSize; const float textBoxScale = tilePixelRatio * fontScale; const float textMaxBoxScale = tilePixelRatio * textMaxSize / glyphSize; - const float iconBoxScale = tilePixelRatio * layout.get(); + const float iconBoxScale = tilePixelRatio * layoutIconSize; const float symbolSpacing = tilePixelRatio * layout.get(); const bool avoidEdges = layout.get() && layout.get() != SymbolPlacementType::Line; const float textPadding = layout.get() * tilePixelRatio; @@ -325,6 +332,8 @@ void SymbolLayout::addFeature(const std::size_t index, : layout.get(); const float textRepeatDistance = symbolSpacing / 2; IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, bucketName, symbolInstances.size()}; + + auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers @@ -347,7 +356,8 @@ void SymbolLayout::addFeature(const std::size_t index, const bool addToBuffers = mode == MapMode::Still || withinPlus0; - symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout, addToBuffers, symbolInstances.size(), + symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout, layoutTextSize, + addToBuffers, symbolInstances.size(), textBoxScale, textPadding, textPlacement, iconBoxScale, iconPadding, iconPlacement, face, indexedFeature, index); @@ -422,7 +432,7 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe } std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) { - auto bucket = std::make_unique(layout, layerPaintProperties, zoom, sdfIcons, iconsNeedLinear); + auto bucket = std::make_unique(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear); // Calculate which labels can be shown and when they can be shown and // create the bufers used for rendering. @@ -486,6 +496,7 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) iconScale = util::max(iconScale, glyphScale); } + const auto& feature = features.at(symbolInstance.featureIndex); // Insert final placement into collision tree and add glyphs/icons to buffers @@ -495,7 +506,7 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) if (glyphScale < collisionTile.maxScale) { for (const auto& symbol : symbolInstance.glyphQuads) { addSymbol( - bucket->text, symbol, placementZoom, + bucket->text, bucket->textSizeData, symbol, feature, textSize, placementZoom, keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes); } } @@ -506,12 +517,11 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get()); if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) { addSymbol( - bucket->icon, *symbolInstance.iconQuad, placementZoom, + bucket->icon, bucket->iconSizeData, *symbolInstance.iconQuad, feature, iconSize, placementZoom, keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes); } } - const auto& feature = features.at(symbolInstance.featureIndex); for (auto& pair : bucket->paintPropertyBinders) { pair.second.first.populateVertexVectors(feature, bucket->icon.vertices.vertexSize()); pair.second.second.populateVertexVectors(feature, bucket->text.vertices.vertexSize()); @@ -527,7 +537,10 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) template void SymbolLayout::addSymbol(Buffer& buffer, + SymbolSizeData& sizeData, const SymbolQuad& symbol, + const SymbolFeature& feature, + const style::DataDrivenPropertyValue& size, const float placementZoom, const bool keepUpright, const style::SymbolPlacementType placement, @@ -589,6 +602,36 @@ void SymbolLayout::addSymbol(Buffer& buffer, minZoom, maxZoom, placementZoom, glyphAngle)); buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom, glyphAngle)); + + + size.match( + [&] (const style::CompositeFunction& fn) { + const auto sizeVertex = SymbolSizeAttributes::Vertex { + {{ + static_cast(fn.evaluate(sizeData.coveringZoomStops->min, feature, sizeData.defaultSize) * 10), + static_cast(fn.evaluate(sizeData.coveringZoomStops->max, feature, sizeData.defaultSize) * 10), + static_cast(fn.evaluate(zoom + 1, feature, sizeData.defaultSize) * 10) + }} + }; + auto& vertexVector = sizeData.vertices.get>(); + vertexVector.emplace_back(sizeVertex); + vertexVector.emplace_back(sizeVertex); + vertexVector.emplace_back(sizeVertex); + vertexVector.emplace_back(sizeVertex); + }, + [&] (const style::SourceFunction& fn) { + const auto sizeVertex = SymbolSizeAttributes::SourceFunctionVertex { + {{ static_cast(fn.evaluate(feature, sizeData.defaultSize) * 10) }} + }; + + auto& vertexVector = sizeData.vertices.get>(); + vertexVector.emplace_back(sizeVertex); + vertexVector.emplace_back(sizeVertex); + vertexVector.emplace_back(sizeVertex); + vertexVector.emplace_back(sizeVertex); + }, + [] (const auto&) {} + ); // add the two triangles, referencing the four coordinates we just inserted. buffer.triangles.emplace_back(index + 0, index + 1, index + 2); diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 5b14090d97c..8fd31b1c7ef 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -68,7 +69,8 @@ class SymbolLayout { // Adds placed items to the buffer. template - void addSymbol(Buffer&, const SymbolQuad&, float scale, + void addSymbol(Buffer&, SymbolSizeData& sizeData, const SymbolQuad&, const SymbolFeature& feature, + const style::DataDrivenPropertyValue& size, float scale, const bool keepUpright, const style::SymbolPlacementType, const float placementAngle, WritingModeType writingModes); @@ -79,7 +81,6 @@ class SymbolLayout { const MapMode mode; style::SymbolLayoutProperties::Evaluated layout; - float textMaxSize; SpriteAtlas& spriteAtlas; @@ -88,6 +89,9 @@ class SymbolLayout { bool sdfIcons = false; bool iconsNeedLinear = false; + + style::TextSize::UnevaluatedType textSize; + style::IconSize::UnevaluatedType iconSize; GlyphRangeSet ranges; std::vector symbolInstances; diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index e43a87aa2b4..ffef44cab34 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -25,10 +25,15 @@ MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude); MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset); MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos); -template +template struct a_data { static auto name() { return "a_data"; } - using Type = gl::Attribute; + using Type = gl::Attribute; +}; + +struct a_size { + static auto name() { return "a_size"; } + using Type = gl::Attribute; }; template diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 19fe2bc2f66..ef5bd552989 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace mbgl { @@ -11,7 +12,9 @@ using namespace style; static_assert(sizeof(SymbolLayoutVertex) == 16, "expected SymbolLayoutVertex size"); template -Values makeValues(const style::SymbolPropertyValues& values, +Values makeValues(const bool isText, + const style::SymbolPropertyValues& values, + const SymbolSizeData& sizeData, const Size& texsize, const std::array& pixelsToGLUnits, const RenderTile& tile, @@ -30,8 +33,24 @@ Values makeValues(const style::SymbolPropertyValues& values, }}; } - // adjust min/max zooms for variable font sies - float zoomAdjust = std::log(values.paintSize / values.layoutSize) / std::log(2); + float sizeInterpolationT; + float renderSize = sizeData.layoutSize; + float layoutSize = sizeData.layoutSize; + sizeData.sizePropertyValue.match( + [&] (const CompositeFunction&) { + const auto& coveringStops = *sizeData.coveringZoomStops; + const float t = (state.getZoom() - coveringStops.min) / (coveringStops.max - coveringStops.min); + sizeInterpolationT = util::clamp(t, 0.0f, 1.0f); + }, + [&] (const CameraFunction& fn) { + const auto& coveringStops = *sizeData.coveringZoomStops; + const float t = (state.getZoom() - coveringStops.min) / (coveringStops.max - coveringStops.min); + const float lowerValue = fn.evaluate(coveringStops.min); + const float upperValue = fn.evaluate(coveringStops.max); + renderSize = lowerValue + (upperValue - lowerValue) * util::clamp(t, 0.0f, 1.0f); + }, + [&] (const auto&) {} + ); return Values { uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate, @@ -39,23 +58,33 @@ Values makeValues(const style::SymbolPropertyValues& values, state) }, uniforms::u_extrude_scale::Value{ extrudeScale }, uniforms::u_texsize::Value{ std::array {{ float(texsize.width) / 4, float(texsize.height) / 4 }} }, - uniforms::u_zoom::Value{ float((state.getZoom() - zoomAdjust) * 10) }, + uniforms::u_zoom::Value{ float(state.getZoom()) * 10 }, uniforms::u_rotate_with_map::Value{ values.rotationAlignment == AlignmentType::Map }, uniforms::u_texture::Value{ 0 }, uniforms::u_fadetexture::Value{ 1 }, + uniforms::u_is_text::Value{ isText }, + uniforms::u_is_zoom_constant::Value{ sizeData.sizePropertyValue.isZoomConstant() }, + uniforms::u_is_feature_constant::Value{ !sizeData.sizePropertyValue.isDataDriven() }, + uniforms::u_size_t::Value{ sizeInterpolationT }, + uniforms::u_size::Value{ renderSize }, + uniforms::u_layout_size::Value{ layoutSize }, std::forward(args)... }; } SymbolIconProgram::UniformValues -SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values, +SymbolIconProgram::uniformValues(const bool isText, + const style::SymbolPropertyValues& values, + const SymbolSizeData& sizeData, const Size& texsize, const std::array& pixelsToGLUnits, const RenderTile& tile, const TransformState& state) { return makeValues( + isText, values, + sizeData, texsize, pixelsToGLUnits, tile, @@ -64,12 +93,15 @@ SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values, } template -typename SymbolSDFProgram::UniformValues SymbolSDFProgram::uniformValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - const SymbolSDFPart part) +typename SymbolSDFProgram::UniformValues SymbolSDFProgram::uniformValues( + const bool isText, + const style::SymbolPropertyValues& values, + const SymbolSizeData& sizeData, + const Size& texsize, + const std::array& pixelsToGLUnits, + const RenderTile& tile, + const TransformState& state, + const SymbolSDFPart part) { const float scale = values.paintSize / values.sdfScale; @@ -78,7 +110,9 @@ typename SymbolSDFProgram::UniformValues SymbolSDFProgram::UniformValues>( + isText, values, + sizeData, texsize, pixelsToGLUnits, tile, diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 060c7fdd5ac..eeeca641e37 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -1,6 +1,16 @@ #pragma once -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + #include #include #include @@ -10,6 +20,7 @@ #include #include + #include #include @@ -32,12 +43,18 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo); MBGL_DEFINE_UNIFORM_SCALAR(float, u_font_scale); MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale); + +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_text); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_zoom_constant); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_feature_constant); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_size_t); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_size); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_layout_size); } // namespace uniforms struct SymbolLayoutAttributes : gl::Attributes< attributes::a_pos_offset, - attributes::a_data<4, uint16_t>, - attributes::a_size> + attributes::a_data<4, uint16_t>> { static Vertex vertex(Point a, Point o, @@ -66,14 +83,196 @@ struct SymbolLayoutAttributes : gl::Attributes< static_cast(minzoom * 10), static_cast(::fmin(maxzoom, 25) * 10) ) - }}, - {{ }} }; } }; + +struct SymbolSizeAttributes : gl::Attributes { + using SourceFunctionVertex = gl::detail::Vertex>; + struct monostate {}; + using VertexVector = variant< + monostate, + gl::VertexVector, + gl::VertexVector>; + + using VertexBuffer = variant< + monostate, + gl::VertexBuffer, + gl::VertexBuffer>; + + using Attribute = attributes::a_size::Type; + + static Bindings attributeBindings(const VertexBuffer& buffer) { + return buffer.match( + [&] (const monostate&) { return Bindings { Attribute::ConstantBinding {} }; }, + [&] (const gl::VertexBuffer& buffer) { + return Bindings { Attribute::variableBinding(buffer, 0, 1) }; + }, + [&] (const gl::VertexBuffer& buffer) { + return Bindings { Attribute::variableBinding(buffer, 0) }; + } + ); + }; +}; -class SymbolIconProgram : public Program< + +class SymbolSizeData { +public: + SymbolSizeData(float tileZoom, const style::DataDrivenPropertyValue& size, float defaultSize_) + : sizePropertyValue(size), + layoutZoom(tileZoom + 1), + defaultSize(defaultSize_) { + size.match( + [&] (float constantSize) { layoutSize = constantSize; }, + [&] (const style::CameraFunction& fn) { + layoutSize = fn.evaluate(layoutZoom); + coveringZoomStops = fn.coveringZoomStops(tileZoom, tileZoom + 1); + }, + [&] (const style::SourceFunction&) { + vertices = gl::VertexVector {}; + }, + [&] (const style::CompositeFunction& fn) { + coveringZoomStops = fn.coveringZoomStops(tileZoom, tileZoom + 1); + vertices = gl::VertexVector {}; + }, + [&] (const auto&) {} + ); + }; + + + void upload(gl::Context& context) { + vertices.match( + // TODO: what's the best way to avoid copying the vertexVector here? + [&] (gl::VertexVector vertexVector) { + // TODO - why does directly assigning vertexBuffer = context.createVertexBuffer(...) fail? + vertexBuffer = SymbolSizeAttributes::VertexBuffer { context.createVertexBuffer(std::move(vertexVector)) }; + }, + [&] (gl::VertexVector vertexVector) { + vertexBuffer = SymbolSizeAttributes::VertexBuffer { context.createVertexBuffer(std::move(vertexVector)) }; + }, + [&] (const auto&) {} + ); + } + + style::DataDrivenPropertyValue sizePropertyValue; + float layoutZoom; + float layoutSize; + float defaultSize; + optional> coveringZoomStops; + SymbolSizeAttributes::VertexVector vertices; + optional vertexBuffer; +}; + + +template +class SymbolProgram { +public: + using LayoutAttributes = LayoutAttrs; + using LayoutVertex = typename LayoutAttributes::Vertex; + + using LayoutAndSizeAttributes = gl::ConcatenateAttributes; + + using PaintPropertyBinders = typename PaintProperties::Binders; + using PaintAttributes = typename PaintPropertyBinders::Attributes; + using Attributes = gl::ConcatenateAttributes; + + using UniformValues = typename Uniforms::Values; + using PaintUniforms = typename PaintPropertyBinders::Uniforms; + using AllUniforms = gl::ConcatenateUniforms; + + using ProgramType = gl::Program; + + ProgramType program; + + SymbolProgram(gl::Context& context, const ProgramParameters& programParameters) + : program([&] { +#if MBGL_HAS_BINARY_PROGRAMS + if (!programParameters.cacheDir.empty() && context.supportsProgramBinaries()) { + const std::string vertexSource = + shaders::vertexSource(programParameters, Shaders::vertexSource); + const std::string fragmentSource = + shaders::fragmentSource(programParameters, Shaders::fragmentSource); + const std::string cachePath = + shaders::programCachePath(programParameters, Shaders::name); + const std::string identifier = + shaders::programIdentifier(vertexSource, fragmentSource); + + try { + if (auto cachedBinaryProgram = util::readFile(cachePath)) { + const BinaryProgram binaryProgram(std::move(*cachedBinaryProgram)); + if (binaryProgram.identifier() == identifier) { + return ProgramType{ context, binaryProgram }; + } else { + Log::Warning(Event::OpenGL, + "Cached program %s changed. Recompilation required.", + Shaders::name); + } + } + } catch (std::runtime_error& error) { + Log::Warning(Event::OpenGL, "Could not load cached program: %s", + error.what()); + } + + // Compile the shader + ProgramType result{ context, vertexSource, fragmentSource }; + + try { + if (const auto binaryProgram = + result.template get(context, identifier)) { + util::write_file(cachePath, binaryProgram->serialize()); + Log::Warning(Event::OpenGL, "Caching program in: %s", cachePath.c_str()); + } + } catch (std::runtime_error& error) { + Log::Warning(Event::OpenGL, "Failed to cache program: %s", error.what()); + } + + return std::move(result); + } +#endif + return ProgramType{ + context, shaders::vertexSource(programParameters, Shaders::vertexSource), + shaders::fragmentSource(programParameters, Shaders::fragmentSource) + }; + }()) { + } + + template + void draw(gl::Context& context, + DrawMode drawMode, + gl::DepthMode depthMode, + gl::StencilMode stencilMode, + gl::ColorMode colorMode, + UniformValues&& uniformValues, + const gl::VertexBuffer& layoutVertexBuffer, + const SymbolSizeAttributes::VertexBuffer& sizeVertexBuffer, + const gl::IndexBuffer& indexBuffer, + const gl::SegmentVector& segments, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::Evaluated& currentProperties, + float currentZoom) { + program.draw( + context, + std::move(drawMode), + std::move(depthMode), + std::move(stencilMode), + std::move(colorMode), + uniformValues + .concat(paintPropertyBinders.uniformValues(currentZoom)), + LayoutAttributes::allVariableBindings(layoutVertexBuffer) + .concat(SymbolSizeAttributes::attributeBindings(sizeVertexBuffer)) + .concat(paintPropertyBinders.attributeBindings(currentProperties)), + indexBuffer, + segments + ); + } +}; + +class SymbolIconProgram : public SymbolProgram< shaders::symbol_icon, gl::Triangle, SymbolLayoutAttributes, @@ -84,13 +283,21 @@ class SymbolIconProgram : public Program< uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, - uniforms::u_fadetexture>, + uniforms::u_fadetexture, + uniforms::u_is_text, + uniforms::u_is_zoom_constant, + uniforms::u_is_feature_constant, + uniforms::u_size_t, + uniforms::u_size, + uniforms::u_layout_size>, style::IconPaintProperties> { public: - using Program::Program; + using SymbolProgram::SymbolProgram; - static UniformValues uniformValues(const style::SymbolPropertyValues&, + static UniformValues uniformValues(const bool isText, + const style::SymbolPropertyValues&, + const SymbolSizeData& sizeData, const Size& texsize, const std::array& pixelsToGLUnits, const RenderTile&, @@ -103,7 +310,7 @@ enum class SymbolSDFPart { }; template -class SymbolSDFProgram : public Program< +class SymbolSDFProgram : public SymbolProgram< shaders::symbol_sdf, gl::Triangle, SymbolLayoutAttributes, @@ -115,6 +322,12 @@ class SymbolSDFProgram : public Program< uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, + uniforms::u_is_text, + uniforms::u_is_zoom_constant, + uniforms::u_is_feature_constant, + uniforms::u_size_t, + uniforms::u_size, + uniforms::u_layout_size, uniforms::u_font_scale, uniforms::u_gamma_scale, uniforms::u_pitch, @@ -125,7 +338,7 @@ class SymbolSDFProgram : public Program< PaintProperties> { public: - using BaseProgram = Program& pixelsToGLUnits, const RenderTile&, diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index 48c2e7ff663..de9aa6fe46e 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -33,6 +33,7 @@ void Painter::renderSymbol(PaintParameters& parameters, auto draw = [&] (auto& program, auto&& uniformValues, const auto& buffers, + const optional& sizeBuffer, const SymbolPropertyValues& values_, const auto& binders, const auto& paintProperties) @@ -52,6 +53,7 @@ void Painter::renderSymbol(PaintParameters& parameters, colorModeForRenderPass(), std::move(uniformValues), *buffers.vertexBuffer, + *sizeBuffer, *buffers.indexBuffer, buffers.segments, binders, @@ -74,8 +76,9 @@ void Painter::renderSymbol(PaintParameters& parameters, if (bucket.sdfIcons) { if (values.hasHalo) { draw(parameters.programs.symbolIconSDF, - SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), + SymbolSDFIconProgram::uniformValues(false, values, bucket.iconSizeData, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.icon, + bucket.iconSizeData.vertexBuffer, values, bucket.paintPropertyBinders.at(layer.getID()).first, paintPropertyValues); @@ -83,16 +86,18 @@ void Painter::renderSymbol(PaintParameters& parameters, if (values.hasFill) { draw(parameters.programs.symbolIconSDF, - SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), + SymbolSDFIconProgram::uniformValues(false, values, bucket.iconSizeData, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.icon, + bucket.iconSizeData.vertexBuffer, values, bucket.paintPropertyBinders.at(layer.getID()).first, paintPropertyValues); } } else { draw(parameters.programs.symbolIcon, - SymbolIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state), + SymbolIconProgram::uniformValues(false, values, bucket.iconSizeData, texsize, pixelsToGLUnits, tile, state), bucket.icon, + bucket.iconSizeData.vertexBuffer, values, bucket.paintPropertyBinders.at(layer.getID()).first, paintPropertyValues); @@ -109,8 +114,9 @@ void Painter::renderSymbol(PaintParameters& parameters, if (values.hasHalo) { draw(parameters.programs.symbolGlyph, - SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), + SymbolSDFTextProgram::uniformValues(true, values, bucket.textSizeData, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.text, + bucket.textSizeData.vertexBuffer, values, bucket.paintPropertyBinders.at(layer.getID()).second, paintPropertyValues); @@ -118,8 +124,9 @@ void Painter::renderSymbol(PaintParameters& parameters, if (values.hasFill) { draw(parameters.programs.symbolGlyph, - SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), + SymbolSDFTextProgram::uniformValues(true, values, bucket.textSizeData, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.text, + bucket.textSizeData.vertexBuffer, values, bucket.paintPropertyBinders.at(layer.getID()).second, paintPropertyValues); diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 1f2c81bc1c9..4d42196a2ee 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -11,12 +11,17 @@ using namespace style; SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::Evaluated layout_, const std::map>& layerPaintProperties, + const style::DataDrivenPropertyValue& textSize, + const style::DataDrivenPropertyValue& iconSize, float zoom, bool sdfIcons_, bool iconsNeedLinear_) : layout(std::move(layout_)), sdfIcons(sdfIcons_), - iconsNeedLinear(iconsNeedLinear_) { + iconsNeedLinear(iconsNeedLinear_), + textSizeData(zoom, textSize, TextSize::defaultValue()), + iconSizeData(zoom, iconSize, IconSize::defaultValue()) { + for (const auto& pair : layerPaintProperties) { paintPropertyBinders.emplace( std::piecewise_construct, @@ -32,11 +37,13 @@ void SymbolBucket::upload(gl::Context& context) { if (hasTextData()) { text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); text.indexBuffer = context.createIndexBuffer(std::move(text.triangles)); + textSizeData.upload(context); } if (hasIconData()) { icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); + iconSizeData.upload(context); } if (!collisionBox.vertices.empty()) { diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index d5141d2d96e..150c005ce20 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -18,6 +19,8 @@ class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::Evaluated, const std::map>&, + const style::DataDrivenPropertyValue& textSize, + const style::DataDrivenPropertyValue& iconSize, float zoom, bool sdfIcons, bool iconsNeedLinear); @@ -36,6 +39,8 @@ class SymbolBucket : public Bucket { std::map> paintPropertyBinders; + + SymbolSizeData textSizeData; struct TextBuffer { gl::VertexVector vertices; @@ -45,7 +50,9 @@ class SymbolBucket : public Bucket { optional> vertexBuffer; optional> indexBuffer; } text; - + + SymbolSizeData iconSizeData; + struct IconBuffer { gl::VertexVector vertices; gl::IndexVector triangles; diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index ff59b14d65c..31347f65072 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.cpp +++ b/src/mbgl/style/layers/symbol_layer_impl.cpp @@ -13,10 +13,6 @@ void SymbolLayer::Impl::cascade(const CascadeParameters& parameters) { bool SymbolLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) { paint.evaluate(parameters); - // text-size and icon-size are layout properties but they also need to be evaluated as paint properties: - iconSize = layout.evaluate(parameters); - textSize = layout.evaluate(parameters); - auto hasIconOpacity = paint.evaluated.get().constantOr(Color::black()).a > 0 || paint.evaluated.get().constantOr(Color::black()).a > 0; auto hasTextOpacity = paint.evaluated.get().constantOr(Color::black()).a > 0 || @@ -72,7 +68,6 @@ SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutPro return SymbolPropertyValues { layout_.get(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment layout_.get(), - layout_.get(), paint.evaluated.get(), paint.evaluated.get(), iconSize, @@ -87,7 +82,6 @@ SymbolPropertyValues SymbolLayer::Impl::textPropertyValues(const SymbolLayoutPro return SymbolPropertyValues { layout_.get(), layout_.get(), - layout_.get(), paint.evaluated.get(), paint.evaluated.get(), textSize, diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp index 1e9f05e4c77..5b9db4b93e8 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.hpp +++ b/src/mbgl/style/layers/symbol_layer_impl.hpp @@ -43,7 +43,6 @@ class SymbolPropertyValues { // Layout AlignmentType pitchAlignment; AlignmentType rotationAlignment; - float layoutSize; // Paint std::array translate; diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index b13a6a71e54..188f88655d7 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -19,6 +19,7 @@ SymbolQuad getIconQuad(const Anchor& anchor, const PositionedIcon& shapedIcon, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, + const float layoutTextSize, const style::SymbolPlacementType placement, const Shaping& shapedText) { auto image = *(shapedIcon.image); @@ -36,7 +37,7 @@ SymbolQuad getIconQuad(const Anchor& anchor, if (layout.get() != IconTextFitType::None && shapedText) { auto iconWidth = right - left; auto iconHeight = bottom - top; - auto size = layout.get() / 24.0f; + auto size = layoutTextSize / 24.0f; auto textLeft = shapedText.left * size; auto textRight = shapedText.right * size; auto textTop = shapedText.top * size; diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index f1529d88294..333000627b0 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -55,6 +55,7 @@ SymbolQuad getIconQuad(const Anchor& anchor, const PositionedIcon& shapedIcon, const GeometryCoordinates& line, const style::SymbolLayoutProperties::Evaluated&, + const float layoutTextSize, style::SymbolPlacementType placement, const Shaping& shapedText);