Batch up scheduling of deferred deletions (#3030)

This commit is contained in:
Tim Sylvester
2024-12-13 10:21:48 -08:00
committed by GitHub
parent 72d2c70130
commit 12f596ce7d
5 changed files with 62 additions and 27 deletions

View File

@@ -82,6 +82,7 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
tiles.clear();
renderedTiles.clear();
cache.deferPendingReleases();
return;
}
@@ -279,6 +280,8 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
tile.usedByRenderedLayers |= tile.layerPropertiesUpdated(layerProperties);
}
}
cache.deferPendingReleases();
}
void TilePyramid::handleWrapJump(float lng) {

View File

@@ -1,10 +1,22 @@
#include <mbgl/tile/tile_cache.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/util/instrumentation.hpp>
#include <cassert>
namespace mbgl {
TileCache::~TileCache() {
MLN_TRACE_FUNC();
clear();
pendingReleases.clear();
std::unique_lock counterLock{deferredSignalLock};
deferredSignal.wait(counterLock, [&]() { return deferredDeletionsPending == 0; });
}
void TileCache::setSize(size_t size_) {
MLN_TRACE_FUNC();
@@ -26,16 +38,20 @@ void TileCache::setSize(size_t size_) {
}
namespace {
/// This exists solely to prevent a problem where temporary lambda captures
/// are retained for the duration of the scope instead of being destroyed immediately.
template <typename T>
struct CaptureWrapper {
CaptureWrapper(std::unique_ptr<T>&& item_)
: item(std::move(item_)) {}
CaptureWrapper(std::vector<std::unique_ptr<Tile>>&& items_)
: items(items_.size()) {
std::ranges::move(items_, items.begin());
}
CaptureWrapper(CaptureWrapper&&) = default;
/// This copy constructor is required to build, but doesn't seem to be called.
CaptureWrapper(const CaptureWrapper& other)
: item(other.item) {}
std::shared_ptr<T> item;
: items(other.items) {}
std::vector<std::shared_ptr<Tile>> items;
};
} // namespace
@@ -43,6 +59,25 @@ void TileCache::deferredRelease(std::unique_ptr<Tile>&& tile) {
MLN_TRACE_FUNC();
tile->cancel();
pendingReleases.push_back(std::move(tile));
}
void TileCache::deferPendingReleases() {
MLN_TRACE_FUNC();
constexpr std::size_t scheduleThreshold = 1;
if (pendingReleases.size() < scheduleThreshold) {
return;
}
// Block destruction until the cleanup task is complete
{
std::lock_guard counterLock{deferredSignalLock};
deferredDeletionsPending++;
}
CaptureWrapper wrap{std::move(pendingReleases)};
pendingReleases.clear();
// The `std::function` must be created in a separate statement from the `schedule` call.
// Creating a `std::function` from a lambda involves a copy, which is why we must use
@@ -51,17 +86,16 @@ void TileCache::deferredRelease(std::unique_ptr<Tile>&& tile) {
// If this temporary outlives the `schedule` call, and the function is executed immediately
// by a waiting thread and is already complete, that temporary reference ends up being the
// last one and the destruction actually occurs here on this thread.
std::function<void()> func{[tile_{CaptureWrapper<Tile>{std::move(tile)}}, this]() mutable {
tile_.item = {};
std::function<void()> func{[tile_{CaptureWrapper{std::move(wrap)}}, this]() mutable {
MLN_TRACE_ZONE(deferPendingReleases lambda);
MLN_ZONE_VALUE(wrap_.releases.size());
tile_.items.clear();
std::lock_guard<std::mutex> counterLock(deferredSignalLock);
deferredDeletionsPending--;
deferredSignal.notify_all();
}};
std::unique_lock<std::mutex> counterLock(deferredSignalLock);
deferredDeletionsPending++;
threadPool.schedule(std::move(func));
}

View File

@@ -18,15 +18,7 @@ public:
TileCache(const TaggedScheduler& threadPool_, size_t size_ = 0)
: threadPool(threadPool_),
size(size_) {}
~TileCache() {
clear();
std::unique_lock<std::mutex> counterLock(deferredSignalLock);
while (deferredDeletionsPending != 0) {
deferredSignal.wait(counterLock);
}
}
~TileCache();
/// Change the maximum size of the cache.
void setSize(size_t);
@@ -43,13 +35,17 @@ public:
bool has(const OverscaledTileID& key);
void clear();
/// Destroy a tile without blocking
/// Set aside a tile to be destroyed later, without blocking
void deferredRelease(std::unique_ptr<Tile>&&);
/// Schedule any accumulated deferred tiles to be destroyed
void deferPendingReleases();
private:
std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles;
std::list<OverscaledTileID> orderedKeys;
TaggedScheduler threadPool;
std::vector<std::unique_ptr<Tile>> pendingReleases;
size_t deferredDeletionsPending{0};
std::mutex deferredSignalLock;
std::condition_variable deferredSignal;

View File

@@ -596,7 +596,7 @@ void Context::buildUniformDescriptorSetLayout(vk::UniqueDescriptorSetLayout& lay
for (size_t i = 0; i < uniformCount; ++i) {
bindings.push_back(vk::DescriptorSetLayoutBinding()
.setBinding(i)
.setBinding(static_cast<uint32_t>(i))
.setStageFlags(stageFlags)
.setDescriptorType(vk::DescriptorType::eUniformBuffer)
.setDescriptorCount(1));

View File

@@ -50,7 +50,7 @@ void DescriptorSet::createDescriptorPool(DescriptorPoolGrowable& growablePool) {
const auto descriptorPoolInfo = vk::DescriptorPoolCreateInfo(poolFlags).setPoolSizes(size).setMaxSets(maxSets);
growablePool.pools.emplace_back(device->createDescriptorPoolUnique(descriptorPoolInfo), maxSets);
growablePool.currentPoolIndex = growablePool.pools.size() - 1;
growablePool.currentPoolIndex = static_cast<int32_t>(growablePool.pools.size() - 1);
};
void DescriptorSet::allocate() {
@@ -73,7 +73,8 @@ void DescriptorSet::allocate() {
growablePool.pools.begin(), growablePool.pools.end(), [&](const auto& p) { return !p.unusedSets.empty(); });
if (unusedPoolIt != growablePool.pools.end()) {
growablePool.currentPoolIndex = std::distance(growablePool.pools.begin(), unusedPoolIt);
growablePool.currentPoolIndex = static_cast<int32_t>(
std::distance(growablePool.pools.begin(), unusedPoolIt));
} else
#endif
{
@@ -83,7 +84,8 @@ void DescriptorSet::allocate() {
[&](const auto& p) { return p.remainingSets >= layouts.size(); });
if (freePoolIt != growablePool.pools.end()) {
growablePool.currentPoolIndex = std::distance(growablePool.pools.begin(), freePoolIt);
growablePool.currentPoolIndex = static_cast<int32_t>(
std::distance(growablePool.pools.begin(), freePoolIt));
} else {
createDescriptorPool(growablePool);
}
@@ -161,7 +163,7 @@ void UniformDescriptorSet::update(const gfx::UniformBufferArray& uniforms,
.setBufferInfo(descriptorBufferInfo)
.setDescriptorCount(1)
.setDescriptorType(vk::DescriptorType::eUniformBuffer)
.setDstBinding(index)
.setDstBinding(static_cast<uint32_t>(index))
.setDstSet(descriptorSets[frameIndex]);
device->updateDescriptorSets(writeDescriptorSet, nullptr);
@@ -197,7 +199,7 @@ void ImageDescriptorSet::update(const std::array<gfx::Texture2DPtr, shaders::max
.setImageInfo(descriptorImageInfo)
.setDescriptorCount(1)
.setDescriptorType(vk::DescriptorType::eCombinedImageSampler)
.setDstBinding(id)
.setDstBinding(static_cast<uint32_t>(id))
.setDstSet(descriptorSets[frameIndex]);
device->updateDescriptorSets(writeDescriptorSet, nullptr);