Add Chromium-only Blender WebEngine parity work
This commit is contained in:
616
blender-5.2.0/source/blender/imbuf/intern/cache.cc
Normal file
616
blender-5.2.0/source/blender/imbuf/intern/cache.cc
Normal file
@@ -0,0 +1,616 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#undef DEBUG_MESSAGES
|
||||
|
||||
#include <cstdlib> /* for qsort */
|
||||
#include <memory.h>
|
||||
#include <mutex>
|
||||
|
||||
#include "MEM_CacheLimiterC-Api.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_mempool.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "IMB_cache.hh"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifdef DEBUG_MESSAGES
|
||||
# if defined __GNUC__
|
||||
# define PRINT(format, args...) printf(format, ##args)
|
||||
# else
|
||||
# define PRINT(format, ...) printf(__VA_ARGS__)
|
||||
# endif
|
||||
#else
|
||||
# define PRINT(format, ...)
|
||||
#endif
|
||||
|
||||
static MEM_CacheLimiterC *limitor = nullptr;
|
||||
|
||||
/* Image buffers managed by a ImBufCache might be using their own caches (used by color
|
||||
* management). In practice this means that, for example, freeing ImBufCache used by MovieClip
|
||||
* will request freeing ImBufCache owned by ImBuf. Freeing ImBufCache needs to be thread-safe,
|
||||
* so regular mutex will not work here, hence the recursive lock. */
|
||||
static std::recursive_mutex limitor_lock;
|
||||
|
||||
struct ImBufCache {
|
||||
char name[64];
|
||||
|
||||
GHash *hash;
|
||||
GHashHashFP hashfp;
|
||||
GHashCmpFP cmpfp;
|
||||
ImBufCacheGetKeyDataFP getdatafp;
|
||||
|
||||
ImBufCacheGetPriorityDataFP getprioritydatafp;
|
||||
ImBufCacheGetItemPriorityFP getitempriorityfp;
|
||||
ImBufCachePriorityDeleterFP prioritydeleterfp;
|
||||
|
||||
BLI_mempool *keys_pool;
|
||||
BLI_mempool *items_pool;
|
||||
BLI_mempool *userkeys_pool;
|
||||
|
||||
int keysize;
|
||||
|
||||
void *last_userkey;
|
||||
|
||||
int totseg, *points, proxy, render_flags; /* for visual statistics optimization */
|
||||
int pad;
|
||||
};
|
||||
|
||||
struct ImBufCacheKey {
|
||||
ImBufCache *cache_owner;
|
||||
void *userkey;
|
||||
};
|
||||
|
||||
struct ImBufCacheItem {
|
||||
ImBufCache *cache_owner;
|
||||
ImBuf *ibuf;
|
||||
MEM_CacheLimiterHandleC *c_handle;
|
||||
void *priority_data;
|
||||
/* Indicates that #ibuf is null, because there was an error during load. */
|
||||
bool added_empty;
|
||||
};
|
||||
|
||||
static uint imbufcache_hashhash(const void *keyv)
|
||||
{
|
||||
const ImBufCacheKey *key = static_cast<const ImBufCacheKey *>(keyv);
|
||||
|
||||
return key->cache_owner->hashfp(key->userkey);
|
||||
}
|
||||
|
||||
static bool imbufcache_hashcmp(const void *av, const void *bv)
|
||||
{
|
||||
const ImBufCacheKey *a = static_cast<const ImBufCacheKey *>(av);
|
||||
const ImBufCacheKey *b = static_cast<const ImBufCacheKey *>(bv);
|
||||
|
||||
return a->cache_owner->cmpfp(a->userkey, b->userkey);
|
||||
}
|
||||
|
||||
static void imbufcache_keyfree(void *val)
|
||||
{
|
||||
ImBufCacheKey *key = static_cast<ImBufCacheKey *>(val);
|
||||
|
||||
BLI_mempool_free(key->cache_owner->userkeys_pool, key->userkey);
|
||||
|
||||
BLI_mempool_free(key->cache_owner->keys_pool, key);
|
||||
}
|
||||
|
||||
static void imbufcache_valfree(void *val)
|
||||
{
|
||||
ImBufCacheItem *item = static_cast<ImBufCacheItem *>(val);
|
||||
ImBufCache *cache = item->cache_owner;
|
||||
|
||||
PRINT("%s: cache '%s' free item %p buffer %p\n", __func__, cache->name, item, item->ibuf);
|
||||
|
||||
if (item->c_handle) {
|
||||
limitor_lock.lock();
|
||||
MEM_CacheLimiter_unmanage(item->c_handle);
|
||||
limitor_lock.unlock();
|
||||
}
|
||||
|
||||
if (item->ibuf) {
|
||||
IMB_freeImBuf(item->ibuf);
|
||||
}
|
||||
|
||||
if (item->priority_data && cache->prioritydeleterfp) {
|
||||
cache->prioritydeleterfp(item->priority_data);
|
||||
}
|
||||
|
||||
BLI_mempool_free(item->cache_owner->items_pool, item);
|
||||
}
|
||||
|
||||
static void check_unused_keys(ImBufCache *cache)
|
||||
{
|
||||
GHashIterator gh_iter;
|
||||
|
||||
BLI_ghashIterator_init(&gh_iter, cache->hash);
|
||||
|
||||
while (!BLI_ghashIterator_done(&gh_iter)) {
|
||||
const ImBufCacheKey *key = static_cast<const ImBufCacheKey *>(
|
||||
BLI_ghashIterator_getKey(&gh_iter));
|
||||
const ImBufCacheItem *item = static_cast<const ImBufCacheItem *>(
|
||||
BLI_ghashIterator_getValue(&gh_iter));
|
||||
|
||||
BLI_ghashIterator_step(&gh_iter);
|
||||
|
||||
if (item->added_empty) {
|
||||
/* Don't remove entries that have been added empty. Those indicate that the image couldn't be
|
||||
* loaded correctly. */
|
||||
continue;
|
||||
}
|
||||
|
||||
bool remove = !item->ibuf;
|
||||
|
||||
if (remove) {
|
||||
PRINT("%s: cache '%s' remove item %p without buffer\n", __func__, cache->name, item);
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
BLI_ghash_remove(cache->hash, key, imbufcache_keyfree, imbufcache_valfree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int compare_int(const void *av, const void *bv)
|
||||
{
|
||||
const int *a = static_cast<int *>(const_cast<void *>(av));
|
||||
const int *b = static_cast<int *>(const_cast<void *>(bv));
|
||||
return *a - *b;
|
||||
}
|
||||
|
||||
static void imbufcache_destructor(void *p)
|
||||
{
|
||||
ImBufCacheItem *item = static_cast<ImBufCacheItem *>(p);
|
||||
|
||||
if (item) {
|
||||
item->c_handle = nullptr;
|
||||
if (item->ibuf) {
|
||||
ImBufCache *cache = item->cache_owner;
|
||||
|
||||
PRINT("%s: cache '%s' destroy item %p buffer %p\n", __func__, cache->name, item, item->ibuf);
|
||||
|
||||
IMB_freeImBuf(item->ibuf);
|
||||
|
||||
item->ibuf = nullptr;
|
||||
|
||||
/* force cached segments to be updated */
|
||||
MEM_SAFE_DELETE(cache->points);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t get_size_in_memory(ImBuf *ibuf)
|
||||
{
|
||||
/* Keep textures in the memory to avoid constant file reload on viewport update. */
|
||||
if (ibuf->userflags & IB_PERSISTENT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return IMB_get_size_in_memory(ibuf);
|
||||
}
|
||||
static size_t get_item_size(void *p)
|
||||
{
|
||||
size_t size = sizeof(ImBufCacheItem);
|
||||
ImBufCacheItem *item = static_cast<ImBufCacheItem *>(p);
|
||||
|
||||
if (item->ibuf) {
|
||||
size += get_size_in_memory(item->ibuf);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int get_item_priority(void *item_v, int default_priority)
|
||||
{
|
||||
ImBufCacheItem *item = static_cast<ImBufCacheItem *>(item_v);
|
||||
ImBufCache *cache = item->cache_owner;
|
||||
int priority;
|
||||
|
||||
if (!cache->getitempriorityfp) {
|
||||
PRINT("%s: cache '%s' item %p use default priority %d\n",
|
||||
__func__,
|
||||
cache->name,
|
||||
item,
|
||||
default_priority);
|
||||
|
||||
return default_priority;
|
||||
}
|
||||
|
||||
priority = cache->getitempriorityfp(cache->last_userkey, item->priority_data);
|
||||
|
||||
PRINT("%s: cache '%s' item %p priority %d\n", __func__, cache->name, item, priority);
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
static bool get_item_destroyable(void *item_v)
|
||||
{
|
||||
ImBufCacheItem *item = static_cast<ImBufCacheItem *>(item_v);
|
||||
if (item->ibuf == nullptr) {
|
||||
return true;
|
||||
}
|
||||
/* IB_BITMAPDIRTY means image was modified from inside blender and
|
||||
* changes are not saved to disk.
|
||||
*
|
||||
* Such buffers are never to be freed.
|
||||
*/
|
||||
if ((item->ibuf->userflags & IB_BITMAPDIRTY) || (item->ibuf->userflags & IB_PERSISTENT)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IMB_cache_init()
|
||||
{
|
||||
limitor = new_MEM_CacheLimiter(imbufcache_destructor, get_item_size);
|
||||
|
||||
MEM_CacheLimiter_ItemPriority_Func_set(limitor, get_item_priority);
|
||||
MEM_CacheLimiter_ItemDestroyable_Func_set(limitor, get_item_destroyable);
|
||||
}
|
||||
|
||||
void IMB_cache_destruct()
|
||||
{
|
||||
if (limitor) {
|
||||
delete_MEM_CacheLimiter(limitor);
|
||||
limitor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ImBufCache *IMB_cache_create(const char *name, int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp)
|
||||
{
|
||||
ImBufCache *cache;
|
||||
|
||||
PRINT("%s: cache '%s' create\n", __func__, name);
|
||||
|
||||
cache = MEM_new_zeroed<ImBufCache>("ImBufCache");
|
||||
|
||||
STRNCPY(cache->name, name);
|
||||
|
||||
cache->keys_pool = BLI_mempool_create(sizeof(ImBufCacheKey), 0, 64, BLI_MEMPOOL_NOP);
|
||||
cache->items_pool = BLI_mempool_create(sizeof(ImBufCacheItem), 0, 64, BLI_MEMPOOL_NOP);
|
||||
cache->userkeys_pool = BLI_mempool_create(keysize, 0, 64, BLI_MEMPOOL_NOP);
|
||||
cache->hash = BLI_ghash_new(
|
||||
imbufcache_hashhash, imbufcache_hashcmp, "MovieClip ImBuf cache hash");
|
||||
|
||||
cache->keysize = keysize;
|
||||
cache->hashfp = hashfp;
|
||||
cache->cmpfp = cmpfp;
|
||||
cache->proxy = -1;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
void IMB_cache_set_getdata_callback(ImBufCache *cache, ImBufCacheGetKeyDataFP getdatafp)
|
||||
{
|
||||
cache->getdatafp = getdatafp;
|
||||
}
|
||||
|
||||
void IMB_cache_set_priority_callback(ImBufCache *cache,
|
||||
ImBufCacheGetPriorityDataFP getprioritydatafp,
|
||||
ImBufCacheGetItemPriorityFP getitempriorityfp,
|
||||
ImBufCachePriorityDeleterFP prioritydeleterfp)
|
||||
{
|
||||
cache->last_userkey = MEM_new_uninitialized(cache->keysize, "movie cache last user key");
|
||||
|
||||
cache->getprioritydatafp = getprioritydatafp;
|
||||
cache->getitempriorityfp = getitempriorityfp;
|
||||
cache->prioritydeleterfp = prioritydeleterfp;
|
||||
}
|
||||
|
||||
static void do_imbufcache_put(ImBufCache *cache, void *userkey, ImBuf *ibuf, bool need_lock)
|
||||
{
|
||||
ImBufCacheKey *key;
|
||||
ImBufCacheItem *item;
|
||||
|
||||
if (!limitor) {
|
||||
IMB_cache_init();
|
||||
}
|
||||
|
||||
if (ibuf != nullptr) {
|
||||
IMB_refImBuf(ibuf);
|
||||
}
|
||||
|
||||
key = static_cast<ImBufCacheKey *>(BLI_mempool_alloc(cache->keys_pool));
|
||||
key->cache_owner = cache;
|
||||
key->userkey = BLI_mempool_alloc(cache->userkeys_pool);
|
||||
memcpy(key->userkey, userkey, cache->keysize);
|
||||
|
||||
item = static_cast<ImBufCacheItem *>(BLI_mempool_alloc(cache->items_pool));
|
||||
|
||||
PRINT("%s: cache '%s' put %p, item %p\n", __func__, cache->name, ibuf, item);
|
||||
|
||||
item->ibuf = ibuf;
|
||||
item->cache_owner = cache;
|
||||
item->c_handle = nullptr;
|
||||
item->priority_data = nullptr;
|
||||
item->added_empty = ibuf == nullptr;
|
||||
|
||||
if (cache->getprioritydatafp) {
|
||||
item->priority_data = cache->getprioritydatafp(userkey);
|
||||
}
|
||||
|
||||
BLI_ghash_reinsert(cache->hash, key, item, imbufcache_keyfree, imbufcache_valfree);
|
||||
|
||||
if (cache->last_userkey) {
|
||||
memcpy(cache->last_userkey, userkey, cache->keysize);
|
||||
}
|
||||
|
||||
if (need_lock) {
|
||||
limitor_lock.lock();
|
||||
}
|
||||
|
||||
item->c_handle = MEM_CacheLimiter_insert(limitor, item);
|
||||
|
||||
MEM_CacheLimiter_ref(item->c_handle);
|
||||
MEM_CacheLimiter_enforce_limits(limitor);
|
||||
MEM_CacheLimiter_unref(item->c_handle);
|
||||
|
||||
if (need_lock) {
|
||||
limitor_lock.unlock();
|
||||
}
|
||||
|
||||
/* cache limiter can't remove unused keys which points to destroyed values */
|
||||
check_unused_keys(cache);
|
||||
|
||||
MEM_SAFE_DELETE(cache->points);
|
||||
}
|
||||
|
||||
void IMB_cache_put(ImBufCache *cache, void *userkey, ImBuf *ibuf)
|
||||
{
|
||||
do_imbufcache_put(cache, userkey, ibuf, true);
|
||||
}
|
||||
|
||||
bool IMB_cache_put_if_possible(ImBufCache *cache, void *userkey, ImBuf *ibuf)
|
||||
{
|
||||
size_t mem_in_use, mem_limit, elem_size;
|
||||
bool result = false;
|
||||
|
||||
elem_size = (ibuf == nullptr) ? 0 : get_size_in_memory(ibuf);
|
||||
mem_limit = MEM_CacheLimiter_get_maximum();
|
||||
|
||||
limitor_lock.lock();
|
||||
mem_in_use = MEM_CacheLimiter_get_memory_in_use(limitor);
|
||||
|
||||
if (mem_in_use + elem_size <= mem_limit) {
|
||||
do_imbufcache_put(cache, userkey, ibuf, false);
|
||||
result = true;
|
||||
}
|
||||
|
||||
limitor_lock.unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void IMB_cache_remove(ImBufCache *cache, void *userkey)
|
||||
{
|
||||
ImBufCacheKey key;
|
||||
key.cache_owner = cache;
|
||||
key.userkey = userkey;
|
||||
BLI_ghash_remove(cache->hash, &key, imbufcache_keyfree, imbufcache_valfree);
|
||||
}
|
||||
|
||||
ImBuf *IMB_cache_get(ImBufCache *cache, void *userkey, bool *r_is_cached_empty)
|
||||
{
|
||||
ImBufCacheKey key;
|
||||
ImBufCacheItem *item;
|
||||
|
||||
key.cache_owner = cache;
|
||||
key.userkey = userkey;
|
||||
item = static_cast<ImBufCacheItem *>(BLI_ghash_lookup(cache->hash, &key));
|
||||
|
||||
if (r_is_cached_empty) {
|
||||
*r_is_cached_empty = false;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
if (item->ibuf) {
|
||||
std::lock_guard lock(limitor_lock);
|
||||
/* Check again, the condition might have changed before we acquired the lock. */
|
||||
if (item->ibuf) {
|
||||
MEM_CacheLimiter_touch(item->c_handle);
|
||||
IMB_refImBuf(item->ibuf);
|
||||
return item->ibuf;
|
||||
}
|
||||
}
|
||||
if (r_is_cached_empty && item->added_empty) {
|
||||
*r_is_cached_empty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IMB_cache_has_frame(ImBufCache *cache, void *userkey)
|
||||
{
|
||||
ImBufCacheKey key;
|
||||
ImBufCacheItem *item;
|
||||
|
||||
key.cache_owner = cache;
|
||||
key.userkey = userkey;
|
||||
item = static_cast<ImBufCacheItem *>(BLI_ghash_lookup(cache->hash, &key));
|
||||
|
||||
return item != nullptr;
|
||||
}
|
||||
|
||||
void IMB_cache_free(ImBufCache *cache)
|
||||
{
|
||||
PRINT("%s: cache '%s' free\n", __func__, cache->name);
|
||||
|
||||
BLI_ghash_free(cache->hash, imbufcache_keyfree, imbufcache_valfree);
|
||||
|
||||
BLI_mempool_destroy(cache->keys_pool);
|
||||
BLI_mempool_destroy(cache->items_pool);
|
||||
BLI_mempool_destroy(cache->userkeys_pool);
|
||||
|
||||
if (cache->points) {
|
||||
MEM_delete(cache->points);
|
||||
}
|
||||
|
||||
if (cache->last_userkey) {
|
||||
MEM_delete_void(cache->last_userkey);
|
||||
}
|
||||
|
||||
MEM_delete(cache);
|
||||
}
|
||||
|
||||
void IMB_cache_cleanup(ImBufCache *cache,
|
||||
bool(cleanup_check_cb)(ImBuf *ibuf, void *userkey, void *userdata),
|
||||
void *userdata)
|
||||
{
|
||||
GHashIterator gh_iter;
|
||||
|
||||
check_unused_keys(cache);
|
||||
|
||||
BLI_ghashIterator_init(&gh_iter, cache->hash);
|
||||
|
||||
while (!BLI_ghashIterator_done(&gh_iter)) {
|
||||
ImBufCacheKey *key = static_cast<ImBufCacheKey *>(BLI_ghashIterator_getKey(&gh_iter));
|
||||
ImBufCacheItem *item = static_cast<ImBufCacheItem *>(BLI_ghashIterator_getValue(&gh_iter));
|
||||
|
||||
BLI_ghashIterator_step(&gh_iter);
|
||||
|
||||
if (cleanup_check_cb(item->ibuf, key->userkey, userdata)) {
|
||||
PRINT("%s: cache '%s' remove item %p\n", __func__, cache->name, item);
|
||||
|
||||
BLI_ghash_remove(cache->hash, key, imbufcache_keyfree, imbufcache_valfree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMB_cache_get_cache_segments(
|
||||
ImBufCache *cache, int proxy, int render_flags, int *r_totseg, int **r_points)
|
||||
{
|
||||
*r_totseg = 0;
|
||||
*r_points = nullptr;
|
||||
|
||||
if (!cache->getdatafp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cache->proxy != proxy || cache->render_flags != render_flags) {
|
||||
MEM_SAFE_DELETE(cache->points);
|
||||
}
|
||||
|
||||
if (cache->points) {
|
||||
*r_totseg = cache->totseg;
|
||||
*r_points = cache->points;
|
||||
}
|
||||
else {
|
||||
int totframe = BLI_ghash_len(cache->hash);
|
||||
int *frames = MEM_new_array_zeroed<int>(totframe, "movieclip cache frames");
|
||||
int a, totseg = 0;
|
||||
GHashIterator gh_iter;
|
||||
|
||||
a = 0;
|
||||
GHASH_ITER (gh_iter, cache->hash) {
|
||||
ImBufCacheKey *key = static_cast<ImBufCacheKey *>(BLI_ghashIterator_getKey(&gh_iter));
|
||||
ImBufCacheItem *item = static_cast<ImBufCacheItem *>(BLI_ghashIterator_getValue(&gh_iter));
|
||||
int framenr, curproxy, curflags;
|
||||
|
||||
if (item->ibuf) {
|
||||
cache->getdatafp(key->userkey, &framenr, &curproxy, &curflags);
|
||||
|
||||
if (curproxy == proxy && curflags == render_flags) {
|
||||
frames[a++] = framenr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qsort(frames, totframe, sizeof(int), compare_int);
|
||||
|
||||
/* count */
|
||||
for (a = 0; a < totframe; a++) {
|
||||
if (a && frames[a] - frames[a - 1] != 1) {
|
||||
totseg++;
|
||||
}
|
||||
|
||||
if (a == totframe - 1) {
|
||||
totseg++;
|
||||
}
|
||||
}
|
||||
|
||||
if (totseg) {
|
||||
int b, *points;
|
||||
|
||||
points = MEM_new_array_zeroed<int>(2 * size_t(totseg), "movieclip cache segments");
|
||||
|
||||
/* fill */
|
||||
for (a = 0, b = 0; a < totframe; a++) {
|
||||
if (a == 0) {
|
||||
points[b++] = frames[a];
|
||||
}
|
||||
|
||||
if (a && frames[a] - frames[a - 1] != 1) {
|
||||
points[b++] = frames[a - 1];
|
||||
points[b++] = frames[a];
|
||||
}
|
||||
|
||||
if (a == totframe - 1) {
|
||||
points[b++] = frames[a];
|
||||
}
|
||||
}
|
||||
|
||||
*r_totseg = totseg;
|
||||
*r_points = points;
|
||||
|
||||
cache->totseg = totseg;
|
||||
cache->points = points;
|
||||
cache->proxy = proxy;
|
||||
cache->render_flags = render_flags;
|
||||
}
|
||||
|
||||
MEM_delete(frames);
|
||||
}
|
||||
}
|
||||
|
||||
ImBufCacheIter *IMB_cacheIter_new(ImBufCache *cache)
|
||||
{
|
||||
GHashIterator *iter;
|
||||
|
||||
check_unused_keys(cache);
|
||||
iter = BLI_ghashIterator_new(cache->hash);
|
||||
|
||||
return reinterpret_cast<ImBufCacheIter *>(iter);
|
||||
}
|
||||
|
||||
void IMB_cacheIter_free(ImBufCacheIter *iter)
|
||||
{
|
||||
BLI_ghashIterator_free(reinterpret_cast<GHashIterator *>(iter));
|
||||
}
|
||||
|
||||
bool IMB_cacheIter_done(ImBufCacheIter *iter)
|
||||
{
|
||||
return BLI_ghashIterator_done(reinterpret_cast<GHashIterator *>(iter));
|
||||
}
|
||||
|
||||
void IMB_cacheIter_step(ImBufCacheIter *iter)
|
||||
{
|
||||
BLI_ghashIterator_step(reinterpret_cast<GHashIterator *>(iter));
|
||||
}
|
||||
|
||||
ImBuf *IMB_cacheIter_getImBuf(ImBufCacheIter *iter)
|
||||
{
|
||||
ImBufCacheItem *item = static_cast<ImBufCacheItem *>(
|
||||
BLI_ghashIterator_getValue(reinterpret_cast<GHashIterator *>(iter)));
|
||||
return item->ibuf;
|
||||
}
|
||||
|
||||
void *IMB_cacheIter_getUserKey(ImBufCacheIter *iter)
|
||||
{
|
||||
ImBufCacheKey *key = static_cast<ImBufCacheKey *>(
|
||||
BLI_ghashIterator_getKey(reinterpret_cast<GHashIterator *>(iter)));
|
||||
return key->userkey;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user