Prevent circular buffer overflow during UboManager reallocation (#9714)

When UboManager::reallocate() is triggered, a large number of material instances may be invalidated simultaneously. This leads to a massive spike in descriptor set updates and command generation, which can overflow the circular buffer.

To prevent this, we now flush commands in batches, we trigger a flush whenever the command buffer usage exceeds half of its capacity. (Like what RenderPass::Executor::execute does)

BUGS = [474264976, 479079631]
This commit is contained in:
Doris Wu
2026-02-14 00:39:14 +08:00
committed by doriswu
parent e00be09af6
commit 487f4d077d

View File

@@ -735,14 +735,21 @@ void FEngine::prepare(DriverApi& driver) {
}
UboManager* uboManager = mUboManager;
size_t const capacity = getMinCommandBufferSize();
for (auto& materialInstanceList: mMaterialInstances) {
materialInstanceList.second.forEach([&driver, uboManager](FMaterialInstance const* item) {
// post-process materials instances must be commited explicitly because their
// parameters are typically not set at this point in time.
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
item->commit(driver, uboManager);
}
});
materialInstanceList.second.forEach(
[this, &driver, uboManager, capacity](FMaterialInstance const* item) {
// post-process materials instances must be commited explicitly because their
// parameters are typically not set at this point in time.
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
// If the remaining space is less than half the capacity, we flush right
// away to allow some headroom for commands that might come later.
if (UTILS_UNLIKELY(driver.getCircularBuffer().getUsed() > capacity / 2)) {
flush();
}
item->commit(driver, uboManager);
}
});
}
if (useUboBatching) {