From c6bcb7681845d873376998c18c642b0df887cf5a Mon Sep 17 00:00:00 2001 From: Stefan Karschti Date: Mon, 15 Apr 2024 20:37:26 +0300 Subject: [PATCH] Add wide vector shader option to Custom Drawable Layer (#2183) --- CMakeLists.txt | 1 + bazel/core.bzl | 3 + include/mbgl/gfx/drawable.hpp | 14 + include/mbgl/gfx/drawable_builder.hpp | 15 + include/mbgl/gfx/vertex_attribute.hpp | 2 +- include/mbgl/mtl/drawable.hpp | 2 + include/mbgl/renderer/layer_tweaker.hpp | 3 +- .../mbgl/shaders/gl/drawable_wide_vector.hpp | 16 + include/mbgl/shaders/gl/shader_program_gl.hpp | 3 + include/mbgl/shaders/mtl/background.hpp | 1 + .../mbgl/shaders/mtl/background_pattern.hpp | 1 + include/mbgl/shaders/mtl/circle.hpp | 1 + include/mbgl/shaders/mtl/clipping_mask.hpp | 1 + include/mbgl/shaders/mtl/collision_box.hpp | 1 + include/mbgl/shaders/mtl/collision_circle.hpp | 1 + .../mbgl/shaders/mtl/custom_symbol_icon.hpp | 1 + include/mbgl/shaders/mtl/debug.hpp | 1 + include/mbgl/shaders/mtl/fill.hpp | 4 + include/mbgl/shaders/mtl/fill_extrusion.hpp | 1 + .../shaders/mtl/fill_extrusion_pattern.hpp | 1 + include/mbgl/shaders/mtl/heatmap.hpp | 1 + include/mbgl/shaders/mtl/heatmap_texture.hpp | 1 + include/mbgl/shaders/mtl/hillshade.hpp | 1 + .../mbgl/shaders/mtl/hillshade_prepare.hpp | 1 + include/mbgl/shaders/mtl/line.hpp | 4 + include/mbgl/shaders/mtl/line_gradient.hpp | 1 + include/mbgl/shaders/mtl/raster.hpp | 1 + include/mbgl/shaders/mtl/shader_group.hpp | 3 + include/mbgl/shaders/mtl/shader_program.hpp | 20 +- include/mbgl/shaders/mtl/symbol_icon.hpp | 1 + include/mbgl/shaders/mtl/symbol_sdf.hpp | 1 + .../mbgl/shaders/mtl/symbol_text_and_icon.hpp | 1 + include/mbgl/shaders/mtl/widevector.hpp | 587 ++++++++++++++++++ include/mbgl/shaders/shader_defines.hpp | 52 +- include/mbgl/shaders/shader_manifest.hpp | 1 + include/mbgl/shaders/shader_program_base.hpp | 3 + include/mbgl/shaders/shader_source.hpp | 1 + include/mbgl/shaders/widevector_ubo.hpp | 58 ++ .../style/layers/custom_drawable_layer.hpp | 42 +- include/mbgl/util/color.hpp | 1 + .../app/ExampleCustomDrawableStyleLayer.mm | 155 ++++- .../ios/iosapp-UITests/iosapp_UITests.swift | 12 + shaders/_empty.glsl | 0 shaders/manifest.json | 7 + src/mbgl/gfx/drawable_builder.cpp | 29 + src/mbgl/gfx/drawable_builder_impl.cpp | 187 ++++++ src/mbgl/gfx/drawable_builder_impl.hpp | 27 +- src/mbgl/mtl/drawable.cpp | 46 +- src/mbgl/mtl/drawable_impl.hpp | 6 +- src/mbgl/mtl/renderer_backend.cpp | 4 +- src/mbgl/renderer/layer_tweaker.cpp | 3 + src/mbgl/shaders/mtl/shader_program.cpp | 31 +- .../style/layers/custom_drawable_layer.cpp | 236 +++++-- src/mbgl/util/mat4.cpp | 7 + src/mbgl/util/mat4.hpp | 1 + test/api/custom_drawable_layer.test.cpp | 102 +-- 56 files changed, 1545 insertions(+), 162 deletions(-) create mode 100644 include/mbgl/shaders/gl/drawable_wide_vector.hpp create mode 100644 include/mbgl/shaders/mtl/widevector.hpp create mode 100644 include/mbgl/shaders/widevector_ubo.hpp create mode 100644 shaders/_empty.glsl diff --git a/CMakeLists.txt b/CMakeLists.txt index 8faf667cd..6c8fedabe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1150,6 +1150,7 @@ if(MLN_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/raster_layer_ubo.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/shader_defines.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/symbol_layer_ubo.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/widevector_ubo.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/shader_program_gl.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/drawable_gl.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/drawable_gl_builder.hpp diff --git a/bazel/core.bzl b/bazel/core.bzl index 06cd361ea..d43ff79c3 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -88,6 +88,7 @@ MLN_GENERATED_OPENGL_SHADER_HEADERS = [ "include/mbgl/shaders/gl/drawable_symbol_icon.hpp", "include/mbgl/shaders/gl/drawable_symbol_sdf.hpp", "include/mbgl/shaders/gl/drawable_symbol_text_and_icon.hpp", + "include/mbgl/shaders/gl/drawable_wide_vector.hpp", "include/mbgl/shaders/gl/fill_extrusion_pattern.hpp", "include/mbgl/shaders/gl/fill_extrusion.hpp", "include/mbgl/shaders/gl/fill_outline_pattern.hpp", @@ -1000,6 +1001,7 @@ MLN_DRAWABLES_HEADERS = [ "include/mbgl/shaders/shader_defines.hpp", "include/mbgl/shaders/shader_program_base.hpp", "include/mbgl/shaders/symbol_layer_ubo.hpp", + "include/mbgl/shaders/widevector_ubo.hpp", "include/mbgl/util/suppress_copies.hpp", "include/mbgl/style/layers/custom_drawable_layer.hpp", "include/mbgl/layermanager/custom_drawable_layer_factory.hpp", @@ -1123,4 +1125,5 @@ MLN_DRAWABLES_MTL_HEADERS = [ "include/mbgl/shaders/mtl/symbol_sdf.hpp", "include/mbgl/shaders/mtl/symbol_text_and_icon.hpp", "include/mbgl/style/layers/mtl/custom_layer_render_parameters.hpp", + "include/mbgl/shaders/mtl/widevector.hpp", ] diff --git a/include/mbgl/gfx/drawable.hpp b/include/mbgl/gfx/drawable.hpp index 42143d84e..3153a0cf0 100644 --- a/include/mbgl/gfx/drawable.hpp +++ b/include/mbgl/gfx/drawable.hpp @@ -189,6 +189,12 @@ public: /// Set vertex attribute array void setVertexAttributes(gfx::VertexAttributeArrayPtr value) noexcept { vertexAttributes = std::move(value); } + /// Get the instance attributes + const gfx::VertexAttributeArrayPtr& getInstanceAttributes() const noexcept { return instanceAttributes; } + + /// Set instance attribute array + void setInstanceAttributes(gfx::VertexAttributeArrayPtr value) noexcept { instanceAttributes = std::move(value); } + /// Provide raw data for vertices. Incompatible with adding primitives virtual void setVertices(std::vector&&, std::size_t, AttributeDataType) = 0; @@ -232,6 +238,12 @@ public: void setLayerTweaker(LayerTweakerPtr tweaker) { layerTweaker = std::move(tweaker); } const LayerTweakerPtr& getLayerTweaker() const { return layerTweaker; } + /// Get origin point + const std::optional>& getOrigin() const { return origin; } + + /// Set origin point + void setOrigin(std::optional> p) { origin = std::move(p); } + protected: bool enabled = true; bool enableColor = true; @@ -251,6 +263,7 @@ protected: DepthMaskType depthType; // = DepthMaskType::ReadOnly; UniqueDrawableData drawableData{}; gfx::VertexAttributeArrayPtr vertexAttributes; + gfx::VertexAttributeArrayPtr instanceAttributes; struct Impl; std::unique_ptr impl; @@ -260,6 +273,7 @@ protected: LayerTweakerPtr layerTweaker; std::size_t type = 0; + std::optional> origin; }; using DrawablePtr = std::shared_ptr; diff --git a/include/mbgl/gfx/drawable_builder.hpp b/include/mbgl/gfx/drawable_builder.hpp index 5ec1f7c64..e18261307 100644 --- a/include/mbgl/gfx/drawable_builder.hpp +++ b/include/mbgl/gfx/drawable_builder.hpp @@ -160,6 +160,13 @@ public: /// A set of attribute values to be added for each vertex void setVertexAttributes(VertexAttributeArrayPtr attrs) { vertexAttrs = std::move(attrs); } + /// Get the instance attributes that override default values in the shader program + VertexAttributeArrayPtr& getInstanceAttributes() { return instanceAttrs; } + const VertexAttributeArrayPtr& getInstanceAttributes() const { return instanceAttrs; } + + /// A set of attribute values to be added for each instance + void setInstanceAttributes(VertexAttributeArrayPtr attrs) { instanceAttrs = std::move(attrs); } + /// Add some vertex elements, returns the index of the first one added std::size_t addVertices(const std::vector>& vertices, std::size_t vertexOffset, @@ -200,6 +207,12 @@ public: /// Add a polyline. If the last point equals the first it will be closed, otherwise open void addPolyline(const GeometryCoordinates& coordinates, const gfx::PolylineGeneratorOptions&); + /// Add a polyline in Tile coordinates, using wide vectors. + void addWideVectorPolylineLocal(const GeometryCoordinates& coordinates, const gfx::PolylineGeneratorOptions&); + + /// Add a polyline in geographic coordinates, using wide vectors. + void addWideVectorPolylineGlobal(const LineString& coordinates, const gfx::PolylineGeneratorOptions&); + /// return the curent vertex count std::size_t curVertexCount() const; @@ -232,6 +245,8 @@ protected: gfx::Drawable::Textures textures; std::vector tweakers; gfx::VertexAttributeArrayPtr vertexAttrs; + gfx::VertexAttributeArrayPtr instanceAttrs; + std::optional> origin; class Impl; std::unique_ptr impl; diff --git a/include/mbgl/gfx/vertex_attribute.hpp b/include/mbgl/gfx/vertex_attribute.hpp index a5916d210..fccce52b3 100644 --- a/include/mbgl/gfx/vertex_attribute.hpp +++ b/include/mbgl/gfx/vertex_attribute.hpp @@ -319,7 +319,7 @@ public: /// Do something with each attribute template - void visitAttributes(Func f) { + void visitAttributes(Func f) const { std::for_each(attrs.begin(), attrs.end(), [&](const auto& attr) { if (attr) { f(*attr); diff --git a/include/mbgl/mtl/drawable.hpp b/include/mbgl/mtl/drawable.hpp index a307b0b52..685da891d 100644 --- a/include/mbgl/mtl/drawable.hpp +++ b/include/mbgl/mtl/drawable.hpp @@ -64,6 +64,8 @@ protected: void bindAttributes(RenderPass&) const noexcept; void unbindAttributes(RenderPass&) const noexcept {} + void bindInstanceAttributes(RenderPass&) const noexcept; + void bindUniformBuffers(RenderPass&) const noexcept; void unbindUniformBuffers(RenderPass&) const noexcept {} diff --git a/include/mbgl/renderer/layer_tweaker.hpp b/include/mbgl/renderer/layer_tweaker.hpp index ac64c2ff5..91871893b 100644 --- a/include/mbgl/renderer/layer_tweaker.hpp +++ b/include/mbgl/renderer/layer_tweaker.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -28,8 +29,6 @@ class PaintParameters; class RenderTree; class UnwrappedTileID; -using mat4 = std::array; - /** Base class for layer tweakers, which manipulate layer group per frame */ diff --git a/include/mbgl/shaders/gl/drawable_wide_vector.hpp b/include/mbgl/shaders/gl/drawable_wide_vector.hpp new file mode 100644 index 000000000..2e4fda6b6 --- /dev/null +++ b/include/mbgl/shaders/gl/drawable_wide_vector.hpp @@ -0,0 +1,16 @@ +// Generated code, do not modify this file! +#pragma once +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr const char* name = "WideVectorShader"; + static constexpr const char* vertex = R"()"; + static constexpr const char* fragment = R"()"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/gl/shader_program_gl.hpp b/include/mbgl/shaders/gl/shader_program_gl.hpp index 222049a17..8e625fb7b 100644 --- a/include/mbgl/shaders/gl/shader_program_gl.hpp +++ b/include/mbgl/shaders/gl/shader_program_gl.hpp @@ -46,6 +46,8 @@ public: const gfx::VertexAttributeArray& getVertexAttributes() const override { return vertexAttributes; } + const gfx::VertexAttributeArray& getInstanceAttributes() const override { return instanceAttributes; } + ProgramID getGLProgramID() const { return glProgram; } protected: @@ -56,6 +58,7 @@ protected: UniformBlockArrayGL uniformBlocks; VertexAttributeArrayGL vertexAttributes; + VertexAttributeArrayGL instanceAttributes; SamplerLocationArray samplerLocations; }; diff --git a/include/mbgl/shaders/mtl/background.hpp b/include/mbgl/shaders/mtl/background.hpp index 051f2bd4b..9598440da 100644 --- a/include/mbgl/shaders/mtl/background.hpp +++ b/include/mbgl/shaders/mtl/background.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/background_pattern.hpp b/include/mbgl/shaders/mtl/background_pattern.hpp index 0c44ca41c..b9c8c34fa 100644 --- a/include/mbgl/shaders/mtl/background_pattern.hpp +++ b/include/mbgl/shaders/mtl/background_pattern.hpp @@ -12,6 +12,7 @@ struct ShaderSource static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/circle.hpp b/include/mbgl/shaders/mtl/circle.hpp index 9f1079853..e958b0bb4 100644 --- a/include/mbgl/shaders/mtl/circle.hpp +++ b/include/mbgl/shaders/mtl/circle.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/clipping_mask.hpp b/include/mbgl/shaders/mtl/clipping_mask.hpp index 1ba32e282..aff9b5e91 100644 --- a/include/mbgl/shaders/mtl/clipping_mask.hpp +++ b/include/mbgl/shaders/mtl/clipping_mask.hpp @@ -21,6 +21,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/collision_box.hpp b/include/mbgl/shaders/mtl/collision_box.hpp index 6f87dfce9..0f6cf4a0a 100644 --- a/include/mbgl/shaders/mtl/collision_box.hpp +++ b/include/mbgl/shaders/mtl/collision_box.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/collision_circle.hpp b/include/mbgl/shaders/mtl/collision_circle.hpp index 52a9b2e24..69bd046ad 100644 --- a/include/mbgl/shaders/mtl/collision_circle.hpp +++ b/include/mbgl/shaders/mtl/collision_circle.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/custom_symbol_icon.hpp b/include/mbgl/shaders/mtl/custom_symbol_icon.hpp index 229a7b5f2..a2dd97502 100644 --- a/include/mbgl/shaders/mtl/custom_symbol_icon.hpp +++ b/include/mbgl/shaders/mtl/custom_symbol_icon.hpp @@ -15,6 +15,7 @@ struct ShaderSource static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/debug.hpp b/include/mbgl/shaders/mtl/debug.hpp index b59dc284f..998db726f 100644 --- a/include/mbgl/shaders/mtl/debug.hpp +++ b/include/mbgl/shaders/mtl/debug.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/fill.hpp b/include/mbgl/shaders/mtl/fill.hpp index d69c3476f..1951457b8 100644 --- a/include/mbgl/shaders/mtl/fill.hpp +++ b/include/mbgl/shaders/mtl/fill.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; @@ -101,6 +102,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; @@ -191,6 +193,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; @@ -349,6 +352,7 @@ struct ShaderSource attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/fill_extrusion.hpp b/include/mbgl/shaders/mtl/fill_extrusion.hpp index e94ea3db6..48e76079e 100644 --- a/include/mbgl/shaders/mtl/fill_extrusion.hpp +++ b/include/mbgl/shaders/mtl/fill_extrusion.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp b/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp index 6dcda6026..ae7d125aa 100644 --- a/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp +++ b/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp @@ -15,6 +15,7 @@ struct ShaderSource attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/heatmap.hpp b/include/mbgl/shaders/mtl/heatmap.hpp index 5d9026844..5ac4d6c88 100644 --- a/include/mbgl/shaders/mtl/heatmap.hpp +++ b/include/mbgl/shaders/mtl/heatmap.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/heatmap_texture.hpp b/include/mbgl/shaders/mtl/heatmap_texture.hpp index f1f143c40..05cfeafd5 100644 --- a/include/mbgl/shaders/mtl/heatmap_texture.hpp +++ b/include/mbgl/shaders/mtl/heatmap_texture.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/hillshade.hpp b/include/mbgl/shaders/mtl/hillshade.hpp index 77b35d65e..7d784a6ef 100644 --- a/include/mbgl/shaders/mtl/hillshade.hpp +++ b/include/mbgl/shaders/mtl/hillshade.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/hillshade_prepare.hpp b/include/mbgl/shaders/mtl/hillshade_prepare.hpp index f3d586491..6024eaef0 100644 --- a/include/mbgl/shaders/mtl/hillshade_prepare.hpp +++ b/include/mbgl/shaders/mtl/hillshade_prepare.hpp @@ -15,6 +15,7 @@ struct ShaderSource static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/line.hpp b/include/mbgl/shaders/mtl/line.hpp index cbc63e1e3..20b88845f 100644 --- a/include/mbgl/shaders/mtl/line.hpp +++ b/include/mbgl/shaders/mtl/line.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; @@ -179,6 +180,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; @@ -430,6 +432,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; @@ -658,6 +661,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/line_gradient.hpp b/include/mbgl/shaders/mtl/line_gradient.hpp index 28653583b..86afc4312 100644 --- a/include/mbgl/shaders/mtl/line_gradient.hpp +++ b/include/mbgl/shaders/mtl/line_gradient.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/raster.hpp b/include/mbgl/shaders/mtl/raster.hpp index 15b032ed2..463995069 100644 --- a/include/mbgl/shaders/mtl/raster.hpp +++ b/include/mbgl/shaders/mtl/raster.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/shader_group.hpp b/include/mbgl/shaders/mtl/shader_group.hpp index f27abb256..04bed97fb 100644 --- a/include/mbgl/shaders/mtl/shader_group.hpp +++ b/include/mbgl/shaders/mtl/shader_group.hpp @@ -78,6 +78,9 @@ public: shader->initAttribute(attrib); } } + for (const auto& attrib : ShaderClass::instanceAttributes) { + shader->initInstanceAttribute(attrib); + } for (const auto& uniform : ShaderClass::uniforms) { shader->initUniformBlock(uniform); } diff --git a/include/mbgl/shaders/mtl/shader_program.hpp b/include/mbgl/shaders/mtl/shader_program.hpp index e40253f1c..5b03e82da 100644 --- a/include/mbgl/shaders/mtl/shader_program.hpp +++ b/include/mbgl/shaders/mtl/shader_program.hpp @@ -14,13 +14,21 @@ namespace mbgl { namespace shaders { struct AttributeInfo { - AttributeInfo(std::size_t index, gfx::AttributeDataType dataType, std::size_t id); + constexpr AttributeInfo(std::size_t index_, gfx::AttributeDataType dataType_, std::size_t id_) + : index(index_), + dataType(dataType_), + id(id_) {} std::size_t index; gfx::AttributeDataType dataType; std::size_t id; }; struct UniformBlockInfo { - UniformBlockInfo(std::size_t index, bool vertex, bool fragment, std::size_t size, std::size_t id); + constexpr UniformBlockInfo(std::size_t index_, bool vertex_, bool fragment_, std::size_t size_, std::size_t id_) + : index(index_), + vertex(vertex_), + fragment(fragment_), + size(size_), + id(id_) {} std::size_t index; bool vertex; bool fragment; @@ -28,7 +36,9 @@ struct UniformBlockInfo { std::size_t id; }; struct TextureInfo { - TextureInfo(std::size_t index, std::size_t id); + constexpr TextureInfo(std::size_t index_, std::size_t id_) + : index(index_), + id(id_) {} std::size_t index; std::size_t id; }; @@ -58,10 +68,13 @@ public: const gfx::VertexAttributeArray& getVertexAttributes() const override { return vertexAttributes; } + const gfx::VertexAttributeArray& getInstanceAttributes() const override { return instanceAttributes; } + const gfx::UniformBlockArray& getUniformBlocks() const override { return uniformBlocks; } gfx::UniformBlockArray& mutableUniformBlocks() override { return uniformBlocks; } void initAttribute(const shaders::AttributeInfo&); + void initInstanceAttribute(const shaders::AttributeInfo&); void initUniformBlock(const shaders::UniformBlockInfo&); void initTexture(const shaders::TextureInfo&); @@ -72,6 +85,7 @@ protected: MTLFunctionPtr fragmentFunction; UniformBlockArray uniformBlocks; VertexAttributeArray vertexAttributes; + VertexAttributeArray instanceAttributes; std::array, shaders::maxTextureCountPerShader> textureBindings; }; diff --git a/include/mbgl/shaders/mtl/symbol_icon.hpp b/include/mbgl/shaders/mtl/symbol_icon.hpp index 9c658ae12..8ac0429c9 100644 --- a/include/mbgl/shaders/mtl/symbol_icon.hpp +++ b/include/mbgl/shaders/mtl/symbol_icon.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/symbol_sdf.hpp b/include/mbgl/shaders/mtl/symbol_sdf.hpp index 1d5e5b813..73a45bcec 100644 --- a/include/mbgl/shaders/mtl/symbol_sdf.hpp +++ b/include/mbgl/shaders/mtl/symbol_sdf.hpp @@ -15,6 +15,7 @@ struct ShaderSource { static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp index 4fc259c3e..a56e664da 100644 --- a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp +++ b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp @@ -15,6 +15,7 @@ struct ShaderSource static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array attributes; + static constexpr std::array instanceAttributes{}; static const std::array uniforms; static const std::array textures; diff --git a/include/mbgl/shaders/mtl/widevector.hpp b/include/mbgl/shaders/mtl/widevector.hpp new file mode 100644 index 000000000..e02b08f70 --- /dev/null +++ b/include/mbgl/shaders/mtl/widevector.hpp @@ -0,0 +1,587 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr auto name = "WideVectorShader"; + static constexpr auto vertexMainFunction = "vertexTri_wideVecPerf"; + static constexpr auto fragmentMainFunction = "fragmentTri_wideVecPerf"; + + static constexpr std::array attributes{ + AttributeInfo{0, gfx::AttributeDataType::Float3, idWideVectorScreenPos}, + AttributeInfo{1, gfx::AttributeDataType::Float4, idWideVectorColor}, + AttributeInfo{2, gfx::AttributeDataType::Int, idWideVectorIndex}}; + static constexpr std::array uniforms{ + UniformBlockInfo{3, true, false, sizeof(WideVectorUniformsUBO), idWideVectorUniformsUBO}, + UniformBlockInfo{4, true, false, sizeof(WideVectorUniformWideVecUBO), idWideVectorUniformWideVecUBO}, + }; + static constexpr std::array instanceAttributes{ + AttributeInfo{5, gfx::AttributeDataType::Float3, idWideVectorInstanceCenter}, + AttributeInfo{5, gfx::AttributeDataType::Float4, idWideVectorInstanceColor}, + AttributeInfo{5, gfx::AttributeDataType::Int, idWideVectorInstancePrevious}, + AttributeInfo{5, gfx::AttributeDataType::Int, idWideVectorInstanceNext}}; + static constexpr std::array textures{}; + + static constexpr auto source = R"( +#include + +namespace WhirlyKitShader +{ + +/** Expressions are used to change values like width and opacity over zoom levels. **/ +#define WKSExpStops 8 + +// Line Joins +// These are assumed to match WideVectorLineJoinType +typedef enum { + WKSVertexLineJoinMiter = 0, + WKSVertexLineJoinBevel = 1, + WKSVertexLineJoinRound = 2, + WKSVertexLineJoinMiterClip = 3, + WKSVertexLineJoinMiterSimple = 4, + WKSVertexLineJoinNone = 5, +} WKSVertexLineJoinType; + +// Line Caps +// These are assumed to match WideVectorLineCapType +typedef enum { + WKSVertexLineCapRound = 0, + WKSVertexLineCapButt = 1, + WKSVertexLineCapSquare = 2, +} WKSVertexLineCapType; + +// Uniforms for the basic case. +struct Uniforms +{ + simd::float4x4 mvpMatrix; + simd::float4x4 mvpMatrixDiff; + simd::float4x4 mvMatrix; + simd::float4x4 mvMatrixDiff; + simd::float4x4 pMatrix; + simd::float4x4 pMatrixDiff; + simd::float2 frameSize; +}; + +// Instructions to the wide vector shaders, usually per-drawable +struct UniformWideVec { + simd::float4 color; // Color + float w2; // Width / 2.0 in screen space + float offset; // Offset from center in screen space + float edge; // Edge falloff control + float texRepeat; // Texture scaling specific to wide vectors + simd::float2 texOffset; // Texture offset. + float miterLimit; // Miter join limit, multiples of width + WKSVertexLineJoinType join; // Line joins + WKSVertexLineCapType cap; // Line endcaps + bool hasExp; // Look for a UniformWideVecExp structure for color, opacity, and width + float interClipLimit; // Allow clipping of out-of-bounds intersection points + // Value is the multiple of distance-squared that is allowed. +}; + +// Instance info for the wide vector (new) vertex shader +typedef struct +{ + // Center of the point on the line + simd::float3 center; + // Color + simd::float4 color; + // Used to track loops and such + int prev,next; // set to -1 for non-loops +} VertexTriWideVecInstance; + +} + +// These have syntax that only the shader language can grok +// We put them here so we can include them in other Metal libraries +#ifdef __METAL_VERSION__ + +/** + Wide Vector Shaders + These work to build/render objects in 2D space, but based + on 3D locations. + */ + +// Vertex definition for wide vector (new version) +struct VertexTriWideVecB +{ + // x, y offset around the center + float3 screenPos [[attribute(0)]]; + float4 color [[attribute(1)]]; + int index [[attribute(2)]]; +}; + +// Wide vector vertex passed to fragment shader (new version) +struct ProjVertexTriWideVecPerf { + float4 position [[invariant]] [[position]]; // transformed to NDC + float2 screenPos; // un-transformed vertex position + float2 centerPos; // un-transformed circle center + float2 midDir; // Turn direction + float4 color; + float2 texCoord; + float w2; + float edge; + uint2 maskIDs; + bool roundJoin; + + //uint whichVert; // helpful for debugging +}; + +#else + +#endif + +using namespace metal; +using namespace WhirlyKitShader; + +struct IntersectInfo { + bool valid; + float2 interPt; + float c; + float ta,tb; +}; + +// wedge product (2D cross product) +// if A ^ B > 0, A is to the right of B +float wedge(float2 a, float2 b) { + return a.x * b.y - a.y * b.x; +} + +// Intersect two lines +IntersectInfo intersectLines(float2 a0, float2 a1, float2 b0, float2 b1) +{ + const float2 dA = a0 - a1; + const float2 dB = b0 - b1; + + // Solve the system of equations formed by equating the lines to get their common point. + const float denom = wedge(dA, dB); + if (denom == 0.0) { + // If the denominator comes out zero, the lines are parallel and do not intersect + return { .valid = false }; + } + + const float tA = wedge(a0, a1); + const float tB = wedge(b0, b1); + const float2 inter = float2((tA * dB.x - dA.x * tB), (tA * dB.y - dA.y * tB)) / denom; + + return { + .valid = true, + .interPt = inter, + .ta = 0.0, + .tb = 0.0, + }; +} + +// Intersect two offset lines +IntersectInfo intersectWideLines(float2 p0,float2 p1,float2 p2, + float2 n0,float2 n1) +{ + return intersectLines(p0 + n0, p1 + n0, p1 + n1, p2 + n1); +} + +// Used to track what info we have about a center point +struct CenterInfo { + /// Screen coordinates of the line segment endpoint + float2 screenPos; + /// Length of the segment (in screen coordinates) + float len; + /// Normalized direction of the segment + float2 nDir; + /// Normalized plane normal, perpendicular to the segment + float2 norm; +}; + +float3 viewPos(constant simd::float4x4 &mat, float3 vec) { + const float4 p = mat * float4(vec,1.0); + return p.xyz; // / p.w; ? +} + +float2 screenPos_MVP(constant Uniforms &u, float3 viewPos) { + const float4 p4 = float4(viewPos, 1.0); + + // Use the MVP matrix + const float4 s = u.mvpMatrix * p4; + + return s.xy/s.w; +} + +constant constexpr float wideVecMinTurnThreshold = 1e-5; +constant constexpr float wideVecMaxTurnThreshold = 0.99999998476; // sin(89.99 deg) +constant constexpr int WideVecPolyStartGeom = 0; +constant constexpr int WideVecPolyBodyGeom = 1; +constant constexpr int WideVecPolyEndGeom = 2; +constant constexpr float4 discardPt(0,0,-1e6,NAN); + +// Performance version of wide vector shader +vertex ProjVertexTriWideVecPerf vertexTri_wideVecPerf( + thread const VertexTriWideVecB vert [[ stage_in ]], + constant Uniforms &uniforms [[ buffer(3) ]], + constant UniformWideVec &wideVec [[ buffer(4) ]], + uint instanceID [[ instance_id ]], + constant VertexTriWideVecInstance *wideVecInsts [[ buffer(5) ]]) +{ + ProjVertexTriWideVecPerf outVert = { + .position = discardPt, + .roundJoin = false, + }; + + // Vertex index within the instance, 0-11 + // Odd indexes are on the left, evens are on the right. + const int whichVert = (vert.index >> 16) & 0xffff; + // Polygon index within the segment. 0=Start cap, 1=body, 2=end cap + const int whichPoly = vert.index & 0xffff; + // Are we on the left edge, or the right? + const bool isLeft = (whichVert & 1); + // Are we on the starting end of the segment or the end? + const bool isEnd = (whichVert > 5); + + // Track vertex for debugging + //outVert.whichVert = whichVert; + + // Pull out the width and possibly calculate one + float w2 = wideVec.w2; + + // w2 includes edge-blend, strokeWidth does not + const float strokeWidth = 2 * w2; + if (w2 > 0.0) { + w2 = w2 + wideVec.edge; + } + + // Disable joins for narrow lines + auto joinType = (w2 >= 1) ? wideVec.join : WKSVertexLineJoinNone; + + // Find the various instances representing center points. + // We need one behind and one ahead of us. + // previous segment + // | this segment + // | | next segment + // | | | + bool instValid[4] = { false, true, false, false }; + VertexTriWideVecInstance inst[4] = { {}, wideVecInsts[instanceID], {}, {} }; + + if (inst[1].prev != -1 && joinType != WKSVertexLineJoinNone) { + inst[0] = wideVecInsts[inst[1].prev]; + instValid[0] = true; + } + if (inst[1].next != -1) { + inst[2] = wideVecInsts[inst[1].next]; + instValid[2] = true; + + if (inst[2].next != -1 && joinType != WKSVertexLineJoinNone) { + inst[3] = wideVecInsts[inst[2].next]; + instValid[3] = true; + } + } else { + // We need at least this and next + return outVert; + } + + const auto capType = wideVec.cap; + const auto isStartCap = (whichPoly == WideVecPolyStartGeom && !instValid[0]); + const auto isEndCap = (whichPoly == WideVecPolyEndGeom && !instValid[3]); + + // Butt is the default cap style, the line ends at the point. + if ((isStartCap || isEndCap) && capType == WhirlyKitShader::WKSVertexLineCapButt) { + return outVert; + } + + // Figure out position on the screen for each center point. + // centers[1] represents the segment leading to the current point. + // centers[2] represents the segment leading from the current point. + // centers[X] = vector from centers[X-1] to centers[X] + CenterInfo centers[4]; + for (unsigned int ii=0;ii<4;ii++) { + if (!instValid[ii]) { + continue; + } + centers[ii].screenPos = screenPos_MVP(uniforms, inst[ii].center); + } + + const float2 screenScale(2.0/uniforms.frameSize.x,2.0/uniforms.frameSize.y); // ~(0.001,0.001) + + // Calculate directions and normals. Done in isotropic coords to + // avoid skewing everything when later multiplying by `screenScale`. + for (unsigned int ii=1;ii<4;ii++) { + if (instValid[ii-1]) { + const float2 scaledDir = (centers[ii].screenPos - centers[ii-1].screenPos) / screenScale; + centers[ii].len = length(scaledDir); + centers[ii].nDir = normalize(scaledDir); + centers[ii].norm = float2(-centers[ii].nDir.y, centers[ii].nDir.x); + } + } + + // Pull out the center line offset, or calculate one + float centerLine = wideVec.offset; + + // Intersect on the left or right depending + const float interSgn = (whichVert & 1) ? 1 : -1; + + // Do the offset intersection + bool intersectValid = false; + bool turningLeft = false; + const int interIdx = isEnd ? 1 : 0; + float2 offsetCenter = centers[interIdx + 1].screenPos + centers[2].norm * centerLine * screenScale; + float2 interPt(0, 0), realInterPt(0, 0); + float dotProd = 0, theta = 0, miterLength = 0; + + // If we're on the far end of the body segment, we need this and the next two segments. + // Otherwise we need the previous, this, and the next segment. + if (instValid[interIdx] && instValid[interIdx+1] && instValid[interIdx+2]) { + + // Don't even bother computing intersections for very acute angles or very small turns + dotProd = dot(centers[interIdx+1].nDir, centers[interIdx+2].nDir); + if (-wideVecMaxTurnThreshold < dotProd && + dotProd < wideVecMaxTurnThreshold && + abs(abs(dotProd) - 1) >= wideVecMinTurnThreshold) { + + // Interior turn angle, both vectors are normalized. + theta = M_PI_F - acos(dotProd); + + // "If the miter length divided by the stroke width exceeds the miterlimit then: + // miter: the join is converted to a bevel + // miter-clip: the miter is clipped at half the miter length from the intersection" + if (joinType == WKSVertexLineJoinMiter || joinType == WKSVertexLineJoinMiterClip) { + miterLength = abs(1 / sin(theta / 2)); + if (miterLength > wideVec.miterLimit) { + if (joinType == WKSVertexLineJoinMiter) { + joinType = WKSVertexLineJoinBevel; + } else if (joinType == WKSVertexLineJoinMiterClip) { + miterLength = wideVec.miterLimit; + } + } + } + + // Intersect the left or right sides of prev-this and this-next, plus offset + thread const CenterInfo &prev = centers[interIdx+0]; + thread const CenterInfo &cur = centers[interIdx+1]; + thread const CenterInfo &next = centers[interIdx+2]; + const float2 edgeDist = screenScale * (interSgn * w2 + centerLine); + const float2 n0 = edgeDist * cur.norm; + const float2 n1 = edgeDist * next.norm; + const IntersectInfo interInfo = intersectLines(prev.screenPos + n0, cur.screenPos + n0, + cur.screenPos + n1, next.screenPos + n1); + if (interInfo.valid) { + const float c = wedge(next.screenPos - cur.screenPos, cur.screenPos - prev.screenPos); + turningLeft = (c < 0); + realInterPt = interPt = interInfo.interPt; + + // Limit the distance to the smaller of half way back along the previous segment + // or half way forward along the next one to keep consecutive segments from colliding. + const float maxAdjDist = min(cur.len, next.len) / 2; + + // If we're using offsets, we also need to know the point where the offset lines + // intersect, as the distance to the original intersection point isn't helpful. + // todo: this doesn't make sense for small angles, but what's the actual threshold? + if (centerLine != 0) { + const float2 cn0 = cur.norm * centerLine * screenScale; + const float2 cn1 = next.norm * centerLine * screenScale; + const IntersectInfo i2 = intersectLines(prev.screenPos + cn0, cur.screenPos + cn0, + cur.screenPos + cn1, next.screenPos + cn1); + if (i2.valid && length_squared((i2.interPt - offsetCenter)/screenScale) < maxAdjDist*maxAdjDist) { + offsetCenter = i2.interPt; + } + } + + const float2 interVec = (interPt - offsetCenter) / screenScale; + const float interDist2 = length_squared(interVec); + const float maxClipDist2 = (maxAdjDist * wideVec.interClipLimit) * + (maxAdjDist * wideVec.interClipLimit); + + // Limit intersection distance. + // For up to a multiple of that, adjust the intersection point back along its + // length to that limit, effectively narrowing the line instead of just giving up. + if (interDist2 <= maxAdjDist*maxAdjDist) { + intersectValid = true; + } else if (wideVec.interClipLimit > 0 && interDist2 <= maxClipDist2) { + interPt = offsetCenter + normalize(interVec) * sqrt(interDist2) * screenScale; + intersectValid = true; + } + } + } + } + + // Endcaps not used for miter case, discard them. + if ((joinType == WKSVertexLineJoinMiter || joinType == WKSVertexLineJoinMiterSimple) && + whichPoly != WideVecPolyBodyGeom && !isStartCap && !isEndCap) { + return outVert; + } + + // Get the color from the uniform: + float4 color = wideVec.color; + + outVert.color = color; + outVert.w2 = w2; + outVert.edge = wideVec.edge; + + // Work out the corner positions by extending the normals + const float2 realEdge = interSgn * w2 * screenScale; + const float2 corner = offsetCenter + centers[2].norm * realEdge; + const float turnSgn = turningLeft ? -1 : 1; + const bool isInsideEdge = (isLeft == turningLeft); + + // Current corner is the default result for all points. + float2 pos = corner; + + bool discardTri = false; + + if (isStartCap || isEndCap) { + // Square extends beyond the point by half a width. + // Round uses the same geometry but rounds it off with the fragment shader. + if (capType == WhirlyKitShader::WKSVertexLineCapSquare || + capType == WhirlyKitShader::WKSVertexLineCapRound) { + switch (whichVert) { + case 0: case 1: case 10: case 11: + pos = corner + centers[2].nDir * w2 * screenScale * (isEnd ? 1 : -1); + break; + } + } + if (capType == WhirlyKitShader::WKSVertexLineCapRound) { + outVert.roundJoin = true; + outVert.centerPos = offsetCenter / screenScale; + outVert.screenPos = pos / screenScale; + outVert.midDir = centers[2].nDir * (isEnd ? 1 : -1); + } + } else { + if (joinType == WKSVertexLineJoinNone || !intersectValid) { + // Trivial case, just use the corner + outVert.position = float4(pos, 0, 1); + return outVert; + } + + // Since there is one, use the intersect point by default + pos = interPt; + + // We'll need the corner on the opposite side for several things. + const float2 realOtherEdge = -interSgn * w2 * screenScale; + const float2 otherCorner = offsetCenter + centers[2].norm * realOtherEdge; + + // For a bevel, use the intersect point for the inside of the turn, but not the outside. + // Round piggypacks on bevel. + if (joinType == WKSVertexLineJoinBevel || joinType == WKSVertexLineJoinRound) { + switch (whichVert) { + // Start cap, 0-3-1, 0-2-3 + case 2: case 10: + discardTri = true; // Not using triangle #2 + break; + // Merge inside corners to avoid overlap, use default outside corner. + case 0: case 3: case 8: case 9: + case 4: case 5: case 6: case 7: + pos = isInsideEdge ? interPt : corner; + break; + case 1: case 11: { + // Use the point halfway between the outside corner and the one on the opposite side. + const float2 norm = centers[(whichVert == 1) ? 1 : 3].norm; + const float2 c = offsetCenter + centers[2].norm * realEdge * turnSgn; + const float2 e = turnSgn * interSgn * w2 * screenScale; + pos = (offsetCenter + c + norm * e) / 2; + break; + } + } + + // For the round case, extend the center of the bevel out into a "tip," which will be + // turned into a round extension by the fragment shader. This isn't exactly right, but + // I think we need more geometry to do better. + // todo: fix texture Y-coords + if (joinType == WKSVertexLineJoinRound && whichPoly != WideVecPolyBodyGeom && !discardTri) { + outVert.roundJoin = true; + outVert.centerPos = offsetCenter / screenScale; + outVert.screenPos = pos / screenScale; + + // Direction bisecting the turn toward the outside (right for a left turn) + const float2 mid = isEnd ? (centers[2].nDir - centers[3].nDir) : + (centers[1].nDir - centers[2].nDir); + outVert.midDir = normalize(mid / screenScale); + + if (whichVert == 1 || whichVert == 11) { + // Extend the corner far enough to cover the necessary round-ness. + // This should probably be related to the turn angle, we're just fudging it. + const float extend = 2 * w2; + pos += outVert.midDir * extend * screenScale; + outVert.screenPos = pos / screenScale; + } + } + } else if (joinType == WKSVertexLineJoinMiterClip) { + // Direction of intersect point (bisecting the segment directions) + const float2 interVec = (realInterPt - offsetCenter) / screenScale; + const float2 interDir = normalize(interVec) * turnSgn * interSgn; + // And the perpendicular + const float2 interNorm = float2(-interDir.y, interDir.x); + // Distance from centerline intersect and edge intersect + const float interDist = length(interVec); + // "the miter is clipped by a line perpendicular to the line bisecting + // the angle between the two path segments at a distance of half the + // value of miter length from the intersection of the two path segments." + const float midExt = miterLength / 2 * strokeWidth + wideVec.edge; + + switch (whichVert) { + // Start cap, 0-3-1, 0-2-3 + case 0: case 8: { + // Out to the miter cap, then perpendicular to the line edge. + // The half-width of the clipped miter cap is the tangent of half the interior + // turn angle times the amount the original intersection extends beyond the cap. + const float miterEdgeW2 = (interDist - midExt) * tan(theta / 2); + pos = offsetCenter + screenScale * (interDir * midExt - + interNorm * miterEdgeW2 * turnSgn * (whichVert ? -1 : 1)); + break; + } + case 1: case 10: + // Extend the turn bisector to the miter clip edge + pos = turningLeft ? (offsetCenter + interDir * midExt * screenScale) : + ((whichVert == 1) ? corner : otherCorner); + break; + case 2: case 9: + // Extend segment endpoint outward along the intersection angle by the miter length + pos = turningLeft ? ((whichVert == 2) ? corner : otherCorner) : + (offsetCenter + interDir * midExt * screenScale); + break; + case 3: case 11: + // The edge intersect, or the one on the other side + pos = turningLeft ? interPt : (offsetCenter + (offsetCenter - interPt)); + break; + // Body segment, 4-7-5, 4-6-7 + // Merge inside corners to avoid overlap, use default outside corner. + case 4: case 5: case 6: case 7: + pos = isInsideEdge ? interPt : corner; + break; + } + } + } + + if (!discardTri) { + outVert.position = float4(pos, 0, 1); + } + + return outVert; +} + +// Fragment shader that takes the back of the globe into account +fragment float4 fragmentTri_wideVecPerf( + ProjVertexTriWideVecPerf vert [[stage_in]], + constant Uniforms &uniforms [[ buffer(3) ]]) +{ + float patternAlpha = 1.0; + + // Reduce alpha of the "tip" for round joins to get a round look. + float roundAlpha = 1.0; + if (vert.roundJoin && dot(vert.screenPos - vert.centerPos, vert.midDir) > 0) { + const float r = length_squared(vert.screenPos - vert.centerPos) / vert.w2 / vert.w2; + roundAlpha = clamp((r > 0.95) ? (1 - r) * 20 : 1.0, 0.0, 1.0); + } + + // Reduce alpha along the edges to get smooth blending. + const float edgeAlpha = (vert.edge > 0) ? clamp((1 - abs(vert.texCoord.x)) * vert.w2 / vert.edge, 0.0, 1.0) : 1.0; + + return vert.color * float4(1,1,1,edgeAlpha * patternAlpha * roundAlpha); +} + +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/shader_defines.hpp b/include/mbgl/shaders/shader_defines.hpp index e033c0ea3..4c7133368 100644 --- a/include/mbgl/shaders/shader_defines.hpp +++ b/include/mbgl/shaders/shader_defines.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -47,7 +48,8 @@ static constexpr auto maxUBOCountPerShader = std::max({static_cast(backg static_cast(lineSDFUBOCount), static_cast(lineBasicUBOCount), static_cast(rasterUBOCount), - static_cast(symbolUBOCount)}); + static_cast(symbolUBOCount), + static_cast(wideVectorUBOCount)}); // Texture defines enum { @@ -259,20 +261,40 @@ enum { symbolVertexAttributeCount }; -static constexpr auto maxVertexAttributeCountPerShader = std::max( - {static_cast(backgroundVertexAttributeCount), - static_cast(circleVertexAttributeCount), - static_cast(clippingMaskVertexAttributeCount), - static_cast(collisionVertexAttributeCount), - static_cast(customSymbolVertexAttributeCount), - static_cast(debugVertexAttributeCount), - static_cast(fillVertexAttributeCount), - static_cast(fillExtrusionVertexAttributeCount), - static_cast(heatmapVertexAttributeCount), - static_cast(hillshadeVertexAttributeCount), - static_cast(lineVertexAttributeCount), - static_cast(rasterVertexAttributeCount), - static_cast(symbolVertexAttributeCount)}); +enum { + idWideVectorScreenPos, + idWideVectorColor, + idWideVectorIndex, + + wideVectorAttributeCount +}; + +enum { + idWideVectorInstanceCenter, + idWideVectorInstanceColor, + idWideVectorInstancePrevious, + idWideVectorInstanceNext, + + wideVectorInstanceAttributeCount +}; + +static constexpr auto maxVertexAttributeCountPerShader = std::max({ + static_cast(backgroundVertexAttributeCount), + static_cast(circleVertexAttributeCount), + static_cast(clippingMaskVertexAttributeCount), + static_cast(collisionVertexAttributeCount), + static_cast(customSymbolVertexAttributeCount), + static_cast(debugVertexAttributeCount), + static_cast(fillVertexAttributeCount), + static_cast(fillExtrusionVertexAttributeCount), + static_cast(heatmapVertexAttributeCount), + static_cast(hillshadeVertexAttributeCount), + static_cast(lineVertexAttributeCount), + static_cast(rasterVertexAttributeCount), + static_cast(symbolVertexAttributeCount), + static_cast(wideVectorAttributeCount), + static_cast(wideVectorInstanceAttributeCount), +}); } // namespace shaders } // namespace mbgl diff --git a/include/mbgl/shaders/shader_manifest.hpp b/include/mbgl/shaders/shader_manifest.hpp index 44882c401..f8d2188fe 100644 --- a/include/mbgl/shaders/shader_manifest.hpp +++ b/include/mbgl/shaders/shader_manifest.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/include/mbgl/shaders/shader_program_base.hpp b/include/mbgl/shaders/shader_program_base.hpp index 9bd778dfd..7c4e4b678 100644 --- a/include/mbgl/shaders/shader_program_base.hpp +++ b/include/mbgl/shaders/shader_program_base.hpp @@ -40,6 +40,9 @@ public: /// Get the available vertex attributes and their default values virtual const gfx::VertexAttributeArray& getVertexAttributes() const = 0; + /// Get the available instance attributes and their default values + virtual const gfx::VertexAttributeArray& getInstanceAttributes() const = 0; + protected: virtual gfx::UniformBlockArray& mutableUniformBlocks() = 0; diff --git a/include/mbgl/shaders/shader_source.hpp b/include/mbgl/shaders/shader_source.hpp index 7facb567c..6e3382cd3 100644 --- a/include/mbgl/shaders/shader_source.hpp +++ b/include/mbgl/shaders/shader_source.hpp @@ -35,6 +35,7 @@ enum class BuiltIn { SymbolSDFIconShader, SymbolTextAndIconShader, CustomSymbolIconShader, + WideVectorShader, Prelude, BackgroundProgram, BackgroundPatternProgram, diff --git a/include/mbgl/shaders/widevector_ubo.hpp b/include/mbgl/shaders/widevector_ubo.hpp new file mode 100644 index 000000000..208ed42d9 --- /dev/null +++ b/include/mbgl/shaders/widevector_ubo.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace mbgl { +namespace shaders { + +struct alignas(16) WideVectorUniformsUBO { + std::array mvpMatrix; + std::array mvpMatrixDiff; + std::array mvMatrix; + std::array mvMatrixDiff; + std::array pMatrix; + std::array pMatrixDiff; + std::array frameSize; +}; +static_assert(sizeof(WideVectorUniformsUBO) % 16 == 0); + +struct alignas(16) WideVectorUniformWideVecUBO { + std::array color; + float w2; + float offset; + float edge; + float texRepeat; + std::array texOffset; + float miterLimit; + int32_t join; + int32_t cap; + int32_t hasExp; + float interClipLimit; +}; +static_assert(sizeof(WideVectorUniformWideVecUBO) % 16 == 0); + +enum { + idWideVectorUniformsUBO, + idWideVectorUniformWideVecUBO, + wideVectorUBOCount +}; + +struct VertexTriWideVecB { + // x, y offset around the center + std::array screenPos; + std::array color; + int index; +}; +static_assert(sizeof(VertexTriWideVecB) == 32); + +struct VertexTriWideVecInstance { + std::array center; + std::array color; + int32_t prev; + int32_t next; + int64_t pad_; +}; +static_assert(sizeof(VertexTriWideVecInstance) == 48); + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/style/layers/custom_drawable_layer.hpp b/include/mbgl/style/layers/custom_drawable_layer.hpp index c2ffdea1e..7ba5481e8 100644 --- a/include/mbgl/style/layers/custom_drawable_layer.hpp +++ b/include/mbgl/style/layers/custom_drawable_layer.hpp @@ -33,14 +33,20 @@ public: class CustomDrawableLayerHost::Interface { public: + enum class LineShaderType { + Classic, + MetalWideVector + }; + struct LineOptions { - Color color; + gfx::PolylineGeneratorOptions geometry; float blur = 0.f; float opacity = 1.f; float gapWidth = 0.f; float offset = 0.f; float width = 1.f; - gfx::PolylineGeneratorOptions geometry; + LineShaderType shaderType = LineShaderType::Classic; + Color color; }; struct FillOptions { @@ -106,24 +112,34 @@ public: /** * @brief Add a polyline * - * @param coordinates - * @param options Polyline options + * @param coordinates in tile range + * @return true if the polyline was added */ - void addPolyline(const GeometryCoordinates& coordinates); + bool addPolyline(const GeometryCoordinates& coordinates); + + /** + * @brief Add a polyline + * + * @param coordinates Geographic coordinates + * @return true if the polyline was added + */ + bool addPolyline(const LineString& coordinates); /** * @brief Add a multipolygon area fill * * @param geometry a collection of rings with optional holes + * @return true if the fill was added */ - void addFill(const GeometryCollection& geometry); + bool addFill(const GeometryCollection& geometry); /** * @brief Add a symbol * * @param point + * @return true if the symbol was added */ - void addSymbol(const GeometryCoordinate& point); + bool addSymbol(const GeometryCoordinate& point); /** * @brief Finish the current drawable building session @@ -143,10 +159,20 @@ public: private: gfx::ShaderPtr lineShaderDefault() const; + gfx::ShaderPtr lineShaderWideVector() const; gfx::ShaderPtr fillShaderDefault() const; gfx::ShaderPtr symbolShaderDefault() const; + enum class BuilderType { + None, + LineClassic, + LineWideVector, + Fill, + Symbol + }; + std::unique_ptr createBuilder(const std::string& name, gfx::ShaderPtr shader) const; + bool updateBuilder(BuilderType type, const std::string& name, gfx::ShaderPtr shader); std::unique_ptr builder; std::optional tileID; @@ -158,6 +184,8 @@ private: LineOptions lineOptions; FillOptions fillOptions; SymbolOptions symbolOptions; + + BuilderType builderType{BuilderType::None}; }; class CustomDrawableLayer final : public Layer { diff --git a/include/mbgl/util/color.hpp b/include/mbgl/util/color.hpp index f60c9f4cb..a09479863 100644 --- a/include/mbgl/util/color.hpp +++ b/include/mbgl/util/color.hpp @@ -43,6 +43,7 @@ public: static std::optional parse(const std::string&); std::string stringify() const; std::array toArray() const; + operator std::array() const { return {r, g, b, a}; } mbgl::Value toObject() const; mbgl::Value serialize() const; }; diff --git a/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm b/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm index 62cde00f0..ecf173469 100644 --- a/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm +++ b/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm @@ -47,30 +47,31 @@ public: if (interface.getDrawableCount()) return; - // set tile - interface.setTileID({11, 327, 791}); constexpr float extent = mbgl::util::EXTENT; - // add polylines + // add classic polylines { using namespace mbgl; + // set tile + interface.setTileID({11, 327, 792}); + constexpr auto numLines = 6; Interface::LineOptions options[numLines] { - {/*color=*/Color::red(), /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, {} }, - {/*color=*/Color::blue(), /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, {} }, - {/*color=*/Color(1.f, 0.5f, 0, 0.5f), /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, {} }, - {/*color=*/Color(1.f, 1.f, 0, 0.3f), /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, {} }, - {/*color=*/Color::black(), /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, {} }, - {/*color=*/Color(1.f, 0, 1.f, 0.2f), /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, {} }, + {/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, /*shaderType=*/{}, /*color=*/Color::red() }, + {/*geometry=*/{}, /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, /*shaderType=*/{}, /*color=*/Color::blue() }, + {/*geometry=*/{}, /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, /*shaderType=*/{}, /*color=*/Color(1.f, 0.5f, 0, 0.5f) }, + {/*geometry=*/{}, /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, /*shaderType=*/{}, /*color=*/Color(1.f, 1.f, 0, 0.3f) }, + {/*geometry=*/{}, /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, /*shaderType=*/{}, /*color=*/Color::black() }, + {/*geometry=*/{}, /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, /*shaderType=*/{}, /*color=*/Color(1.f, 0, 1.f, 0.2f) }, }; for(auto& opt: options) { - opt.geometry.beginCap = style::LineCapType::Round; - opt.geometry.endCap = style::LineCapType::Round; - opt.geometry.joinType = style::LineJoinType::Round; + opt.geometry.beginCap = style::LineCapType::Butt; + opt.geometry.endCap = style::LineCapType::Butt; + opt.geometry.joinType = style::LineJoinType::Miter; } - constexpr auto numPoints = 100; + constexpr auto numPoints = 10; GeometryCoordinates polyline; for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { polyline.emplace_back(ipoint * extent / numPoints, std::sin(ipoint * 2 * M_PI / numPoints) * extent / numLines / 2.f); @@ -89,10 +90,64 @@ public: } } + // add wide vector polylines with tile coordinates + { + using namespace mbgl; + + // set tile + interface.setTileID({11, 327, 792}); + + constexpr auto numLines = 6; + Interface::LineOptions options[numLines] { + {/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, /*shaderType=*/Interface::LineShaderType::MetalWideVector, /*color=*/Color::red() }, + {/*geometry=*/{}, /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, /*shaderType=*/Interface::LineShaderType::MetalWideVector, /*color=*/Color::blue() }, + {/*geometry=*/{}, /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, /*shaderType=*/Interface::LineShaderType::MetalWideVector, /*color=*/Color(1.f, 0.5f, 0, 0.5f) }, + {/*geometry=*/{}, /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, /*shaderType=*/Interface::LineShaderType::MetalWideVector, /*color=*/Color(1.f, 1.f, 0, 0.3f) }, + {/*geometry=*/{}, /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, /*shaderType=*/Interface::LineShaderType::MetalWideVector, /*color=*/Color::black() }, + {/*geometry=*/{}, /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, /*shaderType=*/Interface::LineShaderType::MetalWideVector, /*color=*/Color(1.f, 0, 1.f, 0.2f) }, + }; + for(auto& opt: options) { + opt.geometry.beginCap = style::LineCapType::Butt; + opt.geometry.endCap = style::LineCapType::Butt; + opt.geometry.joinType = style::LineJoinType::Miter; + } + + constexpr auto numPoints = 10; + GeometryCoordinates polyline; + for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { + polyline.emplace_back(ipoint * extent / numPoints, std::sin(ipoint * 2 * M_PI / numPoints) * extent / numLines / 2.f); + } + + for (auto index {0}; index < numLines; ++index) { + for(auto &p : polyline) { + if (0 == index) p.y += 0.25f * extent / numLines; + p.y += extent / numLines; + } + + // set property values + interface.setLineOptions(options[index]); + + // add polyline + interface.addPolyline(polyline); + + // add clone + for(auto &p : polyline) { + p.y += 0.05f * extent / numLines; + } + interface.addPolyline(polyline); + for(auto &p : polyline) { + p.y -= 0.05f * extent / numLines; + } + } + } + // add fill polygon { using namespace mbgl; + // set tile + interface.setTileID({11, 327, 790}); + GeometryCollection geometry{ { // ring 1 @@ -122,6 +177,10 @@ public: // add symbol { using namespace mbgl; + + // set tile + interface.setTileID({11, 327, 789}); + GeometryCoordinate position {static_cast(extent* 0.5f), static_cast(extent* 0.5f)}; // load image @@ -148,7 +207,75 @@ public: // add symbol interface.addSymbol(position); } - + + // add polylines using wide vectors using geographic coordinates + { + using namespace mbgl; + + // add polyline with geographic coordinates + Interface::LineOptions options = {/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/12.0f, /*shaderType=*/Interface::LineShaderType::MetalWideVector, /*color=*/{.0f, .0f, .0f, .5f} }; + options.geometry.beginCap = style::LineCapType::Square; + options.geometry.endCap = style::LineCapType::Square; + options.geometry.joinType = style::LineJoinType::Bevel; + options.geometry.type = FeatureType::LineString; + interface.setLineOptions(options); + + LineString polyline_geo { + // San Francisco + {-122.38186800073211, 37.77466003457463}, + {-122.3869373450997, 37.774352128895615}, + {-122.38680767979824, 37.773294612880306}, + {-122.38476465260224, 37.773350946288765}, + {-122.38146223043374, 37.77194168964067}, + {-122.6813560305925, 37.666084247570964}, + {-122.26765538866474, 37.65037232584494}, + {-122.42528413673159, 38.020443518012584}, + // Seattle + {-122.34927775216809, 47.62050663596438}, + // New York + {-74.04454331829972, 40.6892168305434}, + }; + interface.addPolyline(polyline_geo); + } + + // add polylines using wide vectors in tile coordinates + { + using namespace mbgl; + + // set tile + interface.setTileID({11, 327, 790}); + + Interface::LineOptions options + {/*geometry=*/{}, /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/7.0f, /*shaderType=*/Interface::LineShaderType::MetalWideVector, /*color=*/{1.0f, 0, 0, .5f} }; + options.geometry.beginCap = style::LineCapType::Round; + options.geometry.endCap = style::LineCapType::Round; + options.geometry.joinType = style::LineJoinType::Round; + + // add polyline with tile coordinates + GeometryCollection polyline_tile{ + { + // ring 1 + {static_cast(extent* 0.1f), static_cast(extent* 0.2f)}, + {static_cast(extent* 0.5f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.7f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.5f), static_cast(extent* 1.0f)}, + {static_cast(extent* 0.0f), static_cast(extent* 0.5f)}, + }, + { + // ring 2 + {static_cast(extent* 0.1f), static_cast(extent* 0.25f)}, + {static_cast(extent* 0.15f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.25f), static_cast(extent* 0.45f)}, + }, + }; + + options.geometry.type = FeatureType::Polygon; + interface.setLineOptions(options); + interface.addPolyline(polyline_tile[0]); + interface.addPolyline(polyline_tile[1]); + + } + // finish interface.finish(); } diff --git a/platform/ios/iosapp-UITests/iosapp_UITests.swift b/platform/ios/iosapp-UITests/iosapp_UITests.swift index 46d272872..2a5ed77ee 100644 --- a/platform/ios/iosapp-UITests/iosapp_UITests.swift +++ b/platform/ios/iosapp-UITests/iosapp_UITests.swift @@ -247,4 +247,16 @@ class iosapp_UITests: XCTestCase { } } } + + /// Make sure the Custom Drawable Layer does not crash + func testCustomDrawableLayer() { + app.windows.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.doubleTap() + + let mapSettingsButton = app.navigationBars["MapLibre Basic"].buttons["Map settings"] + mapSettingsButton.tap() + + let tablesQuery = app.tables + tablesQuery.staticTexts["Add Custom Drawable Layer"].tap() + sleep(5) + } } diff --git a/shaders/_empty.glsl b/shaders/_empty.glsl new file mode 100644 index 000000000..e69de29bb diff --git a/shaders/manifest.json b/shaders/manifest.json index 0280508b0..64d930d25 100644 --- a/shaders/manifest.json +++ b/shaders/manifest.json @@ -181,6 +181,13 @@ "glsl_frag": "drawable.custom.symbol_icon.fragment.glsl", "uses_ubos": true }, + { + "name": "WideVectorShader", + "header": "drawable_wide_vector", + "glsl_vert": "_empty.glsl", + "glsl_frag": "_empty.glsl", + "uses_ubos": false + }, { "name": "Prelude", diff --git a/src/mbgl/gfx/drawable_builder.cpp b/src/mbgl/gfx/drawable_builder.cpp index 72bff5023..8f193ad24 100644 --- a/src/mbgl/gfx/drawable_builder.cpp +++ b/src/mbgl/gfx/drawable_builder.cpp @@ -44,6 +44,9 @@ void DrawableBuilder::flush(gfx::Context& context) { if (Impl::Mode::Polylines == impl->getMode()) { // setup for polylines impl->setupForPolylines(context, *this); + } else if (Impl::Mode::WideVectorLocal == impl->getMode() || Impl::Mode::WideVectorGlobal == impl->getMode()) { + // setup for wide vectors + impl->setupForWideVectors(context, *this); } const auto& draw = getCurrentDrawable(/*createIfNone=*/true); @@ -62,6 +65,7 @@ void DrawableBuilder::flush(gfx::Context& context) { draw->setShader(shader); draw->setTextures(textures); draw->setTweakers(tweakers); + draw->setOrigin(origin); if (vertexAttrs) { draw->setVertexAttributes(vertexAttrs); @@ -69,10 +73,17 @@ void DrawableBuilder::flush(gfx::Context& context) { draw->setVertexAttributes(context.createVertexAttributeArray()); } + if (instanceAttrs) { + draw->setInstanceAttributes(instanceAttrs); + } + init(); // reset mode impl->setMode(Impl::Mode::Custom); + + // reset origin + origin.reset(); } if (currentDrawable) { drawables.emplace_back(std::move(currentDrawable)); @@ -280,5 +291,23 @@ void DrawableBuilder::addPolyline(const GeometryCoordinates& coordinates, impl->addPolyline(*this, coordinates, options); } +void DrawableBuilder::addWideVectorPolylineLocal(const GeometryCoordinates& coordinates, + const gfx::PolylineGeneratorOptions& options) { + // mark the current mode + if (!impl->checkAndSetMode(Impl::Mode::WideVectorLocal)) return; + + // append polyline + impl->addWideVectorPolylineLocal(*this, coordinates, options); +} + +void DrawableBuilder::addWideVectorPolylineGlobal(const LineString& coordinates, + const gfx::PolylineGeneratorOptions& options) { + // mark the current mode + if (!impl->checkAndSetMode(Impl::Mode::WideVectorGlobal)) return; + + // append polyline + origin = impl->addWideVectorPolylineGlobal(*this, coordinates, options); +} + } // namespace gfx } // namespace mbgl diff --git a/src/mbgl/gfx/drawable_builder_impl.cpp b/src/mbgl/gfx/drawable_builder_impl.cpp index a8e88a66c..523b00b82 100644 --- a/src/mbgl/gfx/drawable_builder_impl.cpp +++ b/src/mbgl/gfx/drawable_builder_impl.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -13,6 +14,8 @@ namespace gfx { using namespace shaders; +// MARK: - Polylines + DrawableBuilder::Impl::LineLayoutVertex DrawableBuilder::Impl::layoutVertex( Point p, Point e, bool round, bool up, int8_t dir, int32_t linesofar /*= 0*/) { /* @@ -91,6 +94,186 @@ void DrawableBuilder::Impl::setupForPolylines(gfx::Context& context, gfx::Drawab sharedIndexes = std::make_shared(std::move(polylineIndexes)); } +// MARK: - Wide Vector Polylines + +namespace { +inline void genInstanceLinks( + int32_t& outPrev, int32_t& outNext, const int base, const int coord_size, const int index, bool loop) { + if (loop) { + // loop line + outPrev = base + (index - 1 + coord_size) % coord_size; + outNext = base + (index + 1) % coord_size; + } else { + // line string + outPrev = (0 == index) ? -1 : base + index - 1; + outNext = (index + 1 >= coord_size) ? -1 : base + index + 1; + } +} +} // namespace + +void DrawableBuilder::Impl::addWideVectorPolylineLocal(gfx::DrawableBuilder& /*builder*/, + const GeometryCoordinates& coordinates, + const gfx::PolylineGeneratorOptions& options) { + // add instance data + const int base = static_cast(wideVectorInstanceData.elements()); + const int coord_size = static_cast(coordinates.size()); + int index = 0; + for (const auto& coord : coordinates) { + VertexTriWideVecInstance data; + data.center = {static_cast(coord.x), static_cast(coord.y), 0}; + genInstanceLinks(data.prev, data.next, base, coord_size, index, FeatureType::Polygon == options.type); + wideVectorInstanceData.emplace_back(std::move(data)); + ++index; + } +} + +mbgl::Point DrawableBuilder::Impl::addWideVectorPolylineGlobal(gfx::DrawableBuilder& /*builder*/, + const LineString& coordinates, + const gfx::PolylineGeneratorOptions& options) { + constexpr int32_t zoom = 0; + + // get center + constexpr double maxd = std::numeric_limits::max(); + constexpr double mind = std::numeric_limits::min(); + Point minPoint{maxd, maxd}, maxPoint{mind, mind}; + for (const auto& coord : coordinates) { + auto merc = Projection::project(LatLng(coord.y, coord.x), zoom); + Point pSource{merc.x * mbgl::util::EXTENT, merc.y * mbgl::util::EXTENT}; + minPoint.x = std::min(pSource.x, minPoint.x); + minPoint.y = std::min(pSource.y, minPoint.y); + maxPoint.x = std::max(pSource.x, maxPoint.x); + maxPoint.y = std::max(pSource.y, maxPoint.y); + } + Point pCenter{(minPoint.x + maxPoint.x) / 2.0, (minPoint.y + maxPoint.y) / 2.0}; + + // add centerline instance data + const int base = static_cast(wideVectorInstanceData.elements()); + const int coord_size = static_cast(coordinates.size()); + int index = 0; + for (const auto& coord : coordinates) { + auto merc = Projection::project(LatLng(coord.y, coord.x), zoom); + Point pSource{merc.x * mbgl::util::EXTENT, merc.y * mbgl::util::EXTENT}; + pSource.x -= pCenter.x; + pSource.y -= pCenter.y; + VertexTriWideVecInstance data; + data.center = {static_cast(pSource.x), static_cast(pSource.y), 0}; + genInstanceLinks(data.prev, data.next, base, coord_size, index, FeatureType::Polygon == options.type); + wideVectorInstanceData.emplace_back(std::move(data)); + ++index; + } + + return pCenter; +} + +void DrawableBuilder::Impl::setupForWideVectors(gfx::Context& context, gfx::DrawableBuilder& builder) { + // vertices + if (!wideVectorVertices) { + constexpr shaders::VertexTriWideVecB kWideVectorVertices[]{ + {{0}, {0}, (0 << 16) + 0}, + {{0}, {0}, (1 << 16) + 0}, + {{0}, {0}, (2 << 16) + 0}, + {{0}, {0}, (3 << 16) + 0}, + + {{0}, {0}, (4 << 16) + 1}, + {{0}, {0}, (5 << 16) + 1}, + {{0}, {0}, (6 << 16) + 1}, + {{0}, {0}, (7 << 16) + 1}, + + {{0}, {0}, (8 << 16) + 2}, + {{0}, {0}, (9 << 16) + 2}, + {{0}, {0}, (10 << 16) + 2}, + {{0}, {0}, (11 << 16) + 2}, + }; + wideVectorVertices = std::make_shared>(); + for (auto& v : kWideVectorVertices) { + wideVectorVertices->emplace_back(v); + } + } + + // instance data + using InstanceVector = gfx::VertexVector; + const std::shared_ptr sharedInstanceData = std::make_shared( + std::move(wideVectorInstanceData)); + + // indexes + if (!wideVectorIndexes) { + wideVectorIndexes = std::make_shared>(); + wideVectorIndexes->emplace_back( + 0, 3, 1, 0, 2, 3, 4 + 0, 4 + 3, 4 + 1, 4 + 0, 4 + 2, 4 + 3, 8 + 0, 8 + 3, 8 + 1, 8 + 0, 8 + 2, 8 + 3); + } + + // segments + SegmentVector triangleSegments; + triangleSegments.emplace_back(Segment{0, 0, 12, 18}); + + // add to builder + { + // add vertex attributes + auto vertexAttributes = context.createVertexAttributeArray(); + if (const auto& attr = vertexAttributes->set(idWideVectorScreenPos)) { + attr->setSharedRawData(wideVectorVertices, + offsetof(VertexTriWideVecB, screenPos), + /*vertexOffset=*/0, + sizeof(VertexTriWideVecB), + gfx::AttributeDataType::Float3); + } + if (const auto& attr = vertexAttributes->set(idWideVectorColor)) { + attr->setSharedRawData(wideVectorVertices, + offsetof(VertexTriWideVecB, color), + /*vertexOffset=*/0, + sizeof(VertexTriWideVecB), + gfx::AttributeDataType::Float4); + } + if (const auto& attr = vertexAttributes->set(idWideVectorIndex)) { + attr->setSharedRawData(wideVectorVertices, + offsetof(VertexTriWideVecB, index), + /*vertexOffset=*/0, + sizeof(VertexTriWideVecB), + gfx::AttributeDataType::Int); + } + builder.setVertexAttributes(std::move(vertexAttributes)); + } + + { + // add instance attributes + auto instanceAttributes = context.createVertexAttributeArray(); + if (const auto& attr = instanceAttributes->set(idWideVectorInstanceCenter)) { + attr->setSharedRawData(sharedInstanceData, + offsetof(VertexTriWideVecInstance, center), + /*vertexOffset=*/0, + sizeof(VertexTriWideVecInstance), + gfx::AttributeDataType::Float3); + } + if (const auto& attr = instanceAttributes->set(idWideVectorInstanceColor)) { + attr->setSharedRawData(sharedInstanceData, + offsetof(VertexTriWideVecInstance, color), + /*vertexOffset=*/0, + sizeof(VertexTriWideVecInstance), + gfx::AttributeDataType::Float4); + } + if (const auto& attr = instanceAttributes->set(idWideVectorInstancePrevious)) { + attr->setSharedRawData(sharedInstanceData, + offsetof(VertexTriWideVecInstance, prev), + /*vertexOffset=*/0, + sizeof(VertexTriWideVecInstance), + gfx::AttributeDataType::Int); + } + if (const auto& attr = instanceAttributes->set(idWideVectorInstanceNext)) { + attr->setSharedRawData(sharedInstanceData, + offsetof(VertexTriWideVecInstance, next), + /*vertexOffset=*/0, + sizeof(VertexTriWideVecInstance), + gfx::AttributeDataType::Int); + } + builder.setInstanceAttributes(std::move(instanceAttributes)); + } + + builder.setRawVertices({}, wideVectorVertices->elements(), gfx::AttributeDataType::Float2); + builder.setSegments(gfx::Triangles(), wideVectorIndexes, triangleSegments.data(), triangleSegments.size()); +} + +// MARK: - Common + bool DrawableBuilder::Impl::checkAndSetMode(Mode target) { if (target != mode && vertexCount()) { // log error @@ -101,6 +284,10 @@ bool DrawableBuilder::Impl::checkAndSetMode(Mode target) { return "Mode::Primitives"; case Mode::Polylines: return "Mode::Polylines"; + case Mode::WideVectorLocal: + return "Mode::WideVectorLocal"; + case Mode::WideVectorGlobal: + return "Mode::WideVectorGlobal"; case Mode::Custom: return "Mode::Custom"; default: diff --git a/src/mbgl/gfx/drawable_builder_impl.hpp b/src/mbgl/gfx/drawable_builder_impl.hpp index 2359960a3..d3fec0b1a 100644 --- a/src/mbgl/gfx/drawable_builder_impl.hpp +++ b/src/mbgl/gfx/drawable_builder_impl.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -21,9 +22,11 @@ class DrawableBuilder::Impl { public: using VT = gfx::detail::VertexType>; enum class Mode { - Primitives, ///< building primitive drawables. Not implemented - Polylines, ///< building drawables for thick polylines - Custom ///< building custom drawables. + Primitives, ///< building primitive drawables. Not implemented + Polylines, ///< building drawables for thick polylines + WideVectorLocal, ///< building drawables for thick polylines using wide vectors in local coordinates + WideVectorGlobal, ///< building drawables for thick polylines using wide vectors in global coordinates + Custom ///< building custom drawables. }; struct LineLayoutVertex { std::array a1; @@ -39,6 +42,10 @@ public: gfx::VertexVector polylineVertices; gfx::IndexVector polylineIndexes; + std::shared_ptr> wideVectorVertices; + gfx::VertexVector wideVectorInstanceData; + std::shared_ptr> wideVectorIndexes; + std::vector buildIndexes; std::shared_ptr sharedIndexes; std::vector> segments; @@ -47,12 +54,23 @@ public: gfx::ColorMode colorMode = gfx::ColorMode::disabled(); gfx::CullFaceMode cullFaceMode = gfx::CullFaceMode::disabled(); + // methods void addPolyline(gfx::DrawableBuilder& builder, const GeometryCoordinates& coordinates, const gfx::PolylineGeneratorOptions& options); void setupForPolylines(gfx::Context&, gfx::DrawableBuilder&); + void addWideVectorPolylineLocal(gfx::DrawableBuilder& builder, + const GeometryCoordinates& coordinates, + const gfx::PolylineGeneratorOptions& options); + + mbgl::Point addWideVectorPolylineGlobal(gfx::DrawableBuilder& builder, + const LineString& coordinates, + const gfx::PolylineGeneratorOptions& options); + + void setupForWideVectors(gfx::Context&, gfx::DrawableBuilder&); + bool checkAndSetMode(Mode); Mode getMode() const { return mode; }; @@ -60,7 +78,8 @@ public: bool setMode(Mode value) { return mode == value; }; std::size_t vertexCount() const { - return std::max(rawVerticesCount, std::max(vertices.elements(), polylineVertices.elements())); + return std::max( + {rawVerticesCount, vertices.elements(), polylineVertices.elements(), wideVectorInstanceData.elements()}); } void clear() { diff --git a/src/mbgl/mtl/drawable.cpp b/src/mbgl/mtl/drawable.cpp index 6e5a85a3a..9dd20a18c 100644 --- a/src/mbgl/mtl/drawable.cpp +++ b/src/mbgl/mtl/drawable.cpp @@ -186,6 +186,7 @@ void Drawable::draw(PaintParameters& parameters) const { #endif bindAttributes(renderPass); + bindInstanceAttributes(renderPass); bindUniformBuffers(renderPass); bindTextures(renderPass); @@ -256,7 +257,7 @@ void Drawable::draw(PaintParameters& parameters) const { const auto primitiveType = getPrimitiveType(mode.type); constexpr auto indexType = MTL::IndexType::IndexTypeUInt16; constexpr auto indexSize = sizeof(std::uint16_t); - constexpr NS::UInteger instanceCount = 1; + const NS::UInteger instanceCount = instanceAttributes ? instanceAttributes->getMaxCount() : 1; constexpr NS::UInteger baseInstance = 0; const NS::UInteger indexOffset = static_cast(indexSize * mlSegment.indexOffset); // in bytes, not indexes @@ -360,6 +361,25 @@ void Drawable::bindAttributes(RenderPass& renderPass) const noexcept { } } +void Drawable::bindInstanceAttributes(RenderPass& renderPass) const noexcept { + const auto& encoder = renderPass.getMetalEncoder(); + + NS::UInteger attributeIndex = 0; + for (const auto& binding : impl->instanceBindings) { + if (binding.has_value()) { + const auto* buffer = static_cast(binding->vertexBufferResource); + if (buffer && buffer->get()) { + renderPass.bindVertex(buffer->get(), /*offset=*/0, attributeIndex); + } else { + const auto* shaderMTL = static_cast(shader.get()); + auto& context = renderPass.getCommandEncoder().getContext(); + renderPass.bindVertex(context.getEmptyBuffer(), /*offset=*/0, attributeIndex); + } + } + attributeIndex += 1; + } +} + void Drawable::bindUniformBuffers(RenderPass& renderPass) const noexcept { if (shader) { const auto& uniformBlocks = shader->getUniformBlocks(); @@ -584,6 +604,30 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) { } } + // build instance buffer + const bool buildInstanceBuffer = (instanceAttributes && instanceAttributes->isDirty()); + + if (buildInstanceBuffer) { + // Build instance attribute buffers + std::vector> instanceBuffers; + auto instanceBindings_ = uploadPass.buildAttributeBindings(instanceAttributes->getMaxCount(), + /*vertexType*/ gfx::AttributeDataType::Byte, + /*vertexAttributeIndex=*/-1, + /*vertexData=*/{}, + shader->getInstanceAttributes(), + *instanceAttributes, + usage, + instanceBuffers); + impl->instanceBuffers = std::move(instanceBuffers); + + // clear dirty flag + instanceAttributes->visitAttributes([](gfx::VertexAttribute& attrib) { attrib.setDirty(false); }); + + if (impl->instanceBindings != instanceBindings_) { + impl->instanceBindings = std::move(instanceBindings_); + } + } + const bool texturesNeedUpload = std::any_of( textures.begin(), textures.end(), [](const auto& texture) { return texture && texture->needsUpload(); }); diff --git a/src/mbgl/mtl/drawable_impl.hpp b/src/mbgl/mtl/drawable_impl.hpp index b9d826e92..2958b00f8 100644 --- a/src/mbgl/mtl/drawable_impl.hpp +++ b/src/mbgl/mtl/drawable_impl.hpp @@ -45,6 +45,10 @@ public: gfx::AttributeDataType vertexType = gfx::AttributeDataType::Invalid; std::vector attributeBuffers; + gfx::AttributeBindingArray attributeBindings; + + std::vector instanceBuffers; + gfx::AttributeBindingArray instanceBindings; UniformBufferArray uniformBuffers; @@ -56,8 +60,6 @@ public: VertexBufferResource* noBindingBuffer = nullptr; - gfx::AttributeBindingArray attributeBindings; - MTLRenderPipelineStatePtr pipelineState; std::optional renderPassDescriptor; diff --git a/src/mbgl/mtl/renderer_backend.cpp b/src/mbgl/mtl/renderer_backend.cpp index a7351175f..19279c8e9 100644 --- a/src/mbgl/mtl/renderer_backend.cpp +++ b/src/mbgl/mtl/renderer_backend.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -150,7 +151,8 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::RasterShader, shaders::BuiltIn::SymbolIconShader, shaders::BuiltIn::SymbolSDFIconShader, - shaders::BuiltIn::SymbolTextAndIconShader>(shaders, programParameters); + shaders::BuiltIn::SymbolTextAndIconShader, + shaders::BuiltIn::WideVectorShader>(shaders, programParameters); } } // namespace mtl diff --git a/src/mbgl/renderer/layer_tweaker.cpp b/src/mbgl/renderer/layer_tweaker.cpp index b39b25c75..2158661c5 100644 --- a/src/mbgl/renderer/layer_tweaker.cpp +++ b/src/mbgl/renderer/layer_tweaker.cpp @@ -37,6 +37,9 @@ mat4 LayerTweaker::getTileMatrix(const UnwrappedTileID& tileID, // from RenderTile::prepare mat4 tileMatrix; parameters.state.matrixFor(/*out*/ tileMatrix, tileID); + if (const auto& origin{drawable.getOrigin()}; origin.has_value()) { + matrix::translate(tileMatrix, tileMatrix, origin->x, origin->y, 0); + } // nearClippedMatrix has near plane moved further, to enhance depth buffer precision auto projMatrix = aligned ? parameters.transformParams.alignedProjMatrix diff --git a/src/mbgl/shaders/mtl/shader_program.cpp b/src/mbgl/shaders/mtl/shader_program.cpp index 466854487..46fb4670a 100644 --- a/src/mbgl/shaders/mtl/shader_program.cpp +++ b/src/mbgl/shaders/mtl/shader_program.cpp @@ -23,23 +23,6 @@ using namespace std::string_literals; namespace mbgl { -shaders::AttributeInfo::AttributeInfo(std::size_t index_, gfx::AttributeDataType dataType_, std::size_t id_) - : index(index_), - dataType(dataType_), - id(id_) {} - -shaders::UniformBlockInfo::UniformBlockInfo( - std::size_t index_, bool vertex_, bool fragment_, std::size_t size_, std::size_t id_) - : index(index_), - vertex(vertex_), - fragment(fragment_), - size(size_), - id(id_) {} - -shaders::TextureInfo::TextureInfo(std::size_t index_, std::size_t id_) - : index(index_), - id(id_) {} - namespace mtl { namespace { MTL::BlendOperation metalBlendOperation(const gfx::ColorBlendEquationType& colorBlend) { @@ -194,16 +177,30 @@ void ShaderProgram::initAttribute(const shaders::AttributeInfo& info) { #if !defined(NDEBUG) // Indexes must be unique, if there's a conflict check the `attributes` array in the shader vertexAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); }); + instanceAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); }); uniformBlocks.visit([&](const gfx::UniformBlock& block) { assert(block.getIndex() != index); }); #endif vertexAttributes.set(info.id, index, info.dataType, 1); } +void ShaderProgram::initInstanceAttribute(const shaders::AttributeInfo& info) { + // Index is the block index of the instance attribute + const auto index = static_cast(info.index); +#if !defined(NDEBUG) + // Indexes must not be reused by regular attributes or uniform blocks + // More than one instance attribute can have the same index, if they share the block + vertexAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); }); + uniformBlocks.visit([&](const gfx::UniformBlock& block) { assert(block.getIndex() != index); }); +#endif + instanceAttributes.set(info.id, index, info.dataType, 1); +} + void ShaderProgram::initUniformBlock(const shaders::UniformBlockInfo& info) { const auto index = static_cast(info.index); #if !defined(NDEBUG) // Indexes must be unique, if there's a conflict check the `attributes` array in the shader vertexAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); }); + instanceAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); }); uniformBlocks.visit([&](const gfx::UniformBlock& block) { assert(block.getIndex() != index); }); #endif if (const auto& block_ = uniformBlocks.set(info.id, index, info.size)) { diff --git a/src/mbgl/style/layers/custom_drawable_layer.cpp b/src/mbgl/style/layers/custom_drawable_layer.cpp index 4c579140a..19a879ac1 100644 --- a/src/mbgl/style/layers/custom_drawable_layer.cpp +++ b/src/mbgl/style/layers/custom_drawable_layer.cpp @@ -22,6 +22,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include @@ -84,7 +91,6 @@ class LineDrawableTweaker : public gfx::DrawableTweaker { public: LineDrawableTweaker(const shaders::LinePropertiesUBO& properties) : linePropertiesUBO(properties) {} - ~LineDrawableTweaker() override = default; void init(gfx::Drawable&) override {}; @@ -129,12 +135,74 @@ private: shaders::LinePropertiesUBO linePropertiesUBO; }; +class WideVectorDrawableTweaker : public gfx::DrawableTweaker { +public: + WideVectorDrawableTweaker(const CustomDrawableLayerHost::Interface::LineOptions& options) + : options(options) {} + + void init(gfx::Drawable&) override {}; + + void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override { + if (!drawable.getTileID().has_value()) { + return; + } + + const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped(); + + mat4 tileMatrix; + parameters.state.matrixFor(/*out*/ tileMatrix, tileID); + if (const auto& origin{drawable.getOrigin()}; origin.has_value()) { + matrix::translate(tileMatrix, tileMatrix, origin->x, origin->y, 0); + } + + mat4 projMatrix = parameters.transformParams.projMatrix; + const auto matrix = LayerTweaker::getTileMatrix( + tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, drawable, false); + + matf4 mvpMatrix, mvpMatrixDiff, mvMatrix, mvMatrixDiff, pMatrix, pMatrixDiff; + matrix::diffsplit(mvpMatrix, mvpMatrixDiff, matrix); + matrix::diffsplit(mvMatrix, mvMatrixDiff, tileMatrix); + matrix::diffsplit(pMatrix, pMatrixDiff, projMatrix); + + const auto renderableSize = parameters.backend.getDefaultRenderable().getSize(); + shaders::WideVectorUniformsUBO uniform{ + /*mvpMatrix */ mvpMatrix, + /*mvpMatrixDiff */ mvpMatrixDiff, + /*mvMatrix */ mvMatrix, + /*mvMatrixDiff */ mvMatrixDiff, + /*pMatrix */ pMatrix, + /*pMatrixDiff */ pMatrixDiff, + /*frameSize */ {(float)renderableSize.width, (float)renderableSize.height}}; + + shaders::WideVectorUniformWideVecUBO wideVec{ + /*color */ options.color, + /*w2 */ options.width, + /*offset */ options.offset, + /*edge */ 0.0f, // TODO: MLN does not provide a value. Analyze impact. + /*texRepeat */ 0.0f, // N/A + /*texOffset */ {}, // N/A + /*miterLimit */ options.geometry.miterLimit, + /*join */ static_cast(options.geometry.joinType), + /*cap */ static_cast(options.geometry.beginCap), // TODO: MLN option for endCap to be + // implemented in the shader! + /*hasExp */ false, // N/A + /*interClipLimit*/ 0.0f // N/A + }; + + auto& uniforms = drawable.mutableUniformBuffers(); + uniforms.createOrUpdate(idWideVectorUniformsUBO, &uniform, parameters.context); + uniforms.createOrUpdate(idWideVectorUniformWideVecUBO, &wideVec, parameters.context); + }; + +private: + CustomDrawableLayerHost::Interface::LineOptions options; +}; + class FillDrawableTweaker : public gfx::DrawableTweaker { public: FillDrawableTweaker(const Color& color_, float opacity_) : color(color_), opacity(opacity_) {} - ~FillDrawableTweaker() override = default; void init(gfx::Drawable&) override {}; @@ -181,7 +249,6 @@ class SymbolDrawableTweaker : public gfx::DrawableTweaker { public: SymbolDrawableTweaker(const CustomDrawableLayerHost::Interface::SymbolOptions& options_) : options(options_) {} - ~SymbolDrawableTweaker() override = default; void init(gfx::Drawable&) override {}; @@ -261,6 +328,7 @@ std::size_t CustomDrawableLayerHost::Interface::getDrawableCount() const { } void CustomDrawableLayerHost::Interface::setTileID(OverscaledTileID tileID_) { + if (tileID != tileID_) finish(); tileID = tileID_; } @@ -279,27 +347,65 @@ void CustomDrawableLayerHost::Interface::setSymbolOptions(const SymbolOptions& o symbolOptions = options; } -void CustomDrawableLayerHost::Interface::addPolyline(const GeometryCoordinates& coordinates) { - if (!lineShader) lineShader = lineShaderDefault(); - assert(lineShader); - if (!builder || builder->getShader() != lineShader) { +bool CustomDrawableLayerHost::Interface::updateBuilder(BuilderType type, + const std::string& name, + gfx::ShaderPtr shader) { + if (!shader) return false; + if (type != builderType || !builder || builder->getShader() != shader) { finish(); - builder = createBuilder("lines", lineShader); + builder = createBuilder(name, shader); + builderType = type; } assert(builder); - assert(builder->getShader() == lineShader); - builder->addPolyline(coordinates, lineOptions.geometry); + assert(builder->getShader() == shader); + return true; +}; + +bool CustomDrawableLayerHost::Interface::addPolyline(const LineString& coordinates) { + switch (lineOptions.shaderType) { + case LineShaderType::Classic: { + // TODO: build classic polyline with Geo coordinates + return false; + } break; + + case LineShaderType::MetalWideVector: { + // build wide vector polyline with Geo coordinates + if (!updateBuilder(BuilderType::LineWideVector, "custom-lines-widevector", lineShaderWideVector())) + return false; + + // geographic coordinates require tile {0, 0, 0} + setTileID({0, 0, 0}); + + builder->addWideVectorPolylineGlobal(coordinates, lineOptions.geometry); + } break; + } + + return true; } -void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geometry) { - if (!fillShader) fillShader = fillShaderDefault(); - assert(fillShader); - if (!builder || builder->getShader() != fillShader) { - finish(); - builder = createBuilder("fill", fillShader); +bool CustomDrawableLayerHost::Interface::addPolyline(const GeometryCoordinates& coordinates) { + switch (lineOptions.shaderType) { + case LineShaderType::Classic: { + // build classic polyline with Tile coordinates + if (!updateBuilder(BuilderType::LineClassic, "custom-lines", lineShaderDefault())) return false; + builder->addPolyline(coordinates, lineOptions.geometry); + } break; + + case LineShaderType::MetalWideVector: { + // build wide vector polyline + if (!updateBuilder(BuilderType::LineWideVector, "custom-lines-widevector", lineShaderWideVector())) + return false; + + builder->addWideVectorPolylineLocal(coordinates, lineOptions.geometry); + } break; } - assert(builder); - assert(builder->getShader() == fillShader); + + return true; +} + +bool CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geometry) { + // build fill + if (!updateBuilder(BuilderType::Fill, "custom-fill", fillShaderDefault())) return false; // provision buffers for fill vertices, indexes and segments using VertexVector = gfx::VertexVector; @@ -330,17 +436,13 @@ void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geome // flush current builder drawable builder->flush(context); + + return true; } -void CustomDrawableLayerHost::Interface::addSymbol(const GeometryCoordinate& point) { - if (!symbolShader) symbolShader = symbolShaderDefault(); - assert(symbolShader); - if (!builder || builder->getShader() != symbolShader) { - finish(); - builder = createBuilder("symbol", symbolShader); - } - assert(builder); - assert(builder->getShader() == symbolShader); +bool CustomDrawableLayerHost::Interface::addSymbol(const GeometryCoordinate& point) { + // build symbol + if (!updateBuilder(BuilderType::Symbol, "custom-symbol", symbolShaderDefault())) return false; // temporary: buffers struct CustomSymbolIcon { @@ -403,11 +505,16 @@ void CustomDrawableLayerHost::Interface::addSymbol(const GeometryCoordinate& poi // flush current builder drawable builder->flush(context); + + return true; } void CustomDrawableLayerHost::Interface::finish() { if (builder && !builder->empty()) { - // finish + // flush current builder drawable + builder->flush(context); + + // finish function const auto finish_ = [this](gfx::DrawableTweakerPtr tweaker) { builder->flush(context); for (auto& drawable : builder->clearDrawables()) { @@ -422,42 +529,58 @@ void CustomDrawableLayerHost::Interface::finish() { } }; - if (builder->getShader() == lineShader) { - // finish building lines + // what were we building? + switch (builderType) { + case BuilderType::LineClassic: { + // finish building classic lines - // create line tweaker - const shaders::LinePropertiesUBO linePropertiesUBO{lineOptions.color, - lineOptions.blur, - lineOptions.opacity, - lineOptions.gapWidth, - lineOptions.offset, - lineOptions.width, - 0, - 0, - 0}; - auto tweaker = std::make_shared(linePropertiesUBO); + // create line tweaker + const shaders::LinePropertiesUBO linePropertiesUBO{lineOptions.color, + lineOptions.blur, + lineOptions.opacity, + lineOptions.gapWidth, + lineOptions.offset, + lineOptions.width, + 0, + 0, + 0}; + auto tweaker = std::make_shared(linePropertiesUBO); - // finish drawables - finish_(tweaker); - } else if (builder->getShader() == fillShader) { - // finish building fills + // finish drawables + finish_(tweaker); + } break; + case BuilderType::LineWideVector: { + // finish building wide vector lines - // create fill tweaker - auto tweaker = std::make_shared(fillOptions.color, fillOptions.opacity); + // create line tweaker + auto tweaker = std::make_shared(lineOptions); - // finish drawables - finish_(tweaker); - } else if (builder->getShader() == symbolShader) { - // finish building symbols + // finish drawables + finish_(tweaker); + } break; + case BuilderType::Fill: { + // finish building fills - // finish drawables - finish_(nullptr); + // create fill tweaker + auto tweaker = std::make_shared(fillOptions.color, fillOptions.opacity); + + // finish drawables + finish_(tweaker); + } break; + case BuilderType::Symbol: + // finish building symbols + finish_(nullptr); + break; + default: + break; } } } gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const { gfx::ShaderGroupPtr shaderGroup = shaders.getShaderGroup("LineShader"); + assert(shaderGroup); + if (!shaderGroup) return gfx::ShaderPtr(); const StringIDSetsPair propertiesAsUniforms{{"a_color", "a_blur", "a_opacity", "a_gapwidth", "a_offset", "a_width"}, {idLineColorVertexAttribute, @@ -470,6 +593,13 @@ gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const { return shaderGroup->getOrCreateShader(context, propertiesAsUniforms); } +gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderWideVector() const { + gfx::ShaderGroupPtr shaderGroup = shaders.getShaderGroup("WideVectorShader"); + if (!shaderGroup) return gfx::ShaderPtr(); + + return shaderGroup->getOrCreateShader(context, {}); +} + gfx::ShaderPtr CustomDrawableLayerHost::Interface::fillShaderDefault() const { gfx::ShaderGroupPtr shaderGroup = shaders.getShaderGroup("FillShader"); diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp index b69eb3262..444a888bd 100644 --- a/src/mbgl/util/mat4.cpp +++ b/src/mbgl/util/mat4.cpp @@ -407,6 +407,13 @@ void transformMat4(vec4& out, const vec4& a, const mat4& m) { out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; } +void diffsplit(matf4& outValue, matf4& outDiff, const mat4& source) { + for (auto i = 0; i < 16; ++i) { + outValue[i] = static_cast(source[i]); + outDiff[i] = source[i] - outValue[i]; + } +} + } // namespace matrix } // namespace mbgl diff --git a/src/mbgl/util/mat4.hpp b/src/mbgl/util/mat4.hpp index c3e8c0e18..f5600a896 100644 --- a/src/mbgl/util/mat4.hpp +++ b/src/mbgl/util/mat4.hpp @@ -53,5 +53,6 @@ void multiply(mat4& out, const mat4& a, const mat4& b); void transformMat4(vec4& out, const vec4& a, const mat4& m); +void diffsplit(matf4& outValue, matf4& outDiff, const mat4& source); } // namespace matrix } // namespace mbgl diff --git a/test/api/custom_drawable_layer.test.cpp b/test/api/custom_drawable_layer.test.cpp index e3c27f58c..8b69b3bd4 100644 --- a/test/api/custom_drawable_layer.test.cpp +++ b/test/api/custom_drawable_layer.test.cpp @@ -38,48 +38,66 @@ public: constexpr auto numLines = 6; Interface::LineOptions options[numLines]{ - {/*color=*/Color::red(), - /*blur=*/0.0f, - /*opacity=*/1.0f, - /*gapWidth=*/0.0f, - /*offset=*/0.0f, - /*width=*/8.0f, - {}}, - {/*color=*/Color::blue(), - /*blur=*/4.0f, - /*opacity=*/1.0f, - /*gapWidth=*/2.0f, - /*offset=*/-1.0f, - /*width=*/4.0f, - {}}, - {/*color=*/Color(1.f, 0.5f, 0, 0.5f), - /*blur=*/16.0f, - /*opacity=*/1.0f, - /*gapWidth=*/1.0f, - /*offset=*/2.0f, - /*width=*/16.0f, - {}}, - {/*color=*/Color(1.f, 1.f, 0, 0.3f), - /*blur=*/2.0f, - /*opacity=*/1.0f, - /*gapWidth=*/1.0f, - /*offset=*/-2.0f, - /*width=*/2.0f, - {}}, - {/*color=*/Color::black(), - /*blur=*/0.5f, - /*opacity=*/0.5f, - /*gapWidth=*/1.0f, - /*offset=*/0.5f, - /*width=*/0.5f, - {}}, - {/*color=*/Color(1.f, 0, 1.f, 0.2f), - /*blur=*/24.0f, - /*opacity=*/0.5f, - /*gapWidth=*/1.0f, - /*offset=*/-5.0f, - /*width=*/24.0f, - {}}, + { + /*geometry=*/{}, + /*blur=*/0.0f, + /*opacity=*/1.0f, + /*gapWidth=*/0.0f, + /*offset=*/0.0f, + /*width=*/8.0f, + /*shaderType*/ {}, + /*color=*/Color::red(), + }, + { + /*geometry=*/{}, + /*blur=*/4.0f, + /*opacity=*/1.0f, + /*gapWidth=*/2.0f, + /*offset=*/-1.0f, + /*width=*/4.0f, + {}, + /*color=*/Color::blue(), + }, + { + /*geometry=*/{}, + /*blur=*/16.0f, + /*opacity=*/1.0f, + /*gapWidth=*/1.0f, + /*offset=*/2.0f, + /*width=*/16.0f, + {}, + /*color=*/Color(1.f, 0.5f, 0, 0.5f), + }, + { + /*geometry=*/{}, + /*blur=*/2.0f, + /*opacity=*/1.0f, + /*gapWidth=*/1.0f, + /*offset=*/-2.0f, + /*width=*/2.0f, + {}, + /*color=*/Color(1.f, 1.f, 0, 0.3f), + }, + { + /*geometry=*/{}, + /*blur=*/0.5f, + /*opacity=*/0.5f, + /*gapWidth=*/1.0f, + /*offset=*/0.5f, + /*width=*/0.5f, + {}, + /*color=*/Color::black(), + }, + { + /*geometry=*/{}, + /*blur=*/24.0f, + /*opacity=*/0.5f, + /*gapWidth=*/1.0f, + /*offset=*/-5.0f, + /*width=*/24.0f, + {}, + /*color=*/Color(1.f, 0, 1.f, 0.2f), + }, }; for (auto& opt : options) { opt.geometry.beginCap = style::LineCapType::Round;