Vulkan: Delay frees for device memory slabs.

This commit is contained in:
Unknown W. Brackets 2016-03-24 23:27:14 -07:00
parent 7d5a8aa470
commit 973a9f6124
3 changed files with 81 additions and 3 deletions

View file

@ -56,6 +56,16 @@ struct VulkanPhysicalDeviceInfo {
// This is a bit repetitive...
class VulkanDeleteList {
struct Callback {
explicit Callback(void (*f)(void *userdata1, void *userdata2), void *u1, void *u2)
: func(f), userdata1(u1), userdata2(u2) {
}
void (*func)(void *userdata1, void *userdata2);
void *userdata1;
void *userdata2;
};
public:
void QueueDeleteDescriptorPool(VkDescriptorPool pool) { descPools_.push_back(pool); }
void QueueDeleteShaderModule(VkShaderModule module) { modules_.push_back(module); }
@ -68,6 +78,7 @@ public:
void QueueDeletePipelineCache(VkPipelineCache pipelineCache) { pipelineCaches_.push_back(pipelineCache); }
void QueueDeleteRenderPass(VkRenderPass renderPass) { renderPasses_.push_back(renderPass); }
void QueueDeleteFramebuffer(VkFramebuffer framebuffer) { framebuffers_.push_back(framebuffer); }
void QueueCallback(void (*func)(void *userdata1, void *userdata2), void *userdata1, void *userdata2) { callbacks_.push_back(Callback(func, userdata1, userdata2)); }
void Take(VulkanDeleteList &del) {
assert(descPools_.size() == 0);
@ -81,6 +92,7 @@ public:
assert(pipelineCaches_.size() == 0);
assert(renderPasses_.size() == 0);
assert(framebuffers_.size() == 0);
assert(callbacks_.size() == 0);
descPools_ = std::move(del.descPools_);
modules_ = std::move(del.modules_);
buffers_ = std::move(del.buffers_);
@ -92,6 +104,7 @@ public:
pipelineCaches_ = std::move(del.pipelineCaches_);
renderPasses_ = std::move(del.renderPasses_);
framebuffers_ = std::move(del.framebuffers_);
callbacks_ = std::move(del.callbacks_);
}
void PerformDeletes(VkDevice device) {
@ -139,6 +152,10 @@ public:
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
framebuffers_.clear();
for (auto &callback : callbacks_) {
callback.func(callback.userdata1, callback.userdata2);
}
callbacks_.clear();
}
private:
@ -153,6 +170,7 @@ private:
std::vector<VkPipelineCache> pipelineCaches_;
std::vector<VkRenderPass> renderPasses_;
std::vector<VkFramebuffer> framebuffers_;
std::vector<Callback> callbacks_;
};
// VulkanContext sets up the basics necessary for rendering to a window, including framebuffers etc.

View file

@ -198,6 +198,45 @@ bool VulkanDeviceAllocator::AllocateFromSlab(Slab &slab, size_t &start, size_t b
}
void VulkanDeviceAllocator::Free(VkDeviceMemory deviceMemory, size_t offset) {
// First, let's validate. This will allow stack traces to tell us when frees are bad.
size_t start = offset >> SLAB_GRAIN_SHIFT;
bool found = false;
for (Slab &slab : slabs_) {
if (slab.deviceMemory != deviceMemory) {
continue;
}
auto it = slab.allocSizes.find(start);
if (it == slab.allocSizes.end()) {
// Ack, a double free?
Crash();
}
if (slab.usage[start] != 1) {
// This means a double free, while queued to actually free.
Crash();
}
// Mark it as "free in progress".
slab.usage[start] = 2;
found = true;
break;
}
// Wrong deviceMemory even? Maybe it was already decimated, but that means a double-free.
if (!found) {
Crash();
}
// Okay, now enqueue. It's valid.
FreeInfo *info = new FreeInfo(deviceMemory, offset);
vulkan_->Delete().QueueCallback(&DispatchFree, this, info);
}
void VulkanDeviceAllocator::ExecuteFree(FreeInfo *userdata) {
VkDeviceMemory deviceMemory = userdata->deviceMemory;
size_t offset = userdata->offset;
// Revalidate in case something else got freed and made things inconsistent.
size_t start = offset >> SLAB_GRAIN_SHIFT;
bool found = false;
for (Slab &slab : slabs_) {
@ -206,21 +245,26 @@ void VulkanDeviceAllocator::Free(VkDeviceMemory deviceMemory, size_t offset) {
}
auto it = slab.allocSizes.find(start);
// Let's hope we don't get any double-frees or misfrees.
assert(it == slab.allocSizes.end());
if (it != slab.allocSizes.end()) {
size_t size = it->second;
for (size_t i = 0; i < size; ++i) {
slab.usage[start + i] = 0;
}
slab.allocSizes.erase(it);
} else {
// Ack, a double free?
Crash();
}
found = true;
break;
}
// Wrong deviceMemory even? Maybe it was already decimated, but that means a double-free.
assert(found);
if (!found) {
Crash();
}
delete userdata;
}
bool VulkanDeviceAllocator::AllocateSlab(size_t minBytes) {

View file

@ -163,9 +163,25 @@ private:
}
};
struct FreeInfo {
explicit FreeInfo(VkDeviceMemory d, size_t o)
: deviceMemory(d), offset(o) {
}
VkDeviceMemory deviceMemory;
size_t offset;
};
static void DispatchFree(void *thiz, void *userdata) {
auto allocator = static_cast<VulkanDeviceAllocator *>(thiz);
auto freeInfo = static_cast<FreeInfo *>(userdata);
allocator->ExecuteFree(freeInfo);
}
bool AllocateSlab(size_t minBytes);
bool AllocateFromSlab(Slab &slab, size_t &start, size_t blocks);
void Decimate();
void ExecuteFree(FreeInfo *userdata);
VulkanContext *vulkan_;
std::vector<Slab> slabs_;