diff options
-rw-r--r-- | common/Makefile.am | 1 | ||||
-rw-r--r-- | common/bo-cache.c | 123 | ||||
-rw-r--r-- | common/bo-cache.h | 41 |
3 files changed, 165 insertions, 0 deletions
diff --git a/common/Makefile.am b/common/Makefile.am index e803fdf..57d3d1b 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -19,6 +19,7 @@ AM_CFLAGS = $(filter-out -Wnested-externs -Wcast-qual -Wredundant-decls \ noinst_LTLIBRARIES = libcommon.la libcommon_la_SOURCES = \ + bo-cache.c \ boxutil.c \ drawable_desc.c \ get_first_pixel.c \ diff --git a/common/bo-cache.c b/common/bo-cache.c new file mode 100644 index 0000000..061ab1d --- /dev/null +++ b/common/bo-cache.c @@ -0,0 +1,123 @@ +#include <stdlib.h> +#include <time.h> + +#include "bo-cache.h" + +/* The interval in seconds between cache cleans */ +#define BO_CACHE_CLEAN_INTERVAL 1 +/* The maximum age in seconds of a BO in the cache */ +#define BO_CACHE_MAX_AGE 2 + +/* + * These sizes come from the i915 DRM backend - which uses roughly + * for n = 2.. + * (4096 << n) + (4096 << n) * 1 / 4 + * (4096 << n) + (4096 << n) * 2 / 4 + * (4096 << n) + (4096 << n) * 3 / 4 + * The reasoning being that powers of two are too wasteful in X. + * + * We also add in caches for 720p and 1080p too. + */ +static size_t bucket_size[NUM_BUCKETS] = { + 4096, 8192, 12288, + 20480, 24576, 28672, + 40960, 49152, 57344, + 81920, 98304, 114688, + 163840, 196608, 229376, + 327680, 393216, 458752, + 655360, 786432, 917504, + 1310720, 1572864, 1835008, + 2621440, 3145728, 3670016, + 3686400, 8294400, +}; + +void bo_cache_init(struct bo_cache *cache, void (*free)(struct bo_entry *)) +{ + struct timespec time; + unsigned i; + + clock_gettime(CLOCK_MONOTONIC, &time); + + cache->free = free; + cache->last_cleaned = time.tv_sec; + xorg_list_init(&cache->head); + + for (i = 0; i < NUM_BUCKETS; i++) { + xorg_list_init(&cache->buckets[i].head); + cache->buckets[i].size = bucket_size[i]; + } +} + +void bo_cache_fini(struct bo_cache *cache) +{ + struct timespec time; + + clock_gettime(CLOCK_MONOTONIC, &time); + + /* Free all entries by winding time forward */ + bo_cache_clean(cache, time.tv_sec + BO_CACHE_MAX_AGE + 1); +} + +struct bo_bucket *bo_cache_bucket_find(struct bo_cache *cache, size_t size) +{ + unsigned i; + + for (i = 0; i < NUM_BUCKETS; i++) { + struct bo_bucket *bucket = &cache->buckets[i]; + + if (bucket->size >= size) + return bucket; + } + + return NULL; +} + +struct bo_entry *bo_cache_bucket_get(struct bo_bucket *bucket) +{ + struct bo_entry *be = NULL; + + if (!xorg_list_is_empty(&bucket->head)) { + be = xorg_list_entry(bucket->head.next, struct bo_entry, + bucket_node); + + xorg_list_del(&be->bucket_node); + xorg_list_del(&be->free_node); + } + + return be; +} + +void bo_cache_clean(struct bo_cache *cache, time_t time) +{ + if (time - cache->last_cleaned < BO_CACHE_CLEAN_INTERVAL) + return; + + cache->last_cleaned = time; + + while (!xorg_list_is_empty(&cache->head)) { + struct bo_entry *entry; + + entry = xorg_list_first_entry(&cache->head, struct bo_entry, + free_node); + if (time - entry->free_time < BO_CACHE_MAX_AGE) + break; + + xorg_list_del(&entry->bucket_node); + xorg_list_del(&entry->free_node); + + cache->free(entry); + } +} + +void bo_cache_put(struct bo_cache *cache, struct bo_entry *entry) +{ + struct bo_bucket *bucket = entry->bucket; + struct timespec time; + + clock_gettime(CLOCK_MONOTONIC, &time); + entry->free_time = time.tv_sec; + xorg_list_append(&entry->bucket_node, &bucket->head); + xorg_list_append(&entry->free_node, &cache->head); + + bo_cache_clean(cache, time.tv_sec); +} diff --git a/common/bo-cache.h b/common/bo-cache.h new file mode 100644 index 0000000..0bc5ac0 --- /dev/null +++ b/common/bo-cache.h @@ -0,0 +1,41 @@ +#ifndef BO_CACHE_H +#define BO_CACHE_H + +#include <sys/time.h> +#include <sys/types.h> +#include <X11/Xdefs.h> +#include "compat-list.h" + +/* Number of buckets in the BO cache */ +#define NUM_BUCKETS (3*9 + 2) + +struct bo_cache; +struct bo_entry; + +struct bo_bucket { + struct xorg_list head; + size_t size; +}; + +struct bo_cache { + struct bo_bucket buckets[NUM_BUCKETS]; + struct xorg_list head; + time_t last_cleaned; + void (*free)(struct bo_entry *); +}; + +struct bo_entry { + struct bo_bucket *bucket; + struct xorg_list bucket_node; + struct xorg_list free_node; + time_t free_time; +}; + +void bo_cache_init(struct bo_cache *cache, void (*free)(struct bo_entry *)); +void bo_cache_fini(struct bo_cache *cache); +struct bo_bucket *bo_cache_bucket_find(struct bo_cache *cache, size_t size); +struct bo_entry *bo_cache_bucket_get(struct bo_bucket *bucket); +void bo_cache_clean(struct bo_cache *cache, time_t time); +void bo_cache_put(struct bo_cache *cache, struct bo_entry *entry); + +#endif |