mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 22:28:32 +09:00
[Perf] (DirectVulkan): memoize sampled-texture resources across draws
This commit is contained in:
@@ -611,6 +611,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
ReclaimCompletedUploads(/*waitAll=*/true);
|
ReclaimCompletedUploads(/*waitAll=*/true);
|
||||||
}
|
}
|
||||||
DestroyDeferredReleases();
|
DestroyDeferredReleases();
|
||||||
|
++m_resourceEraseEpoch; // every memoized resource pointer dies with the map
|
||||||
m_textureResources.clear();
|
m_textureResources.clear();
|
||||||
m_aliveObjects.clear();
|
m_aliveObjects.clear();
|
||||||
m_storageImageTextures.clear();
|
m_storageImageTextures.clear();
|
||||||
@@ -663,6 +664,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
m_aliveObjects.erase(identity);
|
m_aliveObjects.erase(identity);
|
||||||
m_storageImageTextures.erase(identity);
|
m_storageImageTextures.erase(identity);
|
||||||
|
// Invalidate every cross-draw sampled-texture memo: the erased
|
||||||
|
// resource's address may be reused by a future emplace.
|
||||||
|
++m_resourceEraseEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VkTextureManager::PruneStaleTextureAliases(MG_State::GLState::ITextureObject* texture) {
|
void VkTextureManager::PruneStaleTextureAliases(MG_State::GLState::ITextureObject* texture) {
|
||||||
@@ -718,6 +722,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cross-draw memo probe (see SyncedTextureMemoEntry): skips both map
|
||||||
|
// lookups and the (re)registration path for repeat-bound textures.
|
||||||
|
TextureResource* resourcePtr = nullptr;
|
||||||
|
for (Uint32 i = 0; i < kSyncedTextureMemoSize; ++i) {
|
||||||
|
const SyncedTextureMemoEntry& memo = m_syncedTextureMemo[i];
|
||||||
|
if (memo.texture == &texture && memo.lifetimeId == identity.lifetimeId &&
|
||||||
|
memo.eraseEpoch == m_resourceEraseEpoch) {
|
||||||
|
resourcePtr = memo.resource;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourcePtr == nullptr) {
|
||||||
auto aliveIt = m_aliveObjects.find(identity);
|
auto aliveIt = m_aliveObjects.find(identity);
|
||||||
if (aliveIt != m_aliveObjects.end() && aliveIt->second.expired()) {
|
if (aliveIt != m_aliveObjects.end() && aliveIt->second.expired()) {
|
||||||
EraseTrackedTexture(aliveIt->first);
|
EraseTrackedTexture(aliveIt->first);
|
||||||
@@ -755,8 +772,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
auto [insertIt, _] = m_textureResources.emplace(identity, Move(initial));
|
auto [insertIt, _] = m_textureResources.emplace(identity, Move(initial));
|
||||||
it = insertIt;
|
it = insertIt;
|
||||||
}
|
}
|
||||||
|
resourcePtr = &(it->second);
|
||||||
|
m_syncedTextureMemo[m_syncedTextureMemoNext] =
|
||||||
|
SyncedTextureMemoEntry{&texture, identity.lifetimeId, m_resourceEraseEpoch, resourcePtr};
|
||||||
|
m_syncedTextureMemoNext = (m_syncedTextureMemoNext + 1) % kSyncedTextureMemoSize;
|
||||||
|
}
|
||||||
|
|
||||||
if (!SyncTexture(texture, it->second)) {
|
if (!SyncTexture(texture, *resourcePtr)) {
|
||||||
MGLOG_D("%s: Syncing texture %d failed", __func__, texture.GetExternalIndex());
|
MGLOG_D("%s: Syncing texture %d failed", __func__, texture.GetExternalIndex());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -770,11 +792,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!recorded) {
|
if (!recorded) {
|
||||||
m_drawSyncedThisDraw.push_back({identity, &(it->second)});
|
m_drawSyncedThisDraw.push_back({identity, resourcePtr});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &(it->second);
|
return resourcePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageView VkTextureManager::GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel) {
|
VkImageView VkTextureManager::GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel) {
|
||||||
|
|||||||
@@ -454,6 +454,23 @@ private:
|
|||||||
TextureResource* resource = nullptr;
|
TextureResource* resource = nullptr;
|
||||||
};
|
};
|
||||||
Vector<DrawSyncedTexture> m_drawSyncedThisDraw;
|
Vector<DrawSyncedTexture> m_drawSyncedThisDraw;
|
||||||
|
// Cross-draw sampled-texture memo: the same few textures (atlas, lightmap)
|
||||||
|
// are resolved on every draw, so cache their resource pointers and skip the
|
||||||
|
// alive/resource map lookups. Node-based std::unordered_map keeps the
|
||||||
|
// pointees stable across inserts; erases bump m_resourceEraseEpoch, which
|
||||||
|
// every memo entry must match. SyncTexture still runs on memo hits, so
|
||||||
|
// content/param freshness is unaffected. A dead-then-reused texture address
|
||||||
|
// cannot false-hit: the new object carries a new lifetime id.
|
||||||
|
struct SyncedTextureMemoEntry {
|
||||||
|
const MG_State::GLState::ITextureObject* texture = nullptr;
|
||||||
|
Uint64 lifetimeId = 0;
|
||||||
|
Uint64 eraseEpoch = 0;
|
||||||
|
TextureResource* resource = nullptr;
|
||||||
|
};
|
||||||
|
static constexpr Uint32 kSyncedTextureMemoSize = 8;
|
||||||
|
SyncedTextureMemoEntry m_syncedTextureMemo[kSyncedTextureMemoSize];
|
||||||
|
Uint32 m_syncedTextureMemoNext = 0;
|
||||||
|
Uint64 m_resourceEraseEpoch = 1;
|
||||||
// Formats whose mutable-image probe failed on this device; their images are created
|
// Formats whose mutable-image probe failed on this device; their images are created
|
||||||
// without MUTABLE_FORMAT_BIT so repeat syncs neither re-probe nor flag-mismatch.
|
// without MUTABLE_FORMAT_BIT so repeat syncs neither re-probe nor flag-mismatch.
|
||||||
std::unordered_set<VkFormat> m_mutableFormatUnsupported;
|
std::unordered_set<VkFormat> m_mutableFormatUnsupported;
|
||||||
|
|||||||
Reference in New Issue
Block a user