Triangulated fill outlines (Metal) (#1880)

This commit is contained in:
Stefan Karschti
2023-11-24 14:19:41 +02:00
committed by GitHub
parent 09e16d78d8
commit 893c97f20e
21 changed files with 821 additions and 68 deletions

View File

@@ -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",

View File

@@ -5,21 +5,44 @@
#include <mbgl/gfx/vertex_vector.hpp>
#include <mbgl/gfx/index_vector.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/programs/line_program.hpp>
namespace mbgl {
namespace gfx {
/// Generate fill buffers, without outline
void generateFillBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<Triangles>& triangles);
gfx::VertexVector<FillLayoutVertex>& fillVertices,
gfx::IndexVector<Triangles>& fillIndexes,
SegmentVector<FillAttributes>& fillSegments);
/// Generate fill and outline buffers, with the outline composed of line primitives.
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
SegmentVector<FillAttributes>& lineSegments,
gfx::IndexVector<gfx::Lines>& lines,
SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<gfx::Triangles>& triangles);
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector<FillAttributes>& fillSegments,
gfx::IndexVector<gfx::Lines>& lineIndexes,
SegmentVector<FillAttributes>& lineSegments);
/// Generate fill and outline buffers, where the outlines are built with triangle primitives
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& fillVertices,
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector<FillAttributes>& fillSegments,
gfx::VertexVector<LineLayoutVertex>& lineVertices,
gfx::IndexVector<gfx::Triangles>& lineIndexes,
SegmentVector<LineAttributes>& 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<FillLayoutVertex>& fillVertices,
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector<FillAttributes>& fillSegments,
gfx::VertexVector<LineLayoutVertex>& lineVertices,
gfx::IndexVector<gfx::Triangles>& lineIndexes,
SegmentVector<LineAttributes>& lineSegments,
gfx::IndexVector<gfx::Lines>& basicLineIndexes,
SegmentVector<FillAttributes>& basicLineSegments);
} // namespace gfx
} // namespace mbgl

View File

@@ -0,0 +1,119 @@
// Generated code, do not modify this file!
#pragma once
#include <mbgl/shaders/shader_source.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::LineBasicShader, gfx::Backend::Type::OpenGL> {
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

View File

@@ -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;

View File

@@ -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;

View File

