mirror of https://gitlab.freedesktop.org/mesa/mesa
183 lines
5.2 KiB
C
183 lines
5.2 KiB
C
/*
|
|
* Copyright 2021 Google LLC
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "vn_renderer_internal.h"
|
|
|
|
/* 3 seconds */
|
|
#define VN_RENDERER_SHMEM_CACHE_EXPIRACY (3ll * 1000 * 1000)
|
|
|
|
static void
|
|
vn_renderer_shmem_cache_dump(struct vn_renderer_shmem_cache *cache)
|
|
{
|
|
simple_mtx_lock(&cache->mutex);
|
|
|
|
vn_log(NULL, "dumping renderer shmem cache");
|
|
vn_log(NULL, " cache skip: %d", cache->debug.cache_skip_count);
|
|
vn_log(NULL, " cache hit: %d", cache->debug.cache_hit_count);
|
|
vn_log(NULL, " cache miss: %d", cache->debug.cache_miss_count);
|
|
|
|
uint32_t bucket_mask = cache->bucket_mask;
|
|
while (bucket_mask) {
|
|
const int idx = u_bit_scan(&bucket_mask);
|
|
const struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx];
|
|
uint32_t count = 0;
|
|
list_for_each_entry(struct vn_renderer_shmem, shmem, &bucket->shmems,
|
|
cache_head)
|
|
count++;
|
|
if (count)
|
|
vn_log(NULL, " buckets[%d]: %d shmems", idx, count);
|
|
}
|
|
|
|
simple_mtx_unlock(&cache->mutex);
|
|
}
|
|
|
|
void
|
|
vn_renderer_shmem_cache_init(struct vn_renderer_shmem_cache *cache,
|
|
struct vn_renderer *renderer,
|
|
vn_renderer_shmem_cache_destroy_func destroy_func)
|
|
{
|
|
/* cache->bucket_mask is 32-bit and u_bit_scan is used */
|
|
static_assert(ARRAY_SIZE(cache->buckets) <= 32, "");
|
|
|
|
cache->renderer = renderer;
|
|
cache->destroy_func = destroy_func;
|
|
|
|
simple_mtx_init(&cache->mutex, mtx_plain);
|
|
|
|
for (uint32_t i = 0; i < ARRAY_SIZE(cache->buckets); i++) {
|
|
struct vn_renderer_shmem_bucket *bucket = &cache->buckets[i];
|
|
list_inithead(&bucket->shmems);
|
|
}
|
|
|
|
cache->initialized = true;
|
|
}
|
|
|
|
void
|
|
vn_renderer_shmem_cache_fini(struct vn_renderer_shmem_cache *cache)
|
|
{
|
|
if (!cache->initialized)
|
|
return;
|
|
|
|
if (VN_DEBUG(CACHE))
|
|
vn_renderer_shmem_cache_dump(cache);
|
|
|
|
while (cache->bucket_mask) {
|
|
const int idx = u_bit_scan(&cache->bucket_mask);
|
|
struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx];
|
|
|
|
list_for_each_entry_safe(struct vn_renderer_shmem, shmem,
|
|
&bucket->shmems, cache_head)
|
|
cache->destroy_func(cache->renderer, shmem);
|
|
}
|
|
|
|
simple_mtx_destroy(&cache->mutex);
|
|
}
|
|
|
|
static struct vn_renderer_shmem_bucket *
|
|
choose_bucket(struct vn_renderer_shmem_cache *cache,
|
|
size_t size,
|
|
int *out_idx)
|
|
{
|
|
assert(size);
|
|
if (unlikely(!util_is_power_of_two_or_zero64(size)))
|
|
return NULL;
|
|
|
|
const uint32_t idx = ffsll(size) - 1;
|
|
if (unlikely(idx >= ARRAY_SIZE(cache->buckets)))
|
|
return NULL;
|
|
|
|
*out_idx = idx;
|
|
return &cache->buckets[idx];
|
|
}
|
|
|
|
static void
|
|
vn_renderer_shmem_cache_remove_expired_locked(
|
|
struct vn_renderer_shmem_cache *cache, int64_t now)
|
|
{
|
|
uint32_t bucket_mask = cache->bucket_mask;
|
|
while (bucket_mask) {
|
|
const int idx = u_bit_scan(&bucket_mask);
|
|
struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx];
|
|
|
|
assert(!list_is_empty(&bucket->shmems));
|
|
const struct vn_renderer_shmem *last_shmem = list_last_entry(
|
|
&bucket->shmems, struct vn_renderer_shmem, cache_head);
|
|
|
|
/* remove expired shmems but keep at least the last one */
|
|
list_for_each_entry_safe(struct vn_renderer_shmem, shmem,
|
|
&bucket->shmems, cache_head) {
|
|
if (shmem == last_shmem ||
|
|
now - shmem->cache_timestamp < VN_RENDERER_SHMEM_CACHE_EXPIRACY)
|
|
break;
|
|
|
|
list_del(&shmem->cache_head);
|
|
cache->destroy_func(cache->renderer, shmem);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
vn_renderer_shmem_cache_add(struct vn_renderer_shmem_cache *cache,
|
|
struct vn_renderer_shmem *shmem)
|
|
{
|
|
assert(!vn_refcount_is_valid(&shmem->refcount));
|
|
|
|
int idx;
|
|
struct vn_renderer_shmem_bucket *bucket =
|
|
choose_bucket(cache, shmem->mmap_size, &idx);
|
|
if (!bucket)
|
|
return false;
|
|
|
|
const int64_t now = os_time_get();
|
|
shmem->cache_timestamp = now;
|
|
|
|
simple_mtx_lock(&cache->mutex);
|
|
|
|
vn_renderer_shmem_cache_remove_expired_locked(cache, now);
|
|
|
|
list_addtail(&shmem->cache_head, &bucket->shmems);
|
|
cache->bucket_mask |= 1 << idx;
|
|
|
|
simple_mtx_unlock(&cache->mutex);
|
|
|
|
return true;
|
|
}
|
|
|
|
struct vn_renderer_shmem *
|
|
vn_renderer_shmem_cache_get(struct vn_renderer_shmem_cache *cache,
|
|
size_t size)
|
|
{
|
|
int idx;
|
|
struct vn_renderer_shmem_bucket *bucket = choose_bucket(cache, size, &idx);
|
|
if (!bucket) {
|
|
VN_TRACE_SCOPE("shmem cache skip");
|
|
simple_mtx_lock(&cache->mutex);
|
|
cache->debug.cache_skip_count++;
|
|
simple_mtx_unlock(&cache->mutex);
|
|
return NULL;
|
|
}
|
|
|
|
struct vn_renderer_shmem *shmem = NULL;
|
|
|
|
simple_mtx_lock(&cache->mutex);
|
|
if (cache->bucket_mask & (1 << idx)) {
|
|
assert(!list_is_empty(&bucket->shmems));
|
|
shmem = list_first_entry(&bucket->shmems, struct vn_renderer_shmem,
|
|
cache_head);
|
|
list_del(&shmem->cache_head);
|
|
|
|
if (list_is_empty(&bucket->shmems))
|
|
cache->bucket_mask &= ~(1 << idx);
|
|
|
|
cache->debug.cache_hit_count++;
|
|
} else {
|
|
VN_TRACE_SCOPE("shmem cache miss");
|
|
cache->debug.cache_miss_count++;
|
|
}
|
|
simple_mtx_unlock(&cache->mutex);
|
|
|
|
return shmem;
|
|
}
|