From a7f5d565d841474148837285522678d3ee14ac09 Mon Sep 17 00:00:00 2001 From: Tim Sylvester Date: Mon, 18 Sep 2023 18:32:24 -0700 Subject: [PATCH] Topic/metal symbol rebase (#1640) Signed-off-by: dependabot[bot] Co-authored-by: mwilsnd Co-authored-by: Alex Cristici Co-authored-by: Stefan Karschti Co-authored-by: Bart Louwers Co-authored-by: Alex Cristici Co-authored-by: Bart Louwers Co-authored-by: Wei Chen Co-authored-by: Siarhei Fedartsou Co-authored-by: tdcosta100 --- CMakeLists.txt | 8 +- bazel/core.bzl | 8 +- ...l_sdf_icon.hpp => drawable_symbol_sdf.hpp} | 0 include/mbgl/shaders/mtl/common.hpp | 65 +++++- include/mbgl/shaders/mtl/symbol_icon.hpp | 148 ++++++++++++ include/mbgl/shaders/mtl/symbol_sdf.hpp | 201 +++++++++++++++++ .../mbgl/shaders/mtl/symbol_text_and_icon.hpp | 212 ++++++++++++++++++ include/mbgl/shaders/shader_manifest.hpp | 3 +- include/mbgl/shaders/shader_source.hpp | 1 - include/mbgl/shaders/symbol_layer_ubo.hpp | 88 ++++++++ platform/ios/app/MBXViewController.m | 2 +- platform/ios/benchmark/locations.cpp | 1 - shaders/manifest.json | 9 +- src/mbgl/gl/renderer_backend.cpp | 1 - src/mbgl/mtl/renderer_backend.cpp | 16 +- .../layers/fill_extrusion_layer_tweaker.cpp | 1 - .../layers/render_fill_extrusion_layer.cpp | 1 - .../renderer/layers/render_symbol_layer.cpp | 38 ++-- .../renderer/layers/render_symbol_layer.hpp | 14 +- .../renderer/layers/symbol_layer_tweaker.cpp | 89 ++++---- .../renderer/layers/symbol_layer_tweaker.hpp | 29 +-- 21 files changed, 822 insertions(+), 113 deletions(-) rename include/mbgl/shaders/gl/{drawable_symbol_sdf_icon.hpp => drawable_symbol_sdf.hpp} (100%) create mode 100644 include/mbgl/shaders/mtl/symbol_icon.hpp create mode 100644 include/mbgl/shaders/mtl/symbol_sdf.hpp create mode 100644 include/mbgl/shaders/mtl/symbol_text_and_icon.hpp create mode 100644 include/mbgl/shaders/symbol_layer_ubo.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 58fb76e1e..67a033ee3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1018,8 +1018,7 @@ if(MLN_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_hillshade.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_raster.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_symbol_icon.hpp - ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_symbol_sdf_text.hpp - ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_symbol_sdf_icon.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_symbol_sdf.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_symbol_text_and_icon.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/background_pattern.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/circle.hpp @@ -1043,8 +1042,8 @@ if(MLN_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/line.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/raster.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/symbol_icon.hpp - ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/symbol_sdf_text.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/symbol_sdf_icon.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/symbol_sdf_text.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/symbol_text_and_icon.hpp ) list(APPEND @@ -1183,6 +1182,9 @@ if(MBGL_WITH_METAL) ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/raster.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/shader_group.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/shader_program.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/symbol_icon.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/symbol_sdf.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp ) list(APPEND SRC_FILES diff --git a/bazel/core.bzl b/bazel/core.bzl index 2cdd1e9f4..5492605e2 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -84,8 +84,7 @@ MLN_GENERATED_OPENGL_SHADER_HEADERS = [ "include/mbgl/shaders/gl/drawable_line.hpp", "include/mbgl/shaders/gl/drawable_raster.hpp", "include/mbgl/shaders/gl/drawable_symbol_icon.hpp", - "include/mbgl/shaders/gl/drawable_symbol_sdf_text.hpp", - "include/mbgl/shaders/gl/drawable_symbol_sdf_icon.hpp", + "include/mbgl/shaders/gl/drawable_symbol_sdf.hpp", "include/mbgl/shaders/gl/drawable_symbol_text_and_icon.hpp", "include/mbgl/shaders/gl/fill_extrusion_pattern.hpp", "include/mbgl/shaders/gl/fill_extrusion.hpp", @@ -970,7 +969,9 @@ MLN_DRAWABLES_HEADERS = [ "include/mbgl/shaders/line_layer_ubo.hpp", "include/mbgl/shaders/raster_layer_ubo.hpp", "include/mbgl/shaders/shader_program_base.hpp", + "include/mbgl/shaders/symbol_layer_ubo.hpp", "include/mbgl/util/identity.hpp", + "include/mbgl/util/suppress_copies.hpp" ] MLN_DRAWABLES_GL_SOURCE = [ @@ -1053,4 +1054,7 @@ MLN_DRAWABLES_MTL_HEADERS = [ "include/mbgl/shaders/mtl/raster.hpp", "include/mbgl/shaders/mtl/shader_group.hpp", "include/mbgl/shaders/mtl/shader_program.hpp", + "include/mbgl/shaders/mtl/symbol_icon.hpp", + "include/mbgl/shaders/mtl/symbol_sdf.hpp", + "include/mbgl/shaders/mtl/symbol_text_and_icon.hpp", ] diff --git a/include/mbgl/shaders/gl/drawable_symbol_sdf_icon.hpp b/include/mbgl/shaders/gl/drawable_symbol_sdf.hpp similarity index 100% rename from include/mbgl/shaders/gl/drawable_symbol_sdf_icon.hpp rename to include/mbgl/shaders/gl/drawable_symbol_sdf.hpp diff --git a/include/mbgl/shaders/mtl/common.hpp b/include/mbgl/shaders/mtl/common.hpp index 34c361e0f..606f56cc4 100644 --- a/include/mbgl/shaders/mtl/common.hpp +++ b/include/mbgl/shaders/mtl/common.hpp @@ -182,7 +182,70 @@ struct alignas(16) LineGradientInterpolationUBO { float pad1, pad2, pad3; }; -// interpolated packed pattern +struct alignas(16) SymbolDrawableTilePropsUBO { + /*bool*/ int is_text; + /*bool*/ int is_halo; + /*bool*/ int pitch_with_map; + /*bool*/ int is_size_zoom_constant; + /*bool*/ int is_size_feature_constant; + float size_t; + float size; + float padding; +}; +static_assert(sizeof(SymbolDrawableTilePropsUBO) == 2 * 16, "unexpected padding"); + +struct alignas(16) SymbolDrawableInterpolateUBO { + float fill_color_t; + float halo_color_t; + float opacity_t; + float halo_width_t; + float halo_blur_t; + float pad1, pad2, pad3; +}; +static_assert(sizeof(SymbolDrawableInterpolateUBO) == 32, "unexpected padding"); + +struct alignas(16) SymbolDrawableUBO { + float4x4 matrix; + float4x4 label_plane_matrix; + float4x4 coord_matrix; + + float2 texsize; + float2 texsize_icon; + + float gamma_scale; + float device_pixel_ratio; + + float camera_to_center_distance; + float pitch; + /*bool*/ int rotate_symbol; + float aspect_ratio; + float fade_change; + float pad; +}; +static_assert(sizeof(SymbolDrawableUBO) == 15 * 16, "unexpected padding"); + +struct alignas(16) SymbolDrawablePaintUBO { + float4 fill_color; + float4 halo_color; + float opacity; + float halo_width; + float halo_blur; + float padding; +}; +static_assert(sizeof(SymbolDrawablePaintUBO) == 3 * 16, "unexpected padding"); + +struct alignas(16) SymbolPermutationUBO { + Attribute fill_color; + Attribute halo_color; + Attribute opacity; + Attribute halo_width; + Attribute halo_blur; + int32_t /*bool*/ overdrawInspector; + float pad1, pad2, pad3; +}; +static_assert(sizeof(SymbolPermutationUBO) == 4 * 16, "unexpected padding"); + + float4 patternFor(device const Attribute& attrib, device const float4& constValue, thread const ushort4& vertexValue, diff --git a/include/mbgl/shaders/mtl/symbol_icon.hpp b/include/mbgl/shaders/mtl/symbol_icon.hpp new file mode 100644 index 000000000..5f307c164 --- /dev/null +++ b/include/mbgl/shaders/mtl/symbol_icon.hpp @@ -0,0 +1,148 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr auto name = "SymbolIconShader"; + static constexpr auto vertexMainFunction = "vertexMain"; + static constexpr auto fragmentMainFunction = "fragmentMain"; + + static constexpr AttributeInfo attributes[] = { + // always attributes + {0, gfx::AttributeDataType::Short4, 1, "a_pos_offset"}, + {1, gfx::AttributeDataType::UShort4, 1, "a_data"}, + {2, gfx::AttributeDataType::Short4, 1, "a_pixeloffset"}, + {3, gfx::AttributeDataType::Float3, 1, "a_projected_pos"}, + {4, gfx::AttributeDataType::Float, 1, "a_fade_opacity"}, + + // sometimes uniforms + {5, gfx::AttributeDataType::Float, 1, "a_opacity"}, + }; + static constexpr UniformBlockInfo uniforms[] = { + MLN_MTL_UNIFORM_BLOCK(8, true, true, SymbolDrawableUBO), + MLN_MTL_UNIFORM_BLOCK(9, true, true, SymbolDrawablePaintUBO), + MLN_MTL_UNIFORM_BLOCK(10, true, false, SymbolDrawableTilePropsUBO), + MLN_MTL_UNIFORM_BLOCK(11, true, false, SymbolDrawableInterpolateUBO), + MLN_MTL_UNIFORM_BLOCK(12, true, true, SymbolPermutationUBO), + MLN_MTL_UNIFORM_BLOCK(13, true, false, ExpressionInputsUBO), + }; + static constexpr TextureInfo textures[] = { + {0, "u_texture"}, + }; + + static constexpr auto source = R"( +struct VertexStage { + float4 pos_offset [[attribute(0)]]; + float4 data [[attribute(1)]]; + float4 pixeloffset [[attribute(2)]]; + float3 projected_pos [[attribute(3)]]; + float fade_opacity [[attribute(4)]]; + float opacity [[attribute(5)]]; +}; + +struct FragmentStage { + float4 position [[position, invariant]]; + float2 tex; + float fade_opacity; + float opacity; +}; + +FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], + device const SymbolDrawableUBO& drawable [[buffer(8)]], + device const SymbolDrawablePaintUBO& paint [[buffer(9)]], + device const SymbolDrawableTilePropsUBO& props [[buffer(10)]], + device const SymbolDrawableInterpolateUBO& interp [[buffer(11)]], + device const SymbolPermutationUBO& permutation [[buffer(12)]], + device const ExpressionInputsUBO& expr [[buffer(13)]]) { + + const auto opacity = valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr); + + const float2 a_pos = vertx.pos_offset.xy; + const float2 a_offset = vertx.pos_offset.zw; + + const float2 a_tex = vertx.data.xy; + const float2 a_size = vertx.data.zw; + + const float a_size_min = floor(a_size[0] * 0.5); + const float2 a_pxoffset = vertx.pixeloffset.xy; + const float2 a_minFontScale = vertx.pixeloffset.zw / 256.0; + + const float segment_angle = -vertx.projected_pos[2]; + + float size; + if (!props.is_size_zoom_constant && !props.is_size_feature_constant) { + size = mix(a_size_min, a_size[1], props.size_t) / 128.0; + } else if (props.is_size_zoom_constant && !props.is_size_feature_constant) { + size = a_size_min / 128.0; + } else { + size = props.size; + } + + const float4 projectedPoint = drawable.matrix * float4(a_pos, 0, 1); + const float camera_to_anchor_distance = projectedPoint.w; + // See comments in symbol_sdf.vertex + const float distance_ratio = props.pitch_with_map ? + camera_to_anchor_distance / drawable.camera_to_center_distance : + drawable.camera_to_center_distance / camera_to_anchor_distance; + const float perspective_ratio = clamp( + 0.5 + 0.5 * distance_ratio, + 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles + 4.0); + + size *= perspective_ratio; + + const float fontScale = props.is_text ? size / 24.0 : size; + + float symbol_rotation = 0.0; + if (drawable.rotate_symbol) { + // See comments in symbol_sdf.vertex + const float4 offsetProjectedPoint = drawable.matrix * float4(a_pos + float2(1, 0), 0, 1); + + const float2 a = projectedPoint.xy / projectedPoint.w; + const float2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w; + symbol_rotation = atan2((b.y - a.y) / drawable.aspect_ratio, b.x - a.x); + } + + const float angle_sin = sin(segment_angle + symbol_rotation); + const float angle_cos = cos(segment_angle + symbol_rotation); + const float2x2 rotation_matrix = float2x2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); + + const float4 projected_pos = drawable.label_plane_matrix * float4(vertx.projected_pos.xy, 0.0, 1.0); + const float2 pos0 = projected_pos.xy / projected_pos.w; + const float2 posOffset = a_offset * max(a_minFontScale, fontScale) / 32.0 + a_pxoffset / 16.0; + const float4 position = drawable.coord_matrix * float4(pos0 + rotation_matrix * posOffset, 0.0, 1.0); + + const float2 fade_opacity = unpack_opacity(vertx.fade_opacity); + const float fade_change = fade_opacity[1] > 0.5 ? drawable.fade_change : -drawable.fade_change; + + return { + .position = position, + .tex = a_tex / drawable.texsize, + .fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change)), + .opacity = opacity, + }; +} + +half4 fragment fragmentMain(FragmentStage in [[stage_in]], + device const SymbolDrawableUBO& drawable [[buffer(8)]], + device const SymbolPermutationUBO& permutation [[buffer(12)]], + texture2d image [[texture(0)]], + sampler image_sampler [[sampler(0)]]) { + if (permutation.overdrawInspector) { + return half4(1.0); + } + + return half4(image.sample(image_sampler, in.tex) * (in.opacity * in.fade_opacity)); +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/mtl/symbol_sdf.hpp b/include/mbgl/shaders/mtl/symbol_sdf.hpp new file mode 100644 index 000000000..c1e8db6ee --- /dev/null +++ b/include/mbgl/shaders/mtl/symbol_sdf.hpp @@ -0,0 +1,201 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr auto name = "SymbolSDFIconShader"; + static constexpr auto vertexMainFunction = "vertexMain"; + static constexpr auto fragmentMainFunction = "fragmentMain"; + + static constexpr AttributeInfo attributes[] = { + // always attributes + {0, gfx::AttributeDataType::Short4, 1, "a_pos_offset"}, + {1, gfx::AttributeDataType::UShort4, 1, "a_data"}, + {2, gfx::AttributeDataType::Short4, 1, "a_pixeloffset"}, + {3, gfx::AttributeDataType::Float3, 1, "a_projected_pos"}, + {4, gfx::AttributeDataType::Float, 1, "a_fade_opacity"}, + + // sometimes uniforms + {5, gfx::AttributeDataType::Float4, 1, "a_fill_color"}, + {6, gfx::AttributeDataType::Float4, 1, "a_halo_color"}, + {7, gfx::AttributeDataType::Float, 1, "a_opacity"}, + {8, gfx::AttributeDataType::Float, 1, "a_halo_width"}, + {9, gfx::AttributeDataType::Float, 1, "a_halo_blur"}, + }; + static constexpr UniformBlockInfo uniforms[] = { + MLN_MTL_UNIFORM_BLOCK(10, true, true, SymbolDrawableUBO), + MLN_MTL_UNIFORM_BLOCK(11, true, false, SymbolDrawablePaintUBO), + MLN_MTL_UNIFORM_BLOCK(12, true, true, SymbolDrawableTilePropsUBO), + MLN_MTL_UNIFORM_BLOCK(13, true, false, SymbolDrawableInterpolateUBO), + MLN_MTL_UNIFORM_BLOCK(14, true, true, SymbolPermutationUBO), + MLN_MTL_UNIFORM_BLOCK(15, true, false, ExpressionInputsUBO), + }; + static constexpr TextureInfo textures[] = { + {0, "u_texture"}, + }; + + static constexpr auto source = R"( +struct VertexStage { + float4 pos_offset [[attribute(0)]]; + float4 data [[attribute(1)]]; + float4 pixeloffset [[attribute(2)]]; + float3 projected_pos [[attribute(3)]]; + float fade_opacity [[attribute(4)]]; + + float4 fill_color [[attribute(5)]]; + float4 halo_color [[attribute(6)]]; + float opacity [[attribute(7)]]; + float halo_width [[attribute(8)]]; + float halo_blur [[attribute(9)]]; +}; + +struct FragmentStage { + float4 position [[position, invariant]]; + float4 fill_color; + float4 halo_color; + float halo_width; + float halo_blur; + float opacity; + float2 data0; + float3 data1; +}; + +FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], + device const SymbolDrawableUBO& drawable [[buffer(10)]], + device const SymbolDrawablePaintUBO& paint [[buffer(11)]], + device const SymbolDrawableTilePropsUBO& props [[buffer(12)]], + device const SymbolDrawableInterpolateUBO& interp [[buffer(13)]], + device const SymbolPermutationUBO& permutation [[buffer(14)]], + device const ExpressionInputsUBO& expr [[buffer(15)]]) { + + const auto fill_color = colorFor(permutation.fill_color, paint.fill_color, vertx.fill_color, interp.fill_color_t, expr); + const auto halo_color = colorFor(permutation.halo_color, paint.halo_color, vertx.halo_color, interp.halo_color_t, expr); + const auto opacity = valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr); + const auto halo_width = valueFor(permutation.halo_width, paint.halo_width, vertx.halo_width, interp.halo_width_t, expr); + const auto halo_blur = valueFor(permutation.halo_blur, paint.halo_blur, vertx.halo_blur, interp.halo_blur_t, expr); + + const float2 a_pos = vertx.pos_offset.xy; + const float2 a_offset = vertx.pos_offset.zw; + + const float2 a_tex = vertx.data.xy; + const float2 a_size = vertx.data.zw; + + const float a_size_min = floor(a_size[0] * 0.5); + const float2 a_pxoffset = vertx.pixeloffset.xy; + + const float segment_angle = -vertx.projected_pos[2]; + + float size; + if (!props.is_size_zoom_constant && !props.is_size_feature_constant) { + size = mix(a_size_min, a_size[1], props.size_t) / 128.0; + } else if (props.is_size_zoom_constant && !props.is_size_feature_constant) { + size = a_size_min / 128.0; + } else { + size = props.size; + } + + const float4 projectedPoint = drawable.matrix * float4(a_pos, 0, 1); + const float camera_to_anchor_distance = projectedPoint.w; + // If the label is pitched with the map, layout is done in pitched space, + // which makes labels in the distance smaller relative to viewport space. + // We counteract part of that effect by multiplying by the perspective ratio. + // If the label isn't pitched with the map, we do layout in viewport space, + // which makes labels in the distance larger relative to the features around + // them. We counteract part of that effect by dividing by the perspective ratio. + const float distance_ratio = props.pitch_with_map ? + camera_to_anchor_distance / drawable.camera_to_center_distance : + drawable.camera_to_center_distance / camera_to_anchor_distance; + const float perspective_ratio = clamp( + 0.5 + 0.5 * distance_ratio, + 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles + 4.0); + + size *= perspective_ratio; + + const float fontScale = props.is_text ? size / 24.0 : size; + + float symbol_rotation = 0.0; + if (drawable.rotate_symbol) { + // Point labels with 'rotation-alignment: map' are horizontal with respect to tile units + // To figure out that angle in projected space, we draw a short horizontal line in tile + // space, project it, and measure its angle in projected space. + const float4 offsetProjectedPoint = drawable.matrix * float4(a_pos + float2(1, 0), 0, 1); + + const float2 a = projectedPoint.xy / projectedPoint.w; + const float2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w; + + symbol_rotation = atan2((b.y - a.y) / drawable.aspect_ratio, b.x - a.x); + } + + const float angle_sin = sin(segment_angle + symbol_rotation); + const float angle_cos = cos(segment_angle + symbol_rotation); + const float2x2 rotation_matrix = float2x2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); + + const float4 projected_pos = drawable.label_plane_matrix * float4(vertx.projected_pos.xy, 0.0, 1.0); + const float2 pos_rot = a_offset / 32.0 * fontScale + a_pxoffset; + const float2 pos0 = projected_pos.xy / projected_pos.w + rotation_matrix * pos_rot; + const float4 position = drawable.coord_matrix * float4(pos0, 0.0, 1.0); + const float gamma_scale = position.w; + + const float2 fade_opacity = unpack_opacity(vertx.fade_opacity); + const float fade_change = (fade_opacity[1] > 0.5) ? drawable.fade_change : -drawable.fade_change; + const float interpolated_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change)); + + return { + .position = position, + .fill_color = fill_color, + .halo_color = halo_color, + .halo_width = halo_width, + .halo_blur = halo_blur, + .opacity = opacity, + .data0 = a_tex / drawable.texsize, + .data1 = float3(gamma_scale, size, interpolated_fade_opacity), + }; +} + +half4 fragment fragmentMain(FragmentStage in [[stage_in]], + device const SymbolDrawableUBO& drawable [[buffer(10)]], + device const SymbolDrawableTilePropsUBO& props [[buffer(12)]], + device const SymbolPermutationUBO& permutation [[buffer(14)]], + texture2d image [[texture(0)]], + sampler image_sampler [[sampler(0)]]) { + if (permutation.overdrawInspector) { + return half4(1.0); + } + + const float EDGE_GAMMA = 0.105 / drawable.device_pixel_ratio; + + const float2 tex = in.data0.xy; + const float gamma_scale = in.data1.x; + const float size = in.data1.y; + const float fade_opacity = in.data1[2]; + + const float fontScale = props.is_text ? size / 24.0 : size; + + float4 color = in.fill_color; + float gamma = EDGE_GAMMA / (fontScale * drawable.gamma_scale); + float buff = (256.0 - 64.0) / 256.0; + if (props.is_halo) { + color = in.halo_color; + gamma = (in.halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * drawable.gamma_scale); + buff = (6.0 - in.halo_width / fontScale) / SDF_PX; + } + + const float dist = image.sample(image_sampler, tex).a; + const float gamma_scaled = gamma * gamma_scale; + const float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); + + return half4(color * (alpha * in.opacity * fade_opacity)); +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp new file mode 100644 index 000000000..b9d7bdfb7 --- /dev/null +++ b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp @@ -0,0 +1,212 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr auto name = "SymbolTextAndIconShader"; + static constexpr auto vertexMainFunction = "vertexMain"; + static constexpr auto fragmentMainFunction = "fragmentMain"; + + static constexpr AttributeInfo attributes[] = { + // always attributes + {0, gfx::AttributeDataType::Short4, 1, "a_pos_offset"}, + {1, gfx::AttributeDataType::UShort4, 1, "a_data"}, + {2, gfx::AttributeDataType::Float3, 1, "a_projected_pos"}, + {3, gfx::AttributeDataType::Float, 1, "a_fade_opacity"}, + + // sometimes uniforms + {4, gfx::AttributeDataType::Float4, 1, "a_fill_color"}, + {5, gfx::AttributeDataType::Float4, 1, "a_halo_color"}, + {6, gfx::AttributeDataType::Float, 1, "a_opacity"}, + {7, gfx::AttributeDataType::Float, 1, "a_halo_width"}, + {8, gfx::AttributeDataType::Float, 1, "a_halo_blur"}, + }; + static constexpr UniformBlockInfo uniforms[] = { + MLN_MTL_UNIFORM_BLOCK(9, true, true, SymbolDrawableUBO), + MLN_MTL_UNIFORM_BLOCK(10, true, false, SymbolDrawablePaintUBO), + MLN_MTL_UNIFORM_BLOCK(11, true, true, SymbolDrawableTilePropsUBO), + MLN_MTL_UNIFORM_BLOCK(12, true, false, SymbolDrawableInterpolateUBO), + MLN_MTL_UNIFORM_BLOCK(13, true, true, SymbolPermutationUBO), + MLN_MTL_UNIFORM_BLOCK(14, true, false, ExpressionInputsUBO), + }; + static constexpr TextureInfo textures[] = { + {0, "u_texture"}, + {1, "u_texture_icon"}, + }; + + static constexpr auto source = R"( +#define SDF 1.0 +#define ICON 0.0 + +struct VertexStage { + float4 pos_offset [[attribute(0)]]; + float4 data [[attribute(1)]]; + float3 projected_pos [[attribute(2)]]; + float fade_opacity [[attribute(3)]]; + + float4 fill_color [[attribute(4)]]; + float4 halo_color [[attribute(5)]]; + float opacity [[attribute(6)]]; + float halo_width [[attribute(7)]]; + float halo_blur [[attribute(8)]]; +}; + +struct FragmentStage { + float4 position [[position, invariant]]; + float4 fill_color; + float4 halo_color; + float halo_width; + float halo_blur; + float opacity; + float4 data0; + float4 data1; +}; + +FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], + device const SymbolDrawableUBO& drawable [[buffer(9)]], + device const SymbolDrawablePaintUBO& paint [[buffer(10)]], + device const SymbolDrawableTilePropsUBO& props [[buffer(11)]], + device const SymbolDrawableInterpolateUBO& interp [[buffer(12)]], + device const SymbolPermutationUBO& permutation [[buffer(13)]], + device const ExpressionInputsUBO& expr [[buffer(14)]]) { + + const auto fill_color = colorFor(permutation.fill_color, paint.fill_color, vertx.fill_color, interp.fill_color_t, expr); + const auto halo_color = colorFor(permutation.halo_color, paint.halo_color, vertx.halo_color, interp.halo_color_t, expr); + const auto opacity = valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr); + const auto halo_width = valueFor(permutation.halo_width, paint.halo_width, vertx.halo_width, interp.halo_width_t, expr); + const auto halo_blur = valueFor(permutation.halo_blur, paint.halo_blur, vertx.halo_blur, interp.halo_blur_t, expr); + + const float2 a_pos = vertx.pos_offset.xy; + const float2 a_offset = vertx.pos_offset.zw; + + const float2 a_tex = vertx.data.xy; + const float2 a_size = vertx.data.zw; + + const float a_size_min = floor(a_size[0] * 0.5); + const float is_sdf = a_size[0] - 2.0 * a_size_min; + + const float segment_angle = -vertx.projected_pos[2]; + + float size; + if (!props.is_size_zoom_constant && !props.is_size_feature_constant) { + size = mix(a_size_min, a_size[1], props.size_t) / 128.0; + } else if (props.is_size_zoom_constant && !props.is_size_feature_constant) { + size = a_size_min / 128.0; + } else { + size = props.size; + } + + const float4 projectedPoint = drawable.matrix * float4(a_pos, 0, 1); + const float camera_to_anchor_distance = projectedPoint.w; + // If the label is pitched with the map, layout is done in pitched space, + // which makes labels in the distance smaller relative to viewport space. + // We counteract part of that effect by multiplying by the perspective ratio. + // If the label isn't pitched with the map, we do layout in viewport space, + // which makes labels in the distance larger relative to the features around + // them. We counteract part of that effect by dividing by the perspective ratio. + const float distance_ratio = props.pitch_with_map ? + camera_to_anchor_distance / drawable.camera_to_center_distance : + drawable.camera_to_center_distance / camera_to_anchor_distance; + const float perspective_ratio = clamp( + 0.5 + 0.5 * distance_ratio, + 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles + 4.0); + + size *= perspective_ratio; + + const float fontScale = size / 24.0; + + float symbol_rotation = 0.0; + if (drawable.rotate_symbol) { + // Point labels with 'rotation-alignment: map' are horizontal with respect to tile units + // To figure out that angle in projected space, we draw a short horizontal line in tile + // space, project it, and measure its angle in projected space. + const float4 offsetProjectedPoint = drawable.matrix * float4(a_pos + float2(1, 0), 0, 1); + + const float2 a = projectedPoint.xy / projectedPoint.w; + const float2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w; + + symbol_rotation = atan2((b.y - a.y) / drawable.aspect_ratio, b.x - a.x); + } + + const float angle_sin = sin(segment_angle + symbol_rotation); + const float angle_cos = cos(segment_angle + symbol_rotation); + const float2x2 rotation_matrix = float2x2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); + + const float4 projected_pos = drawable.label_plane_matrix * float4(vertx.projected_pos.xy, 0.0, 1.0); + const float2 pos_rot = a_offset / 32.0 * fontScale + a_pxoffset; + const float2 pos0 = projected_pos.xy / projected_pos.w + rotation_matrix * pos_rot; + const float4 position = drawable.coord_matrix * float4(pos0, 0.0, 1.0); + const float gamma_scale = position.w; + + const float2 fade_opacity = unpack_opacity(vertx.fade_opacity); + const float fade_change = (fade_opacity[1] > 0.5) ? drawable.fade_change : -drawable.fade_change; + const float interpolated_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change)); + + return { + .position = position, + .fill_color = fill_color, + .halo_color = halo_color, + .halo_width = halo_width, + .halo_blur = halo_blur, + .opacity = opacity, + .data0 = float4(a_tex / drawable.texsize, a_tex / drawable.texsize_icon), + .data1 = float4(gamma_scale, size, interpolated_fade_opacity, is_sdf), + }; +} + +half4 fragment fragmentMain(FragmentStage in [[stage_in]], + device const SymbolDrawableUBO& drawable [[buffer(9)]], + device const SymbolDrawableTilePropsUBO& props [[buffer(11)]], + device const SymbolPermutationUBO& permutation [[buffer(13)]], + texture2d glyph_image [[texture(0)]], + texture2d icon_image [[texture(1)]], + sampler glyph_sampler [[sampler(0)]], + sampler icon_sampler [[sampler(1)]]) { + if (permutation.overdrawInspector) { + return half4(1.0); + } + + const float fade_opacity = in.data1[2]; + + if (in.data1.w == ICON) { + const float2 tex_icon = in.data0.zw; + const float alpha = opacity * fade_opacity; + return half4(icon_image.sample(icon_sampler, tex_icon) * alpha); + } + + const float2 tex = in.data0.xy; + const float EDGE_GAMMA = 0.105 / drawable.device_pixel_ratio; + + const float gamma_scale = in.data1.x; + const float size = in.data1.y; + + const float fontScale = size / 24.0; + + float4 color = in.fill_color; + float gamma = EDGE_GAMMA / (fontScale * drawable.gamma_scale); + float buff = (256.0 - 64.0) / 256.0; + if (props.is_halo) { + color = in.halo_color; + gamma = (in.halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * drawable.gamma_scale); + buff = (6.0 - in.halo_width / fontScale) / SDF_PX; + } + + const float dist = glyph_image.sample(glyph_sampler, tex).a; + const float gamma_scaled = gamma * gamma_scale; + const float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); + + return half4(color * (alpha * in.opacity * fade_opacity)); +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/shader_manifest.hpp b/include/mbgl/shaders/shader_manifest.hpp index 381ba6a00..561c34557 100644 --- a/include/mbgl/shaders/shader_manifest.hpp +++ b/include/mbgl/shaders/shader_manifest.hpp @@ -25,8 +25,7 @@ #include #include #include -#include -#include +#include #include #include #include diff --git a/include/mbgl/shaders/shader_source.hpp b/include/mbgl/shaders/shader_source.hpp index 4b4092246..ca6519a40 100644 --- a/include/mbgl/shaders/shader_source.hpp +++ b/include/mbgl/shaders/shader_source.hpp @@ -31,7 +31,6 @@ enum class BuiltIn { HillshadeShader, RasterShader, SymbolIconShader, - SymbolSDFTextShader, SymbolSDFIconShader, SymbolTextAndIconShader, Prelude, diff --git a/include/mbgl/shaders/symbol_layer_ubo.hpp b/include/mbgl/shaders/symbol_layer_ubo.hpp new file mode 100644 index 000000000..cb7ce699a --- /dev/null +++ b/include/mbgl/shaders/symbol_layer_ubo.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include + +namespace mbgl { +namespace shaders { + +/// Evaluated properties that depend on the tile +struct alignas(16) SymbolDrawableTilePropsUBO { + /* 0 */ /*bool*/ int is_text; + /* 4 */ /*bool*/ int is_halo; + /* 8 */ /*bool*/ int pitch_with_map; + /* 12 */ /*bool*/ int is_size_zoom_constant; + /* 16 */ /*bool*/ int is_size_feature_constant; + /* 20 */ float size_t; + /* 24 */ float size; + /* 28 */ float padding; + /* 32 */ +}; +static_assert(sizeof(SymbolDrawableTilePropsUBO) == 2 * 16); + +/// Attribute interpolations +struct alignas(16) SymbolDrawableInterpolateUBO { + /* 0 */ float fill_color_t; + /* 4 */ float halo_color_t; + /* 8 */ float opacity_t; + /* 12 */ float halo_width_t; + /* 16 */ float halo_blur_t; + /* 20 */ float pad1, pad2, pad3; + /* 32 */ +}; +static_assert(sizeof(SymbolDrawableInterpolateUBO) == 32); + +struct alignas(16) SymbolDrawableUBO { + /* 0 */ std::array matrix; + /* 64 */ std::array label_plane_matrix; + /* 128 */ std::array coord_matrix; + + /* 192 */ std::array texsize; + /* 200 */ std::array texsize_icon; + + /* 208 */ float gamma_scale; + /* 212 */ float device_pixel_ratio; + + /* 216 */ float camera_to_center_distance; + /* 220 */ float pitch; + /* 224 */ /*bool*/ int rotate_symbol; + /* 228 */ float aspect_ratio; + /* 232 */ std::array pad; + /* 240 */ +}; +static_assert(sizeof(SymbolDrawableUBO) == 15 * 16); + +/// Dynamic UBO +struct alignas(16) SymbolDynamicUBO { + /* 0 */ float fade_change; + /* 4 */ float pad1; + /* 8 */ std::array pad2; + /* 16 */ +}; +static_assert(sizeof(SymbolDynamicUBO) == 16); + +/// Evaluated properties that do not depend on the tile +struct alignas(16) SymbolDrawablePaintUBO { + /* 0 */ Color fill_color; + /* 16 */ Color halo_color; + /* 32 */ float opacity; + /* 36 */ float halo_width; + /* 40 */ float halo_blur; + /* 44 */ float padding; + /* 48 */ +}; +static_assert(sizeof(SymbolDrawablePaintUBO) == 3 * 16); + +struct alignas(16) SymbolPermutationUBO { + /* 0 */ Attribute fill_color; + /* 8 */ Attribute halo_color; + /* 16 */ Attribute opacity; + /* 24 */ Attribute halo_width; + /* 40 */ Attribute halo_blur; + /* 48 */ int32_t /*bool*/ overdrawInspector; + /* 52 */ float pad1, pad2, pad3; + /* 64 */ +}; +static_assert(sizeof(SymbolPermutationUBO) == 4 * 16); + +} // namespace shaders +} // namespace mbgl diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 508067ae9..0b57ed0de 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -2043,7 +2043,7 @@ CLLocationCoordinate2D randomWorldCoordinate(void) { } }); } - + NSAssert(self.styleNames.count == self.styleURLs.count, @"Style names and URLs don’t match."); } diff --git a/platform/ios/benchmark/locations.cpp b/platform/ios/benchmark/locations.cpp index a546bdd7f..9f53877e4 100644 --- a/platform/ios/benchmark/locations.cpp +++ b/platform/ios/benchmark/locations.cpp @@ -4,7 +4,6 @@ namespace mbgl { namespace bench { const std::vector locations = { - { "boston", -71.07766, 42.36513, 16, 0.0 }, { "paris", 2.3411, 48.8664, 11, 0.0 }, { "paris2", 2.3516, 48.8356, 13, 273.8 }, { "alps", 10.6107, 46.9599, 6, 0.0 }, diff --git a/shaders/manifest.json b/shaders/manifest.json index d94879173..6f17f74a8 100644 --- a/shaders/manifest.json +++ b/shaders/manifest.json @@ -153,16 +153,9 @@ "glsl_frag": "drawable.symbol_icon.fragment.glsl", "uses_ubos": true }, - { - "name": "SymbolSDFTextShader", - "header": "drawable_symbol_sdf_text", - "glsl_vert": "drawable.symbol_sdf.vertex.glsl", - "glsl_frag": "drawable.symbol_sdf.fragment.glsl", - "uses_ubos": true - }, { "name": "SymbolSDFIconShader", - "header": "drawable_symbol_sdf_icon", + "header": "drawable_symbol_sdf", "glsl_vert": "drawable.symbol_sdf.vertex.glsl", "glsl_frag": "drawable.symbol_sdf.fragment.glsl", "uses_ubos": true diff --git a/src/mbgl/gl/renderer_backend.cpp b/src/mbgl/gl/renderer_backend.cpp index ab4a43444..89fa4a022 100644 --- a/src/mbgl/gl/renderer_backend.cpp +++ b/src/mbgl/gl/renderer_backend.cpp @@ -115,7 +115,6 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::HillshadeShader, shaders::BuiltIn::RasterShader, shaders::BuiltIn::SymbolIconShader, - shaders::BuiltIn::SymbolSDFTextShader, shaders::BuiltIn::SymbolSDFIconShader, shaders::BuiltIn::SymbolTextAndIconShader>(shaders, programParameters); } diff --git a/src/mbgl/mtl/renderer_backend.cpp b/src/mbgl/mtl/renderer_backend.cpp index ffb444954..080fa1d24 100644 --- a/src/mbgl/mtl/renderer_backend.cpp +++ b/src/mbgl/mtl/renderer_backend.cpp @@ -22,7 +22,11 @@ #include #include #include +#include #include +#include +#include +#include #include #include @@ -126,17 +130,15 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::FillExtrusionPatternShader, shaders::BuiltIn::HeatmapShader, shaders::BuiltIn::HeatmapTextureShader, + // shaders::BuiltIn::HillshadeShader, shaders::BuiltIn::LineShader, shaders::BuiltIn::LineGradientShader, - shaders::BuiltIn::RasterShader/*, - shaders::BuiltIn::LineSDFShader, - shaders::BuiltIn::LinePatternShader, - shaders::BuiltIn::HillshadePrepareShader, - shaders::BuiltIn::HillshadeShader, + // shaders::BuiltIn::LineSDFShader, + // shaders::BuiltIn::LinePatternShader, + shaders::BuiltIn::RasterShader, shaders::BuiltIn::SymbolIconShader, - shaders::BuiltIn::SymbolSDFTextShader, shaders::BuiltIn::SymbolSDFIconShader, - shaders::BuiltIn::SymbolTextAndIconShader*/>(shaders, programParameters); + shaders::BuiltIn::SymbolTextAndIconShader>(shaders, programParameters); } } // namespace mtl diff --git a/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp b/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp index f11f61a90..deb0c8ab9 100644 --- a/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp @@ -22,7 +22,6 @@ using namespace shaders; using namespace style; namespace { - const StringIdentity idFillExtrusionDrawableUBOName = StringIndexer::get("FillExtrusionDrawableUBO"); const StringIdentity idFillExtrusionDrawablePropsUBOName = StringIndexer::get("FillExtrusionDrawablePropsUBO"); const StringIdentity idExpressionInputsUBOName = StringIndexer::get("ExpressionInputsUBO"); diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index 178648909..d0364ac7b 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -38,7 +38,6 @@ using namespace shaders; namespace { #if MLN_DRAWABLE_RENDERER - static const StringIdentity idPosAttribName = StringIndexer::get("a_pos"); static const StringIdentity idNormAttribName = StringIndexer::get("a_normal_ed"); diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 3c7124b54..eb37d3f9b 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #endif // MLN_DRAWABLE_RENDERER @@ -40,13 +41,13 @@ namespace mbgl { using namespace style; -namespace { +using namespace shaders; +namespace { #if MLN_DRAWABLE_RENDERER constexpr std::string_view SymbolIconShaderName = "SymbolIconShader"; -constexpr std::string_view SymbolSDFIconShaderName = "SymbolSDFTextShader"; -constexpr std::string_view SymbolSDFTextShaderName = "SymbolSDFIconShader"; +constexpr std::string_view SymbolSDFIconShaderName = "SymbolSDFIconShader"; constexpr std::string_view SymbolTextAndIconShaderName = "SymbolTextAndIconShader"; constexpr std::string_view CollisionBoxShaderName = "CollisionBoxShader"; constexpr std::string_view CollisionCircleShaderName = "CollisionCircleShader"; @@ -379,6 +380,9 @@ void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) #if MLN_DRAWABLE_RENDERER if (layerGroup && layerGroup->getLayerTweaker()) { +#if MLN_RENDER_BACKEND_METAL + layerGroup->getLayerTweaker()->setPropertiesAsUniforms(propertiesAsUniforms); +#endif // MLN_RENDER_BACKEND_METAL layerGroup->getLayerTweaker()->updateProperties(evaluatedProperties); } #endif // MLN_DRAWABLE_RENDERER @@ -1002,7 +1006,11 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, // Set up a layer group if (!layerGroup) { if (auto layerGroup_ = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID())) { - layerGroup_->setLayerTweaker(std::make_shared(getID(), evaluatedProperties)); + tweaker = std::make_shared(getID(), evaluatedProperties); +#if MLN_RENDER_BACKEND_METAL + tweaker->setPropertiesAsUniforms(propertiesAsUniforms); +#endif // MLN_RENDER_BACKEND_METAL + layerGroup_->setLayerTweaker(tweaker); setLayerGroup(std::move(layerGroup_), changes); } } @@ -1022,11 +1030,8 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, if (!symbolIconGroup) { symbolIconGroup = shaders.getShaderGroup(std::string(SymbolIconShaderName)); } - if (!symbolSDFIconGroup) { - symbolSDFIconGroup = shaders.getShaderGroup(std::string(SymbolSDFIconShaderName)); - } - if (!symbolSDFTextGroup) { - symbolSDFTextGroup = shaders.getShaderGroup(std::string(SymbolSDFTextShaderName)); + if (!symbolSDFGroup) { + symbolSDFGroup = shaders.getShaderGroup(std::string(SymbolSDFIconShaderName)); } if (!symbolTextAndIconGroup) { symbolTextAndIconGroup = shaders.getShaderGroup(std::string(SymbolTextAndIconShaderName)); @@ -1263,6 +1268,13 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, gfx::VertexAttributeArray attribs; const auto uniformProps = updateTileAttributes(buffer, isText, bucketPaintProperties, evaluated, attribs); +#if MLN_RENDER_BACKEND_METAL + propertiesAsUniforms = uniformProps; + if (tweaker) { + tweaker->setPropertiesAsUniforms(propertiesAsUniforms); + } +#endif // MLN_RENDER_BACKEND_METAL + const auto textHalo = evaluated.get().constantOr(Color::black()).a > 0.0f && evaluated.get().constantOr(1); const auto textFill = evaluated.get().constantOr(Color::black()).a > 0.0f; @@ -1382,21 +1394,21 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, } } else { if (textHalo) { - draw(symbolSDFTextGroup, /* isHalo = */ true, "halo"); + draw(symbolSDFGroup, /* isHalo = */ true, "halo"); } if (textFill) { - draw(symbolSDFTextGroup, /* isHalo = */ false, "fill"); + draw(symbolSDFGroup, /* isHalo = */ false, "fill"); } } } else { // icons if (sdfIcons) { if (iconHalo) { - draw(symbolSDFIconGroup, /* isHalo = */ true, "halo"); + draw(symbolSDFGroup, /* isHalo = */ true, "halo"); } if (iconFill) { - draw(symbolSDFIconGroup, /* isHalo = */ false, "fill"); + draw(symbolSDFGroup, /* isHalo = */ false, "fill"); } } else { draw(symbolIconGroup, /* isHalo = */ false, "icon"); diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp index 4c9a8420c..c391d266b 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.hpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp @@ -65,6 +65,11 @@ class SymbolTextAndIconProgram; class CollisionBoxProgram; class CollisionCircleProgram; +#if MLN_DRAWABLE_RENDERER +class SymbolLayerTweaker; +using SymbolLayerTweakerPtr = std::shared_ptr; +#endif // MLN_DRAWABLE_RENDERER + class RenderSymbolLayer final : public RenderLayer { public: struct Programs { @@ -146,15 +151,20 @@ private: #endif // MLN_LEGACY_RENDERER #if MLN_DRAWABLE_RENDERER + SymbolLayerTweakerPtr tweaker; + gfx::ShaderGroupPtr symbolIconGroup; - gfx::ShaderGroupPtr symbolSDFIconGroup; - gfx::ShaderGroupPtr symbolSDFTextGroup; + gfx::ShaderGroupPtr symbolSDFGroup; gfx::ShaderGroupPtr symbolTextAndIconGroup; gfx::ShaderGroupPtr collisionBoxGroup; gfx::ShaderGroupPtr collisionCircleGroup; std::shared_ptr collisionTileLayerGroup; #endif // MLN_DRAWABLE_RENDERER + +#if MLN_RENDER_BACKEND_METAL + std::vector propertiesAsUniforms; +#endif // MLN_RENDER_BACKEND_METAL }; } // namespace mbgl diff --git a/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp b/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp index 7fe815239..a7b7f16bf 100644 --- a/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp @@ -12,57 +12,22 @@ #include #include #include +#include #include #include #include +#if MLN_RENDER_BACKEND_METAL +#include +#endif // MLN_RENDER_BACKEND_METAL + namespace mbgl { using namespace style; +using namespace shaders; namespace { -struct alignas(16) SymbolDrawableUBO { - /* 0 */ std::array matrix; - /* 64 */ std::array label_plane_matrix; - /* 128 */ std::array coord_matrix; - - /* 192 */ std::array texsize; - /* 200 */ std::array texsize_icon; - - /* 208 */ float gamma_scale; - /* 212 */ float device_pixel_ratio; - - /* 216 */ float camera_to_center_distance; - /* 220 */ float pitch; - /* 224 */ /*bool*/ int rotate_symbol; - /* 228 */ float aspect_ratio; - /* 232 */ std::array pad; - /* 240 */ -}; -static_assert(sizeof(SymbolDrawableUBO) == 15 * 16); - -/// Dynamic UBO -struct alignas(16) SymbolDynamicUBO { - /* 0 */ float fade_change; - /* 4 */ float pad1; - /* 8 */ std::array pad2; - /* 16 */ -}; -static_assert(sizeof(SymbolDynamicUBO) == 16); - -/// Evaluated properties that do not depend on the tile -struct alignas(16) SymbolDrawablePaintUBO { - /* 0 */ Color fill_color; - /* 16 */ Color halo_color; - /* 32 */ float opacity; - /* 36 */ float halo_width; - /* 40 */ float halo_blur; - /* 44 */ float padding; - /* 48 */ -}; -static_assert(sizeof(SymbolDrawablePaintUBO) == 3 * 16); - Size getTexSize(const gfx::Drawable& drawable, const StringIdentity nameId) { if (const auto& shader = drawable.getShader()) { if (const auto index = shader->getSamplerLocation(nameId)) { @@ -78,8 +43,10 @@ std::array toArray(const Size& s) { return util::cast(std::array{s.width, s.height}); } -static const StringIdentity idTexUniformName = StringIndexer::get("u_texture"); -static const StringIdentity idTexIconUniformName = StringIndexer::get("u_texture_icon"); +const StringIdentity idTexUniformName = StringIndexer::get("u_texture"); +const StringIdentity idTexIconUniformName = StringIndexer::get("u_texture_icon"); +const StringIdentity idExpressionInputsUBOName = StringIndexer::get("ExpressionInputsUBO"); +const StringIdentity idSymbolPermutationUBOName = StringIndexer::get("SymbolPermutationUBO"); SymbolDrawablePaintUBO buildPaintUBO(bool isText, const SymbolPaintProperties::PossiblyEvaluated& evaluated) { return { @@ -120,6 +87,35 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, const auto debugGroup = parameters.encoder->createDebugGroup(label.c_str()); #endif + const auto zoom = parameters.state.getZoom(); + +#if MLN_RENDER_BACKEND_METAL + if (propertiesChanged) { + const SymbolPermutationUBO permutationUBO = { + /* .fill_color = */ {/*.source=*/getAttributeSource("a_fill_color"), /*.expression=*/{}}, + /* .halo_color = */ {/*.source=*/getAttributeSource("a_halo_color"), /*.expression=*/{}}, + /* .opacity = */ {/*.source=*/getAttributeSource("a_opacity"), /*.expression=*/{}}, + /* .halo_width = */ {/*.source=*/getAttributeSource("a_halo_width"), /*.expression=*/{}}, + /* .halo_blur = */ {/*.source=*/getAttributeSource("a_halo_blur"), /*.expression=*/{}}, + /* .overdrawInspector = */ overdrawInspector, + /* .pad = */ 0, + 0, + 0}; + + if (permutationUniformBuffer) { + permutationUniformBuffer->update(&permutationUBO, sizeof(permutationUBO)); + } else { + permutationUniformBuffer = context.createUniformBuffer(&permutationUBO, sizeof(permutationUBO)); + } + + propertiesChanged = false; + } + if (!expressionUniformBuffer) { + const auto expressionUBO = buildExpressionUBO(zoom, parameters.frameCount); + expressionUniformBuffer = context.createUniformBuffer(&expressionUBO, sizeof(expressionUBO)); + } +#endif + layerGroup.visitDrawables([&](gfx::Drawable& drawable) { if (!drawable.getTileID() || !drawable.getData()) { return; @@ -148,7 +144,7 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, tileID, renderTree, state, translate, anchor, nearClipped, inViewportPixelUnits); // from symbol_program, makeValues - const auto currentZoom = static_cast(state.getZoom()); + const auto currentZoom = static_cast(zoom); const float pixelsToTileUnits = tileID.pixelsToTileUnits(1.f, currentZoom); const bool pitchWithMap = symbolData.pitchAlignment == style::AlignmentType::Map; const bool rotateWithMap = symbolData.rotationAlignment == style::AlignmentType::Map; @@ -198,6 +194,11 @@ void SymbolLayerTweaker::execute(LayerGroupBase& layerGroup, uniforms.createOrUpdate(idSymbolDrawableUBOName, &drawableUBO, context); uniforms.createOrUpdate(idSymbolDynamicUBOName, &dynamicUBO, context); uniforms.addOrReplace(idSymbolDrawablePaintUBOName, isText ? textPaintBuffer : iconPaintBuffer); + +#if MLN_RENDER_BACKEND_METAL + uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer); + uniforms.addOrReplace(idSymbolPermutationUBOName, permutationUniformBuffer); +#endif // MLN_RENDER_BACKEND_METAL }); } diff --git a/src/mbgl/renderer/layers/symbol_layer_tweaker.hpp b/src/mbgl/renderer/layers/symbol_layer_tweaker.hpp index c650f2612..97098d99f 100644 --- a/src/mbgl/renderer/layers/symbol_layer_tweaker.hpp +++ b/src/mbgl/renderer/layers/symbol_layer_tweaker.hpp @@ -29,32 +29,11 @@ public: private: gfx::UniformBufferPtr textPaintBuffer; gfx::UniformBufferPtr iconPaintBuffer; -}; -/// Evaluated properties that depend on the tile -struct alignas(16) SymbolDrawableTilePropsUBO { - /* 0 */ /*bool*/ int is_text; - /* 4 */ /*bool*/ int is_halo; - /* 8 */ /*bool*/ int pitch_with_map; - /* 12 */ /*bool*/ int is_size_zoom_constant; - /* 16 */ /*bool*/ int is_size_feature_constant; - /* 20 */ float size_t; - /* 24 */ float size; - /* 28 */ float padding; - /* 32 */ +#if MLN_RENDER_BACKEND_METAL + gfx::UniformBufferPtr permutationUniformBuffer; + gfx::UniformBufferPtr expressionUniformBuffer; +#endif // MLN_RENDER_BACKEND_METAL }; -static_assert(sizeof(SymbolDrawableTilePropsUBO) == 2 * 16); - -/// Attribute interpolations -struct alignas(16) SymbolDrawableInterpolateUBO { - /* 0 */ float fill_color_t; - /* 4 */ float halo_color_t; - /* 8 */ float opacity_t; - /* 12 */ float halo_width_t; - /* 16 */ float halo_blur_t; - /* 20 */ float pad1, pad2, pad3; - /* 32 */ -}; -static_assert(sizeof(SymbolDrawableInterpolateUBO) == 32); } // namespace mbgl