@@ -480,5 +480,87 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
)";
};
template <>
struct ShaderSource<BuiltIn::LineBasicShader, gfx::Backend::Type::Metal> {
static constexpr auto name = "LineBasicShader";
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 2> attributes;
static const std::array<UniformBlockInfo, 2> uniforms;
static const std::array<TextureInfo, 0> 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

View File

@@ -15,6 +15,7 @@
#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_fill_pattern.hpp>
#include <mbgl/shaders/gl/drawable_fill_outline_pattern.hpp>
#include <mbgl/shaders/gl/drawable_fill_extrusion.hpp>

View File

@@ -21,6 +21,7 @@ enum class BuiltIn {
LinePatternShader,
LineSDFShader,
LineShader,
LineBasicShader,
FillPatternShader,
FillOutlinePatternShader,
FillExtrusionShader,

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -1,4 +1,5 @@
#include <mbgl/gfx/fill_generator.hpp>
#include <mbgl/gfx/polyline_generator.hpp>
#ifdef _MSC_VER
#pragma warning(push)
@@ -51,25 +52,25 @@ std::size_t totalVerticesCheck(const GeometryCollection& polygon) {
return totalVertices;
}
void addFillIndices(SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<gfx::Triangles>& triangles,
void addFillIndices(SegmentVector<FillAttributes>& fillSegments,
gfx::IndexVector<gfx::Triangles>& fillIndexes,
std::vector<uint32_t>& 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<uint16_t>::max()) {
triangleSegments.emplace_back(startVertices, triangles.elements());
if (fillSegments.empty() ||
fillSegments.back().vertexLength + totalVertices > std::numeric_limits<uint16_t>::max()) {
fillSegments.emplace_back(startVertices, fillIndexes.elements());
}
auto& triangleSegment = triangleSegments.back();
auto& triangleSegment = fillSegments.back();
assert(triangleSegment.vertexLength <= std::numeric_limits<uint16_t>::max());
const auto triangleIndex = static_cast<uint16_t>(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<FillAttributes>& triangleSegments,
void addOutlineIndices(std::size_t base,
std::size_t nVertices,
SegmentVector<FillAttributes>& lineSegments,
gfx::IndexVector<gfx::Lines>& lines) {
gfx::IndexVector<gfx::Lines>& lineIndexes) {
if (nVertices == 0) return;
if (lineSegments.empty() || lineSegments.back().vertexLength + nVertices > std::numeric_limits<uint16_t>::max()) {
lineSegments.emplace_back(base, lines.elements());
lineSegments.emplace_back(base, lineIndexes.elements());
}
auto& lineSegment = lineSegments.back();
assert(lineSegment.vertexLength <= std::numeric_limits<uint16_t>::max());
const auto lineIndex = static_cast<uint16_t>(lineSegment.vertexLength);
lines.emplace_back(static_cast<uint16_t>(lineIndex + nVertices - 1), lineIndex);
lineIndexes.emplace_back(static_cast<uint16_t>(lineIndex + nVertices - 1), lineIndex);
for (std::size_t i = 1; i < nVertices; i++) {
lines.emplace_back(static_cast<uint16_t>(lineIndex + i - 1), static_cast<uint16_t>(lineIndex + i));
lineIndexes.emplace_back(static_cast<uint16_t>(lineIndex + i - 1), static_cast<uint16_t>(lineIndex + i));
}
lineSegment.vertexLength += nVertices;
@@ -103,31 +104,31 @@ void addOutlineIndices(std::size_t base,
} // namespace
void generateFillBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<gfx::Triangles>& triangles) {
gfx::VertexVector<FillLayoutVertex>& fillVertices,
gfx::IndexVector<Triangles>& fillIndexes,
SegmentVector<FillAttributes>& 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<uint32_t> indices = mapbox::earcut(polygon);
addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices);
addFillIndices(fillSegments, fillIndexes, indices, startVertices, totalVertices);
}
}
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
SegmentVector<FillAttributes>& lineSegments,
gfx::IndexVector<gfx::Lines>& lines,
SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<gfx::Triangles>& triangles) {
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector<FillAttributes>& fillSegments,
gfx::IndexVector<gfx::Lines>& lineIndexes,
SegmentVector<FillAttributes>& 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<uint32_t> indices = mapbox::earcut(polygon);
addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices);
addFillIndices(fillSegments, fillIndexes, indices, startVertices, totalVertices);
}
}
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& fillVertices,
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector<FillAttributes>& fillSegments,
gfx::VertexVector<LineLayoutVertex>& lineVertices,
gfx::IndexVector<gfx::Triangles>& lineIndexes,
SegmentVector<LineAttributes>& lineSegments) {
gfx::PolylineGenerator<LineLayoutVertex, Segment<LineAttributes>> lineGenerator(
lineVertices,
LineProgram::layoutVertex,
lineSegments,
[](std::size_t vertexOffset, std::size_t indexOffset) -> Segment<LineAttributes> {
return Segment<LineAttributes>(vertexOffset, indexOffset);
},
[](auto& seg) -> Segment<LineAttributes>& { 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<uint32_t> indices = mapbox::earcut(polygon);
addFillIndices(fillSegments, fillIndexes, indices, startVertices, totalVertices);
}
}
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& fillVertices,
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector<FillAttributes>& fillSegments,
gfx::VertexVector<LineLayoutVertex>& lineVertices,
gfx::IndexVector<gfx::Triangles>& lineIndexes,
SegmentVector<LineAttributes>& lineSegments,
gfx::IndexVector<gfx::Lines>& basicLineIndexes,
SegmentVector<FillAttributes>& basicLineSegments) {
gfx::PolylineGenerator<LineLayoutVertex, Segment<LineAttributes>> lineGenerator(
lineVertices,
LineProgram::layoutVertex,
lineSegments,
[](std::size_t vertexOffset, std::size_t indexOffset) -> Segment<LineAttributes> {
return Segment<LineAttributes>(vertexOffset, indexOffset);
},
[](auto& seg) -> Segment<LineAttributes>& { 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<uint32_t> indices = mapbox::earcut(polygon);
addFillIndices(fillSegments, fillIndexes, indices, startVertices, totalVertices);
}
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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<gfx::IndexBuffer>{}
: 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 {

View File

@@ -8,6 +8,23 @@
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/style/layers/fill_layer_properties.hpp>
#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 <mbgl/programs/line_program.hpp>
#endif
#include <vector>
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<LineLayoutVertex>;
const std::shared_ptr<LineVertexVector> sharedLineVertices = std::make_shared<LineVertexVector>();
LineVertexVector& lineVertices = *sharedLineVertices;
using LineIndexVector = gfx::IndexVector<gfx::Triangles>;
const std::shared_ptr<LineIndexVector> sharedLineIndexes = std::make_shared<LineIndexVector>();
LineIndexVector& lineIndexes = *sharedLineIndexes;
SegmentVector<LineAttributes> lineSegments;
#endif // MLN_TRIANGULATE_FILL_OUTLINES
using BasicLineIndexVector = gfx::IndexVector<gfx::Lines>;
const std::shared_ptr<BasicLineIndexVector> sharedBasicLineIndexes = std::make_shared<BasicLineIndexVector>();
BasicLineIndexVector& basicLines = *sharedBasicLineIndexes;
SegmentVector<FillAttributes> basicLineSegments;
using VertexVector = gfx::VertexVector<FillLayoutVertex>;
const std::shared_ptr<VertexVector> sharedVertices = std::make_shared<VertexVector>();
VertexVector& vertices = *sharedVertices;
using LineIndexVector = gfx::IndexVector<gfx::Lines>;
const std::shared_ptr<LineIndexVector> sharedLines = std::make_shared<LineIndexVector>();
LineIndexVector& lines = *sharedLines;
using TriangleIndexVector = gfx::IndexVector<gfx::Triangles>;
const std::shared_ptr<TriangleIndexVector> sharedTriangles = std::make_shared<TriangleIndexVector>();
TriangleIndexVector& triangles = *sharedTriangles;
SegmentVector<FillAttributes> lineSegments;
SegmentVector<FillAttributes> triangleSegments;
#if MLN_LEGACY_RENDERER

View File

@@ -31,6 +31,7 @@
#include <mbgl/shaders/fill_layer_ubo.hpp>
#include <mbgl/shaders/shader_program_base.hpp>
#include <mbgl/util/string_indexer.hpp>
#include <mbgl/shaders/line_layer_ubo.hpp>
#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<FillLayerProperties>(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<TileLayerGroup*>(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<FillOutlineColor>().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<float>(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<TileLayerGroup*>(layerGroup.get());
auto* fillTileLayerGroup = static_cast<TileLayerGroup*>(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<TileLayerGroup*>(outlineLayerGroup.get());
if (!outlineTriangulatedShaderGroup) {
outlineTriangulatedShaderGroup = shaders.getShaderGroup(std::string(FillOutlineTriangulatedShaderName));
}
#endif
if (!layerTweaker) {
layerTweaker = std::make_shared<FillLayerTweaker>(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<StringIdentity> propertiesAsUniforms;
for (const RenderTile& tile : *renderTiles) {
@@ -554,16 +679,76 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
continue;
}
if (unevaluated.get<FillPattern>().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<FillAntialias>();
// 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<FillAntialias>() && (unevaluated.get<FillPattern>().isUndefined() ||
unevaluated.get<FillOutlineColor>().isUndefined());
#if MLN_TRIANGULATE_FILL_OUTLINES
const bool dataDrivenOutline = !evaluated.get<FillOutlineColor>().isConstant() ||
!evaluated.get<FillOpacity>().isConstant();
#endif
if (unevaluated.get<FillPattern>().isUndefined()) {
// Simple fill
if (!fillShaderGroup || (doOutline && !outlineShaderGroup)) {
continue;
}
const auto fillShader = std::static_pointer_cast<gfx::ShaderProgramBase>(
fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms));
#if MLN_TRIANGULATE_FILL_OUTLINES
const auto outlineTriangulatedShader = doOutline && !dataDrivenOutline ? [&]() -> auto {
static const std::unordered_set<StringIdentity> outlinePropertiesAsUniforms{
stringIndexer().get("a_color"),
stringIndexer().get("a_opacity"),
stringIndexer().get("a_width"),
};
return std::static_pointer_cast<gfx::ShaderProgramBase>(
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<OutlineDrawableTweaker>(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<gfx::ShaderProgramBase>(
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<FillOutlineColor>().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<FillOutlineColor>().isUndefined() ? 2 : 0);
createOutline(outlineBuilder,
evaluated.get<FillOutlineColor>().constantOr(FillOutlineColor::defaultValue()),
evaluated.get<FillOpacity>().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<FillAntialias>() && unevaluated.get<FillOutlineColor>().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,

View File

@@ -4,6 +4,7 @@
#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/style/layers/fill_layer_properties.hpp>
#include <mbgl/layout/pattern_layout.hpp>
#include <mbgl/renderer/buckets/fill_bucket.hpp>
#include <memory>
@@ -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> fillOutlineProgram;
std::shared_ptr<FillOutlinePatternProgram> 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

View File

@@ -67,5 +67,15 @@ const std::array<TextureInfo, 1> ShaderSource<BuiltIn::LineSDFShader, gfx::Backe
TextureInfo{0, "u_image"},
};
const std::array<AttributeInfo, 2> ShaderSource<BuiltIn::LineBasicShader, gfx::Backend::Type::Metal>::attributes = {
AttributeInfo{0, gfx::AttributeDataType::Short2, 1, "a_pos_normal"},
AttributeInfo{1, gfx::AttributeDataType::UByte4, 1, "a_data"},
};
const std::array<UniformBlockInfo, 2> ShaderSource<BuiltIn::LineBasicShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{2, true, true, sizeof(LineBasicUBO), "LineBasicUBO"},
UniformBlockInfo{3, true, false, sizeof(LineBasicPropertiesUBO), "LineBasicPropertiesUBO"},
};
const std::array<TextureInfo, 0> ShaderSource<BuiltIn::LineBasicShader, gfx::Backend::Type::Metal>::textures = {};
} // namespace shaders
} // namespace mbgl

View File

@@ -296,7 +296,7 @@ void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geome
SegmentVector<FillAttributes> 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");

View File

@@ -168,7 +168,7 @@ TEST(CustomDrawableLayer, Basic) {
std::make_unique<CustomDrawableLayer>("custom-drawable", std::make_unique<TestDrawableLayer>()));
// 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