Rebase fill-extrusion work to eliminate dependence on render target branch (#1618)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Alex Cristici <alex.cristici@gmail.com>
Co-authored-by: mwilsnd <mwilsnd@gmail.com>
Co-authored-by: Stefan Karschti <stefan.karschti@gmail.com>
Co-authored-by: Bart Louwers <bart@emeel.net>
Co-authored-by: Alex Cristici <alex@skoon.energy>
Co-authored-by: Bart Louwers <bart.louwers@gmail.com>
Co-authored-by: Wei Chen <alanchenboy@gmail.com>
Co-authored-by: Siarhei Fedartsou <siarhei.fedartsou@gmail.com>
Co-authored-by: tdcosta100 <tdcosta100@gmail.com>
This commit is contained in:
Tim Sylvester
2023-09-15 16:02:26 -07:00
committed by GitHub
parent 881d221788
commit 25c1f3c7a6
32 changed files with 979 additions and 139 deletions

View File

@@ -978,7 +978,6 @@ if(MLN_WITH_OPENGL)
message(STATUS "Configuring GL-Native with OpenGL renderer backend")
target_compile_definitions(
mbgl-core
PRIVATE
PRIVATE MLN_RENDER_BACKEND_OPENGL=1
PUBLIC
"MLN_LEGACY_RENDERER=$<BOOL:${MLN_LEGACY_RENDERER}>"
@@ -1115,6 +1114,7 @@ if(MLN_WITH_OPENGL)
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/background_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/circle_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/fill_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/fill_extrusion_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/line_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/raster_layer_ubo.hpp
@@ -1171,7 +1171,10 @@ if(MBGL_WITH_METAL)
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/background.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/background_pattern.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/circle.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/clipping_mask.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/common.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/fill_extrusion.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp
)
list(APPEND
SRC_FILES

View File

@@ -963,6 +963,7 @@ MLN_DRAWABLES_HEADERS = [
"include/mbgl/shaders/background_layer_ubo.hpp",
"include/mbgl/shaders/circle_layer_ubo.hpp",
"include/mbgl/shaders/fill_layer_ubo.hpp",
"include/mbgl/shaders/fill_extrusion_layer_ubo.hpp",
"include/mbgl/shaders/layer_ubo.hpp",
"include/mbgl/shaders/line_layer_ubo.hpp",
"include/mbgl/shaders/raster_layer_ubo.hpp",
@@ -1038,7 +1039,10 @@ MLN_DRAWABLES_MTL_HEADERS = [
"include/mbgl/shaders/mtl/background.hpp",
"include/mbgl/shaders/mtl/background_pattern.hpp",
"include/mbgl/shaders/mtl/circle.hpp",
"include/mbgl/shaders/mtl/clipping_mask.hpp",
"include/mbgl/shaders/mtl/common.hpp",
"include/mbgl/shaders/mtl/fill_extrusion.hpp",
"include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp",
"include/mbgl/shaders/mtl/line.hpp",
"include/mbgl/shaders/mtl/fill.hpp",
"include/mbgl/shaders/mtl/line_gradient.hpp",

View File

@@ -96,6 +96,15 @@ public:
/// @brief Copy assignment operator
UniformBlockArray& operator=(const UniformBlockArray&);
/// Do something with each block
void visit(const std::function<void(const StringIdentity, const UniformBlock&)>& f) {
std::for_each(uniformBlockMap.begin(), uniformBlockMap.end(), [&](const auto& kv) {
if (kv.second) {
f(kv.first, *kv.second);
}
});
}
protected:
const std::unique_ptr<UniformBlock>& add(const StringIdentity id, std::unique_ptr<UniformBlock>&& uniformBlock) {
const auto result = uniformBlockMap.insert(std::make_pair(id, std::unique_ptr<UniformBlock>()));

View File

@@ -11,6 +11,8 @@
#include <mbgl/mtl/mtl_fwd.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/mtl/mtl_fwd.hpp>
#include <memory>
#include <unordered_map>
@@ -20,6 +22,7 @@ class ProgramParameters;
namespace mtl {
class RenderPass;
class RendererBackend;
class ShaderProgram;
@@ -32,6 +35,8 @@ public:
Context(const Context&) = delete;
Context& operator=(const Context& other) = delete;
const RendererBackend& getBackend() const { return backend; }
std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override;
gfx::RenderingStats& renderingStats() { return stats; }
@@ -113,6 +118,10 @@ public:
void clearStencilBuffer(int32_t) override;
MTLDepthStencilStatePtr makeDepthStencilState(const gfx::DepthMode& depthMode,
const gfx::StencilMode& stencilMode,
const mtl::RenderPass& renderPass) const;
virtual bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&, const void* data, std::size_t size);
private:

View File

@@ -22,6 +22,7 @@ class Buffer;
class CommandBuffer;
class CommandQueue;
class Device;
class DepthStencilState;
class Function;
class RenderCommandEncoder;
class RenderPipelineState;
@@ -45,6 +46,7 @@ using MTLBufferPtr = NS::SharedPtr<MTL::Buffer>;
using MTLCommandBufferPtr = NS::SharedPtr<MTL::CommandBuffer>;
using MTLCommandQueuePtr = NS::SharedPtr<MTL::CommandQueue>;
using MTLDevicePtr = NS::SharedPtr<MTL::Device>;
using MTLDepthStencilStatePtr = NS::SharedPtr<MTL::DepthStencilState>;
using MTLFunctionPtr = NS::SharedPtr<MTL::Function>;
using MTLRenderCommandEncoderPtr = NS::SharedPtr<MTL::RenderCommandEncoder>;
using MTLRenderPassDescriptorPtr = NS::SharedPtr<MTL::RenderPassDescriptor>;

View File

@@ -13,6 +13,7 @@ class UniformBuffer;
using UniformBufferPtr = std::shared_ptr<UniformBuffer>;
} // namespace gfx
namespace shaders {
enum class AttributeSource : int32_t;
struct ExpressionInputsUBO;
} // namespace shaders
namespace style {
@@ -52,6 +53,7 @@ public:
/// @brief Check whether a property name exists within the previously set collection.
/// @details The string value provided is expected to have the "a_" prefix, as defined in the shader classes.
bool hasPropertyAsUniform(std::string_view) const;
shaders::AttributeSource getAttributeSource(const std::string_view& attribName) const;
#endif // MLN_RENDER_BACKEND_METAL
void enableOverdrawInspector(bool);

View File

@@ -0,0 +1,71 @@
#pragma once
#include <mbgl/shaders/layer_ubo.hpp>
#include <array>
namespace mbgl {
namespace shaders {
/// Evaluated properties that depend on the tile
struct alignas(16) FillExtrusionDrawableTilePropsUBO {
/* 0 */ std::array<float, 4> pattern_from;
/* 16 */ std::array<float, 4> pattern_to;
/* 32 */
};
static_assert(sizeof(FillExtrusionDrawableTilePropsUBO) == 2 * 16);
/// Attribute interpolations
struct alignas(16) FillExtrusionInterpolateUBO {
/* 0 */ float base_t;
/* 4 */ float height_t;
/* 8 */ float color_t;
/* 12 */ float pattern_from_t;
/* 16 */ float pattern_to_t;
/* 20 */ float pad1, pad2, pad3;
/* 32 */
};
static_assert(sizeof(FillExtrusionInterpolateUBO) == 2 * 16);
struct alignas(16) FillExtrusionDrawableUBO {
/* 0 */ std::array<float, 4 * 4> matrix;
/* 64 */ std::array<float, 4> scale;
/* 80 */ std::array<float, 2> texsize;
/* 88 */ std::array<float, 2> pixel_coord_upper;
/* 96 */ std::array<float, 2> pixel_coord_lower;
/* 104 */ float height_factor;
/* 108 */ float pad;
/* 112 */
};
static_assert(sizeof(FillExtrusionDrawableUBO) == 7 * 16);
/// Evaluated properties that do not depend on the tile
struct alignas(16) FillExtrusionDrawablePropsUBO {
/* 0 */ Color color;
/* 16 */ std::array<float, 3> light_color;
/* 28 */ float pad1;
/* 32 */ std::array<float, 3> light_position;
/* 44 */ float base;
/* 48 */ float height;
/* 52 */ float light_intensity;
/* 56 */ float vertical_gradient;
/* 60 */ float opacity;
/* 64 */ float fade;
/* 68 */ float pad2, pad3, pad4;
/* 80 */
};
static_assert(sizeof(FillExtrusionDrawablePropsUBO) == 5 * 16);
struct alignas(16) FillExtrusionPermutationUBO {
/* 0 */ Attribute color;
/* 8 */ Attribute base;
/* 16 */ Attribute height;
/* 24 */ bool overdrawInspector;
/* 25 */ uint8_t pad1, pad2, pad3;
/* 28 */ float pad4;
/* 32 */
};
static_assert(sizeof(FillExtrusionPermutationUBO) == 2 * 16);
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,63 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/background_layer_ubo.hpp>
#include <mbgl/shaders/mtl/common.hpp>
#include <mbgl/shaders/mtl/shader_program.hpp>
namespace mbgl {
namespace shaders {
struct alignas(16) ClipUBO {
/* 0 */ std::array<float, 4 * 4> matrix;
/* 64 */
};
static_assert(sizeof(ClipUBO) == 4 * 16);
template <>
struct ShaderSource<BuiltIn::ClippingMaskProgram, gfx::Backend::Type::Metal> {
static constexpr auto name = "ClippingMaskProgram";
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static constexpr AttributeInfo attributes[] = {
{0, gfx::AttributeDataType::Float3, 1, "a_pos"},
};
static constexpr UniformBlockInfo uniforms[] = {
MLN_MTL_UNIFORM_BLOCK(1, true, false, ClipUBO),
};
static constexpr TextureInfo textures[] = {};
static constexpr auto source = R"(
#include <metal_stdlib>
using namespace metal;
struct alignas(16) ClipUBO {
/* 0 */ float4x4 matrix;
/* 64 */
};
static_assert(sizeof(ClipUBO) == 4 * 16, "unexpected padding");
struct VertexStage {
short2 position [[attribute(0)]];
};
struct FragmentStage {
float4 position [[position, invariant]];
};
FragmentStage vertex vertexMain(VertexStage in [[stage_in]],
device const ClipUBO& clipUBO [[buffer(1)]]) {
return {
.position = clipUBO.matrix * float4(float2(in.position.xy), 0, 1)
};
}
half4 fragment fragmentMain(FragmentStage in [[stage_in]]) {
return half4(1.0);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -10,10 +10,21 @@ constexpr auto prelude = R"(
#include <metal_stdlib>
using namespace metal;
// The maximum allowed miter limit is 2.0 at the moment. the extrude normal is stored
// in a byte (-128..127). We scale regular normals up to length 63, but there are also
// "special" normals that have a bigger length (of up to 126 in this case).
#define LINE_NORMAL_SCALE (1.0 / (127 / 2))
// The attribute conveying progress along a line is scaled to [0, 2^15).
#define MAX_LINE_DISTANCE 32767.0
#define SDF_PX 8.0
// OpenGL `mod` is `x-y*floor(x/y)` where `floor` rounds down.
// Metal `fmod` is `x-y*trunc(x/y)` where `trunc` rounds toward zero.
// This function provides GL-compatible modulus for porting GLSL shaders.
template <typename T1, typename T2>
inline auto mod(T1 x, T2 y) -> decltype(x - y * floor(x/y)) {
return x - y * metal::floor(x/y);
}
inline auto glMod(T1 x, T2 y) { return x - y * metal::floor(x/y); }
enum class AttributeSource : int32_t {
Constant,
@@ -67,9 +78,9 @@ float4 unpack_mix_color(const float4 packedColors, const float t) {
}
float valueFor(device const Attribute& attrib,
device const float& constValue,
const float constValue,
thread const float2& vertexValue,
device const float& t,
const float t,
device const ExpressionInputsUBO&) {
switch (attrib.source) {
case AttributeSource::PerVertex: return unpack_mix_float(vertexValue, t);
@@ -93,8 +104,8 @@ float4 colorFor(device const Attribute& attrib,
// interpolated packed colors
float4 colorFor(device const Attribute& attrib,
device const float4& constValue,
thread const float4& vertexValue,
device const float& t,
const float4 vertexValue,
const float t,
device const ExpressionInputsUBO&) {
switch (attrib.source) {
case AttributeSource::PerVertex: return unpack_mix_color(vertexValue, t);
@@ -104,14 +115,6 @@ float4 colorFor(device const Attribute& attrib,
}
}
// The maximum allowed miter limit is 2.0 at the moment. the extrude normal is stored
// in a byte (-128..127). We scale regular normals up to length 63, but there are also
// "special" normals that have a bigger length (of up to 126 in this case).
constant float LINE_NORMAL_SCALE = 1.0 / (127 / 2);
// The attribute conveying progress along a line is scaled to [0, 2^15).
constant float MAX_LINE_DISTANCE = 32767.0;
struct alignas(16) LineUBO {
float4x4 matrix;
float2 units_to_pixels;
@@ -196,8 +199,7 @@ float4 patternFor(device const Attribute& attrib,
// unpack pattern position
inline float2 get_pattern_pos(const float2 pixel_coord_upper, const float2 pixel_coord_lower,
const float2 pattern_size, const float tile_units_to_pixels, const float2 pos) {
float2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size);
const float2 offset = glMod(glMod(glMod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size);
return (tile_units_to_pixels * pos + offset) / pattern_size;
}

View File

@@ -18,6 +18,11 @@ struct ShaderSource<BuiltIn::FillShader, gfx::Backend::Type::Metal> {
{0, gfx::AttributeDataType::Short2, 1, "a_pos"},
{1, gfx::AttributeDataType::Float2, 1, "a_color"},
{2, gfx::AttributeDataType::Float2, 1, "a_opacity"},
// This shader doesn't use it, but we need this so that the layer can assign the same
// attributes to all the drawables. If/When `VertexAttributeArray::resolve` allows it,
// this can be removed.
{8, gfx::AttributeDataType::Float2, 1, "a_outline_color"},
};
static constexpr UniformBlockInfo uniforms[] = {
MLN_MTL_UNIFORM_BLOCK(3, true, false, FillDrawableUBO),
@@ -103,6 +108,9 @@ struct ShaderSource<BuiltIn::FillOutlineShader, gfx::Backend::Type::Metal> {
{0, gfx::AttributeDataType::Short2, 1, "a_pos"},
{1, gfx::AttributeDataType::Float2, 1, "a_outline_color"},
{2, gfx::AttributeDataType::Float2, 1, "a_opacity"},
// See `a_outline_color` in FillShader
{8, gfx::AttributeDataType::Float2, 1, "a_color"},
};
static constexpr UniformBlockInfo uniforms[] = {
MLN_MTL_UNIFORM_BLOCK(3, true, false, FillOutlineDrawableUBO),
@@ -308,18 +316,18 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
return half4(1.0);
}
float2 pattern_tl_a = in.pattern_from.xy;
float2 pattern_br_a = in.pattern_from.zw;
float2 pattern_tl_b = in.pattern_to.xy;
float2 pattern_br_b = in.pattern_to.zw;
const float2 pattern_tl_a = in.pattern_from.xy;
const float2 pattern_br_a = in.pattern_from.zw;
const float2 pattern_tl_b = in.pattern_to.xy;
const float2 pattern_br_b = in.pattern_to.zw;
float2 imagecoord = mod(in.v_pos_a, 1.0);
float2 pos = mix(pattern_tl_a / drawable.texsize, pattern_br_a / drawable.texsize, imagecoord);
float4 color1 = image0.sample(image0_sampler, pos);
float2 imagecoord_b = mod(in.v_pos_b, 1.0);
float2 pos2 = mix(pattern_tl_b / drawable.texsize, pattern_br_b / drawable.texsize, imagecoord_b);
float4 color2 = image0.sample(image0_sampler, pos2);
const float2 imagecoord = glMod(in.v_pos_a, 1.0);
const float2 pos = mix(pattern_tl_a / drawable.texsize, pattern_br_a / drawable.texsize, imagecoord);
const float4 color1 = image0.sample(image0_sampler, pos);
const float2 imagecoord_b = glMod(in.v_pos_b, 1.0);
const float2 pos2 = mix(pattern_tl_b / drawable.texsize, pattern_br_b / drawable.texsize, imagecoord_b);
const float4 color2 = image0.sample(image0_sampler, pos2);
return half4(mix(color1, color2, props.fade) * in.opacity);
}
@@ -450,18 +458,18 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
return half4(1.0);
}
float2 pattern_tl_a = in.pattern_from.xy;
float2 pattern_br_a = in.pattern_from.zw;
float2 pattern_tl_b = in.pattern_to.xy;
float2 pattern_br_b = in.pattern_to.zw;
const float2 pattern_tl_a = in.pattern_from.xy;
const float2 pattern_br_a = in.pattern_from.zw;
const float2 pattern_tl_b = in.pattern_to.xy;
const float2 pattern_br_b = in.pattern_to.zw;
float2 imagecoord = mod(in.v_pos_a, 1.0);
float2 pos = mix(pattern_tl_a / drawable.texsize, pattern_br_a / drawable.texsize, imagecoord);
float4 color1 = image0.sample(image0_sampler, pos);
float2 imagecoord_b = mod(in.v_pos_b, 1.0);
float2 pos2 = mix(pattern_tl_b / drawable.texsize, pattern_br_b / drawable.texsize, imagecoord_b);
float4 color2 = image0.sample(image0_sampler, pos2);
const float2 imagecoord = glMod(in.v_pos_a, 1.0);
const float2 pos = mix(pattern_tl_a / drawable.texsize, pattern_br_a / drawable.texsize, imagecoord);
const float4 color1 = image0.sample(image0_sampler, pos);
const float2 imagecoord_b = glMod(in.v_pos_b, 1.0);
const float2 pos2 = mix(pattern_tl_b / drawable.texsize, pattern_br_b / drawable.texsize, imagecoord_b);
const float4 color2 = image0.sample(image0_sampler, pos2);
// TODO: Should triangate the lines into triangles to support thick line and edge antialiased.
//float dist = length(in.v_pos - in.position.xy);

View File

@@ -0,0 +1,173 @@
#pragma once
#include <mbgl/shaders/fill_extrusion_layer_ubo.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/mtl/common.hpp>
#include <mbgl/shaders/mtl/shader_program.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal> {
static constexpr auto name = "FillExtrusionShader";
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static constexpr AttributeInfo attributes[] = {
{0, gfx::AttributeDataType::Short2, 1, "a_pos"},
{1, gfx::AttributeDataType::Short4, 1, "a_normal_ed"},
{2, gfx::AttributeDataType::Float4, 1, "a_color"},
{3, gfx::AttributeDataType::Float, 1, "a_base"},
{4, gfx::AttributeDataType::Float, 1, "a_height"},
};
static constexpr UniformBlockInfo uniforms[] = {
MLN_MTL_UNIFORM_BLOCK(5, true, false, FillExtrusionDrawableUBO),
MLN_MTL_UNIFORM_BLOCK(6, true, false, FillExtrusionDrawablePropsUBO),
MLN_MTL_UNIFORM_BLOCK(7, true, false, FillExtrusionInterpolateUBO),
MLN_MTL_UNIFORM_BLOCK(8, true, false, FillExtrusionPermutationUBO),
MLN_MTL_UNIFORM_BLOCK(9, true, false, ExpressionInputsUBO),
};
static constexpr TextureInfo textures[] = {};
static constexpr auto source = R"(
struct alignas(16) FillExtrusionInterpolateUBO {
/* 0 */ float base_t;
/* 4 */ float height_t;
/* 8 */ float color_t;
/* 12 */ float pattern_from_t;
/* 16 */ float pattern_to_t;
/* 20 */ float pad1, pad2, pad3;
/* 32 */
};
static_assert(sizeof(FillExtrusionInterpolateUBO) == 2 * 16, "unexpected padding");
struct alignas(16) FillExtrusionDrawableUBO {
/* 0 */ float4x4 matrix;
/* 64 */ float4 scale;
/* 80 */ float2 texsize;
/* 88 */ float2 pixel_coord_upper;
/* 96 */ float2 pixel_coord_lower;
/* 104 */ float height_factor;
/* 108 */ float pad;
/* 112 */
};
static_assert(sizeof(FillExtrusionDrawableUBO) == 7 * 16, "unexpected padding");
struct alignas(16) FillExtrusionDrawablePropsUBO {
/* 0 */ float4 color;
/* 16 */ float4 light_color_pad;
/* 32 */ float4 light_position_base;
/* 48 */ float height;
/* 52 */ float light_intensity;
/* 56 */ float vertical_gradient;
/* 60 */ float opacity;
/* 64 */ float fade;
/* 68 */ float pad2, pad3, pad4;
/* 80 */
};
static_assert(sizeof(FillExtrusionDrawablePropsUBO) == 5 * 16, "unexpected padding");
struct alignas(16) FillExtrusionPermutationUBO {
/* 0 */ Attribute color;
/* 8 */ Attribute base;
/* 16 */ Attribute height;
/* 24 */ bool overdrawInspector;
/* 25 */ uint8_t pad1, pad2, pad3;
/* 28 */ float pad4;
/* 32 */
};
static_assert(sizeof(FillExtrusionPermutationUBO) == 2 * 16, "unexpected padding");
struct VertexStage {
short2 pos [[attribute(0)]];
short4 normal_ed [[attribute(1)]];
float4 color [[attribute(2)]];
float base [[attribute(3)]];
float height [[attribute(4)]];
};
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 FillExtrusionDrawableUBO& fill [[buffer(5)]],
device const FillExtrusionDrawablePropsUBO& props [[buffer(6)]],
device const FillExtrusionInterpolateUBO& interp [[buffer(7)]],
device const FillExtrusionPermutationUBO& permutation [[buffer(8)]],
device const ExpressionInputsUBO& expr [[buffer(9)]]) {
const float u_base = props.light_position_base.w;
const auto base = max(valueFor(permutation.base, u_base, vertx.base, interp.base_t, expr), 0.0);
const auto height = max(valueFor(permutation.height, props.height, vertx.height, interp.height_t, expr), 0.0);
const float3 normal = float3(vertx.normal_ed.xyz);
const float t = glMod(normal.x, 2.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 = fill.matrix * float4(float2(vertx.pos), z, 1);
if (permutation.overdrawInspector) {
return {
.position = position,
.color = half4(1.0),
};
}
auto color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr);
// 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 / 16384.0, 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*/ };
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,26 @@
#pragma once
#include <mbgl/shaders/fill_extrusion_layer_ubo.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/mtl/common.hpp>
#include <mbgl/shaders/mtl/shader_program.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Metal> {
static constexpr auto name = "FillExtrusionPatternShader";
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static constexpr AttributeInfo attributes[] = {};
static constexpr UniformBlockInfo uniforms[] = {};
static constexpr TextureInfo textures[] = {};
static constexpr auto source = R"(
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -18,7 +18,9 @@ public:
StringIndexer(StringIndexer&&) = delete;
void operator=(StringIndexer const&) = delete;
static StringIdentity get(const std::string& string);
static StringIdentity get(const char*);
static StringIdentity get(std::string_view);
static StringIdentity get(const std::string&);
static const std::string& get(const StringIdentity id);
@@ -27,7 +29,7 @@ public:
static size_t size();
protected:
StringIndexer() = default;
StringIndexer();
~StringIndexer() = default;
using MapType = std::unordered_map<std::string, StringIdentity>;

View File

@@ -25,7 +25,7 @@ public:
SwapBehaviour swapBehavior = SwapBehaviour::NoFlush,
const gfx::ContextMode contextMode = gfx::ContextMode::Unique) {
#if MLN_RENDER_BACKEND_METAL
return nullptr;
throw std::runtime_error("Not yet implemented");
#else // MLN_RENDER_BACKEND_OPENGL
return Backend::Create<HeadlessBackend, Size, SwapBehaviour, gfx::ContextMode>(size, swapBehavior, contextMode);
#endif

View File

@@ -76,6 +76,11 @@ public:
void swap() override {
[commandBuffer presentDrawable:currentDrawable];
[commandBuffer commit];
// Un-comment for synchronous, which can help troubleshoot rendering problems,
// particularly those related to resource tracking and multiple queued buffers.
//[commandBuffer waitUntilCompleted];
commandBuffer = nil;
commandBufferPtr.reset();
}
@@ -158,7 +163,6 @@ void MLNMapViewMetalImpl::createView() {
resource.mtlView.opaque = mapView.opaque;
resource.mtlView.layer.opaque = mapView.opaque;
resource.mtlView.enableSetNeedsDisplay = YES;
if (@available(iOS 13.0, *)) {
CAMetalLayer* metalLayer = MLN_OBJC_DYNAMIC_CAST(resource.mtlView.layer, CAMetalLayer);
metalLayer.presentsWithTransaction = presentsWithTransaction;

View File

@@ -89,6 +89,15 @@ void VertexAttributeArray::resolve(const VertexAttributeArray& overrides, Resolv
for (auto& kv : attrs) {
delegate(kv.first, *kv.second, overrides.get(kv.first));
}
// For OpenGL, the shader attributes are established with reflection, and we have extra
// entries when we share attributes between, e.g., fill and fill-outline drawables.
#if !defined(NDEBUG) && MLN_RENDERER_BACKEND_METAL
// Every override should match a defined attribute.
for (const auto& kv : overrides.attrs) {
const auto hit = attrs.find(kv.first);
assert(hit != attrs.end());
}
#endif
}
const UniqueVertexAttribute& VertexAttributeArray::add(const StringIdentity id,

View File

@@ -8,6 +8,7 @@
#include <mbgl/mtl/renderable_resource.hpp>
#include <mbgl/mtl/texture2d.hpp>
#include <mbgl/mtl/tile_layer_group.hpp>
#include <mbgl/mtl/render_pass.hpp>
#include <mbgl/mtl/uniform_buffer.hpp>
#include <mbgl/mtl/upload_pass.hpp>
#include <mbgl/programs/program_parameters.hpp>
@@ -214,7 +215,152 @@ void Context::visualizeStencilBuffer() {}
void Context::visualizeDepthBuffer(float depthRangeSize) {}
#endif // !defined(NDEBUG)
void Context::clearStencilBuffer(int32_t) {}
void Context::clearStencilBuffer(int32_t) {
// See `PaintParameters::clearStencil`
assert(false);
}
namespace {
MTL::CompareFunction mapFunc(const gfx::DepthFunctionType func) {
switch (func) {
default:
case gfx::DepthFunctionType::Never:
return MTL::CompareFunction::CompareFunctionNever;
case gfx::DepthFunctionType::Less:
return MTL::CompareFunction::CompareFunctionLess;
case gfx::DepthFunctionType::Equal:
return MTL::CompareFunction::CompareFunctionEqual;
case gfx::DepthFunctionType::LessEqual:
return MTL::CompareFunction::CompareFunctionLessEqual;
case gfx::DepthFunctionType::Greater:
return MTL::CompareFunction::CompareFunctionGreater;
case gfx::DepthFunctionType::NotEqual:
return MTL::CompareFunction::CompareFunctionNotEqual;
case gfx::DepthFunctionType::GreaterEqual:
return MTL::CompareFunction::CompareFunctionGreaterEqual;
case gfx::DepthFunctionType::Always:
return MTL::CompareFunction::CompareFunctionAlways;
};
}
MTL::CompareFunction mapFunc(const gfx::StencilFunctionType func) {
switch (func) {
default:
case gfx::StencilFunctionType::Never:
return MTL::CompareFunction::CompareFunctionNever;
case gfx::StencilFunctionType::Less:
return MTL::CompareFunction::CompareFunctionLess;
case gfx::StencilFunctionType::Equal:
return MTL::CompareFunction::CompareFunctionEqual;
case gfx::StencilFunctionType::LessEqual:
return MTL::CompareFunction::CompareFunctionLessEqual;
case gfx::StencilFunctionType::Greater:
return MTL::CompareFunction::CompareFunctionGreater;
case gfx::StencilFunctionType::NotEqual:
return MTL::CompareFunction::CompareFunctionNotEqual;
case gfx::StencilFunctionType::GreaterEqual:
return MTL::CompareFunction::CompareFunctionGreaterEqual;
case gfx::StencilFunctionType::Always:
return MTL::CompareFunction::CompareFunctionAlways;
};
}
MTL::StencilOperation mapOperation(const gfx::StencilOpType op) {
switch (op) {
case gfx::StencilOpType::Zero:
return MTL::StencilOperation::StencilOperationZero;
default:
case gfx::StencilOpType::Keep:
return MTL::StencilOperation::StencilOperationKeep;
case gfx::StencilOpType::Replace:
return MTL::StencilOperation::StencilOperationReplace;
case gfx::StencilOpType::Increment:
return MTL::StencilOperation::StencilOperationIncrementClamp;
case gfx::StencilOpType::Decrement:
return MTL::StencilOperation::StencilOperationDecrementClamp;
case gfx::StencilOpType::Invert:
return MTL::StencilOperation::StencilOperationInvert;
case gfx::StencilOpType::IncrementWrap:
return MTL::StencilOperation::StencilOperationIncrementWrap;
case gfx::StencilOpType::DecrementWrap:
return MTL::StencilOperation::StencilOperationDecrementWrap;
}
}
void applyDepthMode(const gfx::DepthMode& depthMode, MTL::DepthStencilDescriptor* desc) {
desc->setDepthCompareFunction(mapFunc(depthMode.func));
desc->setDepthWriteEnabled(depthMode.mask == gfx::DepthMaskType::ReadWrite);
// depthMode.range ?
}
struct StencilModeVisitor {
MTL::StencilDescriptor* desc;
template <gfx::StencilFunctionType F>
void operator()(const gfx::StencilMode::SimpleTest<F>& mode) {
apply(mode.func, std::numeric_limits<uint32_t>::max());
}
template <gfx::StencilFunctionType F>
void operator()(const gfx::StencilMode::MaskedTest<F>& mode) {
apply(mode.func, mode.mask);
}
void apply(gfx::StencilFunctionType func, uint32_t mask) {
desc->setStencilCompareFunction(mapFunc(func));
desc->setReadMask(mask);
desc->setWriteMask(mask);
}
};
// helper type for the visitor #4
void applyStencilMode(const gfx::StencilMode& stencilMode, MTL::StencilDescriptor* desc) {
mapbox::util::apply_visitor(StencilModeVisitor{desc}, stencilMode.test);
desc->setStencilFailureOperation(mapOperation(stencilMode.fail));
desc->setDepthFailureOperation(mapOperation(stencilMode.depthFail));
desc->setDepthStencilPassOperation(mapOperation(stencilMode.pass));
}
} // namespace
MTLDepthStencilStatePtr Context::makeDepthStencilState(const gfx::DepthMode& depthMode,
const gfx::StencilMode& stencilMode,
const mtl::RenderPass& renderPass) const {
auto depthStencilDescriptor = NS::TransferPtr(MTL::DepthStencilDescriptor::alloc()->init());
if (!depthStencilDescriptor) {
return {};
}
auto& device = backend.getDevice();
const auto& renderPassDescriptor = renderPass.getDescriptor();
const auto& renderable = renderPassDescriptor.renderable;
const auto& renderableResource = renderable.getResource<RenderableResource>();
if (const auto& rpd = renderableResource.getRenderPassDescriptor()) {
// Setting depth/stencil properties when the corresponding target textures aren't set causes, e.g.:
// `Draw Errors Validation MTLDepthStencilDescriptor sets depth test but MTLRenderPassDescriptor has a nil
// depthAttachment texture`
if (auto* depthTarget = rpd->depthAttachment()) {
if (auto* tex = depthTarget->texture()) {
applyDepthMode(depthMode, depthStencilDescriptor.get());
}
}
if (auto* stencilTarget = rpd->stencilAttachment()) {
if (auto* tex = stencilTarget->texture()) {
auto stencilDescriptor = NS::TransferPtr(MTL::StencilDescriptor::alloc()->init());
if (!stencilDescriptor) {
return {};
}
applyStencilMode(stencilMode, stencilDescriptor.get());
depthStencilDescriptor->setFrontFaceStencil(stencilDescriptor.get());
depthStencilDescriptor->setBackFaceStencil(stencilDescriptor.get());
}
}
}
return NS::TransferPtr(device->newDepthStencilState(depthStencilDescriptor.get()));
}
} // namespace mtl
} // namespace mbgl

View File

@@ -1,12 +1,14 @@
#include <mbgl/mtl/drawable.hpp>
#include <mbgl/gfx/color_mode.hpp>
#include <mbgl/gfx/depth_mode.hpp>
#include <mbgl/mtl/command_encoder.hpp>
#include <mbgl/mtl/context.hpp>
#include <mbgl/mtl/drawable_impl.hpp>
#include <mbgl/mtl/index_buffer_resource.hpp>
#include <mbgl/mtl/render_pass.hpp>
#include <mbgl/mtl/renderable_resource.hpp>
#include <mbgl/mtl/renderer_backend.hpp>
#include <mbgl/mtl/texture2d.hpp>
#include <mbgl/mtl/upload_pass.hpp>
#include <mbgl/mtl/uniform_buffer.hpp>
@@ -15,6 +17,7 @@
#include <mbgl/programs/segment.hpp>
#include <mbgl/shaders/mtl/shader_program.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/variant.hpp>
#include <Metal/Metal.hpp>
@@ -95,6 +98,28 @@ MTL::Buffer* getMetalBuffer(const gfx::IndexVectorBasePtr& indexes) {
return nullptr;
}
MTL::CullMode mapCullMode(const gfx::CullFaceSideType mode) {
switch (mode) {
case gfx::CullFaceSideType::Front:
return MTL::CullModeFront;
case gfx::CullFaceSideType::Back:
return MTL::CullModeBack;
default:
case gfx::CullFaceSideType::FrontAndBack:
return MTL::CullModeNone;
}
}
MTL::Winding mapWindingMode(const gfx::CullFaceWindingType mode) {
switch (mode) {
case gfx::CullFaceWindingType::Clockwise:
return MTL::Winding::WindingClockwise;
default:
case gfx::CullFaceWindingType::CounterClockwise:
return MTL::Winding::WindingCounterClockwise;
}
}
} // namespace
void Drawable::draw(PaintParameters& parameters) const {
@@ -160,17 +185,33 @@ void Drawable::draw(PaintParameters& parameters) const {
assert(!"Vertex descriptor missing");
}
const auto& cullMode = getCullFaceMode();
encoder->setCullMode(cullMode.enabled ? mapCullMode(cullMode.side) : MTL::CullModeNone);
encoder->setFrontFacingWinding(mapWindingMode(cullMode.winding));
if (auto state = shaderMTL.getRenderPipelineState(renderPassDescriptor, impl->vertexDesc, getColorMode())) {
encoder->setRenderPipelineState(state.get());
} else {
assert(!"Failed to create render pipeline state");
return;
}
// For 3D mode, stenciling is handled by the layer group
if (!is3D) {
const auto depthMode = parameters.depthModeForSublayer(getSubLayerIndex(), getDepthType());
const auto stencilMode = enableStencil ? parameters.stencilModeForClipping(tileID->toUnwrapped())
: gfx::StencilMode::disabled();
if (auto depthStencilState = context.makeDepthStencilState(depthMode, stencilMode, renderPass)) {
encoder->setDepthStencilState(depthStencilState.get());
encoder->setStencilReferenceValue(stencilMode.ref);
}
}
for (const auto& seg_ : impl->segments) {
const auto& segment = static_cast<DrawSegment&>(*seg_);
const auto& mlSegment = segment.getSegment();
if (mlSegment.indexLength > 0) {
const auto& mode = segment.getMode();
if (auto state = shaderMTL.getRenderPipelineState(renderPassDescriptor, impl->vertexDesc, getColorMode())) {
encoder->setRenderPipelineState(state.get());
} else {
assert(!"Failed to create render pipeline state");
continue;
}
const auto primitiveType = getPrimitiveType(mode.type);
constexpr auto indexType = MTL::IndexType::IndexTypeUInt16;
@@ -180,6 +221,41 @@ void Drawable::draw(PaintParameters& parameters) const {
const NS::UInteger indexOffset = static_cast<NS::UInteger>(indexSize *
mlSegment.indexOffset); // in bytes, not indexes
const NS::Integer baseVertex = static_cast<NS::Integer>(mlSegment.vertexOffset);
#if !defined(NDEBUG)
const auto indexBufferLength = indexBuffer->length() / indexSize;
const auto* indexes = static_cast<const std::uint16_t*>(const_cast<MTL::Buffer*>(indexBuffer)->contents());
const auto maxIndex = *std::max_element(indexes + mlSegment.indexOffset,
indexes + mlSegment.indexOffset + mlSegment.indexLength);
// Uncomment for a detailed accounting of each draw call
// Log::Warning(Event::General,
// util::toString(getID()) + "/" + getName() +
// " => " + util::toString(mlSegment.indexLength) +
// " idxs @ " + util::toString(mlSegment.indexOffset) +
// " (=" + util::toString(mlSegment.indexLength + mlSegment.indexOffset) +
// " of " + util::toString(indexBufferLength) +
// ") max index " + util::toString(maxIndex) +
// " on base vertex " + util::toString(baseVertex) +
// " (" + util::toString(baseVertex + maxIndex) +
// " of " + util::toString(impl->vertexCount) +
// ") indexBuf=" + util::toString((uint64_t)indexBuffer) +
// "/" + util::toString(indexBuffer->gpuAddress()));
assert(mlSegment.indexOffset + mlSegment.indexLength <= indexBufferLength);
assert(static_cast<std::size_t>(maxIndex) < mlSegment.vertexLength);
for (const auto& binding : attributeBindings) {
if (binding) {
if (const auto buffer = getMetalBuffer(binding ? binding->vertexBufferResource : nullptr)) {
assert((maxIndex + mlSegment.vertexOffset) * binding->vertexStride <= buffer->length());
} else if (const auto buffer = getMetalBuffer(impl->noBindingBuffer.get())) {
assert(binding->vertexStride <= buffer->length());
}
}
}
#endif
encoder->drawIndexedPrimitives(primitiveType,
mlSegment.indexLength,
indexType,
@@ -252,7 +328,7 @@ void Drawable::bindAttributes(const RenderPass& renderPass) const {
NS::UInteger attributeIndex = 0;
for (const auto& binding : attributeBindings) {
if (const auto buffer = getMetalBuffer(binding ? binding->vertexBufferResource : nullptr)) {
assert(getBufferSize(binding->vertexBufferResource) == binding->vertexStride * impl->vertexCount);
assert(binding->vertexStride * impl->vertexCount <= getBufferSize(binding->vertexBufferResource));
encoder->setVertexBuffer(buffer, /*offset=*/0, attributeIndex);
} else if (const auto buffer = getMetalBuffer(impl->noBindingBuffer.get())) {
encoder->setVertexBuffer(buffer, /*offset=*/0, attributeIndex);

View File

@@ -25,6 +25,7 @@ RenderPass::RenderPass(CommandEncoder& commandEncoder_, const char* name, const
if (auto copy = NS::TransferPtr(rpd->copy())) {
if (auto* colorTarget = copy->colorAttachments()->object(0)) {
const auto& c = *descriptor.clearColor;
colorTarget->setLoadAction(MTL::LoadActionClear);
colorTarget->setClearColor(MTL::ClearColor::Make(c.r, c.g, c.b, c.a));
rpd = copy;
}

View File

@@ -14,6 +14,9 @@
#include <mbgl/shaders/mtl/background.hpp>
#include <mbgl/shaders/mtl/background_pattern.hpp>
#include <mbgl/shaders/mtl/circle.hpp>
#include <mbgl/shaders/mtl/clipping_mask.hpp>
#include <mbgl/shaders/mtl/fill_extrusion.hpp>
#include <mbgl/shaders/mtl/fill_extrusion_pattern.hpp>
#include <mbgl/shaders/mtl/line.hpp>
#include <mbgl/shaders/mtl/line_gradient.hpp>
#include <mbgl/shaders/mtl/fill.hpp>
@@ -112,6 +115,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar
registerTypes<shaders::BuiltIn::BackgroundShader,
shaders::BuiltIn::BackgroundPatternShader,
shaders::BuiltIn::CircleShader,
shaders::BuiltIn::ClippingMaskProgram,
shaders::BuiltIn::LineShader,
shaders::BuiltIn::LineGradientShader,
shaders::BuiltIn::RasterShader,
@@ -127,7 +131,9 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar
shaders::BuiltIn::HeatmapShader,
shaders::BuiltIn::HeatmapTextureShader,
shaders::BuiltIn::HillshadePrepareShader,
shaders::BuiltIn::HillshadeShader,
shaders::BuiltIn::HillshadeShader*/,
shaders::BuiltIn::FillExtrusionShader,
shaders::BuiltIn::FillExtrusionPatternShader/*,
shaders::BuiltIn::SymbolIconShader,
shaders::BuiltIn::SymbolSDFTextShader,
shaders::BuiltIn::SymbolSDFIconShader,

View File

@@ -7,8 +7,12 @@
#include <mbgl/gfx/upload_pass.hpp>
#include <mbgl/mtl/context.hpp>
#include <mbgl/mtl/drawable.hpp>
#include <mbgl/mtl/render_pass.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/util/convert.hpp>
#include <mbgl/util/logging.hpp>
#include <Metal/Metal.hpp>
namespace mbgl {
namespace mtl {
@@ -39,6 +43,8 @@ void TileLayerGroup::render(RenderOrchestrator&, PaintParameters& parameters) {
}
auto& context = static_cast<Context&>(parameters.context);
const auto& renderPass = static_cast<const mtl::RenderPass&>(*parameters.renderPass);
const auto& encoder = renderPass.getMetalEncoder();
// `stencilModeFor3D` uses a different stencil mask value each time its called, so if the
// drawables in this layer use 3D stencil mode, we need to set it up here so that all the
@@ -79,13 +85,16 @@ void TileLayerGroup::render(RenderOrchestrator&, PaintParameters& parameters) {
// to draw, set up the single-value stencil mask.
// If we're doing 2D stenciling and have any drawables with tile IDs,
// render each tile into the stencil buffer with a different value.
MTLDepthStencilStatePtr stateWithStencil, stateWithoutStencil;
if (features3d) {
stencilMode3d = stencil3d ? parameters.stencilModeFor3D() : gfx::StencilMode::disabled();
const auto depthMode = parameters.depthModeFor3D();
if (stencil3d) {
stencilMode3d = parameters.stencilModeFor3D();
stateWithStencil = context.makeDepthStencilState(depthMode, stencilMode3d, renderPass);
encoder->setStencilReferenceValue(stencilMode3d.ref);
}
stateWithoutStencil = context.makeDepthStencilState(depthMode, gfx::StencilMode::disabled(), renderPass);
} else if (!tileIDs.empty()) {
#if !defined(NDEBUG)
const auto debugGroup = parameters.encoder->createDebugGroup("tile-clip-masks");
#endif
parameters.renderTileClippingMasks(tileIDs);
}
@@ -101,9 +110,12 @@ void TileLayerGroup::render(RenderOrchestrator&, PaintParameters& parameters) {
// For layer groups with 3D features, enable either the single-value
// stencil mode for features with stencil enabled or disable stenciling.
// 2D drawables will set their own stencil mode within `draw`.
// if (features3d) {
// context.setStencilMode(drawable.getEnableStencil() ? stencilMode3d : gfx::StencilMode::disabled());
//}
if (features3d) {
const auto& state = drawable.getEnableStencil() ? stateWithStencil : stateWithoutStencil;
if (state) {
encoder->setDepthStencilState(state.get());
}
}
drawable.draw(parameters);
});

View File

@@ -177,6 +177,9 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
bindings.resize(std::max(bindings.size(), index + 1));
if (!override_) {
// No attribute was provided, this value will be used from a uniform, but we have to set
// up a valid binding or the validation will complain when the shader code references the
// attribute at compile time, regardless of whether it's ever used at runtime.
bindings[index] = {
/*.attribute = */ {defaultAttr.getDataType(), /*offset=*/0},
/*.vertexStride = */ static_cast<uint32_t>(VertexAttribute::getStrideOf(defaultAttr.getDataType())),
@@ -188,6 +191,9 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
// If the attribute references data shared with a bucket, get the corresponding buffer.
if (const auto& buffer = getBuffer(effectiveAttr.getSharedRawData(), usage)) {
assert(effectiveAttr.getSharedStride() * effectiveAttr.getSharedVertexOffset() <
effectiveAttr.getSharedRawData()->getRawSize() * effectiveAttr.getSharedRawData()->getRawCount());
bindings[index] = {
/*.attribute = */ {effectiveAttr.getSharedType(), effectiveAttr.getSharedOffset()},
/*.vertexStride = */ effectiveAttr.getSharedStride(),

View File

@@ -69,7 +69,7 @@ public:
const auto factor = pow(2, 13);
return LayoutVertex{{{p.x, p.y}},
{{// Multiply normal vector components by 2^14 to pack them into
{{// Multiply normal vector components by 2^13 to pack them into
// integers We pack a bool (`t`) into the x component indicating
// whether it is an upper or lower vertex
static_cast<int16_t>(floor(nx * factor) * 2 + t),

View File

@@ -4,6 +4,7 @@
#include <mbgl/style/layer_properties.hpp>
#include <mbgl/renderer/render_tree.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/shaders/layer_ubo.hpp>
#include <mbgl/util/mat4.hpp>
#if MLN_RENDER_BACKEND_METAL
@@ -73,6 +74,11 @@ bool LayerTweaker::hasPropertyAsUniform(const std::string_view attrName) const {
return name.size() + 2 == attrName.size() && 0 == std::strcmp(name.data(), attrName.data() + 2);
});
}
using namespace shaders;
AttributeSource LayerTweaker::getAttributeSource(const std::string_view& attribName) const {
return hasPropertyAsUniform(attribName) ? AttributeSource::Constant : AttributeSource::PerVertex;
}
#endif // MLN_RENDER_BACKEND_METAL
void LayerTweaker::enableOverdrawInspector(bool value) {

View File

@@ -9,7 +9,7 @@
#include <mbgl/renderer/render_tree.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/paint_property_binder.hpp>
#include <mbgl/shaders/layer_ubo.hpp>
#include <mbgl/shaders/fill_extrusion_layer_ubo.hpp>
#include <mbgl/shaders/shader_program_base.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
#include <mbgl/util/convert.hpp>
@@ -18,42 +18,16 @@
namespace mbgl {
using namespace shaders;
using namespace style;
namespace {
struct alignas(16) FillExtrusionDrawableUBO {
/* 0 */ std::array<float, 4 * 4> matrix;
/* 64 */ std::array<float, 4> scale;
/* 80 */ std::array<float, 2> texsize;
/* 88 */ std::array<float, 2> pixel_coord_upper;
/* 96 */ std::array<float, 2> pixel_coord_lower;
/* 104 */ float height_factor;
/* 108 */ float pad;
/* 112 */
};
static_assert(sizeof(FillExtrusionDrawableUBO) == 7 * 16);
/// Evaluated properties that do not depend on the tile
struct alignas(16) FillExtrusionDrawablePropsUBO {
/* 0 */ Color color;
/* 16 */ std::array<float, 3> light_color;
/* 28 */ float pad1;
/* 32 */ std::array<float, 3> light_position;
/* 44 */ float base;
/* 48 */ float height;
/* 52 */ float light_intensity;
/* 56 */ float vertical_gradient;
/* 60 */ float opacity;
/* 64 */ float fade;
/* 68 */ float pad2, pad3, pad4;
/* 80 */
};
static_assert(sizeof(FillExtrusionDrawablePropsUBO) == 5 * 16);
static const StringIdentity idFillExtrusionDrawableUBOName = StringIndexer::get("FillExtrusionDrawableUBO");
static const StringIdentity idFillExtrusionDrawablePropsUBOName = StringIndexer::get("FillExtrusionDrawablePropsUBO");
static const StringIdentity idTexImageName = StringIndexer::get("u_image");
const StringIdentity idFillExtrusionDrawableUBOName = StringIndexer::get("FillExtrusionDrawableUBO");
const StringIdentity idFillExtrusionDrawablePropsUBOName = StringIndexer::get("FillExtrusionDrawablePropsUBO");
const StringIdentity idExpressionInputsUBOName = StringIndexer::get("ExpressionInputsUBO");
const StringIdentity idFillExtrusionPermutationUBOName = StringIndexer::get("FillExtrusionPermutationUBO");
const StringIdentity idTexImageName = StringIndexer::get("u_image");
} // namespace
@@ -102,6 +76,32 @@ void FillExtrusionLayerTweaker::execute(LayerGroupBase& layerGroup,
propsBuffer->update(&paramsUBO, sizeof(paramsUBO));
}
#if MLN_RENDER_BACKEND_METAL
const auto zoom = parameters.state.getZoom();
if (propertiesChanged) {
const FillExtrusionPermutationUBO permutationUBO = {
/* .color = */ {/*.source=*/getAttributeSource("a_color"), /*.expression=*/{}},
/* .base = */ {/*.source=*/getAttributeSource("a_base"), /*.expression=*/{}},
/* .height = */ {/*.source=*/getAttributeSource("a_height"), /*.expression=*/{}},
/* .overdrawInspector = */ overdrawInspector,
/* .pad = */ 0,
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) {
auto& uniforms = drawable.mutableUniformBuffers();
uniforms.addOrReplace(idFillExtrusionDrawablePropsUBOName, propsBuffer);
@@ -149,6 +149,11 @@ void FillExtrusionLayerTweaker::execute(LayerGroupBase& layerGroup,
/* .pad = */ 0};
uniforms.createOrUpdate(idFillExtrusionDrawableUBOName, &drawableUBO, context);
#if MLN_RENDER_BACKEND_METAL
uniforms.addOrReplace(idExpressionInputsUBOName, expressionUniformBuffer);
uniforms.addOrReplace(idFillExtrusionPermutationUBOName, permutationUniformBuffer);
#endif // MLN_RENDER_BACKEND_METAL
});
}

View File

@@ -25,26 +25,11 @@ public:
private:
gfx::UniformBufferPtr propsBuffer;
};
/// Evaluated properties that depend on the tile
struct alignas(16) FillExtrusionDrawableTilePropsUBO {
/* 0 */ std::array<float, 4> pattern_from;
/* 16 */ std::array<float, 4> pattern_to;
/* 32 */
#if MLN_RENDER_BACKEND_METAL
gfx::UniformBufferPtr permutationUniformBuffer;
gfx::UniformBufferPtr expressionUniformBuffer;
#endif // MLN_RENDER_BACKEND_METAL
};
static_assert(sizeof(FillExtrusionDrawableTilePropsUBO) == 2 * 16);
/// Attribute interpolations
struct alignas(16) FillExtrusionInterpolateUBO {
/* 0 */ float base_t;
/* 4 */ float height_t;
/* 8 */ float color_t;
/* 12 */ float pattern_from_t;
/* 16 */ float pattern_to_t;
/* 20 */ float pad1, pad2, pad3;
/* 32 */
};
static_assert(sizeof(FillExtrusionInterpolateUBO) == 2 * 16);
} // namespace mbgl

View File

@@ -1,3 +1,5 @@
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/gfx/cull_face_mode.hpp>
#include <mbgl/gfx/render_pass.hpp>
@@ -7,7 +9,6 @@
#include <mbgl/programs/programs.hpp>
#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/renderer/render_tile.hpp>
@@ -25,20 +26,19 @@
#include <mbgl/gfx/drawable_builder.hpp>
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/renderer/layers/fill_extrusion_layer_tweaker.hpp>
#include <mbgl/shaders/fill_extrusion_layer_ubo.hpp>
#include <mbgl/shaders/shader_program_base.hpp>
#endif // MLN_DRAWABLE_RENDERER
namespace mbgl {
using namespace style;
using namespace shaders;
namespace {
#if MLN_DRAWABLE_RENDERER
constexpr std::string_view FillExtrusionShaderName = "FillExtrusionShader";
constexpr std::string_view FillExtrusionPatternShaderName = "FillExtrusionPatternShader";
static const StringIdentity idPosAttribName = StringIndexer::get("a_pos");
static const StringIdentity idNormAttribName = StringIndexer::get("a_normal_ed");
@@ -293,10 +293,10 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
}
if (!fillExtrusionGroup) {
fillExtrusionGroup = shaders.getShaderGroup(std::string(FillExtrusionShaderName));
fillExtrusionGroup = shaders.getShaderGroup("FillExtrusionShader");
}
if (!fillExtrusionPatternGroup) {
fillExtrusionPatternGroup = shaders.getShaderGroup(std::string(FillExtrusionPatternShaderName));
fillExtrusionPatternGroup = shaders.getShaderGroup("FillExtrusionPatternShader");
}
auto* tileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
@@ -410,7 +410,6 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
const auto shader = std::static_pointer_cast<gfx::ShaderProgramBase>(
shaderGroup->getOrCreateShader(context, uniformProps));
if (!shader) {
assert(false);
continue;
}

View File

@@ -1,12 +1,29 @@
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/update_parameters.hpp>
#include <mbgl/gfx/command_encoder.hpp>
#include <mbgl/gfx/cull_face_mode.hpp>
#include <mbgl/gfx/render_pass.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/renderer/render_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/gfx/command_encoder.hpp>
#include <mbgl/gfx/render_pass.hpp>
#include <mbgl/gfx/cull_face_mode.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/renderer/update_parameters.hpp>
#if MLN_RENDER_BACKEND_METAL
#include <mbgl/mtl/context.hpp>
#include <mbgl/mtl/render_pass.hpp>
#include <mbgl/mtl/renderer_backend.hpp>
#include <mbgl/shaders/layer_ubo.hpp>
#include <mbgl/shaders/mtl/clipping_mask.hpp>
#include <mbgl/shaders/mtl/shader_program.hpp>
#include <mbgl/shaders/shader_program_base.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/util/convert.hpp>
#include <mbgl/util/logging.hpp>
#include <Foundation/Foundation.hpp>
#include <Metal/Metal.hpp>
#endif // MLN_RENDER_BACKEND_METAL
namespace mbgl {
@@ -88,8 +105,18 @@ gfx::DepthMode PaintParameters::depthModeFor3D() const {
void PaintParameters::clearStencil() {
nextStencilID = 1;
context.clearStencilBuffer(0b00000000);
tileClippingMaskIDs.clear();
#if MLN_RENDER_BACKEND_METAL
// Metal doesn't have an equivalent of `glClear`, so we clear the buffer by drawing zero to (0:0,0)
std::set<UnwrappedTileID> ids{{0, 0, 0}};
auto f = [](const auto& ii) -> const UnwrappedTileID& {
return ii;
};
renderTileClippingMasks(ids.cbegin(), ids.cend(), std::move(f), /*clear=*/true);
#else
context.clearStencilBuffer(0b00000000);
#endif
}
namespace {
@@ -111,14 +138,17 @@ bool tileIDsIdentical(TIter beg, TIter end, GetTileIDFunc<TIter>& f, const TileM
} // namespace
void PaintParameters::renderTileClippingMasks(const std::set<UnwrappedTileID>& tileIDs) {
renderTileClippingMasks(
tileIDs.cbegin(), tileIDs.cend(), [](const auto& ii) -> const UnwrappedTileID& { return ii; });
auto f = [](const auto& ii) -> const UnwrappedTileID& {
return ii;
};
renderTileClippingMasks(tileIDs.cbegin(), tileIDs.cend(), std::move(f), /*clear=*/false);
}
void PaintParameters::renderTileClippingMasks(const RenderTiles& renderTiles) {
renderTileClippingMasks((*renderTiles).cbegin(),
(*renderTiles).cend(),
[](const auto& ii) -> const UnwrappedTileID& { return ii.get().id; });
auto f = [](const auto& ii) -> const UnwrappedTileID& {
return ii.get().id;
};
renderTileClippingMasks((*renderTiles).cbegin(), (*renderTiles).cend(), std::move(f), /*clear=*/false);
}
void PaintParameters::clearTileClippingMasks() {
@@ -128,18 +158,162 @@ void PaintParameters::clearTileClippingMasks() {
}
template <typename TIter>
void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFunc<TIter>&& f) {
void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFunc<TIter>&& f, bool clear) {
if (tileIDsIdentical(beg, end, f, tileClippingMaskIDs)) {
// The current stencil mask is for this source already; no need to draw another one.
return;
}
const auto count = std::distance(beg, end);
if (nextStencilID + count > maxStencilValue) {
if (!clear && nextStencilID + count > maxStencilValue) {
// we'll run out of fresh IDs so we need to clear and start from scratch
clearStencil();
}
if (!renderPass) {
assert(false);
return;
}
#if !defined(NDEBUG)
const auto debugGroup = renderPass->createDebugGroup("tile-clip-masks");
#endif
#if MLN_RENDER_BACKEND_METAL
using ShaderClass = shaders::ShaderSource<shaders::BuiltIn::ClippingMaskProgram, gfx::Backend::Type::Metal>;
const auto group = staticData.shaders->getShaderGroup("ClippingMaskProgram");
if (!group) {
return;
}
const auto shader = std::static_pointer_cast<gfx::ShaderProgramBase>(group->getOrCreateShader(context, {}));
if (!shader) {
return;
}
const auto& mtlContext = static_cast<mtl::Context&>(context);
const auto& mtlShader = static_cast<const mtl::ShaderProgram&>(*shader);
const auto& mtlRenderPass = static_cast<mtl::RenderPass&>(*renderPass);
const auto& encoder = mtlRenderPass.getMetalEncoder();
const auto colorMode = gfx::ColorMode::disabled();
constexpr std::size_t indexCount = 6;
std::optional<mtl::BufferResource> indexRes;
// TODO: refactor to use `Context::makeDepthStencilState`
const auto init = [&]() {
// TODO: a lot of this can be cached
// Create a vertex buffer from the fixed tile coordinates
const auto vertices = RenderStaticData::tileVertices();
constexpr auto vertexSize = sizeof(decltype(vertices)::Vertex::a1);
auto vertexRes = mtlContext.createBuffer(vertices.data(), vertices.bytes(), gfx::BufferUsageType::StaticDraw);
if (!vertexRes) {
return;
}
encoder->setVertexBuffer(vertexRes.getMetalBuffer().get(), /*offset=*/0, ShaderClass::attributes[0].index);
// A vertex descriptor tells Metal what's in the vertex buffer
auto vertDesc = NS::RetainPtr(MTL::VertexDescriptor::vertexDescriptor());
auto attribDesc = NS::TransferPtr(MTL::VertexAttributeDescriptor::alloc()->init());
auto layoutDesc = NS::TransferPtr(MTL::VertexBufferLayoutDescriptor::alloc()->init());
if (!vertDesc || !attribDesc || !layoutDesc) {
return;
}
attribDesc->setBufferIndex(ShaderClass::attributes[0].index);
attribDesc->setOffset(0);
attribDesc->setFormat(MTL::VertexFormatShort2);
layoutDesc->setStride(static_cast<NS::UInteger>(vertexSize));
layoutDesc->setStepFunction(MTL::VertexStepFunctionPerVertex);
layoutDesc->setStepRate(1);
vertDesc->attributes()->setObject(attribDesc.get(), 0);
vertDesc->layouts()->setObject(layoutDesc.get(), 0);
// Create a buffer from the fixed tile indexes
const auto indexes = RenderStaticData::quadTriangleIndices();
assert(indexes.elements() == indexCount);
indexRes.emplace(mtlContext.createBuffer(indexes.data(), indexes.bytes(), gfx::BufferUsageType::StaticDraw));
if (!indexRes || !*indexRes) {
return;
}
// gfx::CullFaceMode::disabled();
encoder->setCullMode(MTL::CullModeNone);
auto stencilDescriptor = NS::TransferPtr(MTL::StencilDescriptor::alloc()->init());
if (!stencilDescriptor) {
return;
}
stencilDescriptor->setStencilCompareFunction(MTL::CompareFunction::CompareFunctionAlways);
stencilDescriptor->setStencilFailureOperation(MTL::StencilOperation::StencilOperationKeep);
stencilDescriptor->setDepthFailureOperation(MTL::StencilOperation::StencilOperationKeep);
stencilDescriptor->setDepthStencilPassOperation(MTL::StencilOperation::StencilOperationReplace);
stencilDescriptor->setReadMask(0);
stencilDescriptor->setWriteMask(0b11111111);
auto depthStencilDescriptor = NS::TransferPtr(MTL::DepthStencilDescriptor::alloc()->init());
if (!depthStencilDescriptor) {
return;
}
depthStencilDescriptor->setDepthCompareFunction(MTL::CompareFunction::CompareFunctionAlways);
depthStencilDescriptor->setDepthWriteEnabled(false);
depthStencilDescriptor->setFrontFaceStencil(stencilDescriptor.get());
depthStencilDescriptor->setBackFaceStencil(stencilDescriptor.get());
auto& device = mtlContext.getBackend().getDevice();
auto depthStencilState = NS::TransferPtr(device->newDepthStencilState(depthStencilDescriptor.get()));
if (!depthStencilState) {
return;
}
encoder->setDepthStencilState(depthStencilState.get());
// Create a render pipeline state, telling Metal how to render the primitives
const auto& renderPassDescriptor = mtlRenderPass.getDescriptor();
if (auto state = mtlShader.getRenderPipelineState(renderPassDescriptor, vertDesc, colorMode)) {
encoder->setRenderPipelineState(state.get());
} else {
return;
}
};
// For each tile in the set...
for (auto i = beg; i != end; ++i) {
const auto& tileID = f(*i);
const int32_t stencilID = clear ? 0 : nextStencilID;
if (!clear) {
const auto result = tileClippingMaskIDs.insert(std::make_pair(tileID, stencilID));
if (result.second) {
// inserted
nextStencilID++;
} else {
// already present
continue;
}
}
if (!indexRes) {
init();
}
encoder->setStencilReferenceValue(stencilID);
const auto ubo = shaders::ClipUBO{/* .matrix = */ util::cast<float>(matrixForTile(tileID))};
// Create a buffer for the per-tile UBO data
if (auto uboRes = mtlContext.createBuffer(&ubo, sizeof(ubo), gfx::BufferUsageType::StaticDraw)) {
encoder->setVertexBuffer(uboRes.getMetalBuffer().get(), /*offset=*/0, ShaderClass::uniforms[0].index);
} else {
break;
}
encoder->drawIndexedPrimitives(MTL::PrimitiveType::PrimitiveTypeTriangle,
static_cast<NS::UInteger>(indexCount),
MTL::IndexType::IndexTypeUInt16,
indexRes->getMetalBuffer().get(),
/*indexOffset=*/0,
/*instanceCount=*/1,
/*baseVertex=*/0,
/*baseInstance=*/0);
}
#else // !MLN_RENDER_BACKEND_METAL
auto program = staticData.shaders->getLegacyGroup().get<ClippingMaskProgram>();
if (!program) {
@@ -188,6 +362,7 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun
ClippingMaskProgram::TextureBindings{},
"clipping/" + util::toString(stencilID));
}
#endif // MLN_RENDER_BACKEND_METAL
}
gfx::StencilMode PaintParameters::stencilModeForClipping(const UnwrappedTileID& tileID) const {

View File

@@ -118,7 +118,7 @@ private:
template <typename TIter>
using GetTileIDFunc = std::function<const UnwrappedTileID&(const typename TIter::value_type&)>;
template <typename TIter>
void renderTileClippingMasks(TIter beg, TIter end, GetTileIDFunc<TIter>&&);
void renderTileClippingMasks(TIter beg, TIter end, GetTileIDFunc<TIter>&&, bool clear);
// This needs to be an ordered map so that we have the same order as the renderTiles.
std::map<UnwrappedTileID, int32_t> tileClippingMaskIDs;

View File

@@ -138,6 +138,11 @@ MTLRenderPipelineStatePtr ShaderProgram::getRenderPipelineState(const gfx::Rende
colorTarget->setSourceAlphaBlendFactor(srcFactor);
colorTarget->setDestinationRGBBlendFactor(destFactor);
colorTarget->setDestinationAlphaBlendFactor(destFactor);
colorTarget->setWriteMask((colorMode.mask.r ? MTL::ColorWriteMaskRed : MTL::ColorWriteMaskNone) |
(colorMode.mask.g ? MTL::ColorWriteMaskGreen : MTL::ColorWriteMaskNone) |
(colorMode.mask.b ? MTL::ColorWriteMaskBlue : MTL::ColorWriteMaskNone) |
(colorMode.mask.a ? MTL::ColorWriteMaskAlpha : MTL::ColorWriteMaskNone));
}
if (depthFormat) {
@@ -170,12 +175,25 @@ std::optional<uint32_t> ShaderProgram::getSamplerLocation(const StringIdentity i
}
void ShaderProgram::initAttribute(const shaders::AttributeInfo& info) {
vertexAttributes.add(StringIndexer::get(info.name.data()), static_cast<int>(info.index), info.dataType, info.count);
const auto index = static_cast<int>(info.index);
#if !defined(NDEBUG)
// Indexes must be unique, if there's a conflict check the `attributes` array in the shader
vertexAttributes.visitAttributes(
[&](auto, const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
uniformBlocks.visit([&](auto, const gfx::UniformBlock& block) { assert(block.getIndex() != index); });
#endif
vertexAttributes.add(StringIndexer::get(info.name), index, info.dataType, info.count);
}
void ShaderProgram::initUniformBlock(const shaders::UniformBlockInfo& info) {
if (const auto& block_ = uniformBlocks.add(
StringIndexer::get(info.name.data()), static_cast<int>(info.index), info.size)) {
const auto index = static_cast<int>(info.index);
#if !defined(NDEBUG)
// Indexes must be unique, if there's a conflict check the `attributes` array in the shader
vertexAttributes.visitAttributes(
[&](auto, const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
uniformBlocks.visit([&](auto, const gfx::UniformBlock& block) { assert(block.getIndex() != index); });
#endif
if (const auto& block_ = uniformBlocks.add(StringIndexer::get(info.name), index, info.size)) {
auto& block = static_cast<UniformBlock&>(*block_);
block.setBindVertex(info.vertex);
block.setBindFragment(info.fragment);

View File

@@ -6,8 +6,25 @@ namespace mbgl {
namespace {
const std::string empty;
constexpr std::size_t initialCapacity = 100;
} // namespace
StringIndexer::StringIndexer()
: stringToIdentity(initialCapacity) {
identityToString.reserve(initialCapacity);
}
// For now, we have to make a copy to search the string-keyed container.
StringIdentity StringIndexer::get(const char* string) {
return get(std::string(string));
}
// Once we can search using a string_view this should be the primary version.
StringIdentity StringIndexer::get(std::string_view string) {
return get(std::string(string));
}
// We take a const reference rather than an lvalue (copy) under the assumption that most calls will
// find a match and insertions where we could move are rare.
StringIdentity StringIndexer::get(const std::string& string) {
{
std::shared_lock<std::shared_mutex> readerLock(instance().sharedMutex);

View File

@@ -14,7 +14,8 @@ TEST(StringIndexer, AddStrings) {
EXPECT_EQ(id1, 0);
EXPECT_EQ(StringIndexer::size(), 1);
const auto id2 = StringIndexer::get("test string2");
const std::string_view s2 = "test string2";
const auto id2 = StringIndexer::get(s2);
EXPECT_EQ(id2, 1);
EXPECT_EQ(StringIndexer::size(), 2);
}