From 3f1807a11a732beb32177ecad0369e7ba12a610d Mon Sep 17 00:00:00 2001 From: Alex Cristici Date: Mon, 2 Oct 2023 18:21:59 +0300 Subject: [PATCH] Metal headless backend (#1690) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- CMakeLists.txt | 22 ++++ bazel/core.bzl | 5 + include/mbgl/gfx/backend.hpp | 6 +- include/mbgl/gfx/types.hpp | 1 + include/mbgl/mtl/context.hpp | 2 + include/mbgl/mtl/offscreen_texture.hpp | 2 +- include/mbgl/shaders/collision_layer_ubo.hpp | 18 +++ include/mbgl/shaders/mtl/collision_box.hpp | 91 +++++++++++++++ include/mbgl/shaders/mtl/collision_circle.hpp | 110 ++++++++++++++++++ .../mbgl/shaders/mtl/symbol_text_and_icon.hpp | 4 +- platform/default/BUILD.bazel | 4 +- .../include/mbgl/gfx/headless_backend.hpp | 4 - .../include/mbgl/mtl/headless_backend.hpp | 33 ++++++ .../default/src/mbgl/mtl/headless_backend.cpp | 107 +++++++++++++++++ src/mbgl/mtl/context.cpp | 9 +- src/mbgl/mtl/offscreen_texture.cpp | 85 +++++++++++--- src/mbgl/mtl/renderer_backend.cpp | 4 + src/mbgl/mtl/texture2d.cpp | 20 ++++ .../layers/collision_layer_tweaker.cpp | 14 +-- src/mbgl/renderer/renderer_impl.cpp | 5 +- src/mbgl/shaders/mtl/background_pattern.cpp | 2 +- src/mbgl/shaders/mtl/collision_box.cpp | 19 +++ src/mbgl/shaders/mtl/collision_circle.cpp | 20 ++++ 23 files changed, 540 insertions(+), 47 deletions(-) create mode 100644 include/mbgl/shaders/collision_layer_ubo.hpp create mode 100644 include/mbgl/shaders/mtl/collision_box.hpp create mode 100644 include/mbgl/shaders/mtl/collision_circle.hpp create mode 100644 platform/default/include/mbgl/mtl/headless_backend.hpp create mode 100644 platform/default/src/mbgl/mtl/headless_backend.cpp create mode 100644 src/mbgl/shaders/mtl/collision_box.cpp create mode 100644 src/mbgl/shaders/mtl/collision_circle.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f35ec847..d67f539df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1110,6 +1110,7 @@ if(MLN_WITH_OPENGL) list(APPEND INCLUDE_FILES ${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/collision_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/heatmap_layer_ubo.hpp @@ -1174,6 +1175,8 @@ if(MBGL_WITH_METAL) ${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/collision_box.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/collision_circle.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/fill.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/fill_extrusion.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp @@ -1210,6 +1213,25 @@ if(MBGL_WITH_METAL) ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/upload_pass.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/vertex_attribute.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/shader_program.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/background.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/background_pattern.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/circle.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/collision_box.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/collision_circle.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/clipping_mask.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/fill.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/fill_extrusion.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/fill_extrusion_pattern.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/heatmap.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/heatmap_texture.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/hillshade.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/hillshade_prepare.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/line.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/line_gradient.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/raster.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/symbol_icon.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/symbol_sdf.cpp + ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/symbol_text_and_icon.cpp ] ) endif() diff --git a/bazel/core.bzl b/bazel/core.bzl index e59e7c4a9..9a13789fa 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -961,6 +961,7 @@ MLN_DRAWABLES_HEADERS = [ "include/mbgl/renderer/render_target.hpp", "include/mbgl/shaders/background_layer_ubo.hpp", "include/mbgl/shaders/circle_layer_ubo.hpp", + "include/mbgl/shaders/collision_layer_ubo.hpp", "include/mbgl/shaders/fill_layer_ubo.hpp", "include/mbgl/shaders/fill_extrusion_layer_ubo.hpp", "include/mbgl/shaders/heatmap_layer_ubo.hpp", @@ -1022,6 +1023,8 @@ MLN_DRAWABLES_MTL_SOURCE = [ "src/mbgl/shaders/mtl/background.cpp", "src/mbgl/shaders/mtl/background_pattern.cpp", "src/mbgl/shaders/mtl/circle.cpp", + "src/mbgl/shaders/mtl/collision_box.cpp", + "src/mbgl/shaders/mtl/collision_circle.cpp", "src/mbgl/shaders/mtl/clipping_mask.cpp", "src/mbgl/shaders/mtl/fill.cpp", "src/mbgl/shaders/mtl/fill_extrusion.cpp", @@ -1062,6 +1065,8 @@ MLN_DRAWABLES_MTL_HEADERS = [ "include/mbgl/shaders/mtl/background_pattern.hpp", "include/mbgl/shaders/mtl/circle.hpp", "include/mbgl/shaders/mtl/clipping_mask.hpp", + "include/mbgl/shaders/mtl/collision_box.hpp", + "include/mbgl/shaders/mtl/collision_circle.hpp", "include/mbgl/shaders/mtl/common.hpp", "include/mbgl/shaders/mtl/fill.hpp", "include/mbgl/shaders/mtl/fill_extrusion.hpp", diff --git a/include/mbgl/gfx/backend.hpp b/include/mbgl/gfx/backend.hpp index bc79032b7..859bc5f23 100644 --- a/include/mbgl/gfx/backend.hpp +++ b/include/mbgl/gfx/backend.hpp @@ -19,7 +19,7 @@ public: #if MLN_RENDER_BACKEND_METAL static constexpr Type DefaultType = Type::Metal; -#else // assume MLN_RENDER_BACKEND_METAL +#else // assume MLN_RENDER_BACKEND_OPENGL static constexpr Type DefaultType = Type::OpenGL; #endif @@ -33,7 +33,11 @@ public: template static std::unique_ptr Create(Args... args) { +#if MLN_RENDER_BACKEND_METAL + return Create(std::forward(args)...); +#else // assume MLN_RENDER_BACKEND_OPENGL return Create(std::forward(args)...); +#endif } private: diff --git a/include/mbgl/gfx/types.hpp b/include/mbgl/gfx/types.hpp index 7d77fe014..0dabb1619 100644 --- a/include/mbgl/gfx/types.hpp +++ b/include/mbgl/gfx/types.hpp @@ -113,6 +113,7 @@ enum class TexturePixelType : uint8_t { enum class TextureChannelDataType : uint8_t { UnsignedByte, ///< 8 bit unsigned byte HalfFloat, ///< 16 bit "half-float" + Float, ///< 32 bit float }; /// Texture mip map type diff --git a/include/mbgl/mtl/context.hpp b/include/mbgl/mtl/context.hpp index 7e47a7695..2b6129339 100644 --- a/include/mbgl/mtl/context.hpp +++ b/include/mbgl/mtl/context.hpp @@ -78,6 +78,8 @@ public: void setDirtyState() override; + std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool); + std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType) override; std::unique_ptr createTextureResource(Size, diff --git a/include/mbgl/mtl/offscreen_texture.hpp b/include/mbgl/mtl/offscreen_texture.hpp index c131838b9..f291a7a90 100644 --- a/include/mbgl/mtl/offscreen_texture.hpp +++ b/include/mbgl/mtl/offscreen_texture.hpp @@ -11,7 +11,7 @@ class Texture2D; class OffscreenTexture final : public gfx::OffscreenTexture { public: - OffscreenTexture(Context&, Size size, gfx::TextureChannelDataType type = gfx::TextureChannelDataType::UnsignedByte); + OffscreenTexture(Context&, Size size, gfx::TextureChannelDataType type, bool depth, bool stencil); bool isRenderable() override; diff --git a/include/mbgl/shaders/collision_layer_ubo.hpp b/include/mbgl/shaders/collision_layer_ubo.hpp new file mode 100644 index 000000000..ac7d762d0 --- /dev/null +++ b/include/mbgl/shaders/collision_layer_ubo.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace mbgl { +namespace shaders { + +struct alignas(16) CollisionUBO { + std::array matrix; + std::array extrude_scale; + float camera_to_center_distance; + float overscale_factor; +}; +static_assert(sizeof(CollisionUBO) % 16 == 0); +static_assert(sizeof(CollisionUBO) == 80); + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/mtl/collision_box.hpp b/include/mbgl/shaders/mtl/collision_box.hpp new file mode 100644 index 000000000..a26249fcd --- /dev/null +++ b/include/mbgl/shaders/mtl/collision_box.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr auto name = "CollisionBoxShader"; + static constexpr auto vertexMainFunction = "vertexMain"; + static constexpr auto fragmentMainFunction = "fragmentMain"; + + static const std::array attributes; + static const std::array uniforms; + static const std::array textures; + + static constexpr auto source = R"( + +struct VertexStage { + short2 pos [[attribute(0)]]; + short2 anchor_pos [[attribute(1)]]; + short2 extrude [[attribute(2)]]; + uchar2 placed [[attribute(3)]]; + float2 shift [[attribute(4)]]; +}; + +struct FragmentStage { + float4 position [[position, invariant]]; + float placed; + float notUsed; +}; + +struct alignas(16) CollisionBoxUBO { + float4x4 matrix; + float2 extrude_scale; + float camera_to_center_distance; + float pad1; +}; + +FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], + device const CollisionBoxUBO& drawable [[buffer(5)]]) { + + float4 projectedPoint = drawable.matrix * float4(float2(vertx.anchor_pos), 0, 1); + float camera_to_anchor_distance = projectedPoint.w; + float collision_perspective_ratio = clamp( + 0.5 + 0.5 * (drawable.camera_to_center_distance / camera_to_anchor_distance), + 0.0, // Prevents oversized near-field boxes in pitched/overzoomed tiles + 4.0); + + float4 position = drawable.matrix * float4(float2(vertx.pos), 0.0, 1.0); + position.xy += (float2(vertx.extrude) + vertx.shift) * drawable.extrude_scale * position.w * collision_perspective_ratio; + + float placed = float(vertx.placed.x); + float notUsed = float(vertx.placed.y); + + return { + .position = position, + .placed = placed, + .notUsed = notUsed, + }; +} + +half4 fragment fragmentMain(FragmentStage in [[stage_in]], + device const CollisionBoxUBO& drawable [[buffer(5)]]) { + + float alpha = 0.5; + + // Red = collision, hide label + float4 color = float4(1.0, 0.0, 0.0, 1.0) * alpha; + + // Blue = no collision, label is showing + if (in.placed > 0.5) { + color = float4(0.0, 0.0, 1.0, 0.5) * alpha; + } + + if (in.notUsed > 0.5) { + // This box not used, fade it out + color *= 0.1; + } + + return half4(color); +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/mtl/collision_circle.hpp b/include/mbgl/shaders/mtl/collision_circle.hpp new file mode 100644 index 000000000..39822a7bb --- /dev/null +++ b/include/mbgl/shaders/mtl/collision_circle.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr auto name = "CollisionCircleShader"; + static constexpr auto vertexMainFunction = "vertexMain"; + static constexpr auto fragmentMainFunction = "fragmentMain"; + + static const std::array attributes; + static const std::array uniforms; + static const std::array textures; + + static constexpr auto source = R"( + +struct VertexStage { + short2 pos [[attribute(0)]]; + short2 anchor_pos [[attribute(1)]]; + short2 extrude [[attribute(2)]]; + uchar2 placed [[attribute(3)]]; +}; + +struct FragmentStage { + float4 position [[position, invariant]]; + float placed; + float notUsed; + float radius; + float2 extrude; + float2 extrude_scale; +}; + +struct alignas(16) CollisionCircleUBO { + float4x4 matrix; + float2 extrude_scale; + float camera_to_center_distance; + float overscale_factor; +}; + +FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], + device const CollisionCircleUBO& drawable [[buffer(4)]]) { + + float4 projectedPoint = drawable.matrix * float4(float2(vertx.anchor_pos), 0, 1); + float camera_to_anchor_distance = projectedPoint.w; + float collision_perspective_ratio = clamp( + 0.5 + 0.5 * (drawable.camera_to_center_distance / camera_to_anchor_distance), + 0.0, // Prevents oversized near-field circles in pitched/overzoomed tiles + 4.0); + + float4 position = drawable.matrix * float4(float2(vertx.pos), 0.0, 1.0); + + float padding_factor = 1.2; // Pad the vertices slightly to make room for anti-alias blur + position.xy += float2(vertx.extrude) * drawable.extrude_scale * padding_factor * position.w * collision_perspective_ratio; + + float placed = float(vertx.placed.x); + float notUsed = float(vertx.placed.y); + float radius = abs(float(vertx.extrude.y)); // We don't pitch the circles, so both units of the extrusion vector are equal in magnitude to the radius + + float2 extrude = float2(vertx.extrude) * padding_factor; + float2 extrude_scale = drawable.extrude_scale * drawable.camera_to_center_distance * collision_perspective_ratio; + + return { + .position = position, + .placed = placed, + .notUsed = notUsed, + .radius = radius, + .extrude = extrude, + .extrude_scale = extrude_scale, + }; +} + +half4 fragment fragmentMain(FragmentStage in [[stage_in]], + device const CollisionCircleUBO& drawable [[buffer(4)]]) { + + float alpha = 0.5; + + // Red = collision, hide label + float4 color = float4(1.0, 0.0, 0.0, 1.0) * alpha; + + // Blue = no collision, label is showing + if (in.placed > 0.5) { + color = float4(0.0, 0.0, 1.0, 0.5) * alpha; + } + + if (in.notUsed > 0.5) { + // This box not used, fade it out + color *= 0.2; + } + + float extrude_scale_length = length(in.extrude_scale); + float extrude_length = length(in.extrude) * extrude_scale_length; + float stroke_width = 15.0 * extrude_scale_length / drawable.overscale_factor; + float radius = in.radius * extrude_scale_length; + + float distance_to_edge = abs(extrude_length - radius); + float opacity_t = smoothstep(-stroke_width, 0.0, -distance_to_edge); + + return half4(opacity_t * color); +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp index afd4dd4b8..f0d68a52f 100644 --- a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp +++ b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp @@ -118,7 +118,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const float2x2 rotation_matrix = float2x2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); const float4 projected_pos = drawable.label_plane_matrix * float4(vertx.projected_pos.xy, 0.0, 1.0); - const float2 pos_rot = a_offset / 32.0 * fontScale + a_pxoffset; + const float2 pos_rot = a_offset / 32.0 * fontScale; const float2 pos0 = projected_pos.xy / projected_pos.w + rotation_matrix * pos_rot; const float4 position = drawable.coord_matrix * float4(pos0, 0.0, 1.0); const float gamma_scale = position.w; @@ -155,7 +155,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], if (in.data1.w == ICON) { const float2 tex_icon = in.data0.zw; - const float alpha = opacity * fade_opacity; + const float alpha = in.opacity * fade_opacity; return half4(icon_image.sample(icon_sampler, tex_icon) * alpha); } diff --git a/platform/default/BUILD.bazel b/platform/default/BUILD.bazel index b76d6f17c..2d3cd2ad7 100644 --- a/platform/default/BUILD.bazel +++ b/platform/default/BUILD.bazel @@ -55,7 +55,7 @@ cc_library( "src/mbgl/util/thread_local.cpp", "src/mbgl/util/utf.cpp", ] + select({ - "//:metal_renderer": [], + "//:metal_renderer": ["src/mbgl/mtl/headless_backend.cpp"], "//conditions:default": ["src/mbgl/gl/headless_backend.cpp"], }) + select({ "//:linux": [ @@ -89,7 +89,7 @@ cc_library( "include/mbgl/storage/sqlite3.hpp", "include/mbgl/text/unaccent.hpp", ] + select({ - "//:metal_renderer": [], + "//:metal_renderer": ["include/mbgl/mtl/headless_backend.hpp"], "//conditions:default": ["include/mbgl/gl/headless_backend.hpp"], }), copts = CPP_FLAGS + MAPLIBRE_FLAGS, diff --git a/platform/default/include/mbgl/gfx/headless_backend.hpp b/platform/default/include/mbgl/gfx/headless_backend.hpp index a2656c431..1dc775531 100644 --- a/platform/default/include/mbgl/gfx/headless_backend.hpp +++ b/platform/default/include/mbgl/gfx/headless_backend.hpp @@ -24,11 +24,7 @@ public: static std::unique_ptr Create(const Size size = {256, 256}, SwapBehaviour swapBehavior = SwapBehaviour::NoFlush, const gfx::ContextMode contextMode = gfx::ContextMode::Unique) { -#if MLN_RENDER_BACKEND_METAL - throw std::runtime_error("Not yet implemented"); -#else // MLN_RENDER_BACKEND_OPENGL return Backend::Create(size, swapBehavior, contextMode); -#endif } virtual PremultipliedImage readStillImage() = 0; diff --git a/platform/default/include/mbgl/mtl/headless_backend.hpp b/platform/default/include/mbgl/mtl/headless_backend.hpp new file mode 100644 index 000000000..af7d1aa7e --- /dev/null +++ b/platform/default/include/mbgl/mtl/headless_backend.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace mtl { + +class HeadlessBackend final : public mtl::RendererBackend, public gfx::HeadlessBackend { +public: + HeadlessBackend(Size = {256, 256}, + SwapBehaviour = SwapBehaviour::NoFlush, + gfx::ContextMode = gfx::ContextMode::Unique); + ~HeadlessBackend() override; + void updateAssumedState() override; + gfx::Renderable& getDefaultRenderable() override; + PremultipliedImage readStillImage() override; + RendererBackend* getRendererBackend() override; + SwapBehaviour getSwapBehaviour(); + +private: + void activate() override; + void deactivate() override; + +private: + bool active = false; + SwapBehaviour swapBehaviour = SwapBehaviour::NoFlush; +}; + +} // namespace mtl +} // namespace mbgl diff --git a/platform/default/src/mbgl/mtl/headless_backend.cpp b/platform/default/src/mbgl/mtl/headless_backend.cpp new file mode 100644 index 000000000..f2dc98112 --- /dev/null +++ b/platform/default/src/mbgl/mtl/headless_backend.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { +namespace mtl { + +class HeadlessRenderableResource final : public mtl::RenderableResource { +public: + HeadlessRenderableResource(HeadlessBackend& backend_, mtl::Context& context_, Size size) + : backend(backend_), + context(context_) { + offscreenTexture = context.createOffscreenTexture(size, gfx::TextureChannelDataType::UnsignedByte, true, true); + } + + ~HeadlessRenderableResource() noexcept override = default; + + void bind() override { offscreenTexture->getResource().bind(); } + + void swap() override { offscreenTexture->getResource().swap(); } + + PremultipliedImage readStillImage() { return offscreenTexture->readStillImage(); } + + const RendererBackend& getBackend() const override { return context.getBackend(); } + + const MTLCommandBufferPtr& getCommandBuffer() const override { + return offscreenTexture->getResource().getCommandBuffer(); + } + + MTLBlitPassDescriptorPtr getUploadPassDescriptor() const override { + return offscreenTexture->getResource().getUploadPassDescriptor(); + } + + MTLRenderPassDescriptorPtr getRenderPassDescriptor() const override { + return offscreenTexture->getResource().getRenderPassDescriptor(); + } + + HeadlessBackend& backend; + Context& context; + std::unique_ptr offscreenTexture; +}; + +HeadlessBackend::HeadlessBackend(const Size size_, + gfx::HeadlessBackend::SwapBehaviour swapBehaviour_, + const gfx::ContextMode contextMode_) + : mbgl::mtl::RendererBackend(contextMode_), + mbgl::gfx::HeadlessBackend(size_), + swapBehaviour(swapBehaviour_) {} + +HeadlessBackend::~HeadlessBackend() { + gfx::BackendScope guard{*this, gfx::BackendScope::ScopeType::Implicit}; + // Explicitly reset the renderable resource + resource.reset(); + // Explicitly reset the context + context.reset(); +} + +void HeadlessBackend::activate() { + active = true; +} + +void HeadlessBackend::deactivate() { + active = false; +} + +gfx::Renderable& HeadlessBackend::getDefaultRenderable() { + if (!resource) { + resource = std::make_unique(*this, static_cast(getContext()), size); + } + return *this; +} + +void HeadlessBackend::updateAssumedState() { + // no-op +} + +PremultipliedImage HeadlessBackend::readStillImage() { + return getResource().readStillImage(); +} + +RendererBackend* HeadlessBackend::getRendererBackend() { + return this; +} + +HeadlessBackend::SwapBehaviour HeadlessBackend::getSwapBehaviour() { + return swapBehaviour; +} + +} // namespace mtl + +namespace gfx { + +template <> +std::unique_ptr Backend::Create( + const Size size, gfx::HeadlessBackend::SwapBehaviour swapBehavior, const gfx::ContextMode contextMode) { + return std::make_unique(size, swapBehavior, contextMode); +} + +} // namespace gfx +} // namespace mbgl diff --git a/src/mbgl/mtl/context.cpp b/src/mbgl/mtl/context.cpp index 1100e34d3..186079043 100644 --- a/src/mbgl/mtl/context.cpp +++ b/src/mbgl/mtl/context.cpp @@ -186,8 +186,15 @@ bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer, const void Context::setDirtyState() {} +std::unique_ptr Context::createOffscreenTexture(Size size, + gfx::TextureChannelDataType type, + bool depth, + bool stencil) { + return std::make_unique(*this, size, type, depth, stencil); +} + std::unique_ptr Context::createOffscreenTexture(Size size, gfx::TextureChannelDataType type) { - return std::make_unique(*this, size, type); + return createOffscreenTexture(size, type, false, false); } std::unique_ptr Context::createTextureResource(Size, diff --git a/src/mbgl/mtl/offscreen_texture.cpp b/src/mbgl/mtl/offscreen_texture.cpp index 11895f807..42bb5fa8f 100644 --- a/src/mbgl/mtl/offscreen_texture.cpp +++ b/src/mbgl/mtl/offscreen_texture.cpp @@ -11,18 +11,39 @@ namespace mtl { class OffscreenTextureResource final : public RenderableResource { public: - OffscreenTextureResource(Context& context_, const Size size_, const gfx::TextureChannelDataType type_) + OffscreenTextureResource( + Context& context_, const Size size_, const gfx::TextureChannelDataType type_, bool depth, bool stencil) : context(context_), size(size_), type(type_) { assert(!size.isEmpty()); - texture = context.createTexture2D(); - texture->setSize(size); - texture->setFormat(gfx::TexturePixelType::RGBA, type); - texture->setSamplerConfiguration( + colorTexture = context.createTexture2D(); + colorTexture->setSize(size); + colorTexture->setFormat(gfx::TexturePixelType::RGBA, type); + colorTexture->setSamplerConfiguration( {gfx::TextureFilterType::Nearest, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp}); - static_cast(texture.get()) + static_cast(colorTexture.get()) ->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite | MTL::TextureUsageRenderTarget); + + if (depth) { + depthTexture = context.createTexture2D(); + depthTexture->setSize(size); + depthTexture->setFormat(gfx::TexturePixelType::Depth, gfx::TextureChannelDataType::Float); + depthTexture->setSamplerConfiguration( + {gfx::TextureFilterType::Nearest, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp}); + static_cast(depthTexture.get()) + ->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite | MTL::TextureUsageRenderTarget); + } + + if (stencil) { + stencilTexture = context.createTexture2D(); + stencilTexture->setSize(size); + stencilTexture->setFormat(gfx::TexturePixelType::Stencil, gfx::TextureChannelDataType::UnsignedByte); + stencilTexture->setSamplerConfiguration( + {gfx::TextureFilterType::Nearest, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp}); + static_cast(stencilTexture.get()) + ->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite | MTL::TextureUsageRenderTarget); + } } ~OffscreenTextureResource() noexcept override = default; @@ -30,7 +51,25 @@ public: void bind() override { assert(context.getBackend().getCommandQueue()); commandBuffer = NS::RetainPtr(context.getBackend().getCommandQueue()->commandBuffer()); - texture->create(); + colorTexture->create(); + + renderPassDescriptor = NS::TransferPtr(MTL::RenderPassDescriptor::alloc()->init()); + if (auto* colorTarget = renderPassDescriptor->colorAttachments()->object(0)) { + colorTarget->setTexture(static_cast(colorTexture.get())->getMetalTexture()); + } + + if (depthTexture) { + depthTexture->create(); + if (auto* depthTarget = renderPassDescriptor->depthAttachment()) { + depthTarget->setTexture(static_cast(depthTexture.get())->getMetalTexture()); + } + } + if (stencilTexture) { + stencilTexture->create(); + if (auto* stencilTarget = renderPassDescriptor->stencilAttachment()) { + stencilTarget->setTexture(static_cast(stencilTexture.get())->getMetalTexture()); + } + } } void swap() override { @@ -38,16 +77,24 @@ public: commandBuffer->commit(); commandBuffer->waitUntilCompleted(); commandBuffer.reset(); + renderPassDescriptor.reset(); } PremultipliedImage readStillImage() { - assert(false); - return PremultipliedImage(); + assert(static_cast(colorTexture.get())->getMetalTexture()); + + auto data = std::make_unique(colorTexture->getDataSize()); + MTL::Region region = MTL::Region::Make2D(0, 0, size.width, size.height); + NS::UInteger bytesPerRow = size.width * colorTexture->getPixelStride(); + + static_cast(colorTexture.get())->getMetalTexture()->getBytes(data.get(), bytesPerRow, region, 0); + + return {size, std::move(data)}; } gfx::Texture2DPtr& getTexture() { - assert(texture); - return texture; + assert(colorTexture); + return colorTexture; } const RendererBackend& getBackend() const override { return context.getBackend(); } @@ -55,15 +102,11 @@ public: const MTLCommandBufferPtr& getCommandBuffer() const override { return commandBuffer; } MTLBlitPassDescriptorPtr getUploadPassDescriptor() const override { - assert(false); return NS::TransferPtr(MTL::BlitPassDescriptor::alloc()->init()); } MTLRenderPassDescriptorPtr getRenderPassDescriptor() const override { - auto renderPassDescriptor = NS::TransferPtr(MTL::RenderPassDescriptor::alloc()->init()); - if (auto* colorTarget = renderPassDescriptor->colorAttachments()->object(0)) { - colorTarget->setTexture(static_cast(texture.get())->getMetalTexture()); - } + assert(renderPassDescriptor); return renderPassDescriptor; } @@ -71,12 +114,16 @@ private: Context& context; const Size size; const gfx::TextureChannelDataType type; - gfx::Texture2DPtr texture; + gfx::Texture2DPtr colorTexture; + gfx::Texture2DPtr depthTexture; + gfx::Texture2DPtr stencilTexture; MTLCommandBufferPtr commandBuffer; + MTLRenderPassDescriptorPtr renderPassDescriptor; }; -OffscreenTexture::OffscreenTexture(Context& context, const Size size_, const gfx::TextureChannelDataType type) - : gfx::OffscreenTexture(size, std::make_unique(context, size_, type)) {} +OffscreenTexture::OffscreenTexture( + Context& context, const Size size_, const gfx::TextureChannelDataType type, bool depth, bool stencil) + : gfx::OffscreenTexture(size, std::make_unique(context, size_, type, depth, stencil)) {} bool OffscreenTexture::isRenderable() { assert(false); diff --git a/src/mbgl/mtl/renderer_backend.cpp b/src/mbgl/mtl/renderer_backend.cpp index b290e605a..deb9cc781 100644 --- a/src/mbgl/mtl/renderer_backend.cpp +++ b/src/mbgl/mtl/renderer_backend.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -124,6 +126,8 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::BackgroundPatternShader, shaders::BuiltIn::CircleShader, shaders::BuiltIn::ClippingMaskProgram, + shaders::BuiltIn::CollisionBoxShader, + shaders::BuiltIn::CollisionCircleShader, shaders::BuiltIn::FillShader, shaders::BuiltIn::FillOutlineShader, shaders::BuiltIn::FillOutlinePatternShader, diff --git a/src/mbgl/mtl/texture2d.cpp b/src/mbgl/mtl/texture2d.cpp index fe0e541d2..6abdfc1bb 100644 --- a/src/mbgl/mtl/texture2d.cpp +++ b/src/mbgl/mtl/texture2d.cpp @@ -56,6 +56,8 @@ size_t Texture2D::getPixelStride() const noexcept { return 1 * numChannels(); case gfx::TextureChannelDataType::HalfFloat: return 2 * numChannels(); + case gfx::TextureChannelDataType::Float: + return 4 * numChannels(); } } @@ -65,6 +67,10 @@ size_t Texture2D::numChannels() const noexcept { return 4; case gfx::TexturePixelType::Alpha: return 1; + case gfx::TexturePixelType::Stencil: + return 1; + case gfx::TexturePixelType::Depth: + return 1; default: return 0; } @@ -78,6 +84,8 @@ MTL::PixelFormat Texture2D::getMetalPixelFormat() const noexcept { return MTL::PixelFormat::PixelFormatRGBA8Unorm; case gfx::TexturePixelType::Alpha: return MTL::PixelFormat::PixelFormatA8Unorm; + case gfx::TexturePixelType::Stencil: + return MTL::PixelFormat::PixelFormatStencil8; default: assert(false); return MTL::PixelFormat::PixelFormatInvalid; @@ -92,6 +100,18 @@ MTL::PixelFormat Texture2D::getMetalPixelFormat() const noexcept { assert(false); return MTL::PixelFormat::PixelFormatInvalid; } + case gfx::TextureChannelDataType::Float: + switch (pixelFormat) { + case gfx::TexturePixelType::RGBA: + return MTL::PixelFormat::PixelFormatRGBA32Float; + case gfx::TexturePixelType::Alpha: + return MTL::PixelFormat::PixelFormatR32Float; + case gfx::TexturePixelType::Depth: + return MTL::PixelFormat::PixelFormatDepth32Float; + default: + assert(false); + return MTL::PixelFormat::PixelFormatInvalid; + } default: assert(false); return MTL::PixelFormat::PixelFormatInvalid; diff --git a/src/mbgl/renderer/layers/collision_layer_tweaker.cpp b/src/mbgl/renderer/layers/collision_layer_tweaker.cpp index 508f16888..d46a60aae 100644 --- a/src/mbgl/renderer/layers/collision_layer_tweaker.cpp +++ b/src/mbgl/renderer/layers/collision_layer_tweaker.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -18,18 +19,7 @@ namespace mbgl { using namespace style; - -namespace { - -struct alignas(16) CollisionUBO { - std::array matrix; - std::array extrude_scale; - float camera_to_center_distance; - float overscale_factor; -}; -static_assert(sizeof(CollisionUBO) == 20 * 4); - -} // namespace +using namespace shaders; const StringIdentity CollisionLayerTweaker::idCollisionCircleUBOName = StringIndexer::get(CollisionCircleUBOName); const StringIdentity CollisionLayerTweaker::idCollisionBoxUBOName = StringIndexer::get(CollisionBoxUBOName); diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index c917bdf69..33bdca101 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -392,10 +392,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, // Ends the RenderPass parameters.renderPass.reset(); - const bool isMapModeContinuous = renderTreeParameters.mapMode == MapMode::Continuous; - if (isMapModeContinuous) { - parameters.encoder->present(parameters.backend.getDefaultRenderable()); - } + parameters.encoder->present(parameters.backend.getDefaultRenderable()); // CommandEncoder destructor submits render commands. parameters.encoder.reset(); diff --git a/src/mbgl/shaders/mtl/background_pattern.cpp b/src/mbgl/shaders/mtl/background_pattern.cpp index fa173422d..88aff4809 100644 --- a/src/mbgl/shaders/mtl/background_pattern.cpp +++ b/src/mbgl/shaders/mtl/background_pattern.cpp @@ -7,7 +7,7 @@ const std::array ShaderSource::uniforms = { UniformBlockInfo{1, sizeof(BackgroundLayerUBO), true, false, "BackgroundLayerUBO"}, UniformBlockInfo{2, sizeof(BackgroundDrawableUBO), true, false, "BackgroundDrawableUBO"}, - UniformBlockInfo{3, sizeof(BackgroundPatternLayerUBO), true, false, "BackgroundPatternLayerUBO"}, + UniformBlockInfo{3, sizeof(BackgroundPatternLayerUBO), true, false, "BackgroundLayerUBO"}, }; } // namespace shaders diff --git a/src/mbgl/shaders/mtl/collision_box.cpp b/src/mbgl/shaders/mtl/collision_box.cpp new file mode 100644 index 000000000..66cbbef9a --- /dev/null +++ b/src/mbgl/shaders/mtl/collision_box.cpp @@ -0,0 +1,19 @@ +#include + +namespace mbgl { +namespace shaders { + +const std::array ShaderSource::attributes = { + AttributeInfo{0, gfx::AttributeDataType::Short2, 1, "a_pos"}, + AttributeInfo{1, gfx::AttributeDataType::Short2, 1, "a_anchor_pos"}, + AttributeInfo{2, gfx::AttributeDataType::Short2, 1, "a_extrude"}, + AttributeInfo{3, gfx::AttributeDataType::UByte2, 1, "a_placed"}, + AttributeInfo{4, gfx::AttributeDataType::Float2, 1, "a_shift"}, +}; +const std::array ShaderSource::uniforms = { + UniformBlockInfo{5, true, true, sizeof(CollisionUBO), "CollisionBoxUBO"}, +}; +const std::array ShaderSource::textures = {}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/mtl/collision_circle.cpp b/src/mbgl/shaders/mtl/collision_circle.cpp new file mode 100644 index 000000000..9eb6934ab --- /dev/null +++ b/src/mbgl/shaders/mtl/collision_circle.cpp @@ -0,0 +1,20 @@ +#include + +namespace mbgl { +namespace shaders { + +const std::array ShaderSource::attributes = + { + AttributeInfo{0, gfx::AttributeDataType::Short2, 1, "a_pos"}, + AttributeInfo{1, gfx::AttributeDataType::Short2, 1, "a_anchor_pos"}, + AttributeInfo{2, gfx::AttributeDataType::Short2, 1, "a_extrude"}, + AttributeInfo{3, gfx::AttributeDataType::UByte2, 1, "a_placed"}, +}; +const std::array + ShaderSource::uniforms = { + UniformBlockInfo{5, true, true, sizeof(CollisionUBO), "CollisionCircleUBO"}, +}; +const std::array ShaderSource::textures = {}; + +} // namespace shaders +} // namespace mbgl