diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp index 35c97386..26b7d6b9 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp @@ -93,6 +93,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const std::lock_guard lock(m_mutex); m_pendingClears.clear(); m_aliveObjects.clear(); + m_pendingCount.store(static_cast(m_pendingClears.size()), std::memory_order_relaxed); } TextureIdentity VkClearManager::MakeTextureIdentity(MG_State::GLState::ITextureObject* texture) { @@ -127,6 +128,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_pendingClears.erase(key); } m_aliveObjects.erase(identity); + m_pendingCount.store(static_cast(m_pendingClears.size()), std::memory_order_relaxed); } Bool VkClearManager::LockTextureIdentityLocked(const TextureIdentity& identity, @@ -221,6 +223,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_aliveObjects[MakeTextureIdentity(texture.get())] = texture; auto& pending = m_pendingClears[key]; MergeClearPayload(pending, clearPayload); + m_pendingCount.store(static_cast(m_pendingClears.size()), std::memory_order_relaxed); } void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload, @@ -238,6 +241,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_aliveObjects[MakeTextureIdentity(texture.get())] = texture; auto& pending = m_pendingClears[key]; MergeClearPayload(pending, clearPayload); + m_pendingCount.store(static_cast(m_pendingClears.size()), std::memory_order_relaxed); } Bool VkClearManager::HasPendingClear(MG_State::GLState::ITextureObject* texture) { @@ -245,6 +249,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } + if (m_pendingCount.load(std::memory_order_relaxed) == 0) { + return false; // per-draw hot path: nothing pending anywhere + } + const Uint64 lifetimeId = texture->GetLifetimeId(); const std::lock_guard lock(m_mutex); for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) { @@ -260,6 +268,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (key.texture == nullptr) { return false; } + if (m_pendingCount.load(std::memory_order_relaxed) == 0) { + return false; // per-draw hot path: nothing pending anywhere + } const std::lock_guard lock(m_mutex); if (m_pendingClears.find(key) == m_pendingClears.end()) { @@ -287,6 +298,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (key.texture == nullptr) { return false; } + if (m_pendingCount.load(std::memory_order_relaxed) == 0) { + return false; // per-draw hot path: nothing pending anywhere + } const std::lock_guard lock(m_mutex); if (!LockTextureLocked(key, outTexture)) { @@ -325,6 +339,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (texture == nullptr) { return false; } + if (m_pendingCount.load(std::memory_order_relaxed) == 0) { + return false; // per-draw hot path: nothing pending anywhere + } const Uint64 lifetimeId = texture->GetLifetimeId(); const std::lock_guard lock(m_mutex); @@ -345,6 +362,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { return; } + if (m_pendingCount.load(std::memory_order_relaxed) == 0) { + return; // per-draw hot path: nothing pending anywhere + } const TextureIdentity identity = MakeTextureIdentity(texture); MGLOG_D("%s: Pop all pending clears for texture %d", __func__, texture->GetExternalIndex()); const std::lock_guard lock(m_mutex); @@ -361,6 +381,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { auto it = m_pendingClears.find(key); if (it != m_pendingClears.end()) { m_pendingClears.erase(it); + m_pendingCount.store(static_cast(m_pendingClears.size()), std::memory_order_relaxed); } } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h index 3400c214..e6739193 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h @@ -14,6 +14,7 @@ #include "MG_Util/Math/VectorTypes.h" #include +#include #include namespace MobileGL::MG_Backend::DirectVulkan { @@ -121,6 +122,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint8 m_gcCounter = 0; mutable std::mutex m_mutex; + // Lock-free mirror of m_pendingClears.size(), maintained under m_mutex + // by every mutation. The per-draw probes (HasPendingClear/GetPending*) + // read it before taking the lock: during draw batches the pending set + // is almost always empty, so this turns several locked map probes per + // draw into one relaxed load. + std::atomic m_pendingCount{0}; std::unordered_map m_pendingClears; std::unordered_map, TextureIdentityHash> m_aliveObjects; };