Optimize fill extrusion memory by using instancing [Core optimization] (#4256)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bart Louwers <bart@emeel.net>
Co-authored-by: Adrian Cojocaru <adrian.cojocaru92@yahoo.com>
This commit is contained in:
Alex Cristici
2026-05-05 19:24:17 +03:00
committed by GitHub
parent e0af63c913
commit 6aa804f7e2
31 changed files with 889 additions and 181 deletions

View File

@@ -94,7 +94,9 @@ MLN_GENERATED_OPENGL_SHADER_HEADERS = [
"include/mbgl/shaders/gl/fill_outline_pattern.hpp",
"include/mbgl/shaders/gl/fill_outline_triangulated.hpp",
"include/mbgl/shaders/gl/fill_extrusion.hpp",
"include/mbgl/shaders/gl/fill_extrusion_instanced.hpp",
"include/mbgl/shaders/gl/fill_extrusion_pattern.hpp",
"include/mbgl/shaders/gl/fill_extrusion_pattern_instanced.hpp",
"include/mbgl/shaders/gl/heatmap.hpp",
"include/mbgl/shaders/gl/heatmap_texture.hpp",
"include/mbgl/shaders/gl/hillshade_prepare.hpp",

View File

@@ -32,7 +32,9 @@ list(APPEND
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/fill_outline_pattern.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/fill_outline_triangulated.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/fill_extrusion.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/fill_extrusion_instanced.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/fill_extrusion_pattern.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/fill_extrusion_pattern_instanced.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/heatmap.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/heatmap_texture.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/hillshade_prepare.hpp

View File

@@ -301,8 +301,8 @@ public:
/// Sum of element strides, and the total size of a vertex in the buffer
std::size_t getTotalSize() const;
/// Get the largest count value of the attribute elements
std::size_t getMaxCount() const;
/// Get the smallest count value of the attribute elements
std::size_t getMinCount() const;
/// Get a attribute element.
/// Returns a pointer to the element on success, or null if the attribute doesn't exists.

View File

@@ -0,0 +1,16 @@
// Generated code, do not modify this file!
#pragma once
#include <mbgl/shaders/shader_source.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::FillExtrusionInstancedShader, gfx::Backend::Type::OpenGL> {
static constexpr const char* name = "FillExtrusionInstancedShader";
static constexpr const char* vertex = R"()";
static constexpr const char* fragment = R"()";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,16 @@
// Generated code, do not modify this file!
#pragma once
#include <mbgl/shaders/shader_source.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternInstancedShader, gfx::Backend::Type::OpenGL> {
static constexpr const char* name = "FillExtrusionPatternInstancedShader";
static constexpr const char* vertex = R"()";
static constexpr const char* fragment = R"()";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -78,6 +78,7 @@ enum {
};
#define MLN_UBO_CONSOLIDATION (MLN_RENDER_BACKEND_METAL || MLN_RENDER_BACKEND_VULKAN || MLN_RENDER_BACKEND_WEBGPU)
#define MLN_USE_FILL_EXTRUSION_INSTANCING (MLN_RENDER_BACKEND_METAL)
} // namespace shaders
} // namespace mbgl

View File

@@ -69,7 +69,7 @@ struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal> {
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 5> attributes;
static const std::array<AttributeInfo, 4> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
@@ -78,16 +78,15 @@ struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal> {
struct VertexStage {
short2 pos [[attribute(0)]];
short4 normal_ed [[attribute(1)]];
#if !defined(HAS_UNIFORM_u_color)
float4 color [[attribute(2)]];
float4 color [[attribute(1)]];
#endif
#if !defined(HAS_UNIFORM_u_base)
float base [[attribute(3)]];
float base [[attribute(2)]];
#endif
#if !defined(HAS_UNIFORM_u_height)
float height [[attribute(4)]];
float height [[attribute(3)]];
#endif
};
@@ -119,8 +118,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const auto height = max(unpack_mix_float(vertx.height, drawable.height_t), 0.0);
#endif
const float3 normal = float3(vertx.normal_ed.xyz);
const float t = glMod(normal.x, 2.0);
const float3 normal = float3(0.0, 0.0, 1.0);
const float t = 1.0;
const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it...
const float4 position = drawable.matrix * float4(float2(vertx.pos), z, 1);
@@ -146,7 +145,149 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
color += min(float4(0.03, 0.03, 0.03, 1.0), float4(1.0));
// Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray
const float directionalFraction = clamp(dot(normal / 16384.0, props.light_position_base.xyz), 0.0, 1.0);
const float directionalFraction = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
// Adjust directional so that the range of values for highlight/shading is
// narrower with lower light intensity and with lighter/brighter surface colors
const float minDirectional = 1.0 - props.light_intensity;
const float maxDirectional = max(1.0 - luminance + props.light_intensity, 1.0);
float directional = mix(minDirectional, maxDirectional, directionalFraction);
// Add gradient along z axis of side surfaces
if (normal.y != 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no
// vertical gradient, and otherwise calculates the gradient based on base + height
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
// the square root when height is a uniform.
const float fMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
const float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + (props.vertical_gradient * factor);
}
// Assign final color based on surface + ambient light color, diffuse light directional,
// and light color with lower bounds adjusted to hue of light so that shading is tinted
// with the complementary (opposite) color to the light color
const float3 light_color = props.light_color_pad.rgb;
const float3 minLight = mix(0.0, 0.3, 1.0 - light_color.rgb);
vcolor += float4(clamp(color.rgb * directional * light_color.rgb, minLight, 1.0), 0.0);
return {
.position = position,
.color = half4(vcolor * props.opacity),
};
}
fragment FragmentOutput fragmentMain(FragmentStage in [[stage_in]]) {
return { in.color/*, in.position.z*/ };
}
)";
};
template <>
struct ShaderSource<BuiltIn::FillExtrusionInstancedShader, gfx::Backend::Type::Metal> {
static constexpr auto name = "FillExtrusionInstancedShader";
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 1> attributes;
static const std::array<AttributeInfo, 5> instanceAttributes;
static const std::array<TextureInfo, 0> textures;
static constexpr auto prelude = fillExtrusionShaderPrelude;
static constexpr auto source = R"(
struct VertexStage {
short2 pos [[attribute(0)]];
#if !defined(HAS_UNIFORM_u_color)
float4 color [[attribute(3)]];
#endif
#if !defined(HAS_UNIFORM_u_base)
float base [[attribute(4)]];
#endif
#if !defined(HAS_UNIFORM_u_height)
float height [[attribute(5)]];
#endif
};
struct OutlineInstance {
short2 pos;
ushort2 ed_discard;
};
struct FragmentStage {
float4 position [[position, invariant]];
half4 color;
};
struct FragmentOutput {
half4 color [[color(0)]];
//float depth [[depth(less)]]; // Write depth value if it's less than what's already there
};
FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
device const uint32_t& uboIndex [[buffer(idGlobalUBOIndex)]],
device const FillExtrusionDrawableUBO* drawableVector [[buffer(idFillExtrusionDrawableUBO)]],
device const FillExtrusionPropsUBO& props [[buffer(idFillExtrusionPropsUBO)]],
uint instanceID [[ instance_id ]],
device const OutlineInstance* outline [[buffer(fillExtrusionUBOCount + 1)]]) {
if (outline[instanceID].ed_discard.y) {
return {
.position = float4(0.0),
.color = half4(0.0),
};
}
device const FillExtrusionDrawableUBO& drawable = drawableVector[uboIndex];
#if defined(HAS_UNIFORM_u_base)
const auto base = props.light_position_base.w;
#else
const auto base = max(unpack_mix_float(vertx.base, drawable.base_t), 0.0);
#endif
#if defined(HAS_UNIFORM_u_height)
const auto height = props.height;
#else
const auto height = max(unpack_mix_float(vertx.height, drawable.height_t), 0.0);
#endif
float2 p1 = float2(outline[instanceID + 1].pos);
float2 p2 = float2(outline[instanceID + 0].pos);
float2 perp = p1 - p2;
float magnitude = sqrt(perp.x * perp.x + perp.y * perp.y);
if (magnitude > 0) {
perp = perp / magnitude;
}
const float3 normal = float3(-perp.y, perp.x, 0.0);
const float t = float(vertx.pos.y);
const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it...
const float4 position = drawable.matrix * float4(float2(outline[instanceID + vertx.pos.x].pos), z, 1);
#if defined(OVERDRAW_INSPECTOR)
return {
.position = position,
.color = half4(1.0),
};
#endif
#if defined(HAS_UNIFORM_u_color)
auto color = props.color;
#else
auto color = unpack_mix_color(vertx.color, drawable.color_t);
#endif
// Relative luminance (how dark/bright is the surface color?)
const float luminance = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;
float4 vcolor = float4(0.0, 0.0, 0.0, 1.0);
// Add slight ambient lighting so no extrusions are totally black
color += min(float4(0.03, 0.03, 0.03, 1.0), float4(1.0));
// Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray
const float directionalFraction = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
// Adjust directional so that the range of values for highlight/shading is
// narrower with lower light intensity and with lighter/brighter surface colors
@@ -190,7 +331,7 @@ struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Met
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 6> attributes;
static const std::array<AttributeInfo, 5> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
@@ -199,19 +340,18 @@ struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Met
struct VertexStage {
short2 pos [[attribute(0)]];
short4 normal_ed [[attribute(1)]];
#if !defined(HAS_UNIFORM_u_base)
float base [[attribute(2)]];
float base [[attribute(1)]];
#endif
#if !defined(HAS_UNIFORM_u_height)
float height [[attribute(3)]];
float height [[attribute(2)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_from)
ushort4 pattern_from [[attribute(4)]];
ushort4 pattern_from [[attribute(3)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
ushort4 pattern_to [[attribute(5)]];
ushort4 pattern_to [[attribute(4)]];
#endif
};
@@ -255,9 +395,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const auto height = max(unpack_mix_float(vertx.height, drawable.height_t), 0.0);
#endif
const float3 normal = float3(vertx.normal_ed.xyz);
const float edgedistance = vertx.normal_ed.w;
const float t = glMod(normal.x, 2.0);
const float3 normal = float3(0.0, 0.0, 1.0);
const float t = 1.0;
const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it...
const float4 position = drawable.matrix * float4(float2(vertx.pos), z, 1);
@@ -296,12 +435,10 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio);
const float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio);
const float2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0
? float2(vertx.pos) // extrusion top
: float2(edgedistance, z * drawable.height_factor); // extrusion side
const float2 pos = float2(vertx.pos); // extrusion top
float4 lighting = float4(0.0, 0.0, 0.0, 1.0);
float directional = clamp(dot(normal / 16383.0, props.light_position_base.xyz), 0.0, 1.0);
float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional);
if (normal.y != 0.0) {
@@ -370,5 +507,225 @@ fragment FragmentOutput fragmentMain(FragmentStage in [[stage_in]],
)";
};
template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternInstancedShader, gfx::Backend::Type::Metal> {
static constexpr auto name = "FillExtrusionPatternInstancedShader";
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 1> attributes;
static const std::array<AttributeInfo, 6> instanceAttributes;
static const std::array<TextureInfo, 1> textures;
static constexpr auto prelude = fillExtrusionShaderPrelude;
static constexpr auto source = R"(
struct VertexStage {
short2 pos [[attribute(0)]];
#if !defined(HAS_UNIFORM_u_base)
float base [[attribute(3)]];
#endif
#if !defined(HAS_UNIFORM_u_height)
float height [[attribute(4)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_from)
ushort4 pattern_from [[attribute(5)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
ushort4 pattern_to [[attribute(6)]];
#endif
};
struct OutlineInstance {
short2 pos;
ushort2 ed_discard;
};
struct FragmentStage {
float4 position [[position, invariant]];
float4 lighting;
float2 pos_a;
float2 pos_b;
#if !defined(HAS_UNIFORM_u_pattern_from)
half4 pattern_from;
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
half4 pattern_to;
#endif
};
struct FragmentOutput {
half4 color [[color(0)]];
//float depth [[depth(less)]]; // Write depth value if it's less than what's already there
};
FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
device const GlobalPaintParamsUBO& paintParams [[buffer(idGlobalPaintParamsUBO)]],
device const uint32_t& uboIndex [[buffer(idGlobalUBOIndex)]],
device const FillExtrusionDrawableUBO* drawableVector [[buffer(idFillExtrusionDrawableUBO)]],
device const FillExtrusionTilePropsUBO* tilePropsVector [[buffer(idFillExtrusionTilePropsUBO)]],
device const FillExtrusionPropsUBO& props [[buffer(idFillExtrusionPropsUBO)]],
uint instanceID [[ instance_id ]],
device const OutlineInstance* outline [[buffer(fillExtrusionUBOCount + 1)]]) {
if (outline[instanceID].ed_discard.y) {
return {
.position = float4(0.0),
.lighting = float4(0.0),
.pos_a = float2(0.0),
.pos_b = float2(0.0),
#if !defined(HAS_UNIFORM_u_pattern_from)
.pattern_from = half4(0.0),
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
.pattern_to = half4(0.0),
#endif
};
}
device const FillExtrusionDrawableUBO& drawable = drawableVector[uboIndex];
device const FillExtrusionTilePropsUBO& tileProps = tilePropsVector[uboIndex];
#if defined(HAS_UNIFORM_u_base)
const auto base = props.light_position_base.w;
#else
const auto base = max(unpack_mix_float(vertx.base, drawable.base_t), 0.0);
#endif
#if defined(HAS_UNIFORM_u_height)
const auto height = props.height;
#else
const auto height = max(unpack_mix_float(vertx.height, drawable.height_t), 0.0);
#endif
float2 p1 = float2(outline[instanceID + 1].pos);
float2 p2 = float2(outline[instanceID + 0].pos);
float2 perp = p1 - p2;
float magnitude = sqrt(perp.x * perp.x + perp.y * perp.y);
if (magnitude > 0) {
perp = perp / magnitude;
}
const float3 normal = float3(-perp.y, perp.x, 0.0);
const float edgedistance = outline[instanceID + 1 - vertx.pos.x].ed_discard.x;
const float t = float(vertx.pos.y);
const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it...
const float4 position = drawable.matrix * float4(float2(outline[instanceID + vertx.pos.x].pos), z, 1);
#if defined(OVERDRAW_INSPECTOR)
return {
.position = position,
.lighting = float4(1.0),
.pos_a = float2(1.0),
.pos_b = float2(1.0),
#if !defined(HAS_UNIFORM_u_pattern_from)
.pattern_from = half4(1.0),
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
.pattern_to = half4(1.0),
#endif
};
#endif
#if defined(HAS_UNIFORM_u_pattern_from)
const auto pattern_from = tileProps.pattern_from;
#else
const auto pattern_from = float4(vertx.pattern_from);
#endif
#if defined(HAS_UNIFORM_u_pattern_to)
const auto pattern_to = tileProps.pattern_to;
#else
const auto pattern_to = float4(vertx.pattern_to);
#endif
const float2 pattern_tl_a = pattern_from.xy;
const float2 pattern_br_a = pattern_from.zw;
const float2 pattern_tl_b = pattern_to.xy;
const float2 pattern_br_b = pattern_to.zw;
const float pixelRatio = paintParams.pixel_ratio;
const float tileZoomRatio = drawable.tile_ratio;
const float fromScale = props.from_scale;
const float toScale = props.to_scale;
const float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio);
const float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio);
const float2 pos = float2(edgedistance, z * drawable.height_factor); // extrusion side
float4 lighting = float4(0.0, 0.0, 0.0, 1.0);
float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional);
if (normal.y != 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient,
// and otherwise calculates the gradient based on base + height
directional *= (
(1.0 - props.vertical_gradient) +
(props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0)));
}
lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(float3(0.0), float3(0.3), 1.0 - props.light_color_pad.rgb), float3(1.0));
lighting *= props.opacity;
return {
.position = position,
.lighting = lighting,
.pos_a = get_pattern_pos(drawable.pixel_coord_upper, drawable.pixel_coord_lower, fromScale * display_size_a, tileZoomRatio, pos),
.pos_b = get_pattern_pos(drawable.pixel_coord_upper, drawable.pixel_coord_lower, toScale * display_size_b, tileZoomRatio, pos),
#if !defined(HAS_UNIFORM_u_pattern_from)
.pattern_from = half4(pattern_from),
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
.pattern_to = half4(pattern_to),
#endif
};
}
fragment FragmentOutput fragmentMain(FragmentStage in [[stage_in]],
device const uint32_t& uboIndex [[buffer(idGlobalUBOIndex)]],
device const FillExtrusionTilePropsUBO* tilePropsVector [[buffer(idFillExtrusionTilePropsUBO)]],
device const FillExtrusionPropsUBO& props [[buffer(idFillExtrusionPropsUBO)]],
texture2d<float, access::sample> image0 [[texture(0)]],
sampler image0_sampler [[sampler(0)]]) {
#if defined(OVERDRAW_INSPECTOR)
return {half4(1.0)/*, in.position.z*/};
#endif
device const FillExtrusionTilePropsUBO& tileProps = tilePropsVector[uboIndex];
#if defined(HAS_UNIFORM_u_pattern_from)
const auto pattern_from = float4(tileProps.pattern_from);
#else
const auto pattern_from = float4(in.pattern_from);
#endif
#if defined(HAS_UNIFORM_u_pattern_to)
const auto pattern_to = float4(tileProps.pattern_to);
#else
const auto pattern_to = float4(in.pattern_to);
#endif
const float2 pattern_tl_a = pattern_from.xy;
const float2 pattern_br_a = pattern_from.zw;
const float2 pattern_tl_b = pattern_to.xy;
const float2 pattern_br_b = pattern_to.zw;
const float2 imagecoord = glMod(in.pos_a, 1.0);
const float2 pos = mix(pattern_tl_a / tileProps.texsize, pattern_br_a / tileProps.texsize, imagecoord);
const float4 color1 = image0.sample(image0_sampler, pos);
const float2 imagecoord_b = glMod(in.pos_b, 1.0);
const float2 pos2 = mix(pattern_tl_b / tileProps.texsize, pattern_br_b / tileProps.texsize, imagecoord_b);
const float4 color2 = image0.sample(image0_sampler, pos2);
return {half4(mix(color1, color2, props.fade) * in.lighting)/*, in.position.z*/};
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -77,11 +77,13 @@ public:
using ShaderClass = shaders::ShaderSource<ShaderID, gfx::Backend::Type::Metal>;
for (const auto& attrib : ShaderClass::attributes) {
if (!propertiesAsUniforms.second.count(attrib.id)) {
shader->initAttribute(attrib);
shader->initVertexAttribute(attrib);
}
}
for (const auto& attrib : ShaderClass::instanceAttributes) {
shader->initInstanceAttribute(attrib);
if (!propertiesAsUniforms.second.count(attrib.id)) {
shader->initInstanceAttribute(attrib);
}
}
for (const auto& texture : ShaderClass::textures) {
shader->initTexture(texture);

View File

@@ -65,7 +65,7 @@ public:
const gfx::VertexAttributeArray& getInstanceAttributes() const override { return instanceAttributes; }
void initAttribute(const shaders::AttributeInfo&);
void initVertexAttribute(const shaders::AttributeInfo&);
void initInstanceAttribute(const shaders::AttributeInfo&);
void initTexture(const shaders::TextureInfo&);

View File

@@ -422,7 +422,13 @@ enum {
enum {
idFillExtrusionPosVertexAttribute,
#if MLN_USE_FILL_EXTRUSION_INSTANCING
idFillExtrusionOutlinePosAttribute,
idFillExtrusionEdDiscardAttribute,
#else
idFillExtrusionNormalEdVertexAttribute,
#endif
// Data driven
idFillExtrusionBaseVertexAttribute,

View File

@@ -19,7 +19,9 @@
#include <mbgl/shaders/gl/fill_outline_pattern.hpp>
#include <mbgl/shaders/gl/fill_outline_triangulated.hpp>
#include <mbgl/shaders/gl/fill_extrusion.hpp>
#include <mbgl/shaders/gl/fill_extrusion_instanced.hpp>
#include <mbgl/shaders/gl/fill_extrusion_pattern.hpp>
#include <mbgl/shaders/gl/fill_extrusion_pattern_instanced.hpp>
#include <mbgl/shaders/gl/heatmap.hpp>
#include <mbgl/shaders/gl/heatmap_texture.hpp>
#include <mbgl/shaders/gl/hillshade_prepare.hpp>

View File

@@ -25,7 +25,9 @@ enum class BuiltIn {
FillOutlinePatternShader,
FillOutlineTriangulatedShader,
FillExtrusionShader,
FillExtrusionInstancedShader,
FillExtrusionPatternShader,
FillExtrusionPatternInstancedShader,
HeatmapShader,
HeatmapTextureShader,
HillshadePrepareShader,

View File

@@ -109,6 +109,13 @@
"glsl_frag": "fill_extrusion.fragment.glsl",
"uses_ubos": true
},
{
"name": "FillExtrusionInstancedShader",
"header": "fill_extrusion_instanced",
"glsl_vert": "_empty.glsl",
"glsl_frag": "_empty.glsl",
"uses_ubos": true
},
{
"name": "FillExtrusionPatternShader",
"header": "fill_extrusion_pattern",
@@ -116,6 +123,13 @@
"glsl_frag": "fill_extrusion_pattern.fragment.glsl",
"uses_ubos": true
},
{
"name": "FillExtrusionPatternInstancedShader",
"header": "fill_extrusion_pattern_instanced",
"glsl_vert": "_empty.glsl",
"glsl_frag": "_empty.glsl",
"uses_ubos": true
},
{
"name": "HeatmapShader",
"header": "heatmap",

View File

@@ -115,10 +115,11 @@ std::size_t VertexAttributeArray::getTotalSize() const {
});
}
std::size_t VertexAttributeArray::getMaxCount() const {
return std::accumulate(attrs.begin(), attrs.end(), std::size_t(0), [](const auto acc, const auto& attr) {
return std::max(acc, (attr ? attr->getCount() : 0));
});
std::size_t VertexAttributeArray::getMinCount() const {
return std::accumulate(
attrs.begin(), attrs.end(), std::numeric_limits<std::size_t>::max(), [](const auto acc, const auto& attr) {
return std::min(acc, (attr ? attr->getCount() : std::numeric_limits<std::size_t>::max()));
});
}
void VertexAttributeArray::clear() {

View File

@@ -279,7 +279,7 @@ void Drawable::draw(PaintParameters& parameters) const {
const auto primitiveType = getPrimitiveType(mode.type);
constexpr auto indexType = MTL::IndexType::IndexTypeUInt16;
constexpr auto indexSize = sizeof(std::uint16_t);
const NS::UInteger instanceCount = instanceAttributes ? instanceAttributes->getMaxCount() : 1;
const NS::UInteger instanceCount = instanceAttributes ? instanceAttributes->getMinCount() : 1;
constexpr NS::UInteger baseInstance = 0;
const NS::UInteger indexOffset = static_cast<NS::UInteger>(indexSize *
mlSegment.indexOffset); // in bytes, not indexes
@@ -549,10 +549,12 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
impl->indexes->setDirty(false);
}
const bool buildAttribs = !impl->vertexDesc || !vertexAttributes || !attributeUpdateTime ||
vertexAttributes->isModifiedAfter(*attributeUpdateTime);
const bool buildVertexAttribs = !impl->vertexDesc || !vertexAttributes || !attributeUpdateTime ||
vertexAttributes->isModifiedAfter(*attributeUpdateTime);
const bool buildInstanceAttribs = instanceAttributes && (!attributeUpdateTime ||
instanceAttributes->isModifiedAfter(*attributeUpdateTime));
if (buildAttribs) {
if (buildVertexAttribs || buildInstanceAttribs) {
#if !defined(NDEBUG)
const auto debugGroup = uploadPass.createDebugGroup(debugLabel(*this));
#endif
@@ -575,8 +577,27 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
vertexAttributes->visitAttributes([](gfx::VertexAttribute& attrib) { attrib.setDirty(false); });
if (impl->attributeBindings != attributeBindings_) {
// Build instance attribute buffers
gfx::AttributeBindingArray instanceBindings_;
if (instanceAttributes) {
std::vector<std::unique_ptr<gfx::VertexBufferResource>> instanceBuffers;
instanceBindings_ = uploadPass.buildAttributeBindings(instanceAttributes->getMinCount(),
/*vertexType*/ gfx::AttributeDataType::Byte,
/*vertexAttributeIndex=*/-1,
/*vertexData=*/{},
shader->getInstanceAttributes(),
*instanceAttributes,
usage,
attributeUpdateTime,
instanceBuffers);
// clear dirty flag
instanceAttributes->visitAttributes([](gfx::VertexAttribute& attrib) { attrib.setDirty(false); });
}
if (impl->attributeBindings != attributeBindings_ || impl->instanceBindings != instanceBindings_) {
impl->attributeBindings = std::move(attributeBindings_);
impl->instanceBindings = std::move(instanceBindings_);
// hash
std::size_t hash{0};
@@ -584,46 +605,52 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
// Create a layout descriptor for each attribute
auto vertDesc = NS::RetainPtr(MTL::VertexDescriptor::vertexDescriptor());
NS::UInteger index = 0;
for (auto& binding : impl->attributeBindings) {
if (!binding) {
assert("Missing attribute binding");
index += 1;
continue;
}
if (!binding->vertexBufferResource && !impl->noBindingBuffer) {
if (const auto& buf = context.getEmptyVertexBuffer()) {
impl->noBindingBuffer = buf.get();
const auto applyBindings = [&](const gfx::AttributeBindingArray& attributeBindings,
MTL::VertexStepFunction stepFunction) {
NS::UInteger index = 0;
for (auto& binding : attributeBindings) {
if (!binding) {
assert("Missing attribute binding");
index += 1;
continue;
}
if (!binding->vertexBufferResource && !impl->noBindingBuffer) {
if (const auto& buf = context.getEmptyVertexBuffer()) {
impl->noBindingBuffer = buf.get();
}
}
const auto& attribDesc = vertDesc->attributes()->object(index);
attribDesc->setBufferIndex(binding->bufferIndex);
attribDesc->setOffset(static_cast<NS::UInteger>(binding->attribute.offset));
attribDesc->setFormat(mtlVertexTypeOf(binding->attribute.dataType));
const auto& layoutDesc = vertDesc->layouts()->object(binding->bufferIndex);
if (!layoutDesc->stride()) {
assert(binding->vertexStride > 0);
layoutDesc->setStride(static_cast<NS::UInteger>(binding->vertexStride));
layoutDesc->setStepFunction(binding->vertexBufferResource ? stepFunction
: MTL::VertexStepFunctionConstant);
layoutDesc->setStepRate(binding->vertexBufferResource ? 1 : 0);
} else {
assert(layoutDesc->stride() == static_cast<NS::UInteger>(binding->vertexStride));
assert(layoutDesc->stepRate() == (binding->vertexBufferResource ? 1 : 0));
}
mbgl::util::hash_combine(hash,
mbgl::util::hash(index,
binding->attribute.offset,
binding->attribute.dataType,
binding->vertexStride,
static_cast<bool>(binding->vertexBufferResource)));
index += 1;
}
};
const auto& attribDesc = vertDesc->attributes()->object(index);
attribDesc->setBufferIndex(binding->bufferIndex);
attribDesc->setOffset(static_cast<NS::UInteger>(binding->attribute.offset));
attribDesc->setFormat(mtlVertexTypeOf(binding->attribute.dataType));
const auto& layoutDesc = vertDesc->layouts()->object(binding->bufferIndex);
if (!layoutDesc->stride()) {
assert(binding->vertexStride > 0);
layoutDesc->setStride(static_cast<NS::UInteger>(binding->vertexStride));
layoutDesc->setStepFunction(binding->vertexBufferResource ? MTL::VertexStepFunctionPerVertex
: MTL::VertexStepFunctionConstant);
layoutDesc->setStepRate(binding->vertexBufferResource ? 1 : 0);
} else {
assert(layoutDesc->stride() == static_cast<NS::UInteger>(binding->vertexStride));
assert(layoutDesc->stepRate() == (binding->vertexBufferResource ? 1 : 0));
}
mbgl::util::hash_combine(hash,
mbgl::util::hash(index,
binding->attribute.offset,
binding->attribute.dataType,
binding->vertexStride,
static_cast<bool>(binding->vertexBufferResource)));
index += 1;
}
applyBindings(impl->attributeBindings, MTL::VertexStepFunctionPerVertex);
applyBindings(impl->instanceBindings, MTL::VertexStepFunctionPerInstance);
impl->vertexDesc = std::move(vertDesc);
impl->vertexDescHash = hash;
@@ -631,31 +658,6 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
}
}
// build instance buffer
const bool buildInstanceBuffer =
(instanceAttributes && (!attributeUpdateTime || instanceAttributes->isModifiedAfter(*attributeUpdateTime)));
if (buildInstanceBuffer) {
// Build instance attribute buffers
std::vector<std::unique_ptr<gfx::VertexBufferResource>> instanceBuffers;
auto instanceBindings_ = uploadPass.buildAttributeBindings(instanceAttributes->getMaxCount(),
/*vertexType*/ gfx::AttributeDataType::Byte,
/*vertexAttributeIndex=*/-1,
/*vertexData=*/{},
shader->getInstanceAttributes(),
*instanceAttributes,
usage,
attributeUpdateTime,
instanceBuffers);
// clear dirty flag
instanceAttributes->visitAttributes([](gfx::VertexAttribute& attrib) { attrib.setDirty(false); });
if (impl->instanceBindings != instanceBindings_) {
impl->instanceBindings = std::move(instanceBindings_);
}
}
const bool texturesNeedUpload = std::any_of(
textures.begin(), textures.end(), [](const auto& texture) { return texture && texture->needsUpload(); });

View File

@@ -117,7 +117,9 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar
shaders::BuiltIn::FillOutlinePatternShader,
shaders::BuiltIn::FillOutlineTriangulatedShader,
shaders::BuiltIn::FillExtrusionShader,
shaders::BuiltIn::FillExtrusionInstancedShader,
shaders::BuiltIn::FillExtrusionPatternShader,
shaders::BuiltIn::FillExtrusionPatternInstancedShader,
shaders::BuiltIn::HeatmapShader,
shaders::BuiltIn::HeatmapTextureShader,
shaders::BuiltIn::HillshadeShader,

View File

@@ -101,6 +101,25 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
for (std::size_t i = 0; i < nVertices; i++) {
const auto& p1 = ring[i];
#if MLN_USE_FILL_EXTRUSION_INSTANCING
vertices.emplace_back(layoutVertex(p1, edgeDistance, i == nVertices - 1));
flatIndices.emplace_back(triangleIndex);
triangleIndex++;
if (i < nVertices - 1) {
const auto& p2 = ring[i + 1];
const auto d1 = convertPoint<double>(p1);
const auto d2 = convertPoint<double>(p2);
const size_t dist = util::dist<uint16_t>(d1, d2);
if (edgeDistance + dist > static_cast<size_t>(std::numeric_limits<uint16_t>::max())) {
edgeDistance = 0;
}
edgeDistance += dist;
}
#else
vertices.emplace_back(
FillExtrusionBucket::layoutVertex(p1, 0, 0, 1, 1, static_cast<uint16_t>(edgeDistance)));
flatIndices.emplace_back(triangleIndex);
@@ -141,6 +160,7 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
triangleSegment.vertexLength += 4;
triangleSegment.indexLength += 6;
}
#endif
}
}

View File

@@ -15,7 +15,13 @@ class BucketParameters;
class RenderFillExtrusionLayer;
using FillExtrusionBinders = PaintPropertyBinders<style::FillExtrusionPaintProperties::DataDrivenProperties>;
using FillExtrusionStaticVertex = gfx::Vertex<TypeList<attributes::pos>>;
#if MLN_USE_FILL_EXTRUSION_INSTANCING
using FillExtrusionLayoutVertex = gfx::Vertex<TypeList<attributes::pos, attributes::ed_discard>>;
#else
using FillExtrusionLayoutVertex = gfx::Vertex<TypeList<attributes::pos, attributes::normal_ed>>;
#endif
class FillExtrusionBucket final : public Bucket {
public:
@@ -43,6 +49,16 @@ public:
void update(const FeatureStates&, const GeometryTileLayer&, const std::string&, const ImagePositions&) override;
#if MLN_USE_FILL_EXTRUSION_INSTANCING
static FillExtrusionLayoutVertex layoutVertex(Point<int16_t> p, uint16_t edgeDistance, bool isDiscarded) {
return FillExtrusionLayoutVertex{{p.x, p.y},
{ // The edgeDistance attribute is used for wrapping fill_extrusion patterns
edgeDistance,
// When used as instance vector this specify if an instance is discarded
static_cast<uint16_t>(isDiscarded)
}};
}
#else
static FillExtrusionLayoutVertex layoutVertex(
Point<int16_t> p, double nx, double ny, double nz, unsigned short t, uint16_t e) {
const auto factor = pow(2, 13);
@@ -57,6 +73,7 @@ public:
// The edgedistance attribute is used for wrapping fill_extrusion patterns
static_cast<int16_t>(e)}}};
}
#endif
static std::array<float, 3> lightColor(const EvaluatedLight&);
static std::array<float, 3> lightPosition(const EvaluatedLight&, const TransformState&);

View File

@@ -163,6 +163,36 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
tileLayerGroup->setStencilTiles(renderTiles);
#if MLN_USE_FILL_EXTRUSION_INSTANCING
if (!fillExtrusionInstancedGroup) {
fillExtrusionInstancedGroup = shaders.getShaderGroup("FillExtrusionInstancedShader");
}
if (!fillExtrusionPatternInstancedGroup) {
fillExtrusionPatternInstancedGroup = shaders.getShaderGroup("FillExtrusionPatternInstancedShader");
}
if (!staticDataVertices) {
staticDataVertices = std::make_shared<FillExtrusionVertexVector>(RenderStaticData::fillExtrusionVertices());
}
if (!staticDataIndices) {
staticDataIndices = std::make_shared<TriangleIndexVector>(RenderStaticData::fillExtrusionTriangleIndices());
}
if (!staticDataSegments) {
staticDataSegments = std::make_shared<SegmentVector>(RenderStaticData::fillExtrusionSegments());
}
const auto& instancedShaderGroup = hasPattern ? fillExtrusionPatternInstancedGroup : fillExtrusionInstancedGroup;
if (!instancedShaderGroup) {
removeAllDrawables();
return;
}
const auto instanceVertexCount = staticDataVertices->elements();
std::unique_ptr<gfx::DrawableBuilder> instancedDepthBuilder;
std::unique_ptr<gfx::DrawableBuilder> instancedColorBuilder;
StringIDSetsPair instancePropertiesAsUniforms;
#endif
StringIDSetsPair propertiesAsUniforms;
for (const RenderTile& tile : *renderTiles) {
const auto& tileID = tile.getOverscaledTileID();
@@ -222,6 +252,31 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
continue;
}
#if MLN_USE_FILL_EXTRUSION_INSTANCING
if (instancedDepthBuilder) {
instancedDepthBuilder->clearTweakers();
}
if (instancedColorBuilder) {
instancedColorBuilder->clearTweakers();
}
instancePropertiesAsUniforms.first.clear();
instancePropertiesAsUniforms.second.clear();
auto instanceAttrs = context.createVertexAttributeArray();
instanceAttrs->readDataDrivenPaintProperties<FillExtrusionBase,
FillExtrusionColor,
FillExtrusionHeight,
FillExtrusionPattern>(
binders, evaluated, instancePropertiesAsUniforms, idFillExtrusionBaseVertexAttribute);
const auto instancedShader = std::static_pointer_cast<gfx::ShaderProgramBase>(
instancedShaderGroup->getOrCreateShader(context, instancePropertiesAsUniforms));
if (!instancedShader) {
continue;
}
#endif
// The non-pattern path in `render()` only uses two-pass rendering if there's translucency.
// The pattern path always uses two passes.
const auto doDepthPass = (!opaque || hasPattern);
@@ -272,6 +327,15 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
if (colorBuilder) {
colorBuilder->addTweaker(tweaker);
}
#if MLN_USE_FILL_EXTRUSION_INSTANCING
if (instancedDepthBuilder) {
instancedDepthBuilder->addTweaker(tweaker);
}
if (instancedColorBuilder) {
instancedColorBuilder->addTweaker(tweaker);
}
#endif
}
}
@@ -282,6 +346,7 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
sizeof(FillExtrusionLayoutVertex),
gfx::AttributeDataType::Short2);
}
#if !MLN_USE_FILL_EXTRUSION_INSTANCING
if (const auto& attr = vertexAttrs->set(idFillExtrusionNormalEdVertexAttribute)) {
attr->setSharedRawData(bucket.sharedVertices,
offsetof(FillExtrusionLayoutVertex, a2),
@@ -289,6 +354,7 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
sizeof(FillExtrusionLayoutVertex),
gfx::AttributeDataType::Short4);
}
#endif
if (doDepthPass) {
depthBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
@@ -321,11 +387,101 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
++stats.drawablesAdded;
}
};
if (doDepthPass) {
finish(*depthBuilder);
}
finish(*colorBuilder);
#if MLN_USE_FILL_EXTRUSION_INSTANCING
if (doDepthPass && !instancedDepthBuilder) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "depthInstanced")) {
builder->setShader(instancedShader);
builder->setIs3D(true);
builder->setEnableColor(false);
builder->setRenderPass(drawPass);
builder->setCullFaceMode(gfx::CullFaceMode::backCCW());
builder->setDrawPriority(0);
if (tweaker) {
builder->addTweaker(tweaker);
}
instancedDepthBuilder = std::move(builder);
}
}
if (!instancedColorBuilder) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "colorInstanced")) {
builder->setShader(instancedShader);
builder->setIs3D(true);
builder->setEnableColor(true);
builder->setColorMode(gfx::ColorMode::alphaBlended());
builder->setRenderPass(drawPass);
builder->setCullFaceMode(gfx::CullFaceMode::backCCW());
builder->setDrawPriority(1);
if (tweaker) {
builder->addTweaker(tweaker);
}
instancedColorBuilder = std::move(builder);
}
}
auto instanceVertexAttrs = context.createVertexAttributeArray();
if (const auto& attr = instanceVertexAttrs->set(idFillExtrusionPosVertexAttribute)) {
attr->setSharedRawData(staticDataVertices,
offsetof(FillExtrusionStaticVertex, a1),
/*vertexOffset=*/0,
sizeof(FillExtrusionStaticVertex),
gfx::AttributeDataType::Short2);
}
if (const auto& attr = instanceAttrs->set(idFillExtrusionOutlinePosAttribute)) {
attr->setSharedRawData(bucket.sharedVertices,
offsetof(FillExtrusionLayoutVertex, a1),
/*vertexOffset=*/0,
sizeof(FillExtrusionLayoutVertex),
gfx::AttributeDataType::Short2);
}
if (const auto& attr = instanceAttrs->set(idFillExtrusionEdDiscardAttribute)) {
attr->setSharedRawData(bucket.sharedVertices,
offsetof(FillExtrusionLayoutVertex, a2),
/*vertexOffset=*/0,
sizeof(FillExtrusionLayoutVertex),
gfx::AttributeDataType::UShort2);
}
if (doDepthPass) {
instancedDepthBuilder->setRawVertices({}, instanceVertexCount, gfx::AttributeDataType::Short2);
instancedDepthBuilder->setVertexAttributes(instanceVertexAttrs);
instancedDepthBuilder->setInstanceAttributes(instanceAttrs);
}
instancedColorBuilder->setEnableStencil(doDepthPass);
instancedColorBuilder->setRawVertices({}, instanceVertexCount, gfx::AttributeDataType::Short2);
instancedColorBuilder->setVertexAttributes(std::move(instanceVertexAttrs));
instancedColorBuilder->setInstanceAttributes(std::move(instanceAttrs));
const auto finishInstance = [&](gfx::DrawableBuilder& instancedBuilder) {
if (!staticDataIndices->elements()) {
return;
}
instancedBuilder.setSegments(
gfx::Triangles(), staticDataIndices, staticDataSegments->data(), staticDataSegments->size());
instancedBuilder.flush(context);
for (auto& drawable : instancedBuilder.clearDrawables()) {
drawable->setTileID(tileID);
drawable->setType(static_cast<std::size_t>(hasPattern));
drawable->setLayerTweaker(layerTweaker);
drawable->setBinders(renderData.bucket, &binders);
drawable->setRenderTile(renderTilesOwner, &tile);
tileLayerGroup->addDrawable(drawPass, tileID, std::move(drawable));
++stats.drawablesAdded;
}
};
if (doDepthPass) {
finishInstance(*instancedDepthBuilder);
}
finishInstance(*instancedColorBuilder);
#endif
}
}

