Fix #128258: Vulkan: Memory leak preview job rendering
When performing preview job rendering the memory wasn't recycled leading to a memory leak. For background rendering we already recycled memory in a correct way. This change enables the same branch during preview rendering. Also adds a better `VKDevice::debug_print` to see the resources being tracked by the different threads and resource pools. Pull Request: https://projects.blender.org/blender/blender/pulls/128377
This commit is contained in:
@@ -466,10 +466,15 @@ void VKBackend::render_end()
|
||||
thread_data.rendering_depth -= 1;
|
||||
BLI_assert_msg(thread_data.rendering_depth >= 0, "Unbalanced `GPU_render_begin/end`");
|
||||
|
||||
if (G.background) {
|
||||
if (G.background || !BLI_thread_is_main()) {
|
||||
/* When **not** running on the main thread (or doing background rendering) we assume that there
|
||||
* is no swap chain in play. Rendering happens on a single thread and when finished all the
|
||||
* resources have been used and are in a state that they can be discarded. It can still be that
|
||||
* a non-main thread discards a resource that is in use by another thread. We move discarded
|
||||
* resources to a device global discard pool (`device.orphaned_data`). The next time the main
|
||||
* thread goes to the next swap chain image the device global discard pool will be added to the
|
||||
* discard pool of the new swap chain image.*/
|
||||
if (thread_data.rendering_depth == 0) {
|
||||
thread_data.resource_pool_next();
|
||||
|
||||
VKResourcePool &resource_pool = thread_data.resource_pool_get();
|
||||
resource_pool.discard_pool.destroy_discarded_resources(device);
|
||||
resource_pool.reset();
|
||||
|
||||
@@ -124,7 +124,14 @@ void VKContext::deactivate()
|
||||
|
||||
void VKContext::begin_frame() {}
|
||||
|
||||
void VKContext::end_frame() {}
|
||||
void VKContext::end_frame()
|
||||
{
|
||||
/* Enable this to track how resources are managed per thread and resource pool. */
|
||||
#if 0
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
device.debug_print();
|
||||
#endif
|
||||
}
|
||||
|
||||
void VKContext::flush() {}
|
||||
|
||||
|
||||
@@ -435,8 +435,38 @@ void VKDevice::memory_statistics_get(int *r_total_mem_kb, int *r_free_mem_kb) co
|
||||
/** \name Debugging/statistics
|
||||
* \{ */
|
||||
|
||||
void VKDevice::debug_print(std::ostream &os, const VKDiscardPool &discard_pool)
|
||||
{
|
||||
if (discard_pool.images_.is_empty() && discard_pool.buffers_.is_empty() &&
|
||||
discard_pool.image_views_.is_empty() && discard_pool.shader_modules_.is_empty() &&
|
||||
discard_pool.pipeline_layouts_.is_empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
os << " Discardable resources: ";
|
||||
if (!discard_pool.images_.is_empty()) {
|
||||
os << "VkImage=" << discard_pool.images_.size() << " ";
|
||||
}
|
||||
if (!discard_pool.image_views_.is_empty()) {
|
||||
os << "VkImageView=" << discard_pool.image_views_.size() << " ";
|
||||
}
|
||||
if (!discard_pool.buffers_.is_empty()) {
|
||||
os << "VkBuffer=" << discard_pool.buffers_.size() << " ";
|
||||
}
|
||||
if (!discard_pool.shader_modules_.is_empty()) {
|
||||
os << "VkShaderModule=" << discard_pool.shader_modules_.size() << " ";
|
||||
}
|
||||
if (!discard_pool.pipeline_layouts_.is_empty()) {
|
||||
os << "VkPipelineLayout=" << discard_pool.pipeline_layouts_.size();
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
void VKDevice::debug_print()
|
||||
{
|
||||
BLI_assert_msg(BLI_thread_is_main(),
|
||||
"VKDevice::debug_print can only be called from the main thread.");
|
||||
|
||||
std::ostream &os = std::cout;
|
||||
|
||||
os << "Pipelines\n";
|
||||
@@ -444,6 +474,23 @@ void VKDevice::debug_print()
|
||||
os << " Compute: " << pipelines.compute_pipelines_.size() << "\n";
|
||||
os << "Descriptor sets\n";
|
||||
os << " VkDescriptorSetLayouts: " << descriptor_set_layouts_.size() << "\n";
|
||||
for (const VKThreadData *thread_data : thread_data_) {
|
||||
/* NOTE: Assumption that this is always called form the main thread. This could be solved by
|
||||
* keeping track of the main thread inside the thread data.*/
|
||||
const bool is_main = pthread_equal(thread_data->thread_id, pthread_self());
|
||||
os << "ThreadData" << (is_main ? " (main-thread)" : "") << ")\n";
|
||||
os << " Rendering_depth: " << thread_data->rendering_depth << "\n";
|
||||
os << " Number of contexts: " << thread_data->num_contexts << "\n";
|
||||
for (int resource_pool_index : IndexRange(thread_data->resource_pools.size())) {
|
||||
const VKResourcePool &resource_pool = thread_data->resource_pools[resource_pool_index];
|
||||
const bool is_active = thread_data->resource_pool_index == resource_pool_index;
|
||||
os << " Resource Pool (index=" << resource_pool_index << (is_active ? " active" : "")
|
||||
<< ")\n";
|
||||
debug_print(os, resource_pool.discard_pool);
|
||||
}
|
||||
}
|
||||
os << "Orphaned data\n";
|
||||
debug_print(os, orphaned_data);
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ struct VKWorkarounds {
|
||||
* Shared resources between contexts that run in the same thread.
|
||||
*/
|
||||
class VKThreadData : public NonCopyable, NonMovable {
|
||||
static constexpr uint32_t resource_pools_count = 3;
|
||||
|
||||
public:
|
||||
/** Thread ID this instance belongs to. */
|
||||
pthread_t thread_id;
|
||||
@@ -70,7 +72,7 @@ class VKThreadData : public NonCopyable, NonMovable {
|
||||
* NOTE: Initialized to `UINT32_MAX` to detect first change.
|
||||
*/
|
||||
uint32_t resource_pool_index = UINT32_MAX;
|
||||
std::array<VKResourcePool, 5> resource_pools;
|
||||
std::array<VKResourcePool, resource_pools_count> resource_pools;
|
||||
|
||||
/**
|
||||
* The current rendering depth.
|
||||
@@ -112,7 +114,7 @@ class VKThreadData : public NonCopyable, NonMovable {
|
||||
resource_pool_index = 1;
|
||||
}
|
||||
else {
|
||||
resource_pool_index = (resource_pool_index + 1) % 5;
|
||||
resource_pool_index = (resource_pool_index + 1) % resource_pools_count;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -309,6 +311,7 @@ class VKDevice : public NonCopyable {
|
||||
Span<std::reference_wrapper<VKContext>> contexts_get() const;
|
||||
|
||||
void memory_statistics_get(int *r_total_mem_kb, int *r_free_mem_kb) const;
|
||||
static void debug_print(std::ostream &os, const VKDiscardPool &discard_pool);
|
||||
void debug_print();
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "vk_immediate.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKDevice;
|
||||
|
||||
/**
|
||||
* Pool of resources that are discarded, but can still be in used and cannot be destroyed.
|
||||
@@ -26,6 +27,8 @@ namespace blender::gpu {
|
||||
* screen.
|
||||
*/
|
||||
class VKDiscardPool {
|
||||
friend class VKDevice;
|
||||
|
||||
private:
|
||||
Vector<std::pair<VkImage, VmaAllocation>> images_;
|
||||
Vector<std::pair<VkBuffer, VmaAllocation>> buffers_;
|
||||
|
||||
Reference in New Issue
Block a user