@@ -48,6 +48,7 @@ void UboManager::FenceManager::track(DriverApi& driver, std::unordered_set<Alloc
4848
4949void UboManager::FenceManager::reclaimCompletedResources (DriverApi& driver,
5050 std::function<void (AllocationId)> const & onReclaimed) {
51+ FILAMENT_TRACING_CALL (FILAMENT_TRACING_CATEGORY_FILAMENT );
5152 uint32_t signaledCount = 0 ;
5253 bool seenSignaledFence = false ;
5354
@@ -115,8 +116,7 @@ UboManager::UboManager(DriverApi& driver, allocation_size_t defaultSlotSizeInByt
115116 reallocate (driver, defaultTotalSizeInBytes);
116117}
117118
118- void UboManager::beginFrame (DriverApi& driver,
119- const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>& materialInstances) {
119+ void UboManager::beginFrame (DriverApi& driver) {
120120 FILAMENT_TRACING_CALL (FILAMENT_TRACING_CATEGORY_FILAMENT );
121121 // Check finished frames and decrement GPU count accordingly.
122122 mFenceManager .reclaimCompletedResources (driver,
@@ -126,7 +126,7 @@ void UboManager::beginFrame(DriverApi& driver,
126126 mAllocator .releaseFreeSlots ();
127127
128128 // Traverse all MIs and see which of them need slot allocation.
129- if (allocateOnDemand (materialInstances ) == SUCCESS ) {
129+ if (allocateOnDemand () == SUCCESS ) {
130130 // No need to grow the buffer, so we can just map the buffer for writing and return.
131131 mMemoryMappedBufferHandle = driver.mapBuffer (mUbHandle , 0 , mUboSize , MapBufferAccessFlags::WRITE_BIT ,
132132 " UboManager" );
@@ -135,25 +135,19 @@ void UboManager::beginFrame(DriverApi& driver,
135135 }
136136
137137 // Calculate the required size and grow the Ubo.
138- const allocation_size_t requiredSize = calculateRequiredSize (materialInstances );
138+ const allocation_size_t requiredSize = calculateRequiredSize ();
139139 reallocate (driver, requiredSize);
140140
141141 // Allocate slots for each MI on the new Ubo.
142- allocateAllInstances (materialInstances );
142+ allocateAllInstances ();
143143
144144 // Map the buffer so that we can write to it
145145 mMemoryMappedBufferHandle =
146146 driver.mapBuffer (mUbHandle , 0 , mUboSize , MapBufferAccessFlags::WRITE_BIT , " UboManager" );
147147
148148 // Invalidate the migrated MIs, so that next commit() call must be triggered.
149- for (const auto & materialInstance : materialInstances) {
150- materialInstance.second .forEach ([](const FMaterialInstance* mi) {
151- if (!mi->isUsingUboBatching ()) {
152- return ;
153- }
154-
155- mi->getUniformBuffer ().invalidate ();
156- });
149+ for (const auto * mi : mManagedInstances ) {
150+ mi->getUniformBuffer ().invalidate ();
157151 }
158152}
159153
@@ -164,24 +158,16 @@ void UboManager::finishBeginFrame(DriverApi& driver) {
164158 }
165159}
166160
167- void UboManager::endFrame (DriverApi& driver,
168- const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>& materialInstances) {
169- BufferAllocator& allocator = mAllocator ;
161+ void UboManager::endFrame (DriverApi& driver) {
170162 std::unordered_set<AllocationId> allocationIds;
171- for (const auto & materialInstance : materialInstances) {
172- materialInstance.second .forEach ([&allocator, &allocationIds](const FMaterialInstance* mi) {
173- if (!mi->isUsingUboBatching ()) {
174- return ;
175- }
176-
177- const AllocationId id = mi->getAllocationId ();
178- if (!BufferAllocator::isValid (id)) {
179- return ;
180- }
163+ for (const auto * mi : mManagedInstances ) {
164+ const AllocationId id = mi->getAllocationId ();
165+ if (UTILS_UNLIKELY (!BufferAllocator::isValid (id))) {
166+ continue ;
167+ }
181168
182- allocator.acquireGpu (id);
183- allocationIds.insert (id);
184- });
169+ mAllocator .acquireGpu (id);
170+ allocationIds.insert (id);
185171 }
186172
187173 mFenceManager .track (driver, std::move (allocationIds));
@@ -194,76 +180,91 @@ void UboManager::terminate(DriverApi& driver) {
194180
195181void UboManager::updateSlot (DriverApi& driver, AllocationId id,
196182 BufferDescriptor bufferDescriptor) const {
197- if (!mMemoryMappedBufferHandle )
183+ if (!mMemoryMappedBufferHandle ) {
198184 return ;
185+ }
199186
200187 const allocation_size_t offset = mAllocator .getAllocationOffset (id);
201188 driver.copyToMemoryMappedBuffer (mMemoryMappedBufferHandle , offset, std::move (bufferDescriptor));
202189}
203190
204- void UboManager::retireSlot (BufferAllocator::AllocationId id) {
205- if (!BufferAllocator::isValid (id))
206- return ;
207- mAllocator .retire (id);
191+ void UboManager::manageMaterialInstance (FMaterialInstance* instance) {
192+ mPendingInstances .insert (instance);
208193}
209194
210- UboManager::AllocationResult UboManager::allocateOnDemand (
211- const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
212- materialInstances) {
213- // Collect all MIs that need allocation into two groups.
214- std::vector<FMaterialInstance*> newInstances;
215- std::vector<FMaterialInstance*> existingInstances;
216- for (const auto & [_, miList] : materialInstances) {
217- miList.forEach ([&](FMaterialInstance* mi) {
218- if (!mi->isUsingUboBatching ()) {
219- return ;
220- }
221- if (BufferAllocator::isValid (mi->getAllocationId ())) {
222- existingInstances.push_back (mi);
223- } else {
224- newInstances.push_back (mi);
225- }
226- });
195+ void UboManager::unmanageMaterialInstance (const FMaterialInstance* materialInstance) {
196+ AllocationId id = materialInstance->getAllocationId ();
197+ // const_cast should be safe here since this cast is just to match the container type.
198+ auto mi = const_cast <FMaterialInstance*>(materialInstance);
199+ mPendingInstances .erase (mi);
200+ mManagedInstances .erase (mi);
201+
202+ if (!BufferAllocator::isValid (id)) {
203+ return ;
227204 }
228205
206+ mAllocator .retire (id);
207+ }
208+
209+ UboManager::AllocationResult UboManager::allocateOnDemand () {
210+ FILAMENT_TRACING_CALL (FILAMENT_TRACING_CATEGORY_FILAMENT );
229211 bool reallocationNeeded = false ;
230212
231213 // Pass 1: Allocate slots for new material instances (that don't have a slot yet).
232- for (FMaterialInstance* mi : newInstances) {
214+ for (auto * mi : mPendingInstances ) {
215+ mManagedInstances .insert (mi);
233216 auto [newId, newOffset] = mAllocator .allocate (mi->getUniformBuffer ().getSize ());
217+
218+ // Even if the newId is not valid, we assign it to the MI so that the following process knows
219+ // this material instance was not allocated successfully. Then we can calculate the new
220+ // required UBO size properly.
234221 mi->assignUboAllocation (mUbHandle , newId, newOffset);
222+
235223 if (!BufferAllocator::isValid (newId)) {
236224 reallocationNeeded = true ;
237225 }
238226 }
227+ mPendingInstances .clear ();
239228
240229 // Pass 2: Allocate slots for existing material instances that need to be orphaned.
241- for (FMaterialInstance* mi : existingInstances) {
242- if (mi->getUniformBuffer ().isDirty () && mAllocator .isLockedByGpu (mi->getAllocationId ())) {
243- mAllocator .retire (mi->getAllocationId ());
244- auto [newId, newOffset] = mAllocator .allocate (mi->getUniformBuffer ().getSize ());
245- mi->assignUboAllocation (mUbHandle , newId, newOffset);
246- if (!BufferAllocator::isValid (newId)) {
247- reallocationNeeded = true ;
248- }
230+ for (auto * mi: mManagedInstances ) {
231+ if (!BufferAllocator::isValid (mi->getAllocationId ())) {
232+ continue ;
233+ }
234+
235+ // This instance doesn't need orphaning.
236+ if (!mi->getUniformBuffer ().isDirty () || !mAllocator .isLockedByGpu (mi->getAllocationId ())) {
237+ continue ;
238+ }
239+
240+ mAllocator .retire (mi->getAllocationId ());
241+
242+ // If the space is already not sufficient, we don't need to give another try on allocation.
243+ if (reallocationNeeded) {
244+ mi->assignUboAllocation (mUbHandle , REALLOCATION_REQUIRED , 0 );
245+ continue ;
246+ }
247+
248+ auto [newId, newOffset] = mAllocator .allocate (mi->getUniformBuffer ().getSize ());
249+
250+ // Even if the newId is not valid, we assign it to the MI so that the following process knows
251+ // this material instance was not allocated successfully. Then we can calculate the new
252+ // required UBO size properly.
253+ mi->assignUboAllocation (mUbHandle , newId, newOffset);
254+
255+ if (!BufferAllocator::isValid (newId)) {
256+ reallocationNeeded = true ;
249257 }
250258 }
251259
252260 return reallocationNeeded ? REALLOCATION_REQUIRED : SUCCESS ;
253261}
254262
255- void UboManager::allocateAllInstances (
256- const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
257- materialInstances) {
258- for (const auto & [_, miList] : materialInstances) {
259- miList.forEach ([this ](FMaterialInstance* mi) {
260- if (!mi->isUsingUboBatching ()) {
261- return ;
262- }
263- auto [newId, newOffset] = mAllocator .allocate (mi->getUniformBuffer ().getSize ());
264- assert_invariant (BufferAllocator::isValid (newId));
265- mi->assignUboAllocation (mUbHandle , newId, newOffset);
266- });
263+ void UboManager::allocateAllInstances () {
264+ for (auto * mi: mManagedInstances ) {
265+ auto [newId, newOffset] = mAllocator .allocate (mi->getUniformBuffer ().getSize ());
266+ assert_invariant (BufferAllocator::isValid (newId));
267+ mi->assignUboAllocation (mUbHandle , newId, newOffset);
267268 }
268269}
269270
@@ -288,28 +289,19 @@ void UboManager::reallocate(DriverApi& driver, allocation_size_t requiredSize) {
288289 BufferUsage::DYNAMIC | BufferUsage::SHARED_WRITE_BIT );
289290}
290291
291- allocation_size_t UboManager::calculateRequiredSize (
292- const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
293- materialInstances) {
294- BufferAllocator& allocator = mAllocator ;
292+ allocation_size_t UboManager::calculateRequiredSize () {
295293 allocation_size_t newBufferSize = 0 ;
296- for (const auto & materialInstance: materialInstances) {
297- materialInstance.second .forEach ([&newBufferSize, &allocator](const FMaterialInstance* mi) {
298- if (!mi->isUsingUboBatching ()) {
299- return ;
300- }
301-
302- const AllocationId allocationId = mi->getAllocationId ();
303- if (allocationId == BufferAllocator::REALLOCATION_REQUIRED ) {
304- // For MIs whose parameters have been updated, aside from the slot it is being
305- // occupied by the GPU, we need to preserve an additional slot for it.
306- newBufferSize += 2 * allocator.alignUp (mi->getUniformBuffer ().getSize ());
307- } else {
308- newBufferSize += allocator.alignUp (mi->getUniformBuffer ().getSize ());
309- }
310- });
294+ for (const auto * mi: mManagedInstances ) {
295+ const AllocationId allocationId = mi->getAllocationId ();
296+ if (allocationId == BufferAllocator::REALLOCATION_REQUIRED ) {
297+ // For MIs whose parameters have been updated, aside from the slot it is being
298+ // occupied by the GPU, we need to preserve an additional slot for it.
299+ newBufferSize += 2 * mAllocator .alignUp (mi->getUniformBuffer ().getSize ());
300+ } else {
301+ newBufferSize += mAllocator .alignUp (mi->getUniformBuffer ().getSize ());
302+ }
311303 }
312- return allocator .alignUp (newBufferSize * BUFFER_SIZE_GROWTH_MULTIPLIER );
304+ return mAllocator .alignUp (newBufferSize * BUFFER_SIZE_GROWTH_MULTIPLIER );
313305}
314306
315307} // namespace filament
0 commit comments