From 893c97f20ec1f0b6eb7c729bc59cab27166ad177 Mon Sep 17 00:00:00 2001 From: Stefan Karschti Date: Fri, 24 Nov 2023 14:19:41 +0200 Subject: [PATCH] Triangulated fill outlines (Metal) (#1880) --- bazel/core.bzl | 1 + include/mbgl/gfx/fill_generator.hpp | 37 ++- .../mbgl/shaders/gl/drawable_line_basic.hpp | 119 ++++++++ include/mbgl/shaders/line_layer_ubo.hpp | 10 + include/mbgl/shaders/mtl/common.hpp | 14 + include/mbgl/shaders/mtl/line.hpp | 82 ++++++ include/mbgl/shaders/shader_manifest.hpp | 1 + include/mbgl/shaders/shader_source.hpp | 1 + shaders/drawable.line_basic.fragment.glsl | 35 +++ shaders/drawable.line_basic.vertex.glsl | 68 +++++ shaders/manifest.json | 7 + src/mbgl/gfx/fill_generator.cpp | 125 ++++++-- src/mbgl/gl/renderer_backend.cpp | 1 + src/mbgl/mtl/renderer_backend.cpp | 1 + src/mbgl/renderer/buckets/fill_bucket.cpp | 37 ++- src/mbgl/renderer/buckets/fill_bucket.hpp | 40 ++- .../renderer/layers/render_fill_layer.cpp | 277 ++++++++++++++++-- .../renderer/layers/render_fill_layer.hpp | 19 +- src/mbgl/shaders/mtl/line.cpp | 10 + .../style/layers/custom_drawable_layer.cpp | 2 +- test/api/custom_drawable_layer.test.cpp | 2 +- 21 files changed, 821 insertions(+), 68 deletions(-) create mode 100644 include/mbgl/shaders/gl/drawable_line_basic.hpp create mode 100644 shaders/drawable.line_basic.fragment.glsl create mode 100644 shaders/drawable.line_basic.vertex.glsl diff --git a/bazel/core.bzl b/bazel/core.bzl index 6da4e34a7..eca6702e0 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -82,6 +82,7 @@ MLN_GENERATED_OPENGL_SHADER_HEADERS = [ "include/mbgl/shaders/gl/drawable_line_pattern.hpp", "include/mbgl/shaders/gl/drawable_line_sdf.hpp", "include/mbgl/shaders/gl/drawable_line.hpp", + "include/mbgl/shaders/gl/drawable_line_basic.hpp", "include/mbgl/shaders/gl/drawable_raster.hpp", "include/mbgl/shaders/gl/drawable_symbol_icon.hpp", "include/mbgl/shaders/gl/drawable_symbol_sdf.hpp", diff --git a/include/mbgl/gfx/fill_generator.hpp b/include/mbgl/gfx/fill_generator.hpp index c273b1ee0..6766cf112 100644 --- a/include/mbgl/gfx/fill_generator.hpp +++ b/include/mbgl/gfx/fill_generator.hpp @@ -5,21 +5,44 @@ #include #include #include +#include namespace mbgl { namespace gfx { +/// Generate fill buffers, without outline void generateFillBuffers(const GeometryCollection& geometry, - gfx::VertexVector& vertices, - SegmentVector& triangleSegments, - gfx::IndexVector& triangles); + gfx::VertexVector& fillVertices, + gfx::IndexVector& fillIndexes, + SegmentVector& fillSegments); +/// Generate fill and outline buffers, with the outline composed of line primitives. void generateFillAndOutineBuffers(const GeometryCollection& geometry, gfx::VertexVector& vertices, - SegmentVector& lineSegments, - gfx::IndexVector& lines, - SegmentVector& triangleSegments, - gfx::IndexVector& triangles); + gfx::IndexVector& fillIndexes, + SegmentVector& fillSegments, + gfx::IndexVector& lineIndexes, + SegmentVector& lineSegments); + +/// Generate fill and outline buffers, where the outlines are built with triangle primitives +void generateFillAndOutineBuffers(const GeometryCollection& geometry, + gfx::VertexVector& fillVertices, + gfx::IndexVector& fillIndexes, + SegmentVector& fillSegments, + gfx::VertexVector& lineVertices, + gfx::IndexVector& lineIndexes, + SegmentVector& lineSegments); + +/// Generate fill and outline buffers, where the outlines are built both with triangle primitives AND with simple lines +void generateFillAndOutineBuffers(const GeometryCollection& geometry, + gfx::VertexVector& fillVertices, + gfx::IndexVector& fillIndexes, + SegmentVector& fillSegments, + gfx::VertexVector& lineVertices, + gfx::IndexVector& lineIndexes, + SegmentVector& lineSegments, + gfx::IndexVector& basicLineIndexes, + SegmentVector& basicLineSegments); } // namespace gfx } // namespace mbgl diff --git a/include/mbgl/shaders/gl/drawable_line_basic.hpp b/include/mbgl/shaders/gl/drawable_line_basic.hpp new file mode 100644 index 000000000..e7f4e1256 --- /dev/null +++ b/include/mbgl/shaders/gl/drawable_line_basic.hpp @@ -0,0 +1,119 @@ +// Generated code, do not modify this file! +#pragma once +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr const char* name = "LineBasicShader"; + static constexpr const char* vertex = R"(// floor(127 / 2) == 63.0 +// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is +// stored in a byte (-128..127). we scale regular normals up to length 63, but +// there are also "special" normals that have a bigger length (of up to 126 in +// this case). +// #define scale 63.0 +#define scale 0.015873016 + +layout (location = 0) in vec2 a_pos_normal; +layout (location = 1) in vec4 a_data; + +layout (std140) uniform LineBasicUBO { + highp mat4 u_matrix; + highp vec2 u_units_to_pixels; + mediump float u_ratio; + lowp float u_device_pixel_ratio; +}; + +layout (std140) uniform LineBasicPropertiesUBO { + highp vec4 u_color; + lowp float u_opacity; + mediump float u_width; + + highp vec2 pad1; +}; + +out vec2 v_normal; +out float v_width; +out float v_gamma_scale; +out highp float v_linesofar; + +void main() { + // the distance over which the line edge fades out. + // Retina devices need a smaller distance to avoid aliasing. + float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + + vec2 a_extrude = a_data.xy - 128.0; + + v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0; + + vec2 pos = floor(a_pos_normal * 0.5); + + // x is 1 if it's a round cap, 0 otherwise + // y is 1 if the normal points up, and -1 if it points down + // We store these in the least significant bit of a_pos_normal + mediump vec2 normal = a_pos_normal - 2.0 * pos; + normal.y = normal.y * 2.0 - 1.0; + v_normal = normal; + + // these transformations used to be applied in the JS and native code bases. + // moved them into the shader for clarity and simplicity. + float halfwidth = u_width / 2.0; + float outset = halfwidth + (halfwidth == 0.0 ? 0.0 : ANTIALIASING); + + // Scale the extrusion vector down to a normal and then up by the line width + // of this vertex. + mediump vec2 dist = outset * a_extrude * scale; + + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos, 0.0, 1.0) + projected_extrude; + + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; + + v_width = outset; +} +)"; + static constexpr const char* fragment = R"(layout (std140) uniform LineBasicUBO { + highp mat4 u_matrix; + highp vec2 u_units_to_pixels; + mediump float u_ratio; + lowp float u_device_pixel_ratio; +}; + +layout (std140) uniform LineBasicPropertiesUBO { + highp vec4 u_color; + lowp float u_opacity; + mediump float u_width; + + highp vec2 pad1; +}; + +in float v_width; +in vec2 v_normal; +in float v_gamma_scale; + +void main() { + // Calculate the distance of the pixel from the line in pixels. + float dist = length(v_normal) * v_width; + + // Calculate the antialiasing fade factor. This is either when fading in + // the line in case of an offset line (v_width2.t) or when fading out + // (v_width2.s) + float blur2 = (1.0 / u_device_pixel_ratio) * v_gamma_scale; + float alpha = clamp(min(dist + blur2, v_width - dist) / blur2, 0.0, 1.0); + + fragColor = u_color * (alpha * u_opacity); + +#ifdef OVERDRAW_INSPECTOR + fragColor = vec4(1.0); +#endif +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/line_layer_ubo.hpp b/include/mbgl/shaders/line_layer_ubo.hpp index 4255eab7e..2ee315426 100644 --- a/include/mbgl/shaders/line_layer_ubo.hpp +++ b/include/mbgl/shaders/line_layer_ubo.hpp @@ -84,6 +84,16 @@ struct alignas(16) LineSDFPropertiesUBO { }; static_assert(sizeof(LineSDFPropertiesUBO) % 16 == 0); +using LineBasicUBO = LineUBO; + +struct alignas(16) LineBasicPropertiesUBO { + Color color; + float opacity; + float width; + float pad1, pad2; +}; +static_assert(sizeof(LineBasicPropertiesUBO) % 16 == 0); + /// Property interpolation UBOs struct alignas(16) LineInterpolationUBO { float color_t; diff --git a/include/mbgl/shaders/mtl/common.hpp b/include/mbgl/shaders/mtl/common.hpp index e211f50d3..f0e00aa20 100644 --- a/include/mbgl/shaders/mtl/common.hpp +++ b/include/mbgl/shaders/mtl/common.hpp @@ -123,6 +123,13 @@ struct alignas(16) LineUBO { float device_pixel_ratio; }; +struct alignas(16) LineBasicUBO { + float4x4 matrix; + float2 units_to_pixels; + float ratio; + float device_pixel_ratio; +}; + struct alignas(16) LineGradientUBO { float4x4 matrix; float2 units_to_pixels; @@ -140,6 +147,13 @@ struct alignas(16) LinePropertiesUBO { float pad1, pad2, pad3; }; +struct alignas(16) LineBasicPropertiesUBO { + float4 color; + float opacity; + float width; + float pad1, pad2; +}; + struct alignas(16) LineGradientPropertiesUBO { float blur; float opacity; diff --git a/include/mbgl/shaders/mtl/line.hpp b/include/mbgl/shaders/mtl/line.hpp index 298812252..19955ba95 100644 --- a/include/mbgl/shaders/mtl/line.hpp +++ b/include/mbgl/shaders/mtl/line.hpp @@ -480,5 +480,87 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], )"; }; +template <> +struct ShaderSource { + static constexpr auto name = "LineBasicShader"; + static constexpr auto vertexMainFunction = "vertexMain"; + static constexpr auto fragmentMainFunction = "fragmentMain"; + + static const std::array attributes; + static const std::array uniforms; + static const std::array textures; + + static constexpr auto source = R"( + +struct VertexStage { + short2 pos_normal [[attribute(0)]]; + uchar4 data [[attribute(1)]]; +}; + +struct FragmentStage { + float4 position [[position, invariant]]; + float width2; + float2 normal; + float gamma_scale; + float4 color; + float opacity; +}; + +FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], + device const LineBasicUBO& line [[buffer(2)]], + device const LineBasicPropertiesUBO& props [[buffer(3)]]) { + + // the distance over which the line edge fades out. + // Retina devices need a smaller distance to avoid aliasing. + const float ANTIALIASING = 1.0 / line.device_pixel_ratio / 2.0; + + const float2 a_extrude = float2(vertx.data.xy) - 128.0; + const float2 pos = floor(float2(vertx.pos_normal) * 0.5); + + // x is 1 if it's a round cap, 0 otherwise + // y is 1 if the normal points up, and -1 if it points down + // We store these in the least significant bit of a_pos_normal + const float2 normal = float2(vertx.pos_normal) - 2.0 * pos; + const float2 v_normal = float2(normal.x, normal.y * 2.0 - 1.0); + + const float halfwidth = props.width / 2.0; + const float outset = halfwidth + (halfwidth == 0.0 ? 0.0 : ANTIALIASING); + + // Scale the extrusion vector down to a normal and then up by the line width of this vertex. + const float2 dist = outset * a_extrude * LINE_NORMAL_SCALE; + + const float4 projected_extrude = line.matrix * float4(dist / line.ratio, 0.0, 0.0); + const float4 position = line.matrix * float4(pos, 0.0, 1.0) + projected_extrude; + + // calculate how much the perspective view squishes or stretches the extrude + const float extrude_length_without_perspective = length(dist); + const float extrude_length_with_perspective = length(projected_extrude.xy / position.w * line.units_to_pixels); + + return { + .position = position, + .width2 = outset, + .normal = v_normal, + .gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective, + .color = props.color, + .opacity = props.opacity, + }; +} + +half4 fragment fragmentMain(FragmentStage in [[stage_in]], + device const LineBasicUBO& line [[buffer(2)]]) { + + // Calculate the distance of the pixel from the line in pixels. + const float dist = length(in.normal) * in.width2; + + // Calculate the antialiasing fade factor. This is either when fading in the + // line in case of an offset line (v_width2.y) or when fading out (v_width2.x) + const float blur2 = (1.0 / line.device_pixel_ratio) * in.gamma_scale; + const float alpha = clamp(min(dist + blur2, in.width2 - dist) / blur2, 0.0, 1.0); + + return half4(in.color * (alpha * in.opacity)); +} +)"; +}; + } // namespace shaders } // namespace mbgl diff --git a/include/mbgl/shaders/shader_manifest.hpp b/include/mbgl/shaders/shader_manifest.hpp index 561c34557..d769192e2 100644 --- a/include/mbgl/shaders/shader_manifest.hpp +++ b/include/mbgl/shaders/shader_manifest.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/include/mbgl/shaders/shader_source.hpp b/include/mbgl/shaders/shader_source.hpp index ca6519a40..8f44c1eec 100644 --- a/include/mbgl/shaders/shader_source.hpp +++ b/include/mbgl/shaders/shader_source.hpp @@ -21,6 +21,7 @@ enum class BuiltIn { LinePatternShader, LineSDFShader, LineShader, + LineBasicShader, FillPatternShader, FillOutlinePatternShader, FillExtrusionShader, diff --git a/shaders/drawable.line_basic.fragment.glsl b/shaders/drawable.line_basic.fragment.glsl new file mode 100644 index 000000000..681a5ed3c --- /dev/null +++ b/shaders/drawable.line_basic.fragment.glsl @@ -0,0 +1,35 @@ +layout (std140) uniform LineBasicUBO { + highp mat4 u_matrix; + highp vec2 u_units_to_pixels; + mediump float u_ratio; + lowp float u_device_pixel_ratio; +}; + +layout (std140) uniform LineBasicPropertiesUBO { + highp vec4 u_color; + lowp float u_opacity; + mediump float u_width; + + highp vec2 pad1; +}; + +in float v_width; +in vec2 v_normal; +in float v_gamma_scale; + +void main() { + // Calculate the distance of the pixel from the line in pixels. + float dist = length(v_normal) * v_width; + + // Calculate the antialiasing fade factor. This is either when fading in + // the line in case of an offset line (v_width2.t) or when fading out + // (v_width2.s) + float blur2 = (1.0 / u_device_pixel_ratio) * v_gamma_scale; + float alpha = clamp(min(dist + blur2, v_width - dist) / blur2, 0.0, 1.0); + + fragColor = u_color * (alpha * u_opacity); + +#ifdef OVERDRAW_INSPECTOR + fragColor = vec4(1.0); +#endif +} diff --git a/shaders/drawable.line_basic.vertex.glsl b/shaders/drawable.line_basic.vertex.glsl new file mode 100644 index 000000000..a73879f78 --- /dev/null +++ b/shaders/drawable.line_basic.vertex.glsl @@ -0,0 +1,68 @@ +// floor(127 / 2) == 63.0 +// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is +// stored in a byte (-128..127). we scale regular normals up to length 63, but +// there are also "special" normals that have a bigger length (of up to 126 in +// this case). +// #define scale 63.0 +#define scale 0.015873016 + +layout (location = 0) in vec2 a_pos_normal; +layout (location = 1) in vec4 a_data; + +layout (std140) uniform LineBasicUBO { + highp mat4 u_matrix; + highp vec2 u_units_to_pixels; + mediump float u_ratio; + lowp float u_device_pixel_ratio; +}; + +layout (std140) uniform LineBasicPropertiesUBO { + highp vec4 u_color; + lowp float u_opacity; + mediump float u_width; + + highp vec2 pad1; +}; + +out vec2 v_normal; +out float v_width; +out float v_gamma_scale; +out highp float v_linesofar; + +void main() { + // the distance over which the line edge fades out. + // Retina devices need a smaller distance to avoid aliasing. + float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + + vec2 a_extrude = a_data.xy - 128.0; + + v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0; + + vec2 pos = floor(a_pos_normal * 0.5); + + // x is 1 if it's a round cap, 0 otherwise + // y is 1 if the normal points up, and -1 if it points down + // We store these in the least significant bit of a_pos_normal + mediump vec2 normal = a_pos_normal - 2.0 * pos; + normal.y = normal.y * 2.0 - 1.0; + v_normal = normal; + + // these transformations used to be applied in the JS and native code bases. + // moved them into the shader for clarity and simplicity. + float halfwidth = u_width / 2.0; + float outset = halfwidth + (halfwidth == 0.0 ? 0.0 : ANTIALIASING); + + // Scale the extrusion vector down to a normal and then up by the line width + // of this vertex. + mediump vec2 dist = outset * a_extrude * scale; + + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos, 0.0, 1.0) + projected_extrude; + + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; + + v_width = outset; +} diff --git a/shaders/manifest.json b/shaders/manifest.json index 6f17f74a8..331f3eac2 100644 --- a/shaders/manifest.json +++ b/shaders/manifest.json @@ -83,6 +83,13 @@ "glsl_frag": "drawable.line.fragment.glsl", "uses_ubos": true }, + { + "name": "LineBasicShader", + "header": "drawable_line_basic", + "glsl_vert": "drawable.line_basic.vertex.glsl", + "glsl_frag": "drawable.line_basic.fragment.glsl", + "uses_ubos": true + }, { "name": "FillPatternShader", "header": "drawable_fill_pattern", diff --git a/src/mbgl/gfx/fill_generator.cpp b/src/mbgl/gfx/fill_generator.cpp index f275ccbd7..f1879fdcd 100644 --- a/src/mbgl/gfx/fill_generator.cpp +++ b/src/mbgl/gfx/fill_generator.cpp @@ -1,4 +1,5 @@ #include +#include #ifdef _MSC_VER #pragma warning(push) @@ -51,25 +52,25 @@ std::size_t totalVerticesCheck(const GeometryCollection& polygon) { return totalVertices; } -void addFillIndices(SegmentVector& triangleSegments, - gfx::IndexVector& triangles, +void addFillIndices(SegmentVector& fillSegments, + gfx::IndexVector& fillIndexes, std::vector& indices, std::size_t startVertices, std::size_t totalVertices) { std::size_t nIndices = indices.size(); assert(nIndices % 3 == 0); - if (triangleSegments.empty() || - triangleSegments.back().vertexLength + totalVertices > std::numeric_limits::max()) { - triangleSegments.emplace_back(startVertices, triangles.elements()); + if (fillSegments.empty() || + fillSegments.back().vertexLength + totalVertices > std::numeric_limits::max()) { + fillSegments.emplace_back(startVertices, fillIndexes.elements()); } - auto& triangleSegment = triangleSegments.back(); + auto& triangleSegment = fillSegments.back(); assert(triangleSegment.vertexLength <= std::numeric_limits::max()); const auto triangleIndex = static_cast(triangleSegment.vertexLength); for (std::size_t i = 0; i < nIndices; i += 3) { - triangles.emplace_back( + fillIndexes.emplace_back( triangleIndex + indices[i], triangleIndex + indices[i + 1], triangleIndex + indices[i + 2]); } @@ -80,20 +81,20 @@ void addFillIndices(SegmentVector& triangleSegments, void addOutlineIndices(std::size_t base, std::size_t nVertices, SegmentVector& lineSegments, - gfx::IndexVector& lines) { + gfx::IndexVector& lineIndexes) { if (nVertices == 0) return; if (lineSegments.empty() || lineSegments.back().vertexLength + nVertices > std::numeric_limits::max()) { - lineSegments.emplace_back(base, lines.elements()); + lineSegments.emplace_back(base, lineIndexes.elements()); } auto& lineSegment = lineSegments.back(); assert(lineSegment.vertexLength <= std::numeric_limits::max()); const auto lineIndex = static_cast(lineSegment.vertexLength); - lines.emplace_back(static_cast(lineIndex + nVertices - 1), lineIndex); + lineIndexes.emplace_back(static_cast(lineIndex + nVertices - 1), lineIndex); for (std::size_t i = 1; i < nVertices; i++) { - lines.emplace_back(static_cast(lineIndex + i - 1), static_cast(lineIndex + i)); + lineIndexes.emplace_back(static_cast(lineIndex + i - 1), static_cast(lineIndex + i)); } lineSegment.vertexLength += nVertices; @@ -103,31 +104,31 @@ void addOutlineIndices(std::size_t base, } // namespace void generateFillBuffers(const GeometryCollection& geometry, - gfx::VertexVector& vertices, - SegmentVector& triangleSegments, - gfx::IndexVector& triangles) { + gfx::VertexVector& fillVertices, + gfx::IndexVector& fillIndexes, + SegmentVector& fillSegments) { for (auto& polygon : classifyRings(geometry)) { // Optimize polygons with many interior rings for earcut tesselation. limitHoles(polygon, 500); std::size_t totalVertices = totalVerticesCheck(polygon); - std::size_t startVertices = vertices.elements(); + std::size_t startVertices = fillVertices.elements(); for (const auto& ring : polygon) { - addRingVertices(vertices, ring); + addRingVertices(fillVertices, ring); } std::vector indices = mapbox::earcut(polygon); - addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices); + addFillIndices(fillSegments, fillIndexes, indices, startVertices, totalVertices); } } void generateFillAndOutineBuffers(const GeometryCollection& geometry, gfx::VertexVector& vertices, - SegmentVector& lineSegments, - gfx::IndexVector& lines, - SegmentVector& triangleSegments, - gfx::IndexVector& triangles) { + gfx::IndexVector& fillIndexes, + SegmentVector& fillSegments, + gfx::IndexVector& lineIndexes, + SegmentVector& lineSegments) { for (auto& polygon : classifyRings(geometry)) { // Optimize polygons with many interior rings for earcut tesselation. limitHoles(polygon, 500); @@ -138,11 +139,89 @@ void generateFillAndOutineBuffers(const GeometryCollection& geometry, for (const auto& ring : polygon) { std::size_t base = vertices.elements(); std::size_t nVertices = addRingVertices(vertices, ring); - addOutlineIndices(base, nVertices, lineSegments, lines); + addOutlineIndices(base, nVertices, lineSegments, lineIndexes); } std::vector indices = mapbox::earcut(polygon); - addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices); + addFillIndices(fillSegments, fillIndexes, indices, startVertices, totalVertices); + } +} + +void generateFillAndOutineBuffers(const GeometryCollection& geometry, + gfx::VertexVector& fillVertices, + gfx::IndexVector& fillIndexes, + SegmentVector& fillSegments, + gfx::VertexVector& lineVertices, + gfx::IndexVector& lineIndexes, + SegmentVector& lineSegments) { + gfx::PolylineGenerator> lineGenerator( + lineVertices, + LineProgram::layoutVertex, + lineSegments, + [](std::size_t vertexOffset, std::size_t indexOffset) -> Segment { + return Segment(vertexOffset, indexOffset); + }, + [](auto& seg) -> Segment& { return seg; }, + lineIndexes); + + gfx::PolylineGeneratorOptions lineOptions; + lineOptions.type = FeatureType::Polygon; + + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = totalVerticesCheck(polygon); + std::size_t startVertices = fillVertices.elements(); + + for (const auto& ring : polygon) { + addRingVertices(fillVertices, ring); + lineGenerator.generate(ring, lineOptions); + } + + std::vector indices = mapbox::earcut(polygon); + addFillIndices(fillSegments, fillIndexes, indices, startVertices, totalVertices); + } +} + +void generateFillAndOutineBuffers(const GeometryCollection& geometry, + gfx::VertexVector& fillVertices, + gfx::IndexVector& fillIndexes, + SegmentVector& fillSegments, + gfx::VertexVector& lineVertices, + gfx::IndexVector& lineIndexes, + SegmentVector& lineSegments, + gfx::IndexVector& basicLineIndexes, + SegmentVector& basicLineSegments) { + gfx::PolylineGenerator> lineGenerator( + lineVertices, + LineProgram::layoutVertex, + lineSegments, + [](std::size_t vertexOffset, std::size_t indexOffset) -> Segment { + return Segment(vertexOffset, indexOffset); + }, + [](auto& seg) -> Segment& { return seg; }, + lineIndexes); + + gfx::PolylineGeneratorOptions lineOptions; + lineOptions.type = FeatureType::Polygon; + + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = totalVerticesCheck(polygon); + std::size_t startVertices = fillVertices.elements(); + + for (const auto& ring : polygon) { + std::size_t base = fillVertices.elements(); + std::size_t nVertices = addRingVertices(fillVertices, ring); + addOutlineIndices(base, nVertices, basicLineSegments, basicLineIndexes); + lineGenerator.generate(ring, lineOptions); + } + + std::vector indices = mapbox::earcut(polygon); + addFillIndices(fillSegments, fillIndexes, indices, startVertices, totalVertices); } } diff --git a/src/mbgl/gl/renderer_backend.cpp b/src/mbgl/gl/renderer_backend.cpp index 89fa4a022..f84d00212 100644 --- a/src/mbgl/gl/renderer_backend.cpp +++ b/src/mbgl/gl/renderer_backend.cpp @@ -101,6 +101,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::DebugShader, shaders::BuiltIn::FillShader, shaders::BuiltIn::FillOutlineShader, + shaders::BuiltIn::LineBasicShader, shaders::BuiltIn::LineShader, shaders::BuiltIn::LineSDFShader, shaders::BuiltIn::LinePatternShader, diff --git a/src/mbgl/mtl/renderer_backend.cpp b/src/mbgl/mtl/renderer_backend.cpp index aec036803..e65bcf079 100644 --- a/src/mbgl/mtl/renderer_backend.cpp +++ b/src/mbgl/mtl/renderer_backend.cpp @@ -140,6 +140,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::HeatmapTextureShader, shaders::BuiltIn::HillshadeShader, shaders::BuiltIn::HillshadePrepareShader, + shaders::BuiltIn::LineBasicShader, shaders::BuiltIn::LineShader, shaders::BuiltIn::LineGradientShader, shaders::BuiltIn::LineSDFShader, diff --git a/src/mbgl/renderer/buckets/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp index 805c78257..f6048c652 100644 --- a/src/mbgl/renderer/buckets/fill_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_bucket.cpp @@ -24,6 +24,8 @@ FillBucket::~FillBucket() { sharedVertices->release(); } +// MLN_TRIANGULATE_FILL_OUTLINES is defined in fill_bucket.hpp +#if MLN_TRIANGULATE_FILL_OUTLINES void FillBucket::addFeature(const GeometryTileFeature& feature, const GeometryCollection& geometry, const ImagePositions& patternPositions, @@ -31,7 +33,15 @@ void FillBucket::addFeature(const GeometryTileFeature& feature, std::size_t index, const CanonicalTileID& canonical) { // generate buffers - gfx::generateFillAndOutineBuffers(geometry, vertices, lineSegments, lines, triangleSegments, triangles); + gfx::generateFillAndOutineBuffers(geometry, + vertices, + triangles, + triangleSegments, + lineVertices, + lineIndexes, + lineSegments, + basicLines, + basicLineSegments); for (auto& pair : paintPropertyBinders) { const auto it = patternDependencies.find(pair.first); @@ -43,12 +53,33 @@ void FillBucket::addFeature(const GeometryTileFeature& feature, } } } +#else // MLN_TRIANGULATE_FILL_OUTLINES +void FillBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry, + const ImagePositions& patternPositions, + const PatternLayerMap& patternDependencies, + std::size_t index, + const CanonicalTileID& canonical) { + // generate buffers + gfx::generateFillAndOutineBuffers(geometry, vertices, triangles, triangleSegments, basicLines, basicLineSegments); + + for (auto& pair : paintPropertyBinders) { + const auto it = patternDependencies.find(pair.first); + if (it != patternDependencies.end()) { + pair.second.populateVertexVectors( + feature, vertices.elements(), index, patternPositions, it->second, canonical); + } else { + pair.second.populateVertexVectors(feature, vertices.elements(), index, patternPositions, {}, canonical); + } + } +} +#endif // MLN_TRIANGULATE_FILL_OUTLINES void FillBucket::upload([[maybe_unused]] gfx::UploadPass& uploadPass) { #if MLN_LEGACY_RENDERER if (!uploaded) { vertexBuffer = uploadPass.createVertexBuffer(std::move(vertices)); - lineIndexBuffer = uploadPass.createIndexBuffer(std::move(lines)); + lineIndexBuffer = uploadPass.createIndexBuffer(std::move(basicLines)); triangleIndexBuffer = triangles.empty() ? std::optional{} : uploadPass.createIndexBuffer(std::move(triangles)); } @@ -62,7 +93,7 @@ void FillBucket::upload([[maybe_unused]] gfx::UploadPass& uploadPass) { } bool FillBucket::hasData() const { - return !triangleSegments.empty() || !lineSegments.empty(); + return !triangleSegments.empty() || !basicLineSegments.empty(); } float FillBucket::getQueryRadius(const RenderLayer& layer) const { diff --git a/src/mbgl/renderer/buckets/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp index 0380ad37f..57b75f985 100644 --- a/src/mbgl/renderer/buckets/fill_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_bucket.hpp @@ -8,6 +8,23 @@ #include #include +#if MLN_DRAWABLE_RENDERER +/** + Control how the fill outlines are being generated: + MLN_TRIANGULATE_FILL_OUTLINES = 0 : Simple line primitives will be generated. Draw using gfx::Lines + MLN_TRIANGULATE_FILL_OUTLINES = 1 : Generate triangulated lines. Draw using gfx::Triangles and a Line shader. + */ +#define MLN_TRIANGULATE_FILL_OUTLINES (MLN_RENDER_BACKEND_METAL) + +#else // MLN_DRAWABLE_RENDERER +// Legacy Renderer is incompatible with triangulated lines +#define MLN_TRIANGULATE_FILL_OUTLINES 0 +#endif // MLN_DRAWABLE_RENDERER + +#if MLN_TRIANGULATE_FILL_OUTLINES +#include +#endif + #include namespace mbgl { @@ -40,19 +57,32 @@ public: void update(const FeatureStates&, const GeometryTileLayer&, const std::string&, const ImagePositions&) override; +#if MLN_TRIANGULATE_FILL_OUTLINES + using LineVertexVector = gfx::VertexVector; + const std::shared_ptr sharedLineVertices = std::make_shared(); + LineVertexVector& lineVertices = *sharedLineVertices; + + using LineIndexVector = gfx::IndexVector; + const std::shared_ptr sharedLineIndexes = std::make_shared(); + LineIndexVector& lineIndexes = *sharedLineIndexes; + + SegmentVector lineSegments; +#endif // MLN_TRIANGULATE_FILL_OUTLINES + + using BasicLineIndexVector = gfx::IndexVector; + const std::shared_ptr sharedBasicLineIndexes = std::make_shared(); + BasicLineIndexVector& basicLines = *sharedBasicLineIndexes; + + SegmentVector basicLineSegments; + using VertexVector = gfx::VertexVector; const std::shared_ptr sharedVertices = std::make_shared(); VertexVector& vertices = *sharedVertices; - using LineIndexVector = gfx::IndexVector; - const std::shared_ptr sharedLines = std::make_shared(); - LineIndexVector& lines = *sharedLines; - using TriangleIndexVector = gfx::IndexVector; const std::shared_ptr sharedTriangles = std::make_shared(); TriangleIndexVector& triangles = *sharedTriangles; - SegmentVector lineSegments; SegmentVector triangleSegments; #if MLN_LEGACY_RENDERER diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index 7441a7a96..c4c13e258 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #endif namespace mbgl { @@ -42,6 +43,9 @@ namespace { #if MLN_DRAWABLE_RENDERER constexpr auto FillShaderName = "FillShader"; +#if MLN_TRIANGULATE_FILL_OUTLINES +constexpr auto FillOutlineTriangulatedShaderName = "LineBasicShader"; +#endif constexpr auto FillOutlineShaderName = "FillOutlineShader"; constexpr auto FillPatternShaderName = "FillPatternShader"; constexpr auto FillOutlinePatternShaderName = "FillOutlinePatternShader"; @@ -109,6 +113,49 @@ bool RenderFillLayer::hasCrossfade() const { return getCrossfade(evaluatedProperties).t != 1; } +#if MLN_TRIANGULATE_FILL_OUTLINES +void RenderFillLayer::markLayerRenderable(bool willRender, UniqueChangeRequestVec& changes) { + RenderLayer::markLayerRenderable(willRender, changes); + if (outlineLayerGroup) { + activateLayerGroup(outlineLayerGroup, willRender, changes); + } +} + +void RenderFillLayer::layerRemoved(UniqueChangeRequestVec& changes) { + RenderLayer::layerRemoved(changes); + if (outlineLayerGroup) { + activateLayerGroup(outlineLayerGroup, false, changes); + } +} + +void RenderFillLayer::layerIndexChanged(int32_t newLayerIndex, UniqueChangeRequestVec& changes) { + RenderLayer::layerIndexChanged(newLayerIndex, changes); + + changeLayerIndex(outlineLayerGroup, newLayerIndex, changes); +} + +std::size_t RenderFillLayer::removeAllDrawables() { + auto count = RenderLayer::removeAllDrawables(); + if (outlineLayerGroup) { + auto count2 = outlineLayerGroup->clearDrawables(); + stats.drawablesRemoved += count2; + count += count2; + } + return count; +} + +std::size_t RenderFillLayer::removeTile(RenderPass renderPass, const OverscaledTileID& tileID) { + RenderLayer::removeTile(renderPass, tileID); + if (const auto tileGroup = static_cast(outlineLayerGroup.get())) { + const auto n = tileGroup->removeDrawables(renderPass, tileID).size(); + stats.drawablesRemoved += n; + return n; + } + return 0; +} + +#endif // MLN_TRIANGULATE_FILL_OUTLINES + #if MLN_LEGACY_RENDERER void RenderFillLayer::render(PaintParameters& parameters) { assert(renderTiles); @@ -188,7 +235,7 @@ void RenderFillLayer::render(PaintParameters& parameters) { parameters.depthModeForSublayer(unevaluated.get().isUndefined() ? 2 : 0, gfx::DepthMaskType::ReadOnly), *bucket.lineIndexBuffer, - bucket.lineSegments, + bucket.basicLineSegments, FillOutlineProgram::TextureBindings{}); } } @@ -269,7 +316,7 @@ void RenderFillLayer::render(PaintParameters& parameters) { gfx::Lines{2.0f}, parameters.depthModeForSublayer(2, gfx::DepthMaskType::ReadOnly), *bucket.lineIndexBuffer, - bucket.lineSegments, + bucket.basicLineSegments, FillOutlinePatternProgram::TextureBindings{ tile.getIconAtlasTextureBinding(gfx::TextureFilterType::Linear), }); @@ -299,6 +346,62 @@ bool RenderFillLayer::queryIntersectsFeature(const GeometryCoordinates& queryGeo #if MLN_DRAWABLE_RENDERER +#if MLN_TRIANGULATE_FILL_OUTLINES +class OutlineDrawableTweaker : public gfx::DrawableTweaker { +public: + OutlineDrawableTweaker(Color color_, float opacity_) + : color(color_), + opacity(opacity_) {} + ~OutlineDrawableTweaker() override = default; + + 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(); + const auto zoom = parameters.state.getZoom(); + auto& uniforms = drawable.mutableUniformBuffers(); + + static const StringIdentity idLineUBOName = stringIndexer().get("LineBasicUBO"); + { + const auto matrix = LayerTweaker::getTileMatrix( + tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, false); + + const shaders::LineBasicUBO lineUBO{ + /*matrix = */ util::cast(matrix), + /*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, + /*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom), + /*device_pixel_ratio = */ parameters.pixelRatio}; + parameters.context.emplaceOrUpdateUniformBuffer(lineUniformBuffer, &lineUBO); + } + uniforms.addOrReplace(idLineUBOName, lineUniformBuffer); + + static const StringIdentity idLinePropertiesUBOName = stringIndexer().get("LineBasicPropertiesUBO"); + if (!linePropertiesUniformBuffer) { + const shaders::LineBasicPropertiesUBO linePropertiesUBO{/*color =*/color, + /*opacity =*/opacity, + /*width =*/1.f, + 0, + 0}; + parameters.context.emplaceOrUpdateUniformBuffer(linePropertiesUniformBuffer, &linePropertiesUBO); + } + if (!uniforms.get(idLinePropertiesUBOName)) { + uniforms.addOrReplace(idLinePropertiesUBOName, linePropertiesUniformBuffer); + } + }; + +private: + Color color; + float opacity; + + gfx::UniformBufferPtr lineUniformBuffer; + gfx::UniformBufferPtr linePropertiesUniformBuffer; +}; +#endif // MLN_TRIANGULATE_FILL_OUTLINES + void RenderFillLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, @@ -312,7 +415,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, return; } - // Set up a layer group + // Set up a layer group for fill if (!layerGroup) { if (auto layerGroup_ = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID())) { setLayerGroup(std::move(layerGroup_), changes); @@ -320,7 +423,19 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, return; } } - auto* tileLayerGroup = static_cast(layerGroup.get()); + auto* fillTileLayerGroup = static_cast(layerGroup.get()); + +#if MLN_TRIANGULATE_FILL_OUTLINES + // Set up a layer group for outlines + if (!outlineLayerGroup) { + outlineLayerGroup = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID()); + activateLayerGroup(outlineLayerGroup, isRenderable, changes); + } + auto* outlineTileLayerGroup = static_cast(outlineLayerGroup.get()); + if (!outlineTriangulatedShaderGroup) { + outlineTriangulatedShaderGroup = shaders.getShaderGroup(std::string(FillOutlineTriangulatedShaderName)); + } +#endif if (!layerTweaker) { layerTweaker = std::make_shared(getID(), evaluatedProperties); @@ -358,13 +473,23 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, builder.setEnableStencil(true); }; - stats.drawablesRemoved += tileLayerGroup->removeDrawablesIf([&](gfx::Drawable& drawable) { +#if MLN_TRIANGULATE_FILL_OUTLINES + for (auto layer : {fillTileLayerGroup, outlineTileLayerGroup}) { + stats.drawablesRemoved += layer->removeDrawablesIf([&](gfx::Drawable& drawable) { + // If the render pass has changed or the tile has dropped out of the cover set, remove it. + const auto& tileID = drawable.getTileID(); + return tileID && !hasRenderTile(*tileID); + }); + } +#else + stats.drawablesRemoved += fillTileLayerGroup->removeDrawablesIf([&](gfx::Drawable& drawable) { // If the render pass has changed or the tile has dropped out of the cover set, remove it. const auto& tileID = drawable.getTileID(); return tileID && !hasRenderTile(*tileID); }); +#endif - tileLayerGroup->setStencilTiles(renderTiles); + fillTileLayerGroup->setStencilTiles(renderTiles); std::unordered_set propertiesAsUniforms; for (const RenderTile& tile : *renderTiles) { @@ -554,16 +679,76 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, continue; } - if (unevaluated.get().isUndefined()) { - // Fill will occur in opaque or translucent pass based on `opaquePassCutoff`. - // Outline always occurs in translucent pass, defaults to fill color - const auto doOutline = evaluated.get(); + // Outline always occurs in translucent pass, defaults to fill color + // Outline does not default to fill in the pattern case + const auto doOutline = evaluated.get() && (unevaluated.get().isUndefined() || + unevaluated.get().isUndefined()); +#if MLN_TRIANGULATE_FILL_OUTLINES + const bool dataDrivenOutline = !evaluated.get().isConstant() || + !evaluated.get().isConstant(); +#endif + if (unevaluated.get().isUndefined()) { + // Simple fill if (!fillShaderGroup || (doOutline && !outlineShaderGroup)) { continue; } const auto fillShader = std::static_pointer_cast( fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms)); + +#if MLN_TRIANGULATE_FILL_OUTLINES + const auto outlineTriangulatedShader = doOutline && !dataDrivenOutline ? [&]() -> auto { + static const std::unordered_set outlinePropertiesAsUniforms{ + stringIndexer().get("a_color"), + stringIndexer().get("a_opacity"), + stringIndexer().get("a_width"), + }; + return std::static_pointer_cast( + outlineTriangulatedShaderGroup->getOrCreateShader(context, outlinePropertiesAsUniforms)); + }() + : nullptr; + + auto createOutline = [&](auto& builder, Color color, float opacity) { + if (doOutline && builder && bucket.sharedLineIndexes->elements()) { + static const StringIdentity idVertexAttribName = stringIndexer().get("a_pos_normal"); + static const StringIdentity idDataAttribName = stringIndexer().get("a_data"); + builder->setVertexAttrNameId(idVertexAttribName); + builder->setShader(outlineTriangulatedShader); + builder->setRawVertices({}, bucket.lineVertices.elements(), gfx::AttributeDataType::Short2); + gfx::VertexAttributeArray attrs; + if (const auto& attr = attrs.add(idVertexAttribName)) { + attr->setSharedRawData(bucket.sharedLineVertices, + offsetof(LineLayoutVertex, a1), + /*vertexOffset=*/0, + sizeof(LineLayoutVertex), + gfx::AttributeDataType::Short2); + } + if (const auto& attr = attrs.add(idDataAttribName)) { + attr->setSharedRawData(bucket.sharedLineVertices, + offsetof(LineLayoutVertex, a2), + /*vertexOffset=*/0, + sizeof(LineLayoutVertex), + gfx::AttributeDataType::UByte4); + } + builder->setVertexAttributes(std::move(attrs)); + builder->setSegments(gfx::Triangles(), + bucket.sharedLineIndexes, + bucket.lineSegments.data(), + bucket.lineSegments.size()); + + auto tweaker = std::make_shared(color, opacity); + + // finish + builder->flush(); + for (auto& drawable : builder->clearDrawables()) { + drawable->setTileID(tileID); + drawable->addTweaker(tweaker); + outlineTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); + ++stats.drawablesAdded; + } + } + }; +#endif const auto outlineShader = doOutline ? std::static_pointer_cast( outlineShaderGroup->getOrCreateShader(context, propertiesAsUniforms)) @@ -586,10 +771,10 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, if (doOutline && !outlineBuilder && outlineShader) { if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline")) { commonInit(*builder); - builder->setLineWidth(2.0f); builder->setDepthType(gfx::DepthMaskType::ReadOnly); - builder->setColorMode(gfx::ColorMode::alphaBlended()); + builder->setLineWidth(2.0f); builder->setSubLayerIndex(unevaluated.get().isUndefined() ? 2 : 0); + builder->setColorMode(gfx::ColorMode::alphaBlended()); builder->setRenderPass(RenderPass::Translucent); outlineBuilder = std::move(builder); } @@ -599,6 +784,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, const StringIdentity interpolateUBONameId, const auto& interpolateUBO, FillVariant type) { + builder.setVertexAttrNameId(idPosAttribName); builder.flush(); for (auto& drawable : builder.clearDrawables()) { @@ -608,19 +794,28 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, auto& uniforms = drawable->mutableUniformBuffers(); uniforms.createOrUpdate(interpolateUBONameId, &interpolateUBO, context); - tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); + fillTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); ++stats.drawablesAdded; } }; if (fillBuilder && bucket.sharedTriangles->elements()) { fillBuilder->setShader(fillShader); - if (outlineBuilder) { +#if MLN_TRIANGULATE_FILL_OUTLINES + if (doOutline && dataDrivenOutline && outlineBuilder) { fillBuilder->setVertexAttributes(vertexAttrs); outlineBuilder->setVertexAttributes(std::move(vertexAttrs)); } else { fillBuilder->setVertexAttributes(std::move(vertexAttrs)); } +#else + if (doOutline && outlineBuilder) { + fillBuilder->setVertexAttributes(vertexAttrs); + outlineBuilder->setVertexAttributes(std::move(vertexAttrs)); + } else { + fillBuilder->setVertexAttributes(std::move(vertexAttrs)); + } +#endif fillBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2); fillBuilder->setSegments(gfx::Triangles(), bucket.sharedTriangles, @@ -631,24 +826,49 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, getFillInterpolateUBO(), FillVariant::Fill); } - if (outlineBuilder && bucket.sharedLines->elements()) { + +#if MLN_TRIANGULATE_FILL_OUTLINES + if (doOutline && outlineBuilder) { + if (!dataDrivenOutline) { + outlineBuilder->setSubLayerIndex(unevaluated.get().isUndefined() ? 2 : 0); + createOutline(outlineBuilder, + evaluated.get().constantOr(FillOutlineColor::defaultValue()), + evaluated.get().constantOr(FillOpacity::defaultValue())); + } else { + if (bucket.sharedBasicLineIndexes->elements()) { + outlineBuilder->setShader(outlineShader); + outlineBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2); + outlineBuilder->setSegments(gfx::Lines(2), + bucket.sharedBasicLineIndexes, + bucket.basicLineSegments.data(), + bucket.basicLineSegments.size()); + finish(*outlineBuilder, + FillLayerTweaker::idFillOutlineInterpolateUBOName, + getFillOutlineInterpolateUBO(), + FillVariant::FillOutline); + } + } + } +#else + if (doOutline && outlineBuilder && bucket.sharedBasicLineIndexes->elements()) { outlineBuilder->setShader(outlineShader); outlineBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2); - outlineBuilder->setSegments( - gfx::Lines(2), bucket.sharedLines, bucket.lineSegments.data(), bucket.lineSegments.size()); + outlineBuilder->setSegments(gfx::Lines(2), + bucket.sharedBasicLineIndexes, + bucket.basicLineSegments.data(), + bucket.basicLineSegments.size()); finish(*outlineBuilder, FillLayerTweaker::idFillOutlineInterpolateUBOName, getFillOutlineInterpolateUBO(), FillVariant::FillOutline); } - } else { // FillPattern is defined +#endif + } else { + // Fill with pattern if ((renderPass & RenderPass::Translucent) == 0) { continue; } - // Outline does not default to fill in the pattern case - const auto doOutline = evaluated.get() && unevaluated.get().isUndefined(); - if (!patternShaderGroup || (doOutline && !outlinePatternShaderGroup)) { continue; } @@ -688,7 +908,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, patternBuilder->clearTweakers(); patternBuilder->addTweaker(getAtlasTweaker()); } - if (outlinePatternBuilder) { + if (doOutline && outlinePatternBuilder) { outlinePatternBuilder->clearTweakers(); outlinePatternBuilder->addTweaker(getAtlasTweaker()); } @@ -709,7 +929,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, auto& uniforms = drawable->mutableUniformBuffers(); uniforms.createOrUpdate(interpolateNameId, &interpolateUBO, context); uniforms.createOrUpdate(tileUBONameId, &tileUBO, context); - tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); + fillTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); ++stats.drawablesAdded; } }; @@ -717,7 +937,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, if (patternBuilder && bucket.sharedTriangles->elements()) { patternBuilder->setShader(fillShader); patternBuilder->setRenderPass(renderPass); - if (outlinePatternBuilder) { + if (doOutline && outlinePatternBuilder) { patternBuilder->setVertexAttributes(vertexAttrs); outlinePatternBuilder->setVertexAttributes(std::move(vertexAttrs)); } else { @@ -736,12 +956,15 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, getFillPatternTilePropsUBO(), FillVariant::FillPattern); } - if (outlinePatternBuilder && bucket.sharedLines->elements()) { + + if (doOutline && outlinePatternBuilder && bucket.sharedBasicLineIndexes->elements()) { outlinePatternBuilder->setShader(outlineShader); outlinePatternBuilder->setRenderPass(renderPass); outlinePatternBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2); - outlinePatternBuilder->setSegments( - gfx::Lines(2), bucket.sharedLines, bucket.lineSegments.data(), bucket.lineSegments.size()); + outlinePatternBuilder->setSegments(gfx::Lines(2), + bucket.sharedBasicLineIndexes, + bucket.basicLineSegments.data(), + bucket.basicLineSegments.size()); finish(*outlinePatternBuilder, idFillOutlinePatternInterpolateUBOName, diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp index 064b1c0c7..dbe71fffa 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_layer.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -44,6 +45,15 @@ public: UniqueChangeRequestVec&) override; #endif +protected: +#if MLN_TRIANGULATE_FILL_OUTLINES + void markLayerRenderable(bool willRender, UniqueChangeRequestVec&) override; + void layerIndexChanged(int32_t newLayerIndex, UniqueChangeRequestVec&) override; + void layerRemoved(UniqueChangeRequestVec&) override; + std::size_t removeAllDrawables() override; + std::size_t removeTile(RenderPass, const OverscaledTileID&) override; +#endif // MLN_DRAWABLE_RENDERER + private: void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; @@ -72,12 +82,19 @@ private: std::shared_ptr fillOutlineProgram; std::shared_ptr fillOutlinePatternProgram; #endif + #if MLN_DRAWABLE_RENDERER gfx::ShaderGroupPtr fillShaderGroup; gfx::ShaderGroupPtr outlineShaderGroup; gfx::ShaderGroupPtr patternShaderGroup; gfx::ShaderGroupPtr outlinePatternShaderGroup; -#endif + +#if MLN_TRIANGULATE_FILL_OUTLINES + LayerGroupBasePtr outlineLayerGroup; + gfx::ShaderGroupPtr outlineTriangulatedShaderGroup; +#endif // MLN_TRIANGULATE_FILL_OUTLINES + +#endif // MLN_DRAWABLE_RENDERER }; } // namespace mbgl diff --git a/src/mbgl/shaders/mtl/line.cpp b/src/mbgl/shaders/mtl/line.cpp index 778e3be8d..4b492546f 100644 --- a/src/mbgl/shaders/mtl/line.cpp +++ b/src/mbgl/shaders/mtl/line.cpp @@ -67,5 +67,15 @@ const std::array ShaderSource ShaderSource::attributes = { + AttributeInfo{0, gfx::AttributeDataType::Short2, 1, "a_pos_normal"}, + AttributeInfo{1, gfx::AttributeDataType::UByte4, 1, "a_data"}, +}; +const std::array ShaderSource::uniforms = { + UniformBlockInfo{2, true, true, sizeof(LineBasicUBO), "LineBasicUBO"}, + UniformBlockInfo{3, true, false, sizeof(LineBasicPropertiesUBO), "LineBasicPropertiesUBO"}, +}; +const std::array ShaderSource::textures = {}; + } // namespace shaders } // namespace mbgl diff --git a/src/mbgl/style/layers/custom_drawable_layer.cpp b/src/mbgl/style/layers/custom_drawable_layer.cpp index 828764f99..860ca6c82 100644 --- a/src/mbgl/style/layers/custom_drawable_layer.cpp +++ b/src/mbgl/style/layers/custom_drawable_layer.cpp @@ -296,7 +296,7 @@ void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geome SegmentVector triangleSegments; // generate fill geometry into buffers - gfx::generateFillBuffers(geometry, vertices, triangleSegments, triangles); + gfx::generateFillBuffers(geometry, vertices, triangles, triangleSegments); // add to builder static const StringIdentity idVertexAttribName = stringIndexer().get("a_pos"); diff --git a/test/api/custom_drawable_layer.test.cpp b/test/api/custom_drawable_layer.test.cpp index d592c5831..c9a2599af 100644 --- a/test/api/custom_drawable_layer.test.cpp +++ b/test/api/custom_drawable_layer.test.cpp @@ -168,7 +168,7 @@ TEST(CustomDrawableLayer, Basic) { std::make_unique("custom-drawable", std::make_unique())); // render and test - test::checkImage("test/fixtures/custom_drawable_layer/basic", frontend.render(map).image, 0.0006, 0.1); + test::checkImage("test/fixtures/custom_drawable_layer/basic", frontend.render(map).image, 0.000657, 0.1); } #endif // MLN_DRAWABLE_RENDERER