From 6f53b9a6bbcf266783568c3357e12f14f91f3270 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sun, 12 Jul 2026 10:51:15 -0400 Subject: [PATCH] [Perf] (MG_Backend/DirectVulkan): raw-ptr sampled-texture walk skips SharedPtr refcount churn per draw --- .../DirectVulkan/Renderer/UniformManager.cpp | 27 ++++++++++++++++--- .../DirectVulkan/Renderer/UniformManager.h | 6 +++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp index aa8fc7dc..90f77342 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp @@ -349,6 +349,25 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } + MG_State::GLState::ITextureObject* UniformManager::ResolveSamplerTextureRaw( + const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, + Uint32 binding) { + MOBILEGL_ASSERT(MG_State::pGLContext != nullptr, "ResolveSamplerTextureRaw: GL context is null"); + MOBILEGL_ASSERT(binding < programObj.samplerUniformLocationByBinding.size(), + "ResolveSamplerTextureRaw: sampler location binding %u out of range", binding); + MOBILEGL_ASSERT(binding < programObj.samplerTextureTargetByBinding.size(), + "ResolveSamplerTextureRaw: sampler target binding %u out of range", binding); + + const Int location = programObj.samplerUniformLocationByBinding[binding]; + const Int unit = ResolveSamplerUnitIndex(program, location, binding); + + auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); + const TextureTarget preferredTarget = programObj.samplerTextureTargetByBinding[binding]; + // GetBoundObject() returns the SharedPtr by const ref; .get() reads the pointer without + // touching the refcount (no atomic inc/dec per binding per draw). + return textureUnit.GetBindingSlot(preferredTarget).GetBoundObject().get(); + } + Bool UniformManager::ResolveTexelBufferDescriptor(const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, Uint32 binding, Uint32 frameIndex, @@ -563,14 +582,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { continue; } - SharedPtr texture; - if (!ResolveSamplerTexture(program, programObj, binding, texture) || !texture) { + MG_State::GLState::ITextureObject* texture = ResolveSamplerTextureRaw(program, programObj, binding); + if (!texture) { continue; } - auto found = std::find(outTextures.begin(), outTextures.end(), texture.get()); + auto found = std::find(outTextures.begin(), outTextures.end(), texture); if (found == outTextures.end()) { - outTextures.push_back(texture.get()); + outTextures.push_back(texture); } } return true; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h index 652b119b..00ffc4d3 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h @@ -73,6 +73,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { static Bool ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, Uint32 binding, SharedPtr& outTexture); + // Raw-pointer variant for the per-draw sampled-texture walk (CollectSampledTextures): + // the bound texture stays alive through the draw via GL binding state, so callers that + // only need the pointer skip the SharedPtr copy's atomic refcount churn. + static MG_State::GLState::ITextureObject* ResolveSamplerTextureRaw( + const MG_State::GLState::ProgramObject& program, + const ProgramFactory::VkProgramObject& programObj, Uint32 binding); SharedPtr GetFallbackTexture(TextureTarget target) const; Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, Uint32 binding,