mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Vulkan: Delay frees for device memory slabs.
This commit is contained in:
parent
7d5a8aa470
commit
973a9f6124
3 changed files with 81 additions and 3 deletions
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
|
|
Loading…
Add table
Reference in a new issue