Blender  V3.3
MEM_CacheLimiter.h
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #ifndef __MEM_CACHELIMITER_H__
8 #define __MEM_CACHELIMITER_H__
9 
42 #include "MEM_Allocator.h"
43 #include <list>
44 #include <queue>
45 #include <vector>
46 
47 template<class T> class MEM_CacheLimiter;
48 
49 #ifndef __MEM_CACHELIMITERC_API_H__
50 extern "C" {
51 void MEM_CacheLimiter_set_maximum(size_t m);
53 void MEM_CacheLimiter_set_disabled(bool disabled);
55 };
56 #endif
57 
58 template<class T> class MEM_CacheLimiterHandle {
59  public:
61  : data(data_), refcount(0), 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;
121  int pos;
122  MEM_CacheLimiter<T> *parent;
123 };
124 
125 template<class T> class MEM_CacheLimiter {
126  public:
127  typedef size_t (*MEM_CacheLimiter_DataSize_Func)(void *data);
128  typedef int (*MEM_CacheLimiter_ItemPriority_Func)(void *item, int default_priority);
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  if (data_size_func) {
201  cur_size = data_size_func(elem->get()->get_data());
202  }
203  else {
204  cur_size = mem_in_use;
205  }
206 
207  if (elem->destroy_if_possible()) {
208  if (data_size_func) {
209  mem_in_use -= cur_size;
210  }
211  else {
212  mem_in_use -= cur_size - MEM_get_memory_in_use();
213  }
214  }
215  }
216  }
217 
219  {
220  /* If we're using custom priority callback re-arranging the queue
221  * doesn't make much sense because we'll iterate it all to get
222  * least priority element anyway.
223  */
224  if (item_priority_func == NULL) {
225  queue[handle->pos] = queue.back();
226  queue[handle->pos]->pos = handle->pos;
227  queue.pop_back();
228  queue.push_back(handle);
229  handle->pos = queue.size() - 1;
230  }
231  }
232 
234  {
235  this->item_priority_func = item_priority_func;
236  }
237 
239  {
240  this->item_destroyable_func = item_destroyable_func;
241  }
242 
243  private:
244  typedef MEM_CacheLimiterHandle<T> *MEM_CacheElementPtr;
245  typedef std::vector<MEM_CacheElementPtr, MEM_Allocator<MEM_CacheElementPtr>> MEM_CacheQueue;
246  typedef typename MEM_CacheQueue::iterator iterator;
247 
248  /* Check whether element can be destroyed when enforcing cache limits */
249  bool can_destroy_element(MEM_CacheElementPtr &elem)
250  {
251  if (!elem->can_destroy()) {
252  /* Element is referenced */
253  return false;
254  }
255  if (item_destroyable_func) {
256  if (!item_destroyable_func(elem->get()->get_data()))
257  return false;
258  }
259  return true;
260  }
261 
262  MEM_CacheElementPtr get_least_priority_destroyable_element(void)
263  {
264  if (queue.empty())
265  return NULL;
266 
267  MEM_CacheElementPtr best_match_elem = NULL;
268 
269  if (!item_priority_func) {
270  for (iterator it = queue.begin(); it != queue.end(); it++) {
271  MEM_CacheElementPtr elem = *it;
272  if (!can_destroy_element(elem))
273  continue;
274  best_match_elem = elem;
275  break;
276  }
277  }
278  else {
279  int best_match_priority = 0;
280  int i;
281 
282  for (i = 0; i < queue.size(); i++) {
283  MEM_CacheElementPtr elem = queue[i];
284 
285  if (!can_destroy_element(elem))
286  continue;
287 
288  /* By default 0 means highest priority element. */
289  /* Casting a size type to int is questionable,
290  * but unlikely to cause problems. */
291  int priority = -((int)(queue.size()) - i - 1);
292  priority = item_priority_func(elem->get()->get_data(), priority);
293 
294  if (priority < best_match_priority || best_match_elem == NULL) {
295  best_match_priority = priority;
296  best_match_elem = elem;
297  }
298  }
299  }
300 
301  return best_match_elem;
302  }
303 
304  MEM_CacheQueue queue;
305  MEM_CacheLimiter_DataSize_Func data_size_func;
306  MEM_CacheLimiter_ItemPriority_Func item_priority_func;
307  MEM_CacheLimiter_ItemDestroyable_Func item_destroyable_func;
308 };
309 
310 #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)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
const T * get() const
MEM_CacheLimiterHandle(T *data_, MEM_CacheLimiter< T > *parent_)
void unmanage(MEM_CacheLimiterHandle< T > *handle)
int(* MEM_CacheLimiter_ItemPriority_Func)(void *item, int default_priority)
bool(* MEM_CacheLimiter_ItemDestroyable_Func)(void *item)
size_t(* MEM_CacheLimiter_DataSize_Func)(void *data)
size_t get_memory_in_use()
MEM_CacheLimiterHandle< T > * insert(T *elem)
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)
T * data_
Definition: eval_output.h:163
uint pos
size_t(* MEM_get_memory_in_use)(void)
Definition: mallocn.c:45
static size_t mem_in_use
#define T
float max