Blender V5.0
MEM_CacheLimiter.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006-2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#ifndef __MEM_CACHELIMITER_H__
10#define __MEM_CACHELIMITER_H__
11
43
44#include "MEM_Allocator.h"
45#include <vector>
46
47template<class T> class MEM_CacheLimiter;
48
49#ifndef __MEM_CACHELIMITERC_API_H__
50extern "C" {
51void MEM_CacheLimiter_set_maximum(size_t m);
53void MEM_CacheLimiter_set_disabled(bool disabled);
55};
56#endif
57
58template<class T> class MEM_CacheLimiterHandle {
59 public:
60 explicit MEM_CacheLimiterHandle(T *data_, MEM_CacheLimiter<T> *parent_)
61 : data(data_), parent(parent_)
62 {
63 }
64
65 void ref()
66 {
67 refcount++;
68 }
69
70 void unref()
71 {
72 refcount--;
73 }
74
75 T *get()
76 {
77 return data;
78 }
79
80 const T *get() const
81 {
82 return data;
83 }
84
85 int get_refcount() const
86 {
87 return refcount;
88 }
89
90 bool can_destroy() const
91 {
92 return !data || !refcount;
93 }
94
96 {
97 if (can_destroy()) {
98 delete data;
99 data = NULL;
100 unmanage();
101 return true;
102 }
103 return false;
104 }
105
106 void unmanage()
107 {
108 parent->unmanage(this);
109 }
110
111 void touch()
112 {
113 parent->touch(this);
114 }
115
116 private:
117 friend class MEM_CacheLimiter<T>;
118
119 T *data;
120 int refcount = 0;
121 int pos;
122 MEM_CacheLimiter<T> *parent;
123};
124
125template<class T> class MEM_CacheLimiter {
126 public:
127 using MEM_CacheLimiter_DataSize_Func = size_t (*)(void *);
128 using MEM_CacheLimiter_ItemPriority_Func = int (*)(void *, int);
130
131 MEM_CacheLimiter(MEM_CacheLimiter_DataSize_Func data_size_func) : data_size_func(data_size_func)
132 {
133 }
134
136 {
137 int i;
138 for (i = 0; i < queue.size(); i++) {
139 delete queue[i];
140 }
141 }
142
144 {
145 queue.push_back(new MEM_CacheLimiterHandle<T>(elem, this));
146 queue.back()->pos = queue.size() - 1;
147 return queue.back();
148 }
149
151 {
152 int pos = handle->pos;
153 queue[pos] = queue.back();
154 queue[pos]->pos = pos;
155 queue.pop_back();
156 delete handle;
157 }
158
160 {
161 size_t size = 0;
162 if (data_size_func) {
163 int i;
164 for (i = 0; i < queue.size(); i++) {
165 size += data_size_func(queue[i]->get()->get_data());
166 }
167 }
168 else {
170 }
171 return size;
172 }
173
175 {
178 size_t mem_in_use, cur_size;
179
180 if (is_disabled) {
181 return;
182 }
183
184 if (max == 0) {
185 return;
186 }
187
189
190 if (mem_in_use <= max) {
191 return;
192 }
193
194 while (!queue.empty() && mem_in_use > max) {
195 MEM_CacheElementPtr elem = get_least_priority_destroyable_element();
196
197 if (!elem) {
198 break;
199 }
200
201 if (data_size_func) {
202 cur_size = data_size_func(elem->get()->get_data());
203 }
204 else {
205 cur_size = mem_in_use;
206 }
207
208 if (elem->destroy_if_possible()) {
209 if (data_size_func) {
210 mem_in_use -= cur_size;
211 }
212 else {
213 mem_in_use -= cur_size - MEM_get_memory_in_use();
214 }
215 }
216 }
217 }
218
220 {
221 /* If we're using custom priority callback re-arranging the queue
222 * doesn't make much sense because we'll iterate it all to get
223 * least priority element anyway.
224 */
225 if (item_priority_func == nullptr) {
226 queue[handle->pos] = queue.back();
227 queue[handle->pos]->pos = handle->pos;
228 queue.pop_back();
229 queue.push_back(handle);
230 handle->pos = queue.size() - 1;
231 }
232 }
233
235 {
236 this->item_priority_func = item_priority_func;
237 }
238
240 {
241 this->item_destroyable_func = item_destroyable_func;
242 }
243
244 private:
245 using MEM_CacheElementPtr = MEM_CacheLimiterHandle<T> *;
246 using MEM_CacheQueue = std::vector<MEM_CacheElementPtr, MEM_Allocator<MEM_CacheElementPtr>>;
247 using iterator = typename MEM_CacheQueue::iterator;
248
249 /* Check whether element can be destroyed when enforcing cache limits */
250 bool can_destroy_element(MEM_CacheElementPtr &elem)
251 {
252 if (!elem->can_destroy()) {
253 /* Element is referenced */
254 return false;
255 }
256 if (item_destroyable_func) {
257 if (!item_destroyable_func(elem->get()->get_data())) {
258 return false;
259 }
260 }
261 return true;
262 }
263
264 MEM_CacheElementPtr get_least_priority_destroyable_element()
265 {
266 if (queue.empty()) {
267 return NULL;
268 }
269
270 MEM_CacheElementPtr best_match_elem = NULL;
271
272 if (!item_priority_func) {
273 for (iterator it = queue.begin(); it != queue.end(); it++) {
274 MEM_CacheElementPtr elem = *it;
275 if (!can_destroy_element(elem)) {
276 continue;
277 }
278 best_match_elem = elem;
279 break;
280 }
281 }
282 else {
283 int best_match_priority = 0;
284 int i;
285
286 for (i = 0; i < queue.size(); i++) {
287 MEM_CacheElementPtr elem = queue[i];
288
289 if (!can_destroy_element(elem)) {
290 continue;
291 }
292
293 /* By default 0 means highest priority element. */
294 /* Casting a size type to int is questionable,
295 * but unlikely to cause problems. */
296 int priority = -((int)(queue.size()) - i - 1);
297 priority = item_priority_func(elem->get()->get_data(), priority);
298
299 if (priority < best_match_priority || best_match_elem == NULL) {
300 best_match_priority = priority;
301 best_match_elem = elem;
302 }
303 }
304 }
305
306 return best_match_elem;
307 }
308
309 MEM_CacheQueue queue;
310 MEM_CacheLimiter_DataSize_Func data_size_func;
311 MEM_CacheLimiter_ItemPriority_Func item_priority_func;
312 MEM_CacheLimiter_ItemDestroyable_Func item_destroyable_func;
313};
314
315#endif // __MEM_CACHELIMITER_H__
static bool is_disabled
void MEM_CacheLimiter_set_maximum(size_t m)
void MEM_CacheLimiter_set_disabled(bool disabled)
size_t MEM_CacheLimiter_get_maximum()
bool MEM_CacheLimiter_is_disabled(void)
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
MEM_CacheLimiterHandle(T *data_, MEM_CacheLimiter< T > *parent_)
const T * get() const
void unmanage(MEM_CacheLimiterHandle< T > *handle)
MEM_CacheLimiter(MEM_CacheLimiter_DataSize_Func data_size_func)
void set_item_destroyable_func(MEM_CacheLimiter_ItemDestroyable_Func item_destroyable_func)
void touch(MEM_CacheLimiterHandle< T > *handle)
void set_item_priority_func(MEM_CacheLimiter_ItemPriority_Func item_priority_func)
MEM_CacheLimiterHandle< T > * insert(T *elem)
uint pos
size_t(* MEM_get_memory_in_use)(void)
Definition mallocn.cc:70
static size_t mem_in_use
#define T
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251