Triangulated fill outlines (Metal) (#1880)
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
119
include/mbgl/shaders/gl/drawable_line_basic.hpp
Normal file
119
include/mbgl/shaders/gl/drawable_line_basic.hpp
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -21,6 +21,7 @@ enum class BuiltIn {
|
||||
LinePatternShader,
|
||||
LineSDFShader,
|
||||
LineShader,
|
||||
LineBasicShader,
|
||||
FillPatternShader,
|
||||
FillOutlinePatternShader,
|
||||
FillExtrusionShader,
|
||||
|
||||
35
shaders/drawable.line_basic.fragment.glsl
Normal file
35
shaders/drawable.line_basic.fragment.glsl
Normal 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
|
||||
}
|
||||
68
shaders/drawable.line_basic.vertex.glsl
Normal file
68
shaders/drawable.line_basic.vertex.glsl
Normal 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;
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user