From 44b5af7c4aa8eb08152858d1bd1eece697465cb3 Mon Sep 17 00:00:00 2001 From: Tim Sylvester Date: Mon, 2 Oct 2023 09:47:17 -0700 Subject: [PATCH] Topic/metal cache (#1704) --- include/mbgl/gfx/drawable.hpp | 8 +- include/mbgl/mtl/drawable.hpp | 13 ++- include/mbgl/mtl/renderable_resource.hpp | 2 +- platform/ios/src/MLNMapView+Metal.mm | 11 ++- src/mbgl/mtl/drawable.cpp | 104 ++++++++++++++--------- src/mbgl/mtl/drawable_impl.hpp | 10 +++ src/mbgl/renderer/paint_parameters.cpp | 45 +++++----- 7 files changed, 112 insertions(+), 81 deletions(-) diff --git a/include/mbgl/gfx/drawable.hpp b/include/mbgl/gfx/drawable.hpp index 13feb6e59..56a140b3f 100644 --- a/include/mbgl/gfx/drawable.hpp +++ b/include/mbgl/gfx/drawable.hpp @@ -134,7 +134,7 @@ public: bool getEnableStencil() const { return enableStencil; } /// Set stencil usage - void setEnableStencil(bool value) { enableStencil = value; } + virtual void setEnableStencil(bool value) { enableStencil = value; } /// not used for anything yet DrawPriority getDrawPriority() const { return drawPriority; } @@ -142,19 +142,19 @@ public: /// Whether to enable depth testing bool getEnableDepth() { return enableDepth; } - void setEnableDepth(bool value) { enableDepth = value; } + virtual void setEnableDepth(bool value) { enableDepth = value; } /// Determines depth range within the layer for 2D drawables int32_t getSubLayerIndex() const { return subLayerIndex; } /// Set sub-layer index - void setSubLayerIndex(int32_t value) { subLayerIndex = value; } + virtual void setSubLayerIndex(int32_t value) { subLayerIndex = value; } /// Depth writability for 2D drawables DepthMaskType getDepthType() const { return depthType; } /// Set depth type - void setDepthType(DepthMaskType value) { depthType = value; } + virtual void setDepthType(DepthMaskType value) { depthType = value; } /// Uses 3D depth mode bool getIs3D() const { return is3D; } diff --git a/include/mbgl/mtl/drawable.hpp b/include/mbgl/mtl/drawable.hpp index c2e96608d..2d0fb7118 100644 --- a/include/mbgl/mtl/drawable.hpp +++ b/include/mbgl/mtl/drawable.hpp @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include @@ -59,6 +57,11 @@ public: void setShader(gfx::ShaderProgramBasePtr) override; + void setEnableStencil(bool) override; + void setEnableDepth(bool) override; + void setSubLayerIndex(int32_t) override; + void setDepthType(gfx::DepthMaskType) override; + protected: // For testing only. Drawable(std::unique_ptr); @@ -76,12 +79,6 @@ protected: class Impl; const std::unique_ptr impl; - - gfx::AttributeBindingArray attributeBindings; - - mutable MTLRenderPipelineStatePtr pipelineState; - - mutable std::optional renderPassDescriptor; }; } // namespace mtl diff --git a/include/mbgl/mtl/renderable_resource.hpp b/include/mbgl/mtl/renderable_resource.hpp index f469f0a34..d8620d636 100644 --- a/include/mbgl/mtl/renderable_resource.hpp +++ b/include/mbgl/mtl/renderable_resource.hpp @@ -23,7 +23,7 @@ public: virtual const mbgl::mtl::RendererBackend& getBackend() const = 0; virtual const mbgl::mtl::MTLCommandBufferPtr& getCommandBuffer() const = 0; virtual mbgl::mtl::MTLBlitPassDescriptorPtr getUploadPassDescriptor() const = 0; - virtual mbgl::mtl::MTLRenderPassDescriptorPtr getRenderPassDescriptor() const = 0; + virtual const mbgl::mtl::MTLRenderPassDescriptorPtr& getRenderPassDescriptor() const = 0; }; } // namespace mtl diff --git a/platform/ios/src/MLNMapView+Metal.mm b/platform/ios/src/MLNMapView+Metal.mm index 8ae82c32f..d8614c920 100644 --- a/platform/ios/src/MLNMapView+Metal.mm +++ b/platform/ios/src/MLNMapView+Metal.mm @@ -69,8 +69,12 @@ public: return NS::TransferPtr(MTL::BlitPassDescriptor::alloc()->init()); } - mbgl::mtl::MTLRenderPassDescriptorPtr getRenderPassDescriptor() const override { - return NS::RetainPtr((__bridge MTL::RenderPassDescriptor*)mtlView.currentRenderPassDescriptor); + const mbgl::mtl::MTLRenderPassDescriptorPtr& getRenderPassDescriptor() const override { + if (!cachedRenderPassDescriptor) { + auto* mtlDesc = mtlView.currentRenderPassDescriptor; + cachedRenderPassDescriptor = NS::RetainPtr((__bridge MTL::RenderPassDescriptor*)mtlDesc); + } + return cachedRenderPassDescriptor; } void swap() override { @@ -83,6 +87,8 @@ public: commandBuffer = nil; commandBufferPtr.reset(); + + cachedRenderPassDescriptor.reset(); } mbgl::Size framebufferSize() { @@ -94,6 +100,7 @@ public: private: MLNMapViewMetalImpl& backend; mbgl::mtl::MTLCommandBufferPtr commandBufferPtr; + mutable mbgl::mtl::MTLRenderPassDescriptorPtr cachedRenderPassDescriptor; public: MLNMapViewImplDelegate* delegate = nil; diff --git a/src/mbgl/mtl/drawable.cpp b/src/mbgl/mtl/drawable.cpp index 57d9c1fc9..b0c638d68 100644 --- a/src/mbgl/mtl/drawable.cpp +++ b/src/mbgl/mtl/drawable.cpp @@ -123,12 +123,32 @@ MTL::Winding mapWindingMode(const gfx::CullFaceWindingType mode) { } // namespace void Drawable::setColorMode(const gfx::ColorMode& value) { - pipelineState.reset(); + impl->pipelineState.reset(); gfx::Drawable::setColorMode(value); } +void Drawable::setEnableStencil(bool value) { + gfx::Drawable::setEnableStencil(value); + impl->depthStencilState.reset(); +} + +void Drawable::setEnableDepth(bool value) { + gfx::Drawable::setEnableDepth(value); + impl->depthStencilState.reset(); +} + +void Drawable::setSubLayerIndex(int32_t value) { + gfx::Drawable::setSubLayerIndex(value); + impl->depthStencilState.reset(); +} + +void Drawable::setDepthType(gfx::DepthMaskType value) { + gfx::Drawable::setDepthType(value); + impl->depthStencilState.reset(); +} + void Drawable::setShader(gfx::ShaderProgramBasePtr value) { - pipelineState.reset(); + impl->pipelineState.reset(); gfx::Drawable::setShader(value); } @@ -151,13 +171,12 @@ void Drawable::draw(PaintParameters& parameters) const { return; } - if (renderPassDescriptor.has_value() && renderPass.getDescriptor() != renderPassDescriptor.value()) { - pipelineState.reset(); + const auto& descriptor = renderPass.getDescriptor(); + if (impl->renderPassDescriptor && descriptor != *impl->renderPassDescriptor) { + impl->pipelineState.reset(); } - renderPassDescriptor.emplace(gfx::RenderPassDescriptor{renderPass.getDescriptor().renderable, - renderPass.getDescriptor().clearColor, - renderPass.getDescriptor().clearDepth, - renderPass.getDescriptor().clearStencil}); + impl->renderPassDescriptor.emplace(gfx::RenderPassDescriptor{ + descriptor.renderable, descriptor.clearColor, descriptor.clearDepth, descriptor.clearStencil}); const auto& shaderMTL = static_cast(*shader); @@ -165,23 +184,6 @@ void Drawable::draw(PaintParameters& parameters) const { const auto debugGroup = parameters.encoder->createDebugGroup(debugLabel(*this)); #endif - /* - context.setDepthMode(getIs3D() ? parameters.depthModeFor3D() - : parameters.depthModeForSublayer(getSubLayerIndex(), getDepthType())); - - // force disable depth test for debugging - // context.setDepthMode({gfx::DepthFunctionType::Always, gfx::DepthMaskType::ReadOnly, {0,1}}); - - // For 3D mode, stenciling is handled by the layer group - if (!is3D) { - context.setStencilMode(makeStencilMode(parameters)); - } - - context.setColorMode(getColorMode()); - context.setCullFaceMode(getCullFaceMode()); - - */ - bindAttributes(renderPass); bindUniformBuffers(renderPass); bindTextures(renderPass); @@ -207,11 +209,11 @@ void Drawable::draw(PaintParameters& parameters) const { encoder->setCullMode(cullMode.enabled ? mapCullMode(cullMode.side) : MTL::CullModeNone); encoder->setFrontFacingWinding(mapWindingMode(cullMode.winding)); - if (!pipelineState) { - pipelineState = shaderMTL.getRenderPipelineState(renderPassDescriptor, impl->vertexDesc, getColorMode()); + if (!impl->pipelineState) { + impl->pipelineState = shaderMTL.getRenderPipelineState(renderPassDescriptor, impl->vertexDesc, getColorMode()); } - if (pipelineState) { - encoder->setRenderPipelineState(pipelineState.get()); + if (impl->pipelineState) { + encoder->setRenderPipelineState(impl->pipelineState.get()); } else { assert(!"Failed to create render pipeline state"); return; @@ -219,12 +221,30 @@ void Drawable::draw(PaintParameters& parameters) const { // 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); + std::optional newStencilMode; + + // If we have a stencil state, we may be able to reuse it, if the tile masks haven't changed. + // We assume that only the reference value needs to be compared. + if (impl->depthStencilState && enableStencil) { + newStencilMode = parameters.stencilModeForClipping(tileID->toUnwrapped()); + if (newStencilMode->ref != impl->previousStencilMode.ref) { + // No, need to rebuild it + impl->depthStencilState.reset(); + } + } + if (!impl->depthStencilState) { + if (enableStencil && !newStencilMode) { + newStencilMode = parameters.stencilModeForClipping(tileID->toUnwrapped()); + } + const auto depthMode = parameters.depthModeForSublayer(getSubLayerIndex(), getDepthType()); + const auto stencilMode = enableStencil ? parameters.stencilModeForClipping(tileID->toUnwrapped()) + : gfx::StencilMode::disabled(); + impl->depthStencilState = context.makeDepthStencilState(depthMode, stencilMode, renderPass); + impl->previousStencilMode = *newStencilMode; + } + if (impl->depthStencilState) { + encoder->setDepthStencilState(impl->depthStencilState.get()); + encoder->setStencilReferenceValue(impl->previousStencilMode.ref); } } @@ -266,7 +286,7 @@ void Drawable::draw(PaintParameters& parameters) const { assert(mlSegment.indexOffset + mlSegment.indexLength <= indexBufferLength); assert(static_cast(maxIndex) < mlSegment.vertexLength); - for (const auto& binding : attributeBindings) { + for (const auto& binding : impl->attributeBindings) { if (binding) { if (const auto buffer = getMetalBuffer(binding ? binding->vertexBufferResource : nullptr)) { assert((maxIndex + mlSegment.vertexOffset) * binding->vertexStride <= buffer->length()); @@ -348,7 +368,7 @@ void Drawable::bindAttributes(const RenderPass& renderPass) const { const auto& encoder = renderPass.getMetalEncoder(); NS::UInteger attributeIndex = 0; - for (const auto& binding : attributeBindings) { + for (const auto& binding : impl->attributeBindings) { if (const auto buffer = getMetalBuffer(binding ? binding->vertexBufferResource : nullptr)) { assert(binding->vertexStride * impl->vertexCount <= getBufferSize(binding->vertexBufferResource)); encoder->setVertexBuffer(buffer, /*offset=*/0, attributeIndex); @@ -363,7 +383,7 @@ void Drawable::unbindAttributes(const RenderPass& renderPass) const { const auto& encoder = renderPass.getMetalEncoder(); NS::UInteger attributeIndex = 0; - for (const auto& binding : attributeBindings) { + for (const auto& binding : impl->attributeBindings) { encoder->setVertexBuffer(nullptr, /*offset=*/0, attributeIndex); attributeIndex += 1; } @@ -558,14 +578,14 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) { impl->vertexAttributes.visitAttributes( [](const auto&, gfx::VertexAttribute& attrib) { attrib.setDirty(false); }); - if (attributeBindings != attributeBindings_) { - attributeBindings = attributeBindings_; + if (impl->attributeBindings != attributeBindings_) { + impl->attributeBindings = attributeBindings_; // Create a layout descriptor for each attribute auto vertDesc = NS::RetainPtr(MTL::VertexDescriptor::vertexDescriptor()); NS::UInteger index = 0; - for (auto& binding : attributeBindings) { + for (auto& binding : impl->attributeBindings) { if (!binding) { assert("Missing attribute binding"); continue; @@ -595,7 +615,7 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) { } impl->vertexDesc = std::move(vertDesc); - pipelineState.reset(); + impl->pipelineState.reset(); } } diff --git a/src/mbgl/mtl/drawable_impl.hpp b/src/mbgl/mtl/drawable_impl.hpp index ef386fe44..a0face522 100644 --- a/src/mbgl/mtl/drawable_impl.hpp +++ b/src/mbgl/mtl/drawable_impl.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,15 @@ public: StringIdentity idVertexAttrName = StringIndexer::get("a_pos"); gfx::UniqueVertexBufferResource noBindingBuffer; + + gfx::AttributeBindingArray attributeBindings; + + MTLRenderPipelineStatePtr pipelineState; + + std::optional renderPassDescriptor; + + MTLDepthStencilStatePtr depthStencilState; + gfx::StencilMode previousStencilMode; }; struct Drawable::DrawSegment final : public gfx::Drawable::DrawSegment { diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index 1b2a14615..0badb071c 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -157,6 +157,22 @@ void PaintParameters::clearTileClippingMasks() { } } +namespace { +const auto clipMaskStencilMode = gfx::StencilMode{ + /*.test=*/gfx::StencilMode::Always(), + /*.ref=*/0, + /*.mask=*/0b11111111, + /*.fail=*/gfx::StencilOpType::Keep, + /*.depthFail=*/gfx::StencilOpType::Keep, + /*.pass=*/gfx::StencilOpType::Replace, +}; +const auto clipMaskDepthMode = gfx::DepthMode{ + .func = gfx::DepthFunctionType::Always, + .mask = gfx::DepthMaskType::ReadOnly, + .range = {0, 1}, +}; +} // namespace + template void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFunc&& f, bool clear) { if (tileIDsIdentical(beg, end, f, tileClippingMaskIDs)) { @@ -238,32 +254,13 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun // gfx::CullFaceMode::disabled(); encoder->setCullMode(MTL::CullModeNone); - auto stencilDescriptor = NS::TransferPtr(MTL::StencilDescriptor::alloc()->init()); - if (!stencilDescriptor) { + if (auto depthStencilState = mtlContext.makeDepthStencilState( + clipMaskDepthMode, clipMaskStencilMode, mtlRenderPass)) { + encoder->setDepthStencilState(depthStencilState.get()); + } else { + assert(!"Failed to create depth-stencil state"); 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();