From d5bc75376404d8c53c2ca7be97dd90ff041bb89e Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sun, 12 Jul 2026 03:41:17 -0400 Subject: [PATCH] [Perf] (MG_Backend/DirectGLES): skip scratch bind + upload for fully-synced mipmap textures --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index c724efec..531ee101 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -1128,6 +1128,46 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } + // Fast path: a fully-synced mipmap texture is the common per-draw case. + // SyncNeccessaryTextures re-syncs every bound texture each draw, and the + // scratch Bind below targets the temp unit - which sequential distinct + // textures thrash, forcing a real glBindTexture per texture per draw. When + // nothing needs uploading, skip the bind + upload machinery entirely; + // BindCurrentTextures() re-establishes the real sampling bindings regardless. + if (m_isInitialized && stateTextureObject->GetStorageType() == TextureStorageType::Mipmap) { + auto* mipmapObject = + static_cast(stateTextureObject.get()); + const auto probeBaseSize = stateTextureObject->GetBaseSize(); + StateTextureBasicInfo probe = {stateTextureObject->GetFormat(), + static_cast(probeBaseSize.x()), + static_cast(probeBaseSize.y()), + static_cast(probeBaseSize.z()), + static_cast(mipmapObject->GetMipmapLevelCount()), + 0, + stateTextureObject->GetSamples(), + stateTextureObject->HasFixedSampleLocations()}; + // Equal info => needsRegeneration is false, and canAppendMipmaps is + // false too (it requires strictly more mip levels than the last sync). + // So the only remaining work would be re-uploading dirty levels. + if (probe == m_prevTextureInfo) { + Bool anyDirty = false; + for (const auto& uploadTarget : mipmapObject->GetUploadTargets()) { + for (SizeT level = 0; level < probe.mipmapLevels; ++level) { + if (mipmapObject->IsStorageDirty(uploadTarget, level)) { + anyDirty = true; + break; + } + } + if (anyDirty) break; + } + if (!anyDirty) { + MGLOG_D("Texture ID %u already fully synced, skipping scratch bind + upload.", + m_backendTextureId); + return; + } + } + } + Bind(target); DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());