From 779b89346816b804af78eeae88933ca130858328 Mon Sep 17 00:00:00 2001 From: AtlasProgramming Date: Wed, 25 Jun 2025 16:09:11 -0400 Subject: [PATCH] Reduce duplicate GPU buffer uploads (#3577) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: “Malcolm <“mtoon@atlasprogramming.com”> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- include/mbgl/mtl/render_pass.hpp | 5 +++++ src/mbgl/mtl/buffer_resource.cpp | 11 +++++++++-- src/mbgl/mtl/drawable.cpp | 2 +- src/mbgl/mtl/render_pass.cpp | 9 +++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/include/mbgl/mtl/render_pass.hpp b/include/mbgl/mtl/render_pass.hpp index a86788fcd..c7a94821b 100644 --- a/include/mbgl/mtl/render_pass.hpp +++ b/include/mbgl/mtl/render_pass.hpp @@ -41,6 +41,9 @@ public: /// Set the sampler for a texture binding void setFragmentSamplerState(const MTLSamplerStatePtr&, int32_t location); + /// Set the render pipeline state + void setRenderPipelineState(const MTLRenderPipelineStatePtr&); + void endEncoding(); /// Resets the states and bindings of the render pass to deal @@ -69,6 +72,8 @@ private: mtl::CommandEncoder& commandEncoder; MTLRenderCommandEncoderPtr encoder; MTLDepthStencilStatePtr currentDepthStencilState; + MTLRenderPipelineStatePtr currentPipelineState; + int32_t currentStencilReferenceValue = 0; std::vector> debugGroups; diff --git a/src/mbgl/mtl/buffer_resource.cpp b/src/mbgl/mtl/buffer_resource.cpp index a2d2db61e..ab2f75174 100644 --- a/src/mbgl/mtl/buffer_resource.cpp +++ b/src/mbgl/mtl/buffer_resource.cpp @@ -103,15 +103,22 @@ void BufferResource::update(const void* newData, std::size_t updateSize, std::si // Until we can be sure that the buffer is not still in use to render the // previous frame, replace it with a new buffer instead of updating it. - auto& device = context.getBackend().getDevice(); const uint8_t* newBufferSource = nullptr; std::unique_ptr tempBuffer; + const bool updateIsEntireBuffer = (offset == 0 && updateSize == size); + // If the entire buffer is being updated, make sure it's changed + if (updateIsEntireBuffer) { + if (memcmp(buffer->contents(), newData, updateSize) == 0) { + return; + } + } + + auto& device = context.getBackend().getDevice(); // `[MTLBuffer contents]` may involve memory mapping and/or synchronization. If the entire // buffer is being replaced, avoid accessing the old one by creating the new buffer directly // from the given data. If it's just being updated, apply the update to a local buffer to // avoid needing to access the new buffer. - const bool updateIsEntireBuffer = (offset == 0 && updateSize == size); if (updateIsEntireBuffer) { newBufferSource = static_cast(newData); } else { diff --git a/src/mbgl/mtl/drawable.cpp b/src/mbgl/mtl/drawable.cpp index 0472ed9af..f41d84d5c 100644 --- a/src/mbgl/mtl/drawable.cpp +++ b/src/mbgl/mtl/drawable.cpp @@ -223,7 +223,7 @@ void Drawable::draw(PaintParameters& parameters) const { mbgl::util::hash(getColorMode().hash(), impl->vertexDescHash)); } if (impl->pipelineState) { - encoder->setRenderPipelineState(impl->pipelineState.get()); + renderPass.setRenderPipelineState(impl->pipelineState); } else { assert(!"Failed to create render pipeline state"); return; diff --git a/src/mbgl/mtl/render_pass.cpp b/src/mbgl/mtl/render_pass.cpp index 4c8ac237a..d053016a9 100644 --- a/src/mbgl/mtl/render_pass.cpp +++ b/src/mbgl/mtl/render_pass.cpp @@ -66,6 +66,7 @@ void RenderPass::endEncoding() { } void RenderPass::resetState() { + currentPipelineState.reset(); currentDepthStencilState.reset(); currentStencilReferenceValue = 0; for (int i = 0; i < maxBinds; ++i) { @@ -193,6 +194,14 @@ void RenderPass::setFragmentSamplerState(const MTLSamplerStatePtr& state, int32_ } } +/// Set the render pipeline state +void RenderPass::setRenderPipelineState(const MTLRenderPipelineStatePtr& pipelineState) { + if (pipelineState != currentPipelineState) { + currentPipelineState = pipelineState; + encoder->setRenderPipelineState(currentPipelineState.get()); + } +} + void RenderPass::setCullMode(const MTL::CullMode mode) { if (mode != currentCullMode) { encoder->setCullMode(mode);