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/programs/symbol_program.hpp | |
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/programs/symbol_program.hpp')
-rw-r--r-- | src/mbgl/programs/symbol_program.hpp | 356 |
1 files changed, 337 insertions, 19 deletions
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&, |