diff options
author | Anand Thakker <anandthakker@users.noreply.github.com> | 2017-04-06 15:29:59 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-06 15:29:59 -0400 |
commit | 693c9f3641b3189b4cd439049904c95a516ae609 (patch) | |
tree | 8341a16f57ff184a2fe9e085c490e8762eb206ce /src/mbgl | |
parent | f9cc044357d60dd5cf15ba951384529f88802089 (diff) | |
download | qtlocation-mapboxgl-693c9f3641b3189b4cd439049904c95a516ae609.tar.gz |
[core] Add DDS support for {text,icon}-size (#8593)
* Update gl-js and generate style code
* Factor out packUint8Pair() helper function
* Draft implementation of DDS for {text,icon}-size
Ports https://github.com/mapbox/mapbox-gl-js/pull/4455
* Fix text-size/composite-function-line-placement test
* Refactor to PaintPropertyBinders-like strategy
* Dedupe gl::Program construction
* Use exponential function base for interpolation
* Dedupe coveringZoomStops method
* Fixup tests
* Fix CI errors (hidden within #if block)
Diffstat (limited to 'src/mbgl')
24 files changed, 681 insertions, 170 deletions
diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index 7d7fca5263..1b23abe2b1 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -8,6 +8,12 @@ #include <mbgl/gl/attribute.hpp> #include <mbgl/gl/uniform.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/programs/binary_program.hpp> +#include <mbgl/programs/program_parameters.hpp> +#include <mbgl/shaders/shaders.hpp> + + #include <string> namespace mbgl { @@ -37,6 +43,61 @@ public: attributeLocations(Attributes::loadNamedLocations(binaryProgram)), uniformsState(Uniforms::loadNamedLocations(binaryProgram)) { } + + static Program createProgram(gl::Context& context, + const ProgramParameters& programParameters, + const char* name, + const char* vertexSource_, + const char* fragmentSource_) { +#if MBGL_HAS_BINARY_PROGRAMS + if (!programParameters.cacheDir.empty() && context.supportsProgramBinaries()) { + const std::string vertexSource = + shaders::vertexSource(programParameters, vertexSource_); + const std::string fragmentSource = + shaders::fragmentSource(programParameters, fragmentSource_); + const std::string cachePath = + shaders::programCachePath(programParameters, 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 Program { context, binaryProgram }; + } else { + Log::Warning(Event::OpenGL, + "Cached program %s changed. Recompilation required.", + name); + } + } + } catch (std::runtime_error& error) { + Log::Warning(Event::OpenGL, "Could not load cached program: %s", + error.what()); + } + + // Compile the shader + Program result{ context, vertexSource, fragmentSource }; + + try { + if (const auto binaryProgram = + result.template get<BinaryProgram>(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 + (void)name; + return Program { + context, shaders::vertexSource(programParameters, vertexSource_), + shaders::fragmentSource(programParameters, fragmentSource_) + }; + } template <class BinaryProgram> optional<BinaryProgram> get(Context& context, const std::string& identifier) const { diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 8bdc528bbb..2a3bf068c1 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<Shaping, Shaping>& 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 70ebfeefa2..efe6cb05aa 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -17,6 +17,7 @@ public: const std::pair<Shaping, Shaping>& 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 978caabd70..907e60a598 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -50,7 +50,10 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, mode(parameters.mode), spriteAtlasMapIndex(_spriteAtlasMapIndex), tileSize(util::tileSize * overscaling), - tilePixelRatio(float(util::EXTENT) / tileSize) { + tilePixelRatio(float(util::EXTENT) / tileSize), + textSize(layers.at(0)->as<SymbolLayer>()->impl->layout.unevaluated.get<TextSize>()), + iconSize(layers.at(0)->as<SymbolLayer>()->impl->layout.unevaluated.get<IconSize>()) + { const SymbolLayer::Impl& leader = *layers.at(0)->as<SymbolLayer>()->impl; @@ -77,11 +80,6 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, layout.get<TextPitchAlignment>() = layout.get<TextRotationAlignment>(); } - textMaxSize = leader.layout.evaluate<TextSize>(PropertyEvaluationParameters(18)); - - layout.get<IconSize>() = leader.layout.evaluate<IconSize>(PropertyEvaluationParameters(zoom + 1)); - layout.get<TextSize>() = leader.layout.evaluate<TextSize>(PropertyEvaluationParameters(zoom + 1)); - const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty(); const bool hasIcon = has<IconImage>(layout); @@ -298,11 +296,20 @@ void SymbolLayout::addFeature(const std::size_t index, const GlyphPositions& glyphs) { const float minScale = 0.5f; const float glyphSize = 24.0f; - - const float fontScale = layout.get<TextSize>() / glyphSize; + + const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature); + const float layoutIconSize = layout.evaluate<IconSize>(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<TextSize>(18, feature); + + const float fontScale = layoutTextSize / glyphSize; const float textBoxScale = tilePixelRatio * fontScale; const float textMaxBoxScale = tilePixelRatio * textMaxSize / glyphSize; - const float iconBoxScale = tilePixelRatio * layout.get<IconSize>(); + const float iconBoxScale = tilePixelRatio * layoutIconSize; const float symbolSpacing = tilePixelRatio * layout.get<SymbolSpacing>(); const bool avoidEdges = layout.get<SymbolAvoidEdges>() && layout.get<SymbolPlacement>() != SymbolPlacementType::Line; const float textPadding = layout.get<TextPadding>() * tilePixelRatio; @@ -316,7 +323,7 @@ void SymbolLayout::addFeature(const std::size_t index, : layout.get<SymbolPlacement>(); 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 // +-------------------+ Symbols with anchors located on tile edges @@ -338,7 +345,9 @@ void SymbolLayout::addFeature(const std::size_t index, const bool addToBuffers = mode == MapMode::Still || withinPlus0; - symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout.evaluate(zoom, feature), addToBuffers, symbolInstances.size(), + symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, + layout.evaluate(zoom, feature), layoutTextSize, + addToBuffers, symbolInstances.size(), textBoxScale, textPadding, textPlacement, iconBoxScale, iconPadding, iconPlacement, glyphs, indexedFeature, index); @@ -413,7 +422,7 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe } std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) { - auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, zoom, sdfIcons, iconsNeedLinear); + auto bucket = std::make_unique<SymbolBucket>(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. @@ -477,6 +486,7 @@ std::unique_ptr<SymbolBucket> 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 @@ -486,7 +496,7 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) if (glyphScale < collisionTile.maxScale) { for (const auto& symbol : symbolInstance.glyphQuads) { addSymbol( - bucket->text, symbol, placementZoom, + bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom, keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes); } } @@ -497,12 +507,11 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>()); if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) { addSymbol( - bucket->icon, *symbolInstance.iconQuad, placementZoom, + bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, 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()); @@ -518,7 +527,9 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) template <typename Buffer> void SymbolLayout::addSymbol(Buffer& buffer, + SymbolSizeBinder& sizeBinder, const SymbolQuad& symbol, + const SymbolFeature& feature, const float placementZoom, const bool keepUpright, const style::SymbolPlacementType placement, @@ -580,6 +591,8 @@ 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)); + + sizeBinder.populateVertexVector(feature); // 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 d79ccb3273..4ec84f0f58 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -6,6 +6,7 @@ #include <mbgl/layout/symbol_instance.hpp> #include <mbgl/text/bidi.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/programs/symbol_program.hpp> #include <memory> #include <map> @@ -64,9 +65,15 @@ private: // Adds placed items to the buffer. template <typename Buffer> - void addSymbol(Buffer&, const SymbolQuad&, float scale, - const bool keepUpright, const style::SymbolPlacementType, const float placementAngle, - WritingModeType writingModes); + void addSymbol(Buffer&, + SymbolSizeBinder& sizeBinder, + const SymbolQuad&, + const SymbolFeature& feature, + float scale, + const bool keepUpright, + const style::SymbolPlacementType, + const float placementAngle, + WritingModeType writingModes); const std::string sourceLayerName; const std::string bucketName; @@ -75,7 +82,6 @@ private: const MapMode mode; style::SymbolLayoutProperties::PossiblyEvaluated layout; - float textMaxSize; uintptr_t spriteAtlasMapIndex; // Actually a pointer to the SpriteAtlas for this symbol's layer, but don't use it from worker threads! @@ -84,6 +90,9 @@ private: bool sdfIcons = false; bool iconsNeedLinear = false; + + style::TextSize::UnevaluatedType textSize; + style::IconSize::UnevaluatedType iconSize; std::vector<SymbolInstance> symbolInstances; std::vector<SymbolFeature> features; diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index 7d39c04395..e9ca18927e 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -8,6 +8,16 @@ namespace mbgl { namespace attributes { +/* + * Pack a pair of values, interpreted as uint8's, into a single float. + * Used to conserve vertex attributes. Values are unpacked in the vertex + * shader using the `unpack_float()` function, defined in _prelude.vertex.glsl. + */ +template <typename T> +inline uint16_t packUint8Pair(T a, T b) { + return static_cast<uint16_t>(a) * 256 + static_cast<uint16_t>(b); +} + // Layout attributes MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos); @@ -15,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 <std::size_t N> +template <typename T, std::size_t N> struct a_data { static auto name() { return "a_data"; } - using Type = gl::Attribute<uint8_t, N>; + using Type = gl::Attribute<T, N>; +}; + +struct a_size { + static auto name() { return "a_size"; } + using Type = gl::Attribute<uint16_t, 3>; }; template <std::size_t N> diff --git a/src/mbgl/programs/collision_box_program.hpp b/src/mbgl/programs/collision_box_program.hpp index 78ed6aa0c9..89b69484fd 100644 --- a/src/mbgl/programs/collision_box_program.hpp +++ b/src/mbgl/programs/collision_box_program.hpp @@ -18,7 +18,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom); using CollisionBoxAttributes = gl::Attributes< attributes::a_pos, attributes::a_extrude, - attributes::a_data<2>>; + attributes::a_data<uint8_t, 2>>; class CollisionBoxProgram : public Program< shaders::collision_box, diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp index 842b4cc602..b2e55a4f3b 100644 --- a/src/mbgl/programs/line_program.hpp +++ b/src/mbgl/programs/line_program.hpp @@ -32,7 +32,7 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_gl_units_to_pixels); struct LineLayoutAttributes : gl::Attributes< attributes::a_pos, - attributes::a_data<4>> + attributes::a_data<uint8_t, 4>> {}; class LineProgram : public Program< diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp index 8925bc75d6..7eec15e755 100644 --- a/src/mbgl/programs/program.hpp +++ b/src/mbgl/programs/program.hpp @@ -34,55 +34,12 @@ public: ProgramType program; Program(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<BinaryProgram>(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) - }; - }()) { + : program(ProgramType::createProgram( + context, + programParameters, + Shaders::name, + Shaders::vertexSource, + Shaders::fragmentSource)) { } template <class DrawMode> diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 19fe2bc2f6..86f61c4ad2 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -3,6 +3,7 @@ #include <mbgl/map/transform_state.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/util/enum.hpp> +#include <mbgl/math/clamp.hpp> namespace mbgl { @@ -10,51 +11,66 @@ using namespace style; static_assert(sizeof(SymbolLayoutVertex) == 16, "expected SymbolLayoutVertex size"); +std::unique_ptr<SymbolSizeBinder> SymbolSizeBinder::create(const float tileZoom, + const style::DataDrivenPropertyValue<float>& sizeProperty, + const float defaultValue) { + return sizeProperty.match( + [&] (const style::CompositeFunction<float>& function) -> std::unique_ptr<SymbolSizeBinder> { + return std::make_unique<CompositeFunctionSymbolSizeBinder>(tileZoom, function, defaultValue); + }, + [&] (const style::SourceFunction<float>& function) { + return std::make_unique<SourceFunctionSymbolSizeBinder>(tileZoom, function, defaultValue); + }, + [&] (const auto& value) -> std::unique_ptr<SymbolSizeBinder> { + return std::make_unique<ConstantSymbolSizeBinder>(tileZoom, value, defaultValue); + } + ); +} + template <class Values, class...Args> -Values makeValues(const style::SymbolPropertyValues& values, +Values makeValues(const bool isText, + const style::SymbolPropertyValues& values, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, Args&&... args) { std::array<float, 2> extrudeScale; - - const float scale = values.paintSize / values.sdfScale; if (values.pitchAlignment == AlignmentType::Map) { - extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()) * scale); + extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom())); } else { extrudeScale = {{ - pixelsToGLUnits[0] * scale * state.getCameraToCenterDistance(), - pixelsToGLUnits[1] * scale * state.getCameraToCenterDistance() + pixelsToGLUnits[0] * state.getCameraToCenterDistance(), + pixelsToGLUnits[1] * state.getCameraToCenterDistance() }}; } - - // adjust min/max zooms for variable font sies - float zoomAdjust = std::log(values.paintSize / values.layoutSize) / std::log(2); - + return Values { uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate, values.translateAnchor, state) }, uniforms::u_extrude_scale::Value{ extrudeScale }, uniforms::u_texsize::Value{ std::array<float, 2> {{ float(texsize.width) / 4, float(texsize.height) / 4 }} }, - uniforms::u_zoom::Value{ float((state.getZoom() - zoomAdjust) * 10) }, + uniforms::u_zoom::Value{ float(state.getZoom()) }, 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 }, std::forward<Args>(args)... }; } SymbolIconProgram::UniformValues -SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values, +SymbolIconProgram::uniformValues(const bool isText, + const style::SymbolPropertyValues& values, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state) { return makeValues<SymbolIconProgram::UniformValues>( + isText, values, texsize, pixelsToGLUnits, @@ -64,26 +80,26 @@ SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values, } template <class PaintProperties> -typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<PaintProperties>::uniformValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - const SymbolSDFPart part) +typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<PaintProperties>::uniformValues( + const bool isText, + const style::SymbolPropertyValues& values, + const Size& texsize, + const std::array<float, 2>& pixelsToGLUnits, + const RenderTile& tile, + const TransformState& state, + const SymbolSDFPart part) { - const float scale = values.paintSize / values.sdfScale; - - const float gammaScale = scale * (values.pitchAlignment == AlignmentType::Map + const float gammaScale = (values.pitchAlignment == AlignmentType::Map ? std::cos(state.getPitch()) : 1.0) * state.getCameraToCenterDistance(); return makeValues<SymbolSDFProgram<PaintProperties>::UniformValues>( + isText, values, texsize, pixelsToGLUnits, tile, state, - uniforms::u_font_scale::Value{ scale }, uniforms::u_gamma_scale::Value{ gammaScale }, uniforms::u_pitch::Value{ state.getPitch() }, uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index fdd1aa5c3b..ae50e790be 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -1,6 +1,10 @@ #pragma once -#include <mbgl/programs/program.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/program.hpp> +#include <mbgl/math/clamp.hpp> +#include <mbgl/util/interpolate.hpp> + #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> #include <mbgl/shaders/symbol_icon.hpp> @@ -10,6 +14,7 @@ #include <mbgl/style/layers/symbol_layer_properties.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> + #include <cmath> #include <array> @@ -30,14 +35,19 @@ MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture); 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_size_zoom_constant); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_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_texture_pos, - attributes::a_data<4>> + attributes::a_data<uint16_t, 4>> { static Vertex vertex(Point<float> a, Point<float> o, @@ -57,19 +67,324 @@ struct SymbolLayoutAttributes : gl::Attributes< }}, {{ static_cast<uint16_t>(tx / 4), - static_cast<uint16_t>(ty / 4) - }}, + static_cast<uint16_t>(ty / 4), + mbgl::attributes::packUint8Pair( + static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160 + static_cast<uint8_t>(labelangle) + ), + mbgl::attributes::packUint8Pair( + static_cast<uint8_t>(minzoom * 10), + static_cast<uint8_t>(::fmin(maxzoom, 25) * 10) + ) + }} + }; + } +}; + +class SymbolSizeAttributes : public gl::Attributes<attributes::a_size> { +public: + using Attribute = attributes::a_size::Type; +}; + +// 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: + using Uniforms = gl::Uniforms< + uniforms::u_is_size_zoom_constant, + uniforms::u_is_size_feature_constant, + uniforms::u_size_t, + uniforms::u_size, + uniforms::u_layout_size>; + using UniformValues = Uniforms::Values; + + static std::unique_ptr<SymbolSizeBinder> create(const float tileZoom, + const style::DataDrivenPropertyValue<float>& sizeProperty, + const float defaultValue); + + virtual SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const = 0; + virtual void populateVertexVector(const GeometryTileFeature& feature) = 0; + virtual UniformValues uniformValues(float currentZoom) const = 0; + virtual void upload(gl::Context&) = 0; +}; + +// Return the smallest range of stops that covers the interval [lowerZoom, upperZoom] +template <class Stops> +Range<float> getCoveringStops(Stops s, float lowerZoom, float upperZoom) { + assert(!s.stops.empty()); + auto minIt = s.stops.lower_bound(lowerZoom); + auto maxIt = s.stops.lower_bound(upperZoom); + + // lower_bound yields first element >= lowerZoom, but we want the *last* + // element <= lowerZoom, so if we found a stop > lowerZoom, back up by one. + if (minIt != s.stops.begin() && minIt->first > lowerZoom) { + minIt--; + } + return Range<float> { + minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first, + maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first + }; +} + +class ConstantSymbolSizeBinder : public SymbolSizeBinder { +public: + using PropertyValue = variant<float, style::CameraFunction<float>>; + + ConstantSymbolSizeBinder(const float /*tileZoom*/, const float& size, const float /*defaultValue*/) + : layoutSize(size) {} + + ConstantSymbolSizeBinder(const float /*tileZoom*/, const style::Undefined&, const float defaultValue) + : layoutSize(defaultValue) {} + + ConstantSymbolSizeBinder(const float tileZoom, const style::CameraFunction<float>& function_, const float /*defaultValue*/) + : layoutSize(function_.evaluate(tileZoom + 1)) { + function_.stops.match( + [&] (const style::ExponentialStops<float>& stops) { + coveringRanges = std::make_tuple( + getCoveringStops(stops, tileZoom, tileZoom + 1), + Range<float> { function_.evaluate(tileZoom), function_.evaluate(tileZoom + 1) } + ); + functionInterpolationBase = stops.base; + }, + [&] (const style::IntervalStops<float>&) { + function = function_; + } + ); + } + + SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float>) const override { + return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} }; + } + void upload(gl::Context&) override {} + void populateVertexVector(const GeometryTileFeature&) override {}; + + UniformValues uniformValues(float currentZoom) const override { + float size = layoutSize; + bool isZoomConstant = !(coveringRanges || function); + 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<float>& zoomLevels = std::get<0>(*coveringRanges); + const Range<float>& sizeLevels = std::get<1>(*coveringRanges); + float t = util::clamp( + util::interpolationFactor(*functionInterpolationBase, zoomLevels, currentZoom), + 0.0f, 1.0f + ); + size = sizeLevels.min + t * (sizeLevels.max - sizeLevels.min); + } else if (function) { + size = function->evaluate(currentZoom); + } + + return UniformValues { + uniforms::u_is_size_zoom_constant::Value{ isZoomConstant }, + uniforms::u_is_size_feature_constant::Value{ true }, + uniforms::u_size_t::Value{ 0.0f }, // unused + uniforms::u_size::Value{ size }, + uniforms::u_layout_size::Value{ layoutSize } + }; + } + + float layoutSize; + // used for exponential functions + optional<std::tuple<Range<float>, Range<float>>> coveringRanges; + optional<float> functionInterpolationBase; + // used for interval functions + optional<style::CameraFunction<float>> function; +}; + +class SourceFunctionSymbolSizeBinder : public SymbolSizeBinder { +public: + using Vertex = gl::detail::Vertex<gl::Attribute<uint16_t, 1>>; + using VertexVector = gl::VertexVector<Vertex>; + using VertexBuffer = gl::VertexBuffer<Vertex>; + + SourceFunctionSymbolSizeBinder(const float /*tileZoom*/, const style::SourceFunction<float>& function_, const float defaultValue_) + : function(function_), + defaultValue(defaultValue_) { + } + + SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const override { + if (currentValue.isConstant()) { + return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} }; + } + + return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::variableBinding(*buffer, 0, 1) }; + } + + void populateVertexVector(const GeometryTileFeature& feature) override { + const auto sizeVertex = Vertex { {{ - static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160 - static_cast<uint8_t>(labelangle), - static_cast<uint8_t>(minzoom * 10), - static_cast<uint8_t>(::fmin(maxzoom, 25) * 10) + static_cast<uint16_t>(function.evaluate(feature, defaultValue) * 10) }} }; + + vertices.emplace_back(sizeVertex); + vertices.emplace_back(sizeVertex); + vertices.emplace_back(sizeVertex); + vertices.emplace_back(sizeVertex); + }; + + UniformValues uniformValues(float) const override { + return UniformValues { + uniforms::u_is_size_zoom_constant::Value{ true }, + uniforms::u_is_size_feature_constant::Value{ false }, + uniforms::u_size_t::Value{ 0.0f }, // unused + uniforms::u_size::Value{ 0.0f }, // unused + uniforms::u_layout_size::Value{ 0.0f } // unused + }; + } + + void upload(gl::Context& context) override { + buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) }; + } + + const style::SourceFunction<float>& function; + const float defaultValue; + + VertexVector vertices; + optional<VertexBuffer> buffer; +}; + +class CompositeFunctionSymbolSizeBinder: public SymbolSizeBinder { +public: + using Vertex = SymbolSizeAttributes::Vertex; + using VertexVector = gl::VertexVector<Vertex>; + using VertexBuffer = gl::VertexBuffer<Vertex>; + + CompositeFunctionSymbolSizeBinder(const float tileZoom, const style::CompositeFunction<float>& function_, const float defaultValue_) + : function(function_), + defaultValue(defaultValue_), + layoutZoom(tileZoom + 1), + coveringZoomStops(function.stops.match( + [&] (const auto& stops) { + return getCoveringStops(stops, tileZoom, tileZoom + 1); })) + {} + + SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const override { + if (currentValue.isConstant()) { + return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} }; + } + + return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::variableBinding(*buffer, 0) }; + } + + void populateVertexVector(const GeometryTileFeature& feature) override { + const auto sizeVertex = Vertex { + {{ + static_cast<uint16_t>(function.evaluate(coveringZoomStops.min, feature, defaultValue) * 10), + static_cast<uint16_t>(function.evaluate(coveringZoomStops.max, feature, defaultValue) * 10), + static_cast<uint16_t>(function.evaluate(layoutZoom, feature, defaultValue) * 10) + }} + }; + + vertices.emplace_back(sizeVertex); + vertices.emplace_back(sizeVertex); + vertices.emplace_back(sizeVertex); + vertices.emplace_back(sizeVertex); + }; + + UniformValues uniformValues(float currentZoom) const override { + float sizeInterpolationT = util::clamp( + util::interpolationFactor(1.0f, coveringZoomStops, currentZoom), + 0.0f, 1.0f + ); + + return UniformValues { + uniforms::u_is_size_zoom_constant::Value{ false }, + uniforms::u_is_size_feature_constant::Value{ false }, + uniforms::u_size_t::Value{ sizeInterpolationT }, + uniforms::u_size::Value{ 0.0f }, // unused + uniforms::u_layout_size::Value{ 0.0f } // unused + }; + } + + void upload(gl::Context& context) override { + buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) }; } + + const style::CompositeFunction<float>& function; + const float defaultValue; + float layoutZoom; + Range<float> coveringZoomStops; + + VertexVector vertices; + optional<VertexBuffer> buffer; }; -class SymbolIconProgram : public Program< + +template <class Shaders, + class Primitive, + class LayoutAttrs, + class Uniforms, + class PaintProperties> +class SymbolProgram { +public: + using LayoutAttributes = LayoutAttrs; + using LayoutVertex = typename LayoutAttributes::Vertex; + + using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolSizeAttributes>; + + using PaintPropertyBinders = typename PaintProperties::Binders; + using PaintAttributes = typename PaintPropertyBinders::Attributes; + using Attributes = gl::ConcatenateAttributes<LayoutAndSizeAttributes, PaintAttributes>; + + using UniformValues = typename Uniforms::Values; + using SizeUniforms = typename SymbolSizeBinder::Uniforms; + using PaintUniforms = typename PaintPropertyBinders::Uniforms; + using AllUniforms = gl::ConcatenateUniforms<Uniforms, gl::ConcatenateUniforms<SizeUniforms, PaintUniforms>>; + + using ProgramType = gl::Program<Primitive, Attributes, AllUniforms>; + + ProgramType program; + + SymbolProgram(gl::Context& context, const ProgramParameters& programParameters) + : program(ProgramType::createProgram( + context, + programParameters, + Shaders::name, + Shaders::vertexSource, + Shaders::fragmentSource)) { + } + + template <class DrawMode> + void draw(gl::Context& context, + DrawMode drawMode, + gl::DepthMode depthMode, + gl::StencilMode stencilMode, + gl::ColorMode colorMode, + UniformValues&& uniformValues, + const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer, + const SymbolSizeBinder& symbolSizeBinder, + const style::PossiblyEvaluatedPropertyValue<float>& currentSizeValue, + const gl::IndexBuffer<DrawMode>& indexBuffer, + const gl::SegmentVector<Attributes>& 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(symbolSizeBinder.uniformValues(currentZoom)) + .concat(paintPropertyBinders.uniformValues(currentZoom)), + LayoutAttributes::allVariableBindings(layoutVertexBuffer) + .concat(symbolSizeBinder.attributeBindings(currentSizeValue)) + .concat(paintPropertyBinders.attributeBindings(currentProperties)), + indexBuffer, + segments + ); + } +}; + +class SymbolIconProgram : public SymbolProgram< shaders::symbol_icon, gl::Triangle, SymbolLayoutAttributes, @@ -80,13 +395,15 @@ 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>, 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 Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile&, @@ -99,7 +416,7 @@ enum class SymbolSDFPart { }; template <class PaintProperties> -class SymbolSDFProgram : public Program< +class SymbolSDFProgram : public SymbolProgram< shaders::symbol_sdf, gl::Triangle, SymbolLayoutAttributes, @@ -111,7 +428,7 @@ class SymbolSDFProgram : public Program< uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, - uniforms::u_font_scale, + uniforms::u_is_text, uniforms::u_gamma_scale, uniforms::u_pitch, uniforms::u_bearing, @@ -121,7 +438,7 @@ class SymbolSDFProgram : public Program< PaintProperties> { public: - using BaseProgram = Program<shaders::symbol_sdf, + using BaseProgram = SymbolProgram<shaders::symbol_sdf, gl::Triangle, SymbolLayoutAttributes, gl::Uniforms< @@ -132,7 +449,7 @@ public: uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, - uniforms::u_font_scale, + uniforms::u_is_text, uniforms::u_gamma_scale, uniforms::u_pitch, uniforms::u_bearing, @@ -147,7 +464,8 @@ public: using BaseProgram::BaseProgram; - static UniformValues uniformValues(const style::SymbolPropertyValues&, + static UniformValues uniformValues(const bool isText, + const style::SymbolPropertyValues&, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile&, diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index 48c2e7ff66..d2f492d17a 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 auto& symbolSizeBinder, const SymbolPropertyValues& values_, const auto& binders, const auto& paintProperties) @@ -52,6 +53,8 @@ void Painter::renderSymbol(PaintParameters& parameters, colorModeForRenderPass(), std::move(uniformValues), *buffers.vertexBuffer, + *symbolSizeBinder, + values_.layoutSize, *buffers.indexBuffer, buffers.segments, binders, @@ -65,7 +68,9 @@ void Painter::renderSymbol(PaintParameters& parameters, auto paintPropertyValues = layer.impl->iconPaintProperties(); SpriteAtlas& atlas = *layer.impl->spriteAtlas; - const bool iconScaled = values.paintSize != 1.0f || frame.pixelRatio != atlas.getPixelRatio() || bucket.iconsNeedLinear; + const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || + frame.pixelRatio != atlas.getPixelRatio() || + bucket.iconsNeedLinear; const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0; atlas.bind(bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed, context, 0); @@ -74,8 +79,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, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.icon, + bucket.iconSizeBinder, values, bucket.paintPropertyBinders.at(layer.getID()).first, paintPropertyValues); @@ -83,16 +89,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, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.icon, + bucket.iconSizeBinder, values, bucket.paintPropertyBinders.at(layer.getID()).first, paintPropertyValues); } } else { draw(parameters.programs.symbolIcon, - SymbolIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state), + SymbolIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state), bucket.icon, + bucket.iconSizeBinder, values, bucket.paintPropertyBinders.at(layer.getID()).first, paintPropertyValues); @@ -109,8 +117,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, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.text, + bucket.textSizeBinder, values, bucket.paintPropertyBinders.at(layer.getID()).second, paintPropertyValues); @@ -118,8 +127,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, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.text, + bucket.textSizeBinder, 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 b046571740..77a9a75ef1 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::PossiblyEvaluated layout_, const std::map<std::string, std::pair< style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>& layerPaintProperties, + const style::DataDrivenPropertyValue<float>& textSize, + const style::DataDrivenPropertyValue<float>& iconSize, float zoom, bool sdfIcons_, bool iconsNeedLinear_) : layout(std::move(layout_)), sdfIcons(sdfIcons_), - iconsNeedLinear(iconsNeedLinear_) { + iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()), + textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())), + iconSizeBinder(SymbolSizeBinder::create(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)); + textSizeBinder->upload(context); } if (hasIconData()) { icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); + iconSizeBinder->upload(context); } if (!collisionBox.vertices.empty()) { diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index 7a498ab17d..659d7a3788 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -9,6 +9,7 @@ #include <mbgl/programs/collision_box_program.hpp> #include <mbgl/text/glyph_range.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> +#include <mbgl/layout/symbol_feature.hpp> #include <vector> @@ -18,6 +19,8 @@ class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated, const std::map<std::string, std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>&, + const style::DataDrivenPropertyValue<float>& textSize, + const style::DataDrivenPropertyValue<float>& iconSize, float zoom, bool sdfIcons, bool iconsNeedLinear); @@ -36,6 +39,8 @@ public: std::map<std::string, std::pair< SymbolIconProgram::PaintPropertyBinders, SymbolSDFTextProgram::PaintPropertyBinders>> paintPropertyBinders; + + std::unique_ptr<SymbolSizeBinder> textSizeBinder; struct TextBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; @@ -45,7 +50,9 @@ public: optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } text; - + + std::unique_ptr<SymbolSizeBinder> iconSizeBinder; + struct IconBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp index 806e655285..cca0f3e3f1 100644 --- a/src/mbgl/shaders/preludes.cpp +++ b/src/mbgl/shaders/preludes.cpp @@ -43,17 +43,25 @@ vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 v } } +// Unpack a pair of values that have been packed into a single float. +// The packed values are assumed to be 8-bit unsigned integers, and are +// packed like so: +// packedValue = floor(input[0]) * 256 + input[1], +vec2 unpack_float(const float packedValue) { + float v0 = floor(packedValue / 256.0); + return vec2(v0, packedValue - v0 * 256.0); +} + // To minimize the number of attributes needed in the mapbox-gl-native shaders, // we encode a 4-component color into a pair of floats (i.e. a vec2) as follows: // [ floor(color.r * 255) * 256 + color.g * 255, // floor(color.b * 255) * 256 + color.g * 255 ] vec4 decode_color(const vec2 encodedColor) { - float r = floor(encodedColor[0]/256.0)/255.0; - float g = (encodedColor[0] - r*256.0*255.0)/255.0; - float b = floor(encodedColor[1]/256.0)/255.0; - float a = (encodedColor[1] - b*256.0*255.0)/255.0; - return vec4(r, g, b, a); + return vec4( + unpack_float(encodedColor[0]) / 255.0, + unpack_float(encodedColor[1]) / 255.0 + ); } // Unpack a pair of paint values and interpolate between them. diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index 9adda0ba16..b6fbed428e 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -9,9 +9,16 @@ const char* symbol_icon::name = "symbol_icon"; const char* symbol_icon::vertexSource = R"MBGL_SHADER( attribute vec4 a_pos_offset; -attribute vec2 a_texture_pos; attribute vec4 a_data; +// icon-size data (see symbol_sdf.vertex.glsl for more) +attribute vec3 a_size; +uniform bool u_is_size_zoom_constant; +uniform bool u_is_size_feature_constant; +uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function +uniform mediump float u_size; // used when size is both zoom and feature constant +uniform mediump float u_layout_size; // used when size is feature constant + uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; varying lowp float opacity; @@ -19,6 +26,7 @@ varying lowp float opacity; // matrix is for the vertex position. uniform mat4 u_matrix; +uniform bool u_is_text; uniform mediump float u_zoom; uniform bool u_rotate_with_map; uniform vec2 u_extrude_scale; @@ -34,16 +42,45 @@ void main() { vec2 a_pos = a_pos_offset.xy; vec2 a_offset = a_pos_offset.zw; - vec2 a_tex = a_texture_pos.xy; - mediump float a_labelminzoom = a_data[0]; - mediump vec2 a_zoom = a_data.pq; + vec2 a_tex = a_data.xy; + mediump vec2 label_data = unpack_float(a_data[2]); + mediump float a_labelminzoom = label_data[0]; + mediump vec2 a_zoom = unpack_float(a_data[3]); mediump float a_minzoom = a_zoom[0]; mediump float a_maxzoom = a_zoom[1]; - // u_zoom is the current zoom level adjusted for the change in font size - mediump float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom)); + float size; + // In order to accommodate placing labels around corners in + // symbol-placement: line, each glyph in a label could have multiple + // "quad"s only one of which should be shown at a given zoom level. + // The min/max zoom assigned to each quad is based on the font size at + // the vector tile's zoom level, which might be different than at the + // currently rendered zoom level if text-size is zoom-dependent. + // Thus, we compensate for this difference by calculating an adjustment + // based on the scale of rendered text size relative to layout text size. + mediump float layoutSize; + if (!u_is_size_zoom_constant && !u_is_size_feature_constant) { + size = mix(a_size[0], a_size[1], u_size_t) / 10.0; + layoutSize = a_size[2] / 10.0; + } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) { + size = a_size[0] / 10.0; + layoutSize = size; + } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) { + size = u_size; + layoutSize = u_layout_size; + } else { + size = u_size; + layoutSize = u_size; + } + + float fontScale = u_is_text ? size / 24.0 : size; + + mediump float zoomAdjust = log2(size / layoutSize); + mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0; + // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise + mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom)); - vec2 extrude = u_extrude_scale * (a_offset / 64.0); + vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0); if (u_rotate_with_map) { gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); gl_Position.z += z * gl_Position.w; diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index 7cbc9babf5..194e624036 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -10,9 +10,23 @@ const char* symbol_sdf::vertexSource = R"MBGL_SHADER( const float PI = 3.141592653589793; attribute vec4 a_pos_offset; -attribute vec2 a_texture_pos; attribute vec4 a_data; +// contents of a_size vary based on the type of property value +// used for {text,icon}-size. +// For constants, a_size is disabled. +// For source functions, we bind only one value per vertex: the value of {text,icon}-size evaluated for the current feature. +// For composite functions: +// [ text-size(lowerZoomStop, feature), +// text-size(upperZoomStop, feature), +// layoutSize == text-size(layoutZoomLevel, feature) ] +attribute vec3 a_size; +uniform bool u_is_size_zoom_constant; +uniform bool u_is_size_feature_constant; +uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function +uniform mediump float u_size; // used when size is both zoom and feature constant +uniform mediump float u_layout_size; // used when size is feature constant + uniform lowp float a_fill_color_t; attribute highp vec4 a_fill_color; varying highp vec4 fill_color; @@ -32,6 +46,7 @@ varying lowp float halo_blur; // matrix is for the vertex position. uniform mat4 u_matrix; +uniform bool u_is_text; uniform mediump float u_zoom; uniform bool u_rotate_with_map; uniform bool u_pitch_with_map; @@ -45,6 +60,7 @@ uniform vec2 u_texsize; varying vec2 v_tex; varying vec2 v_fade_tex; varying float v_gamma_scale; +varying float v_size; void main() { fill_color = unpack_mix_vec4(a_fill_color, a_fill_color_t); @@ -56,24 +72,57 @@ void main() { vec2 a_pos = a_pos_offset.xy; vec2 a_offset = a_pos_offset.zw; - vec2 a_tex = a_texture_pos.xy; - mediump float a_labelminzoom = a_data[0]; - mediump vec2 a_zoom = a_data.pq; + vec2 a_tex = a_data.xy; + + mediump vec2 label_data = unpack_float(a_data[2]); + mediump float a_labelminzoom = label_data[0]; + mediump float a_labelangle = label_data[1]; + + mediump vec2 a_zoom = unpack_float(a_data[3]); mediump float a_minzoom = a_zoom[0]; mediump float a_maxzoom = a_zoom[1]; - // u_zoom is the current zoom level adjusted for the change in font size - mediump float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom)); + // In order to accommodate placing labels around corners in + // symbol-placement: line, each glyph in a label could have multiple + // "quad"s only one of which should be shown at a given zoom level. + // The min/max zoom assigned to each quad is based on the font size at + // the vector tile's zoom level, which might be different than at the + // currently rendered zoom level if text-size is zoom-dependent. + // Thus, we compensate for this difference by calculating an adjustment + // based on the scale of rendered text size relative to layout text size. + mediump float layoutSize; + if (!u_is_size_zoom_constant && !u_is_size_feature_constant) { + v_size = mix(a_size[0], a_size[1], u_size_t) / 10.0; + layoutSize = a_size[2] / 10.0; + } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) { + v_size = a_size[0] / 10.0; + layoutSize = v_size; + } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) { + v_size = u_size; + layoutSize = u_layout_size; + } else { + v_size = u_size; + layoutSize = u_size; + } + + float fontScale = u_is_text ? v_size / 24.0 : v_size; + + mediump float zoomAdjust = log2(v_size / layoutSize); + mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0; + // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise + // Used below to move the vertex out of the clip space for when the current + // zoom is out of the glyph's zoom range. + mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom)); // pitch-alignment: map // rotation-alignment: map | viewport if (u_pitch_with_map) { - lowp float angle = u_rotate_with_map ? (a_data[1] / 256.0 * 2.0 * PI) : u_bearing; + lowp float angle = u_rotate_with_map ? (a_labelangle / 256.0 * 2.0 * PI) : u_bearing; lowp float asin = sin(angle); lowp float acos = cos(angle); mat2 RotationMatrix = mat2(acos, asin, -1.0 * asin, acos); vec2 offset = RotationMatrix * a_offset; - vec2 extrude = u_extrude_scale * (offset / 64.0); + vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0); gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); gl_Position.z += z * gl_Position.w; // pitch-alignment: viewport @@ -84,7 +133,7 @@ void main() { // it goes from 0% foreshortening to up to around 70% foreshortening lowp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75)); - lowp float lineangle = a_data[1] / 256.0 * 2.0 * PI; + lowp float lineangle = a_labelangle / 256.0 * 2.0 * PI; // use the lineangle to position points a,b along the line // project the points and calculate the label angle in projected space @@ -97,13 +146,13 @@ void main() { mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos); vec2 offset = RotationMatrix * (vec2((1.0-pitchfactor)+(pitchfactor*cos(angle*2.0)), 1.0) * a_offset); - vec2 extrude = u_extrude_scale * (offset / 64.0); + vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0); gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); gl_Position.z += z * gl_Position.w; // pitch-alignment: viewport // rotation-alignment: viewport } else { - vec2 extrude = u_extrude_scale * (a_offset / 64.0); + vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0); gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); } @@ -127,12 +176,13 @@ varying lowp float halo_blur; uniform sampler2D u_texture; uniform sampler2D u_fadetexture; -uniform lowp float u_font_scale; uniform highp float u_gamma_scale; +uniform bool u_is_text; varying vec2 v_tex; varying vec2 v_fade_tex; varying float v_gamma_scale; +varying float v_size; void main() { @@ -141,13 +191,15 @@ void main() { + float fontScale = u_is_text ? v_size / 24.0 : v_size; + lowp vec4 color = fill_color; - highp float gamma = EDGE_GAMMA / u_gamma_scale; + highp float gamma = EDGE_GAMMA / (fontScale * u_gamma_scale); lowp float buff = (256.0 - 64.0) / 256.0; if (u_is_halo) { color = halo_color; - gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / u_gamma_scale; - buff = (6.0 - halo_width / u_font_scale) / SDF_PX; + gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_gamma_scale); + buff = (6.0 - halo_width / fontScale) / SDF_PX; } lowp float dist = texture2D(u_texture, v_tex).a; diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index bd5cf30ad6..e79a3f967f 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -161,15 +161,15 @@ void SymbolLayer::setIconRotationAlignment(PropertyValue<AlignmentType> value) { impl->layout.unevaluated.get<IconRotationAlignment>() = value; impl->observer->onLayerLayoutPropertyChanged(*this, "icon-rotation-alignment"); } -PropertyValue<float> SymbolLayer::getDefaultIconSize() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconSize() { return IconSize::defaultValue(); } -PropertyValue<float> SymbolLayer::getIconSize() const { +DataDrivenPropertyValue<float> SymbolLayer::getIconSize() const { return impl->layout.unevaluated.get<IconSize>(); } -void SymbolLayer::setIconSize(PropertyValue<float> value) { +void SymbolLayer::setIconSize(DataDrivenPropertyValue<float> value) { if (value == getIconSize()) return; impl->layout.unevaluated.get<IconSize>() = value; @@ -329,15 +329,15 @@ void SymbolLayer::setTextFont(PropertyValue<std::vector<std::string>> value) { impl->layout.unevaluated.get<TextFont>() = value; impl->observer->onLayerLayoutPropertyChanged(*this, "text-font"); } -PropertyValue<float> SymbolLayer::getDefaultTextSize() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextSize() { return TextSize::defaultValue(); } -PropertyValue<float> SymbolLayer::getTextSize() const { +DataDrivenPropertyValue<float> SymbolLayer::getTextSize() const { return impl->layout.unevaluated.get<TextSize>(); } -void SymbolLayer::setTextSize(PropertyValue<float> value) { +void SymbolLayer::setTextSize(DataDrivenPropertyValue<float> value) { if (value == getTextSize()) return; impl->layout.unevaluated.get<TextSize>() = value; diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index c637770c04..109d1c43e7 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<IconSize>(parameters); - textSize = layout.evaluate<TextSize>(parameters); - auto hasIconOpacity = paint.evaluated.get<IconColor>().constantOr(Color::black()).a > 0 || paint.evaluated.get<IconHaloColor>().constantOr(Color::black()).a > 0; auto hasTextOpacity = paint.evaluated.get<TextColor>().constantOr(Color::black()).a > 0 || diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp index db20989f01..3d81ff1f69 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.hpp +++ b/src/mbgl/style/layers/symbol_layer_impl.hpp @@ -44,7 +44,7 @@ public: // Layout AlignmentType pitchAlignment; AlignmentType rotationAlignment; - float layoutSize; + PossiblyEvaluatedPropertyValue<float> layoutSize; // Paint std::array<float, 2> translate; diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index 4f63ed419a..42f593890b 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -45,7 +45,7 @@ struct IconRotationAlignment : LayoutProperty<AlignmentType> { static AlignmentType defaultValue() { return AlignmentType::Auto; } }; -struct IconSize : LayoutProperty<float> { +struct IconSize : DataDrivenLayoutProperty<float> { static constexpr const char * key = "icon-size"; static float defaultValue() { return 1; } }; @@ -105,7 +105,7 @@ struct TextFont : LayoutProperty<std::vector<std::string>> { static std::vector<std::string> defaultValue() { return { "Open Sans Regular", "Arial Unicode MS Regular" }; } }; -struct TextSize : LayoutProperty<float> { +struct TextSize : DataDrivenLayoutProperty<float> { static constexpr const char * key = "text-size"; static float defaultValue() { return 16; } }; diff --git a/src/mbgl/style/paint_property_binder.hpp b/src/mbgl/style/paint_property_binder.hpp index 7e44b509a1..bf98e6ef9a 100644 --- a/src/mbgl/style/paint_property_binder.hpp +++ b/src/mbgl/style/paint_property_binder.hpp @@ -26,16 +26,12 @@ inline std::array<float, 1> attributeValue(float v) { uses 8-bit precision for each color component, for each float we use the upper 8 bits for one component (e.g. (color.r * 255) * 256), and the lower 8 for another. - Also note: - - Colors come in as floats 0..1, so we scale by 255. - - Casting the scaled values to ints is important: without doing this, e.g., the - fractional part of the `r` component would corrupt the lower-8 bits of the encoded - value, which must be reserved for the `g` component. + Also note that colors come in as floats 0..1, so we scale by 255. */ inline std::array<float, 2> attributeValue(const Color& color) { return {{ - static_cast<float>(static_cast<uint16_t>(color.r * 255) * 256 + static_cast<uint16_t>(color.g * 255)), - static_cast<float>(static_cast<uint16_t>(color.b * 255) * 256 + static_cast<uint16_t>(color.a * 255)) + static_cast<float>(mbgl::attributes::packUint8Pair(255 * color.r, 255 * color.g)), + static_cast<float>(mbgl::attributes::packUint8Pair(255 * color.b, 255 * color.a)) }}; } diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index b13a6a71e5..188f88655d 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<IconTextFit>() != IconTextFitType::None && shapedText) { auto iconWidth = right - left; auto iconHeight = bottom - top; - auto size = layout.get<TextSize>() / 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 f1529d8829..333000627b 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); |