From a8622a3a4f4998a1288cef434c9510caa62fe795 Mon Sep 17 00:00:00 2001 From: Alex Cristici Date: Wed, 21 Jan 2026 02:49:45 +0200 Subject: [PATCH] Make rendering stats thread safe [Core feature] (#4059) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Adrian Cojocaru Co-authored-by: Bart Louwers --- include/mbgl/gfx/context.hpp | 13 ++++++++++ src/mbgl/mtl/texture2d.cpp | 24 ++++++++++------- src/mbgl/renderer/renderer_impl.cpp | 2 +- src/mbgl/vulkan/buffer_resource.cpp | 40 +++++++++++++++++------------ src/mbgl/vulkan/texture2d.cpp | 20 ++++++++++----- 5 files changed, 65 insertions(+), 34 deletions(-) diff --git a/include/mbgl/gfx/context.hpp b/include/mbgl/gfx/context.hpp index 0f13663d9..01816e0ec 100644 --- a/include/mbgl/gfx/context.hpp +++ b/include/mbgl/gfx/context.hpp @@ -13,6 +13,8 @@ #include #include +#include +#include namespace mbgl { @@ -93,6 +95,16 @@ public: gfx::RenderingStats& renderingStats() { return stats; } const gfx::RenderingStats& renderingStats() const { return stats; } + void threadSafeAccessRenderingStats(const std::function& function) { + std::scoped_lock lock(renderingStatsMutex); + function(stats); + } + + RenderingStats threadSafeCopyRenderingStats() { + std::shared_lock lock(renderingStatsMutex); + return stats; + } + #ifndef NDEBUG virtual void visualizeStencilBuffer() = 0; virtual void visualizeDepthBuffer(float depthRangeSize) = 0; @@ -174,6 +186,7 @@ protected: virtual std::unique_ptr createRenderbufferResource(RenderbufferPixelType, Size) = 0; virtual std::unique_ptr createDrawScopeResource() = 0; + std::shared_mutex renderingStatsMutex; gfx::RenderingStats stats; ContextObserver* observer; }; diff --git a/src/mbgl/mtl/texture2d.cpp b/src/mbgl/mtl/texture2d.cpp index 935598ec0..f95bdcf2f 100644 --- a/src/mbgl/mtl/texture2d.cpp +++ b/src/mbgl/mtl/texture2d.cpp @@ -184,9 +184,11 @@ void Texture2D::createMetalTexture() noexcept { if (metalTexture) { textureDirty = false; - context.renderingStats().numCreatedTextures++; - context.renderingStats().numActiveTextures++; - context.renderingStats().memTextures += getDataSize(); + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numCreatedTextures++; + stats.numActiveTextures++; + stats.memTextures += getDataSize(); + }); } } @@ -205,8 +207,10 @@ void Texture2D::destroyMetalTexture() noexcept { } metalTexture.reset(); - context.renderingStats().numActiveTextures--; - context.renderingStats().memTextures -= getDataSize(); + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numActiveTextures--; + stats.memTextures -= getDataSize(); + }); } gfx::Texture2D& Texture2D::setUsage(MTL::TextureUsage usage_) noexcept { @@ -250,11 +254,11 @@ void Texture2D::bind(RenderPass& renderPass, int32_t location) noexcept { renderPass.setFragmentTexture(metalTexture, location); renderPass.setFragmentSamplerState(metalSamplerState, location); - context.renderingStats().numTextureBindings++; + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { stats.numTextureBindings++; }); } void Texture2D::unbind(RenderPass&, int32_t /*location*/) noexcept { - context.renderingStats().numTextureBindings--; + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { stats.numTextureBindings--; }); } void Texture2D::upload(const void* pixelData, const Size& size_) noexcept { @@ -277,8 +281,10 @@ void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16 const MTL::Region region = MTL::Region::Make2D(xOffset, yOffset, size_.width, size_.height); const NS::UInteger bytesPerRow = size_.width * getPixelStride(); metalTexture->replaceRegion(region, 0, pixelData, bytesPerRow); - context.renderingStats().numTextureUpdates++; - context.renderingStats().textureUpdateBytes += bytesPerRow * size_.height; + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numTextureUpdates++; + stats.textureUpdateBytes += bytesPerRow * size_.height; + }); } void Texture2D::upload() noexcept { diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 0a14b3c46..814b6a29b 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -468,7 +468,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr< renderTreeParameters.loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial, renderTreeParameters.needsRepaint, renderTreeParameters.placementChanged, - context.renderingStats()); + context.threadSafeCopyRenderingStats()); if (!renderTreeParameters.loaded) { renderState = RenderState::Partial; diff --git a/src/mbgl/vulkan/buffer_resource.cpp b/src/mbgl/vulkan/buffer_resource.cpp index e8750339b..655fbcce0 100644 --- a/src/mbgl/vulkan/buffer_resource.cpp +++ b/src/mbgl/vulkan/buffer_resource.cpp @@ -105,12 +105,13 @@ BufferResource::BufferResource( } if (isValid()) { - auto& stats = context.renderingStats(); - stats.numBuffers++; - stats.memBuffers += totalSize; - stats.totalBuffers++; + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numBuffers++; + stats.memBuffers += totalSize; + stats.totalBuffers++; - stats.totalBufferObjs++; + stats.totalBufferObjs++; + }); } } @@ -128,13 +129,15 @@ BufferResource::BufferResource(BufferResource&& other) noexcept BufferResource::~BufferResource() noexcept { if (isValid()) { - context.renderingStats().numBuffers--; + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numBuffers--; - if (bufferWindowSize > 0) { - context.renderingStats().memBuffers -= bufferWindowSize * context.getBackend().getMaxFrames(); - } else { - context.renderingStats().memBuffers -= size; - } + if (bufferWindowSize > 0) { + stats.memBuffers -= bufferWindowSize * context.getBackend().getMaxFrames(); + } else { + stats.memBuffers -= size; + } + }); } if (!bufferAllocation) return; @@ -149,8 +152,10 @@ BufferResource BufferResource::clone() const { BufferResource& BufferResource::operator=(BufferResource&& other) noexcept { assert(&context == &other.context); if (isValid()) { - context.renderingStats().numBuffers--; - context.renderingStats().memBuffers -= size; + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numBuffers--; + stats.memBuffers -= size; + }); } size = other.size; @@ -178,10 +183,11 @@ void BufferResource::update(const void* newData, std::size_t updateSize, std::si std::memcpy(data, newData, updateSize); - auto& stats = context.renderingStats(); - stats.bufferUpdateBytes += updateSize; - stats.bufferUpdates++; - stats.bufferObjUpdates++; + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.bufferUpdateBytes += updateSize; + stats.bufferUpdates++; + stats.bufferObjUpdates++; + }); version++; if (version == std::numeric_limits::max()) { diff --git a/src/mbgl/vulkan/texture2d.cpp b/src/mbgl/vulkan/texture2d.cpp index d0e2968b7..067df90d6 100644 --- a/src/mbgl/vulkan/texture2d.cpp +++ b/src/mbgl/vulkan/texture2d.cpp @@ -224,8 +224,10 @@ void Texture2D::uploadSubRegion(const void* pixelData, context.enqueueDeletion(std::move(function)); } - context.renderingStats().numTextureUpdates++; - context.renderingStats().textureUpdateBytes += bufferInfo.size; + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numTextureUpdates++; + stats.textureUpdateBytes += bufferInfo.size; + }); } vk::Format Texture2D::vulkanFormat(const gfx::TexturePixelType pixel, gfx::TextureChannelDataType channel) { @@ -385,9 +387,11 @@ void Texture2D::createTexture() { imageLayout = imageCreateInfo.initialLayout; } - context.renderingStats().numCreatedTextures++; - context.renderingStats().numActiveTextures++; - context.renderingStats().memTextures += getDataSize(); + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numCreatedTextures++; + stats.numActiveTextures++; + stats.memTextures += getDataSize(); + }); textureDirty = false; lastModified = util::MonotonicTimer::now(); @@ -431,8 +435,10 @@ void Texture2D::destroyTexture() { imageLayout = vk::ImageLayout::eUndefined; - context.renderingStats().numActiveTextures--; - context.renderingStats().memTextures -= Texture2D::getDataSize(); + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numActiveTextures--; + stats.memTextures -= Texture2D::getDataSize(); + }); } }