View File

@@ -1,9 +1,9 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
#include <mbgl/gfx/offscreen_texture.hpp>
namespace mbgl {
@@ -40,7 +40,18 @@ private:
gfx::ShaderGroupPtr fillExtrusionGroup;
gfx::ShaderGroupPtr fillExtrusionPatternGroup;
std::unique_ptr<gfx::OffscreenTexture> renderTexture;
#if MLN_USE_FILL_EXTRUSION_INSTANCING
gfx::ShaderGroupPtr fillExtrusionInstancedGroup;
gfx::ShaderGroupPtr fillExtrusionPatternInstancedGroup;
using FillExtrusionVertexVector = gfx::VertexVector<FillExtrusionStaticVertex>;
using TriangleIndexVector = gfx::IndexVector<gfx::Triangles>;
std::shared_ptr<FillExtrusionVertexVector> staticDataVertices;
std::shared_ptr<TriangleIndexVector> staticDataIndices;
std::shared_ptr<SegmentVector> staticDataSegments;
#endif
};
} // namespace mbgl

View File

@@ -141,7 +141,7 @@ void RenderRasterLayer::update(gfx::ShaderRegistry& shaders,
staticDataIndices = std::make_shared<TriangleIndexVector>(RenderStaticData::quadTriangleIndices());
}
if (!staticDataSegments) {
staticDataSegments = std::make_shared<RasterSegmentVector>(RenderStaticData::rasterSegments());
staticDataSegments = std::make_shared<SegmentVector>(RenderStaticData::rasterSegments());
}
const auto createBuilder = [&] {

View File

@@ -61,9 +61,8 @@ private:
using TriangleIndexVectorPtr = std::shared_ptr<TriangleIndexVector>;
TriangleIndexVectorPtr staticDataIndices;
using RasterSegmentVector = SegmentVector;
using RasterSegmentVectorPtr = std::shared_ptr<RasterSegmentVector>;
std::shared_ptr<RasterSegmentVector> staticDataSegments;
using SegmentVectorPtr = std::shared_ptr<SegmentVector>;
SegmentVectorPtr staticDataSegments;
};
} // namespace mbgl

