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:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>()));
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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);
|
||||
|
||||
71
include/mbgl/shaders/fill_extrusion_layer_ubo.hpp
Normal file
71
include/mbgl/shaders/fill_extrusion_layer_ubo.hpp
Normal 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
|
||||
63
include/mbgl/shaders/mtl/clipping_mask.hpp
Normal file
63
include/mbgl/shaders/mtl/clipping_mask.hpp
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
173
include/mbgl/shaders/mtl/fill_extrusion.hpp
Normal file
173
include/mbgl/shaders/mtl/fill_extrusion.hpp
Normal 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
|
||||
26
include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp
Normal file
26
include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp
Normal 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
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(¶msUBO, 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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user