vk: make fbo eviction time configurable (#9808)

The previous eviction time is too large (40), then causes memory
to bloat over time. We set the default to 3 (as in triple
buffering), but make it a configurable option via VulkanPlatform.

Fixes #9786
This commit is contained in:
Powei Feng
2026-03-20 17:57:27 -07:00
parent 90254338d6
commit cab8a89346
4 changed files with 39 additions and 24 deletions

View File

@@ -177,6 +177,12 @@ public:
* presentation. Default is true.
*/
bool transitionSwapChainImageLayoutForPresent = true;
/**
* The number of frames before an unused framebuffer is evicted from the cache.
* Default is 3.
*/
uint32_t timeBeforeEvictionFbo = 3;
};
/**

View File

@@ -260,7 +260,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mPipelineCache(*this, mPlatform->getDevice(), mContext),
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
mBufferCache(mContext, mResourceManager, mAllocator),
mFramebufferCache(mPlatform->getDevice()),
mFramebufferCache(mPlatform->getDevice(),
mPlatform->getCustomization().timeBeforeEvictionFbo),
mYcbcrConversionCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),

View File

@@ -20,6 +20,7 @@
#include "VulkanHandles.h"
#include "vulkan/utils/Image.h"
#include <utils/compiler.h>
#include <utils/Panic.h>
// If any VkRenderPass or VkFramebuffer is unused for more than TIME_BEFORE_EVICTION frames, it
@@ -62,8 +63,9 @@ bool VulkanFboCache::FboKeyEqualFn::operator()(const FboKey& k1, const FboKey& k
return true;
}
VulkanFboCache::VulkanFboCache(VkDevice device)
: mDevice(device) {}
VulkanFboCache::VulkanFboCache(VkDevice device, uint32_t timeBeforeEvictionFbo)
: mDevice(device),
mTimeBeforeEvictionFbo(timeBeforeEvictionFbo) {}
VulkanFboCache::~VulkanFboCache() {
FILAMENT_CHECK_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty())
@@ -376,31 +378,36 @@ void VulkanFboCache::gc() noexcept {
FVK_SYSTRACE_START("fbocache::gc");
// If this is one of the first few frames, return early to avoid wrapping unsigned integers.
if (++mCurrentTime <= TIME_BEFORE_EVICTION) {
return;
}
const uint32_t evictTime = mCurrentTime - TIME_BEFORE_EVICTION;
++mCurrentTime;
for (FboMap::iterator iter = mFramebufferCache.begin(); iter != mFramebufferCache.end(); ) {
const FboVal fbo = iter->second;
if (fbo.timestamp < evictTime && fbo.handle) {
mRenderPassRefCount[iter->first.renderPass]--;
if (UTILS_UNLIKELY(mCurrentTime > mTimeBeforeEvictionFbo)) {
const uint32_t evictTimeFbo = mCurrentTime - mTimeBeforeEvictionFbo;
for (FboMap::iterator iter = mFramebufferCache.begin(); iter != mFramebufferCache.end();) {
const FboVal fbo = iter->second;
if (fbo.timestamp < evictTimeFbo && fbo.handle) {
mRenderPassRefCount[iter->first.renderPass]--;
// erase(iterator) returns the iterator to the next element.
iter = mFramebufferCache.erase(iter);
} else {
++iter;
// erase(iterator) returns the iterator to the next element.
iter = mFramebufferCache.erase(iter);
} else {
++iter;
}
}
}
for (RenderPassMap::iterator iter = mRenderPassCache.begin(); iter != mRenderPassCache.end(); ) {
const VkRenderPass handle = iter->second.handle->getVkRenderPass();
if (iter->second.timestamp < evictTime && handle && mRenderPassRefCount[handle] == 0) {
// erase(iterator) returns the iterator to the next element.
iter = mRenderPassCache.erase(iter);
mRenderPassRefCount.erase(handle);
} else {
++iter;
if (UTILS_UNLIKELY(mCurrentTime > TIME_BEFORE_EVICTION)) {
const uint32_t evictTimeRp = mCurrentTime - TIME_BEFORE_EVICTION;
for (RenderPassMap::iterator iter = mRenderPassCache.begin();
iter != mRenderPassCache.end();) {
const VkRenderPass handle = iter->second.handle->getVkRenderPass();
if (iter->second.timestamp < evictTimeRp && handle &&
mRenderPassRefCount[handle] == 0) {
// erase(iterator) returns the iterator to the next element.
iter = mRenderPassCache.erase(iter);
mRenderPassRefCount.erase(handle);
} else {
++iter;
}
}
}

View File

@@ -100,7 +100,7 @@ public:
bool operator()(const FboKey& k1, const FboKey& k2) const;
};
explicit VulkanFboCache(VkDevice device);
explicit VulkanFboCache(VkDevice device, uint32_t timeBeforeEvictionFbo);
~VulkanFboCache();
// Retrieves or creates a VkFramebuffer handle.
@@ -130,6 +130,7 @@ private:
RenderPassMap mRenderPassCache;
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;
uint32_t mCurrentTime = 0;
uint32_t mTimeBeforeEvictionFbo;
};
} // namespace filament::backend