View File

@@ -5,54 +5,86 @@
namespace mbgl {
gfx::VertexVector<gfx::Vertex<PositionOnlyLayoutAttributes>> RenderStaticData::tileVertices() {
gfx::VertexVector<gfx::Vertex<PositionOnlyLayoutAttributes>> result;
result.emplace_back(gfx::Vertex<PositionOnlyLayoutAttributes>({{{0, 0}}}));
result.emplace_back(gfx::Vertex<PositionOnlyLayoutAttributes>({{{util::EXTENT, 0}}}));
result.emplace_back(gfx::Vertex<PositionOnlyLayoutAttributes>({{{0, util::EXTENT}}}));
result.emplace_back(gfx::Vertex<PositionOnlyLayoutAttributes>({{{util::EXTENT, util::EXTENT}}}));
return result;
}
gfx::IndexVector<gfx::Triangles> RenderStaticData::quadTriangleIndices() {
gfx::IndexVector<gfx::Triangles> result;
result.emplace_back(0, 1, 2);
result.emplace_back(1, 2, 3);
return result;
}
gfx::IndexVector<gfx::LineStrip> RenderStaticData::tileLineStripIndices() {
gfx::IndexVector<gfx::LineStrip> result;
result.emplace_back(0);
result.emplace_back(1);
result.emplace_back(3);
result.emplace_back(2);
result.emplace_back(0);
return result;
}
gfx::VertexVector<RasterLayoutVertex> RenderStaticData::rasterVertices() {
gfx::VertexVector<RasterLayoutVertex> result;
result.emplace_back(RasterBucket::layoutVertex({0, 0}, {0, 0}));
result.emplace_back(RasterBucket::layoutVertex({util::EXTENT, 0}, {util::EXTENT, 0}));
result.emplace_back(RasterBucket::layoutVertex({0, util::EXTENT}, {0, util::EXTENT}));
result.emplace_back(RasterBucket::layoutVertex({util::EXTENT, util::EXTENT}, {util::EXTENT, util::EXTENT}));
return result;
}
gfx::VertexVector<HeatmapTextureLayoutVertex> RenderStaticData::heatmapTextureVertices() {
gfx::VertexVector<HeatmapTextureLayoutVertex> result;
result.emplace_back(HeatmapBucket::textureVertex({0, 0}));
result.emplace_back(HeatmapBucket::textureVertex({1, 0}));
result.emplace_back(HeatmapBucket::textureVertex({0, 1}));
result.emplace_back(HeatmapBucket::textureVertex({1, 1}));
return result;
}
RenderStaticData::RenderStaticData(std::unique_ptr<gfx::ShaderRegistry>&& shaders_)
: shaders(std::move(shaders_)),
clippingMaskSegments(tileTriangleSegments()) {}
void RenderStaticData::upload(gfx::UploadPass& uploadPass) {
if (!uploaded) {
// these are still used by stencil buffer rendering
tileVertexBuffer = uploadPass.createVertexBuffer(
tileVertices(), gfx::BufferUsageType::StaticDraw, /*persistent=*/false);
quadTriangleIndexBuffer = uploadPass.createIndexBuffer(
quadTriangleIndices(), gfx::BufferUsageType::StaticDraw, /*persistent=*/false);
uploaded = true;
}
}
gfx::VertexVector<gfx::Vertex<PositionOnlyLayoutAttributes>> RenderStaticData::tileVertices() {
gfx::VertexVector<gfx::Vertex<PositionOnlyLayoutAttributes>> vertices;
vertices.emplace_back(gfx::Vertex<PositionOnlyLayoutAttributes>({{{0, 0}}}));
vertices.emplace_back(gfx::Vertex<PositionOnlyLayoutAttributes>({{{util::EXTENT, 0}}}));
vertices.emplace_back(gfx::Vertex<PositionOnlyLayoutAttributes>({{{0, util::EXTENT}}}));
vertices.emplace_back(gfx::Vertex<PositionOnlyLayoutAttributes>({{{util::EXTENT, util::EXTENT}}}));
return vertices;
}
gfx::VertexVector<RasterLayoutVertex> RenderStaticData::rasterVertices() {
gfx::VertexVector<RasterLayoutVertex> vertices;
vertices.emplace_back(RasterBucket::layoutVertex({0, 0}, {0, 0}));
vertices.emplace_back(RasterBucket::layoutVertex({util::EXTENT, 0}, {util::EXTENT, 0}));
vertices.emplace_back(RasterBucket::layoutVertex({0, util::EXTENT}, {0, util::EXTENT}));
vertices.emplace_back(RasterBucket::layoutVertex({util::EXTENT, util::EXTENT}, {util::EXTENT, util::EXTENT}));
return vertices;
}
gfx::VertexVector<HeatmapTextureLayoutVertex> RenderStaticData::heatmapTextureVertices() {
gfx::VertexVector<HeatmapTextureLayoutVertex> vertices;
vertices.emplace_back(HeatmapBucket::textureVertex({0, 0}));
vertices.emplace_back(HeatmapBucket::textureVertex({1, 0}));
vertices.emplace_back(HeatmapBucket::textureVertex({0, 1}));
vertices.emplace_back(HeatmapBucket::textureVertex({1, 1}));
return vertices;
}
gfx::VertexVector<FillExtrusionStaticVertex> RenderStaticData::fillExtrusionVertices() {
gfx::VertexVector<HeatmapTextureLayoutVertex> vertices;
vertices.emplace_back(FillExtrusionStaticVertex{1, 0});
vertices.emplace_back(FillExtrusionStaticVertex{1, 1});
vertices.emplace_back(FillExtrusionStaticVertex{0, 0});
vertices.emplace_back(FillExtrusionStaticVertex{0, 1});
return vertices;
}
gfx::IndexVector<gfx::Triangles> RenderStaticData::quadTriangleIndices() {
gfx::IndexVector<gfx::Triangles> indices;
indices.emplace_back(0, 1, 2);
indices.emplace_back(1, 2, 3);
return indices;
}
gfx::IndexVector<gfx::LineStrip> RenderStaticData::tileLineStripIndices() {
gfx::IndexVector<gfx::LineStrip> indices;
indices.emplace_back(0);
indices.emplace_back(1);
indices.emplace_back(3);
indices.emplace_back(2);
indices.emplace_back(0);
return indices;
}
gfx::IndexVector<gfx::Triangles> RenderStaticData::fillExtrusionTriangleIndices() {
gfx::IndexVector<gfx::Triangles> indices;
// ┌──────┐
// │ 0 1 │ Counter-Clockwise winding order.
// │ │ Triangle 1: 0 => 2 => 1
// │ 2 3 │ Triangle 2: 1 => 2 => 3
// └──────┘
indices.emplace_back(0, 2, 1);
indices.emplace_back(1, 2, 3);
return indices;
}
SegmentVector RenderStaticData::tileTriangleSegments() {
SegmentVector segments;
segments.emplace_back(0, 0, 4, 6);
@@ -77,15 +109,10 @@ SegmentVector RenderStaticData::heatmapTextureSegments() {
return segments;
}
void RenderStaticData::upload(gfx::UploadPass& uploadPass) {
if (!uploaded) {
// these are still used by stencil buffer rendering
tileVertexBuffer = uploadPass.createVertexBuffer(
tileVertices(), gfx::BufferUsageType::StaticDraw, /*persistent=*/false);
quadTriangleIndexBuffer = uploadPass.createIndexBuffer(
quadTriangleIndices(), gfx::BufferUsageType::StaticDraw, /*persistent=*/false);
uploaded = true;
}
SegmentVector RenderStaticData::fillExtrusionSegments() {
SegmentVector segments;
segments.emplace_back(0, 0, 4, 6);
return segments;
}
} // namespace mbgl

View File

@@ -6,6 +6,7 @@
#include <mbgl/gfx/shader_registry.hpp>
#include <mbgl/renderer/buckets/heatmap_bucket.hpp>
#include <mbgl/renderer/buckets/raster_bucket.hpp>
#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
#include <string>
#include <optional>
@@ -23,22 +24,22 @@ public:
void upload(gfx::UploadPass&);
std::optional<gfx::VertexBuffer<gfx::Vertex<PositionOnlyLayoutAttributes>>> tileVertexBuffer;
std::optional<gfx::VertexBuffer<RasterLayoutVertex>> rasterVertexBuffer;
std::optional<gfx::VertexBuffer<HeatmapTextureLayoutVertex>> heatmapTextureVertexBuffer;
std::optional<gfx::IndexBuffer> quadTriangleIndexBuffer;
std::optional<gfx::IndexBuffer> tileBorderIndexBuffer;
static gfx::VertexVector<gfx::Vertex<PositionOnlyLayoutAttributes>> tileVertices();
static gfx::IndexVector<gfx::Triangles> quadTriangleIndices();
static gfx::IndexVector<gfx::LineStrip> tileLineStripIndices();
static gfx::VertexVector<RasterLayoutVertex> rasterVertices();
static gfx::VertexVector<HeatmapTextureLayoutVertex> heatmapTextureVertices();
static gfx::VertexVector<FillExtrusionStaticVertex> fillExtrusionVertices();
static gfx::IndexVector<gfx::Triangles> quadTriangleIndices();
static gfx::IndexVector<gfx::LineStrip> tileLineStripIndices();
static gfx::IndexVector<gfx::Triangles> fillExtrusionTriangleIndices();
static SegmentVector tileTriangleSegments();
static SegmentVector tileBorderSegments();
static SegmentVector rasterSegments();
static SegmentVector heatmapTextureSegments();
static SegmentVector fillExtrusionSegments();
std::optional<gfx::Renderbuffer<gfx::RenderbufferPixelType::Depth>> depthRenderbuffer;
bool has3D = false;

View File

@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/gfx/attribute.hpp>
#include <mbgl/shaders/layer_ubo.hpp>
namespace mbgl {
namespace attributes {
@@ -16,13 +17,18 @@ MBGL_DEFINE_ATTRIBUTE(int16_t, 4, pixeloffset);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, label_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, anchor_pos);
MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, texture_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 4, normal_ed);
MBGL_DEFINE_ATTRIBUTE(float, 1, fade_opacity);
MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, placed);
MBGL_DEFINE_ATTRIBUTE(uint16_t, 3, size);
MBGL_DEFINE_ATTRIBUTE(float, 1, offset);
MBGL_DEFINE_ATTRIBUTE(float, 2, shift);
#if MLN_USE_FILL_EXTRUSION_INSTANCING
MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, ed_discard);
#else
MBGL_DEFINE_ATTRIBUTE(int16_t, 4, normal_ed);
#endif
template <typename T, std::size_t N>
struct data {
using Type = gfx::AttributeType<T, N>;

View File

@@ -9,37 +9,80 @@ namespace shaders {
using FillExtrusionShaderSource = ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal>;
const std::array<AttributeInfo, 5> FillExtrusionShaderSource::attributes = {
const std::array<AttributeInfo, 4> FillExtrusionShaderSource::attributes = {
AttributeInfo{0, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 0, idFillExtrusionPosVertexAttribute},
AttributeInfo{1, gfx::AttributeDataType::Short4, fillExtrusionUBOCount + 0, idFillExtrusionNormalEdVertexAttribute},
// Data driven
AttributeInfo{2, gfx::AttributeDataType::Float4, fillExtrusionUBOCount + 1, idFillExtrusionColorVertexAttribute},
AttributeInfo{3, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionBaseVertexAttribute},
AttributeInfo{4, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionHeightVertexAttribute},
AttributeInfo{1, gfx::AttributeDataType::Float4, fillExtrusionUBOCount + 1, idFillExtrusionColorVertexAttribute},
AttributeInfo{2, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionBaseVertexAttribute},
AttributeInfo{3, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionHeightVertexAttribute},
};
const std::array<TextureInfo, 0> FillExtrusionShaderSource::textures = {};
//
// Fill extrusion instanced
using FillExtrusionInstancedShaderSource =
ShaderSource<BuiltIn::FillExtrusionInstancedShader, gfx::Backend::Type::Metal>;
const std::array<AttributeInfo, 1> FillExtrusionInstancedShaderSource::attributes = {
AttributeInfo{0, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 0, idFillExtrusionPosVertexAttribute},
};
const std::array<AttributeInfo, 5> FillExtrusionInstancedShaderSource::instanceAttributes = {
AttributeInfo{1, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 1, idFillExtrusionOutlinePosAttribute},
AttributeInfo{2, gfx::AttributeDataType::UShort2, fillExtrusionUBOCount + 1, idFillExtrusionEdDiscardAttribute},
// Data driven
AttributeInfo{3, gfx::AttributeDataType::Float4, fillExtrusionUBOCount + 2, idFillExtrusionColorVertexAttribute},
AttributeInfo{4, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 2, idFillExtrusionBaseVertexAttribute},
AttributeInfo{5, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 2, idFillExtrusionHeightVertexAttribute},
};
const std::array<TextureInfo, 0> FillExtrusionInstancedShaderSource::textures = {};
//
// Fill extrusion pattern
using FillExtrusionPatternShaderSource = ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Metal>;
const std::array<AttributeInfo, 6> FillExtrusionPatternShaderSource::attributes = {
const std::array<AttributeInfo, 5> FillExtrusionPatternShaderSource::attributes = {
AttributeInfo{0, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 0, idFillExtrusionPosVertexAttribute},
AttributeInfo{1, gfx::AttributeDataType::Short4, fillExtrusionUBOCount + 0, idFillExtrusionNormalEdVertexAttribute},
// Data driven
AttributeInfo{2, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionBaseVertexAttribute},
AttributeInfo{3, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionHeightVertexAttribute},
AttributeInfo{1, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionBaseVertexAttribute},
AttributeInfo{2, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionHeightVertexAttribute},
AttributeInfo{
4, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 1, idFillExtrusionPatternFromVertexAttribute},
3, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 1, idFillExtrusionPatternFromVertexAttribute},
AttributeInfo{
5, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 1, idFillExtrusionPatternToVertexAttribute},
4, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 1, idFillExtrusionPatternToVertexAttribute},
};
const std::array<TextureInfo, 1> FillExtrusionPatternShaderSource::textures = {
TextureInfo{0, idFillExtrusionImageTexture},
};
//
// Fill extrusion pattern instanced
using FillExtrusionPatternInstancedShaderSource =
ShaderSource<BuiltIn::FillExtrusionPatternInstancedShader, gfx::Backend::Type::Metal>;
const std::array<AttributeInfo, 1> FillExtrusionPatternInstancedShaderSource::attributes = {
AttributeInfo{0, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 0, idFillExtrusionPosVertexAttribute},
};
const std::array<AttributeInfo, 6> FillExtrusionPatternInstancedShaderSource::instanceAttributes = {
AttributeInfo{1, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 1, idFillExtrusionOutlinePosAttribute},
AttributeInfo{2, gfx::AttributeDataType::UShort2, fillExtrusionUBOCount + 1, idFillExtrusionEdDiscardAttribute},
// Data driven
AttributeInfo{3, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 2, idFillExtrusionBaseVertexAttribute},
AttributeInfo{4, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 2, idFillExtrusionHeightVertexAttribute},
AttributeInfo{
5, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 2, idFillExtrusionPatternFromVertexAttribute},
AttributeInfo{
6, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 2, idFillExtrusionPatternToVertexAttribute},
};
const std::array<TextureInfo, 1> FillExtrusionPatternInstancedShaderSource::textures = {
TextureInfo{0, idFillExtrusionImageTexture},
};
} // namespace shaders
} // namespace mbgl

