17#define MEMORY_SIZE_2GB 2147483648LL
18#define MEMORY_SIZE_1GB 1073741824LL
19#define MEMORY_SIZE_512MB 536870912LL
20#define MEMORY_SIZE_256MB 268435456LL
35#if MTL_DEBUG_MEMORY_STATISTICS == 1
37 total_allocation_bytes_ = 0;
38 per_frame_allocation_count_ = 0;
42 allocations_in_pool_ = 0;
45 allocations_list_base_ =
nullptr;
46 allocations_list_size_ = 0;
59void MTLBufferPool::free()
61 buffer_pool_lock_.lock();
64 allocations_list_delete_all();
67 for (
int safe_pool_free_index = 0; safe_pool_free_index < completed_safelist_queue_.size();
68 safe_pool_free_index++)
70 delete completed_safelist_queue_[safe_pool_free_index];
72 completed_safelist_queue_.clear();
74 safelist_lock_.lock();
75 if (current_free_list_ !=
nullptr) {
76 delete current_free_list_;
77 current_free_list_ =
nullptr;
79 if (prev_free_buffer_list_ !=
nullptr) {
80 delete prev_free_buffer_list_;
81 prev_free_buffer_list_ =
nullptr;
83 safelist_lock_.unlock();
86 for (std::multiset<blender::gpu::MTLBufferHandle, blender::gpu::CompareMTLBuffer> *buffer_pool :
87 buffer_pools_.values())
92 buffer_pools_.
clear();
93 buffer_pool_lock_.unlock();
126 options = ([device_ hasUnifiedMemory]) ? MTLResourceStorageModeShared :
127 MTLResourceStorageModeManaged;
130 options = MTLResourceStorageModePrivate;
135 buffer_pool_lock_.lock();
137 std::multiset<MTLBufferHandle, CompareMTLBuffer> **pool_search = buffer_pools_.
lookup_ptr(
140 if (pool_search !=
nullptr) {
141 std::multiset<MTLBufferHandle, CompareMTLBuffer> *pool = *pool_search;
143 auto result = pool->lower_bound(size_compare);
144 if (result != pool->end()) {
152 if (found_size >= aligned_alloc_size &&
153 found_size <= (aligned_alloc_size * mtl_buffer_size_threshold_factor_))
156 "[MemoryAllocator] Suitable Buffer of size %lld found, for requested size: %lld",
160 new_buffer = found_buffer;
168 "[MemoryAllocator] Buffer of size %lld found, but was incompatible with requested "
172 new_buffer =
nullptr;
178 if (new_buffer ==
nullptr) {
182 allocations_list_insert(new_buffer);
188#if MTL_DEBUG_MEMORY_STATISTICS == 1
195 allocations_in_pool_ -= new_buffer->
get_size();
203#if MTL_DEBUG_MEMORY_STATISTICS == 1
204 per_frame_allocation_count_++;
208 buffer_pool_lock_.unlock();
257 safelist_lock_.lock();
258 buffer_pool_lock_.lock();
260#if MTL_DEBUG_MEMORY_STATISTICS == 1
261 int num_buffers_added = 0;
265 for (
int safe_pool_free_index = 0; safe_pool_free_index < completed_safelist_queue_.size();
266 safe_pool_free_index++)
268 MTLSafeFreeList *current_pool = completed_safelist_queue_[safe_pool_free_index];
271 while (current_pool !=
nullptr) {
272 current_pool->lock_.lock();
276 int size =
min_ii(current_pool->current_list_index_, MTLSafeFreeList::MAX_NUM_BUFFERS_);
279 while (counter < size) {
288#if MTL_DEBUG_MEMORY_STATISTICS == 1
297 current_pool->lock_.unlock();
299 current_pool =
nullptr;
302 if (next_list !=
nullptr) {
303 current_pool = next_list;
314 const time_t time_now = std::time(
nullptr);
315 for (
auto buffer_pool_list : buffer_pools_.
items()) {
316 MTLBufferPoolOrderedList *pool_allocations = buffer_pool_list.value;
317 MTLBufferPoolOrderedList::iterator pool_iterator = pool_allocations->begin();
318 while (pool_iterator != pool_allocations->end()) {
321 const time_t time_passed = time_now - handle.
insert_time;
326 time_t deletion_time_threshold_s = 600;
329 deletion_time_threshold_s = 2;
334 deletion_time_threshold_s = 4;
338 deletion_time_threshold_s = 15;
342 deletion_time_threshold_s = 60;
345 if (time_passed > deletion_time_threshold_s) {
348 allocations_list_delete(handle.buffer);
351 pool_iterator = pool_allocations->erase(pool_iterator);
352 allocations_in_pool_ -= handle.buffer_size;
353#if MTL_DEBUG_MEMORY_STATISTICS == 1
362#if MTL_DEBUG_MEMORY_STATISTICS == 1
363 printf(
"--- Allocation Stats ---\n");
364 printf(
" Num buffers processed in pool (this frame): %u\n", num_buffers_added);
366 uint framealloc = (
uint)per_frame_allocation_count_;
367 printf(
" Allocations in frame: %u\n", framealloc);
368 printf(
" Total Buffers allocated: %u\n", allocations_list_size_);
369 printf(
" Total Memory allocated: %u MB\n", (
uint)total_allocation_bytes_ / (1024 * 1024));
371 uint allocs = (
uint)(allocations_in_pool_) / 1024 / 2024;
372 printf(
" Free memory in pools: %u MB\n", allocs);
374 uint buffs = (
uint)buffers_in_pool_;
375 printf(
" Buffers in pools: %u\n", buffs);
378 auto key_iterator = buffer_pools_.
keys().begin();
379 auto value_iterator = buffer_pools_.
values().begin();
380 while (key_iterator != buffer_pools_.
keys().end()) {
383 for (
auto it = (*value_iterator)->begin(); it != (*value_iterator)->end(); it++) {
384 mem_in_pool += it->buffer_size;
388 printf(
" Buffers in pool (%u)(%llu): %u (%u MB)\n",
391 (
uint)((*value_iterator)->size()),
392 (
uint)mem_in_pool / 1024 / 1024);
397 per_frame_allocation_count_ = 0;
401 completed_safelist_queue_.clear();
402 buffer_pool_lock_.unlock();
403 safelist_lock_.unlock();
411 safe_list->lock_.lock();
413 BLI_assert(safe_list->reference_count_ == 0 &&
414 "Pool must be fully dereferenced by all in-use cmd buffers before returning.\n");
415 BLI_assert(safe_list->in_free_queue_ ==
false &&
"Pool must not already be in queue");
419 safelist_lock_.lock();
420 completed_safelist_queue_.append(safe_list);
421 safelist_lock_.unlock();
422 safe_list->lock_.unlock();
428 return current_free_list_;
433 safelist_lock_.lock();
437 prev_free_buffer_list_ = active_list;
438 safelist_lock_.unlock();
447void MTLBufferPool::ensure_buffer_pool(MTLResourceOptions
options)
449 std::multiset<MTLBufferHandle, CompareMTLBuffer> **pool_search = buffer_pools_.
lookup_ptr(
451 if (pool_search ==
nullptr) {
452 std::multiset<MTLBufferHandle, CompareMTLBuffer> *pool =
453 new std::multiset<MTLBufferHandle, CompareMTLBuffer>();
467 this->ensure_buffer_pool(
options);
477 std::multiset<MTLBufferHandle, CompareMTLBuffer> *pool = buffer_pools_.
lookup(
options);
479 allocations_in_pool_ += buffer->
get_size();
481#if MTL_DEBUG_MEMORY_STATISTICS == 1
487void MTLBufferPool::allocations_list_insert(
gpu::MTLBuffer *buffer)
495 buffer->
next = current_head;
496 buffer->
prev =
nullptr;
498 if (current_head !=
nullptr) {
499 current_head->
prev = buffer;
502 allocations_list_base_ = buffer;
503 allocations_list_size_++;
505#if MTL_DEBUG_MEMORY_STATISTICS == 1
506 total_allocation_bytes_ += buffer->
get_size();
510void MTLBufferPool::allocations_list_delete(
gpu::MTLBuffer *buffer)
521 if (prev !=
nullptr) {
526 if (
next !=
nullptr) {
531 if (allocations_list_base_ == buffer) {
532 allocations_list_base_ =
next;
535 allocations_list_size_--;
537#if MTL_DEBUG_MEMORY_STATISTICS == 1
538 total_allocation_bytes_ -= buffer->
get_size();
545void MTLBufferPool::allocations_list_delete_all()
548 while (current !=
nullptr) {
553 allocations_list_size_ = 0;
554 allocations_list_base_ =
nullptr;
556#if MTL_DEBUG_MEMORY_STATISTICS == 1
557 total_allocation_bytes_ = 0;
563 reference_count_ = 1;
564 in_free_queue_ =
false;
565 current_list_index_ = 0;
574 uint insert_index = current_list_index_++;
578 if (insert_index >= MTLSafeFreeList::MAX_NUM_BUFFERS_) {
585 std::unique_lock
lock(lock_);
587 next_list = next_.load();
590 next_.store(next_list);
597 current_list_index_ = MTLSafeFreeList::MAX_NUM_BUFFERS_;
601 safe_free_pool_[insert_index] = buffer;
610 referenced_by_workload_ =
true;
620 int ref_count = --reference_count_;
622 if (ref_count == 0) {
635 return ((reference_count_ > 1 || referenced_by_workload_) &&
636 current_list_index_ > MIN_BUFFER_FLUSH_COUNT);
655 alignment_ = alignment;
656 device_ = mtl_device;
657 is_external_ =
false;
662 metal_buffer_ = [device_ newBufferWithLength:aligned_alloc_size
options:
options];
665 size_ = aligned_alloc_size;
667 if (!(options_ & MTLResourceStorageModePrivate)) {
668 data_ = [metal_buffer_ contents];
683 metal_buffer_ = external_buffer;
684 [metal_buffer_ retain];
690 options_ = [metal_buffer_ resourceOptions];
691 size_ = [metal_buffer_ allocatedSize];
693 data_ = [metal_buffer_ contents];
702 if (metal_buffer_ != nil) {
703 [metal_buffer_ release];
714 if (metal_buffer_ != nil) {
715 [metal_buffer_ release];
723 return metal_buffer_;
728 BLI_assert(!(options_ & MTLResourceStorageModePrivate));
746 return options_ & MTLResourceStorageModeManaged;
751 metal_buffer_.label =
str;
759 "Buffer should be marked as 'in-use' if being actively used by an instance. Buffer "
760 "has likely already been freed.");
765 this->debug_ensure_used();
766 if (this->requires_flush()) {
767 [metal_buffer_ didModifyRange:NSMakeRange(0, size_)];
773 this->debug_ensure_used();
774 if (this->requires_flush()) {
776 [metal_buffer_ didModifyRange:NSMakeRange(offset, length)];
792 BLI_assert(size_used > 0 && size_used <= size_);
793 usage_size_ = size_used;
809 return this->options & MTLResourceStorageModeManaged;
837 if (!this->initialised_) {
841 for (
int sb = 0; sb < mtl_max_scratch_buffers_; sb++) {
845 BLI_assert(&(scratch_buffers_[sb]->own_context_) == &context_);
847 current_scratch_buffer_ = 0;
854 initialised_ =
false;
857 for (
int sb = 0; sb < mtl_max_scratch_buffers_; sb++) {
858 delete scratch_buffers_[sb];
859 scratch_buffers_[sb] =
nullptr;
861 current_scratch_buffer_ = 0;
873 alignment =
max_uu(alignment, 256);
875 BLI_assert_msg(current_scratch_buffer_ >= 0,
"Scratch Buffer index not set");
876 MTLCircularBuffer *current_scratch_buff = this->scratch_buffers_[current_scratch_buffer_];
877 BLI_assert_msg(current_scratch_buff !=
nullptr,
"Scratch Buffer does not exist");
880 BLI_assert(allocated_range.
size >= alloc_size && allocated_range.
size <= alloc_size + alignment);
882 return allocated_range;
888 MTLCircularBuffer *active_scratch_buf = scratch_buffers_[current_scratch_buffer_];
889 BLI_assert(&active_scratch_buf->own_context_ == &context_);
896 current_scratch_buffer_ = (current_scratch_buffer_ + 1) % mtl_max_scratch_buffers_;
897 active_scratch_buf = scratch_buffers_[current_scratch_buffer_];
898 active_scratch_buf->
reset();
899 BLI_assert(&active_scratch_buf->own_context_ == &context_);
900 MTL_LOG_INFO(
"Scratch buffer %d reset - (ctx %p)(Frame index: %d)",
901 current_scratch_buffer_,
910 MTLCircularBuffer *active_scratch_buf = scratch_buffers_[current_scratch_buffer_];
911 BLI_assert(&active_scratch_buf->own_context_ == &context_);
912 active_scratch_buf->
flush();
920 MTLResourceOptions
options = ([own_context_.
device hasUnifiedMemory]) ?
921 MTLResourceStorageModeShared :
922 MTLResourceStorageModeManaged;
925 can_resize_ = allow_grow;
929 last_flush_base_offset_ = 0;
933 cbuffer_->
set_label(
@"Circular Scratch Buffer");
953 alignment =
max_uu(alignment, 256);
958 bool can_allocate = (aligned_current_offset + aligned_alloc_size) < cbuffer_->
get_size();
960 BLI_assert(aligned_current_offset >= current_offset_);
963 BLI_assert(aligned_current_offset % alignment == 0);
964 BLI_assert(aligned_alloc_size % alignment == 0);
975 aligned_current_offset + aligned_alloc_size);
977#if MTL_SCRATCH_BUFFER_ALLOW_TEMPORARY_EXPANSION == 1
986 new_size = aligned_alloc_size;
987 MTL_LOG_INFO(
"Temporarily growing Scratch buffer to %d MB", (
int)new_size / 1024 / 1024);
991 MTL_LOG_INFO(
"Shrinking Scratch buffer back to %d MB", (
int)new_size / 1024 / 1024);
998 if (aligned_alloc_size > new_size) {
1004 alloc_range.
data =
nullptr;
1006 alloc_range.
size = 0;
1007 alloc_range.
options = cbuffer_->options;
1013 "Performance Warning: Reached the end of circular buffer of size: %llu, but cannot "
1014 "resize. Starting new buffer",
1021 alloc_range.
data =
nullptr;
1023 alloc_range.
size = 0;
1037 current_offset_ = 0;
1038 last_flush_base_offset_ = 0;
1042 cbuffer_->
set_label(
@"Circular Scratch Buffer");
1044 MTL_LOG_INFO(
"Resized Metal circular buffer to %llu bytes", new_size);
1047 aligned_current_offset = 0;
1055 aligned_current_offset);
1057 alloc_range.
size = aligned_alloc_size;
1062 current_offset_ = aligned_current_offset + aligned_alloc_size;
1063 BLI_assert(current_offset_ <= cbuffer_->get_size());
1071 uint64_t len = current_offset_ - last_flush_base_offset_;
1074 last_flush_base_offset_ = current_offset_;
1083 if (current_offset_ > 0) {
1088 "Trying to reset Circular scratch buffer's while its data is still being used by "
1089 "an in-flight frame");
1091 current_offset_ = 0;
1092 last_flush_base_offset_ = 0;
#define BLI_assert_msg(a, msg)
MINLINE int min_ii(int a, int b)
MINLINE uint max_uu(uint a, uint b)
MINLINE uint64_t ceil_to_multiple_ul(uint64_t a, uint64_t b)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
const Value * lookup_ptr(const Key &key) const
const Value & lookup(const Key &key) const
ValueIterator values() const
void add_new(const Key &key, const Value &value)
ItemIterator items() const
void push_completed_safe_list(MTLSafeFreeList *list)
void init(id< MTLDevice > device)
gpu::MTLBuffer * allocate_with_data(uint64_t size, bool cpu_visible, const void *data=nullptr)
MTLSafeFreeList * get_current_safe_list()
void update_memory_pools()
void begin_new_safe_list()
gpu::MTLBuffer * allocate(uint64_t size, bool cpu_visible)
gpu::MTLBuffer * allocate_aligned(uint64_t size, uint alignment, bool cpu_visible)
gpu::MTLBuffer * allocate_aligned_with_data(uint64_t size, uint alignment, bool cpu_visible, const void *data=nullptr)
bool free_buffer(gpu::MTLBuffer *buffer)
void flag_in_use(bool used)
uint64_t get_size() const
void set_usage_size(uint64_t size_used)
uint64_t get_size_used() const
void * get_host_ptr() const
void flush_range(uint64_t offset, uint64_t length)
MTLBuffer(id< MTLDevice > device, uint64_t size, MTLResourceOptions options, uint alignment=1)
void set_label(NSString *str)
MTLResourceOptions get_resource_options()
id< MTLBuffer > get_metal_buffer() const
MTLCircularBuffer(MTLContext &ctx, uint64_t initial_size, bool allow_grow)
MTLTemporaryBuffer allocate_range_aligned(uint64_t alloc_size, uint alignment)
MTLTemporaryBuffer allocate_range(uint64_t alloc_size)
uint get_current_frame_index()
static MTLBufferPool * get_global_memory_manager()
void increment_reference()
void decrement_reference()
void insert_buffer(gpu::MTLBuffer *buffer)
static constexpr uint mtl_scratch_buffer_max_size_
static constexpr uint mtl_scratch_buffer_initial_size_
MTLTemporaryBuffer scratch_buffer_allocate_range_aligned(uint64_t alloc_size, uint alignment)
void flush_active_scratch_buffer()
void ensure_increment_scratch_buffer()
~MTLScratchBufferManager()
MTLTemporaryBuffer scratch_buffer_allocate_range(uint64_t alloc_size)
CCL_NAMESPACE_BEGIN struct Options options
MINLINE unsigned long long min_ulul(unsigned long long a, unsigned long long b)
MINLINE unsigned long long max_ulul(unsigned long long a, unsigned long long b)
#define MTL_NUM_SAFE_FRAMES
#define MTL_LOG_INFO(info,...)
#define MTL_LOG_WARNING(info,...)
#define MEMORY_SIZE_512MB
#define MEMORY_SIZE_256MB
SymEdge< T > * prev(const SymEdge< T > *se)
unsigned __int64 uint64_t
MTLResourceOptions options
id< MTLBuffer > metal_buffer