View File

@@ -183,13 +183,12 @@ std::optional<size_t> ShaderProgram::getSamplerLocation(const size_t id) const {
return (id < textureBindings.size()) ? textureBindings[id] : std::nullopt;
}
void ShaderProgram::initAttribute(const shaders::AttributeInfo& info) {
void ShaderProgram::initVertexAttribute(const shaders::AttributeInfo& info) {
const auto index = static_cast<int>(info.index);
const auto bufferIndex = static_cast<int>(info.bufferIndex);
#if !defined(NDEBUG)
// Indexes must be unique, if there's a conflict check the `attributes` array in the shader
vertexAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
instanceAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
#endif
vertexAttributes.set(info.id, index, info.dataType, bufferIndex);
}
@@ -201,7 +200,7 @@ void ShaderProgram::initInstanceAttribute(const shaders::AttributeInfo& info) {
#if !defined(NDEBUG)
// Indexes must not be reused by regular attributes or uniform blocks
// More than one instance attribute can have the same index, if they share the block
vertexAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
instanceAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
#endif
instanceAttributes.set(info.id, index, info.dataType, bufferIndex);
}

View File

@@ -24,7 +24,9 @@ MBGL_DEFINE_ENUM(BuiltIn,
{BuiltIn::FillOutlinePatternShader, "FillOutlinePatternShader"},
{BuiltIn::FillOutlineTriangulatedShader, "FillOutlineTriangulatedShader"},
{BuiltIn::FillExtrusionShader, "FillExtrusionShader"},
{BuiltIn::FillExtrusionInstancedShader, "FillExtrusionInstancedShader"},
{BuiltIn::FillExtrusionPatternShader, "FillExtrusionPatternShader"},
{BuiltIn::FillExtrusionPatternInstancedShader, "FillExtrusionPatternInstancedShader"},
{BuiltIn::HeatmapShader, "HeatmapShader"},
{BuiltIn::HeatmapTextureShader, "HeatmapTextureShader"},
{BuiltIn::HillshadePrepareShader, "HillshadePrepareShader"},

View File

@@ -204,7 +204,7 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
if (buildInstanceBuffer) {
// Build instance attribute buffers
std::vector<std::unique_ptr<gfx::VertexBufferResource>> instanceBuffers;
auto instanceBindings_ = uploadPass.buildAttributeBindings(instanceAttributes->getMaxCount(),
auto instanceBindings_ = uploadPass.buildAttributeBindings(instanceAttributes->getMinCount(),
/*vertexType*/ gfx::AttributeDataType::Byte,
/*vertexAttributeIndex=*/-1,
/*vertexData=*/{},
@@ -290,7 +290,7 @@ void Drawable::draw(PaintParameters& parameters) const {
impl->pipelineInfo.setRenderable(renderPass_.getDescriptor().renderable);
const auto instances = instanceAttributes ? instanceAttributes->getMaxCount() : 1;
const auto instances = instanceAttributes ? instanceAttributes->getMinCount() : 1;
for (const auto& seg : impl->segments) {
const auto& segment = seg->getSegment();

View File

@@ -944,7 +944,7 @@ void Drawable::draw(PaintParameters& parameters) const {
const auto& segment = static_cast<DrawSegment&>(*seg_);
const auto& mlSegment = segment.getSegment();
if (mlSegment.indexLength > 0) {
const uint32_t instanceCount = instanceAttributes ? static_cast<uint32_t>(instanceAttributes->getMaxCount())
const uint32_t instanceCount = instanceAttributes ? static_cast<uint32_t>(instanceAttributes->getMinCount())
: 1;
const uint32_t indexOffset = static_cast<uint32_t>(mlSegment.indexOffset);
const int32_t baseVertex = static_cast<int32_t>(mlSegment.vertexOffset);

View File

@@ -140,7 +140,9 @@ TEST(OrderIndependentHash, Shaders) {
BuiltIn::FillOutlinePatternShader,
BuiltIn::FillOutlineTriangulatedShader,
BuiltIn::FillExtrusionShader,
BuiltIn::FillExtrusionInstancedShader,
BuiltIn::FillExtrusionPatternShader,
BuiltIn::FillExtrusionPatternInstancedShader,
BuiltIn::HeatmapShader,
BuiltIn::HeatmapTextureShader,
BuiltIn::HillshadePrepareShader,