summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers/svga
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/drivers/svga')
-rw-r--r--src/gallium/drivers/svga/svga_screen_cache.c132
-rw-r--r--src/gallium/drivers/svga/svga_screen_cache.h8
2 files changed, 137 insertions, 3 deletions
diff --git a/src/gallium/drivers/svga/svga_screen_cache.c b/src/gallium/drivers/svga/svga_screen_cache.c
index eff36e0bccb..13df37fb2f6 100644
--- a/src/gallium/drivers/svga/svga_screen_cache.c
+++ b/src/gallium/drivers/svga/svga_screen_cache.c
@@ -23,10 +23,12 @@
*
**********************************************************/
+#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_hash.h"
#include "svga_debug.h"
+#include "svga_format.h"
#include "svga_winsys.h"
#include "svga_screen.h"
#include "svga_screen_cache.h"
@@ -35,6 +37,42 @@
#define SVGA_SURFACE_CACHE_ENABLED 1
+/**
+ * Return the size of the surface described by the key (in bytes).
+ */
+static unsigned
+surface_size(const struct svga_host_surface_cache_key *key)
+{
+ unsigned bw, bh, bpb, total_size, i;
+
+ assert(key->numMipLevels > 0);
+ assert(key->numFaces > 0);
+
+ if (key->format == SVGA3D_BUFFER) {
+ /* Special case: we don't want to count vertex/index buffers
+ * against the cache size limit, so view them as zero-sized.
+ */
+ return 0;
+ }
+
+ svga_format_size(key->format, &bw, &bh, &bpb);
+
+ total_size = 0;
+
+ for (i = 0; i < key->numMipLevels; i++) {
+ unsigned w = u_minify(key->size.width, i);
+ unsigned h = u_minify(key->size.height, i);
+ unsigned d = u_minify(key->size.depth, i);
+ unsigned img_size = ((w + bw - 1) / bw) * ((h + bh - 1) / bh) * d * bpb;
+ total_size += img_size;
+ }
+
+ total_size *= key->numFaces;
+
+ return total_size;
+}
+
+
/**
* Compute the bucket for this key.
*/
@@ -45,6 +83,11 @@ svga_screen_cache_bucket(const struct svga_host_surface_cache_key *key)
}
+/**
+ * Search the cache for a surface that matches the key. If a match is
+ * found, remove it from the cache and return the surface pointer.
+ * Return NULL otherwise.
+ */
static INLINE struct svga_winsys_surface *
svga_screen_cache_lookup(struct svga_screen *svgascreen,
const struct svga_host_surface_cache_key *key)
@@ -74,9 +117,11 @@ svga_screen_cache_lookup(struct svga_screen *svgascreen,
if(memcmp(&entry->key, key, sizeof *key) == 0 &&
sws->fence_signalled( sws, entry->fence, 0 ) == 0) {
+ unsigned surf_size;
+
assert(sws->surface_is_flushed(sws, entry->handle));
- handle = entry->handle; // Reference is transfered here.
+ handle = entry->handle; /* Reference is transfered here. */
entry->handle = NULL;
LIST_DEL(&entry->bucket_head);
@@ -85,6 +130,14 @@ svga_screen_cache_lookup(struct svga_screen *svgascreen,
LIST_ADD(&entry->head, &cache->empty);
+ /* update the cache size */
+ surf_size = surface_size(&entry->key);
+ assert(surf_size <= cache->total_size);
+ if (surf_size > cache->total_size)
+ cache->total_size = 0; /* should never happen, but be safe */
+ else
+ cache->total_size -= surf_size;
+
break;
}
@@ -102,6 +155,45 @@ svga_screen_cache_lookup(struct svga_screen *svgascreen,
}
+/**
+ * Free the least recently used entries in the surface cache until the
+ * cache size is <= the target size OR there are no unused entries left
+ * to discard. We don't do any flushing to try to free up additional
+ * surfaces.
+ */
+static void
+svga_screen_cache_shrink(struct svga_screen *svgascreen,
+ unsigned target_size)
+{
+ struct svga_host_surface_cache *cache = &svgascreen->cache;
+ struct svga_winsys_screen *sws = svgascreen->sws;
+ struct svga_host_surface_cache_entry *entry, *next_entry;
+
+ /* Walk over the list of unused buffers in reverse order: from oldest
+ * to newest.
+ */
+ LIST_FOR_EACH_ENTRY_SAFE_REV(entry, next_entry, &cache->unused, head) {
+ if (entry->key.format != SVGA3D_BUFFER) {
+ /* we don't want to discard vertex/index buffers */
+
+ cache->total_size -= surface_size(&entry->key);
+
+ assert(entry->handle);
+ sws->surface_reference(sws, &entry->handle, NULL);
+
+ LIST_DEL(&entry->bucket_head);
+ LIST_DEL(&entry->head);
+ LIST_ADD(&entry->head, &cache->empty);
+
+ if (cache->total_size <= target_size) {
+ /* all done */
+ break;
+ }
+ }
+ }
+}
+
+
/*
* Transfers a handle reference.
*/
@@ -115,6 +207,7 @@ svga_screen_cache_add(struct svga_screen *svgascreen,
struct svga_winsys_screen *sws = svgascreen->sws;
struct svga_host_surface_cache_entry *entry = NULL;
struct svga_winsys_surface *handle = *p_handle;
+ unsigned surf_size;
assert(key->cachable);
@@ -122,9 +215,37 @@ svga_screen_cache_add(struct svga_screen *svgascreen,
if(!handle)
return;
+ surf_size = surface_size(key);
+
*p_handle = NULL;
pipe_mutex_lock(cache->mutex);
+ if (surf_size >= SVGA_HOST_SURFACE_CACHE_BYTES) {
+ /* this surface is too large to cache, just free it */
+ sws->surface_reference(sws, &handle, NULL);
+ pipe_mutex_unlock(cache->mutex);
+ return;
+ }
+
+ if (cache->total_size + surf_size > SVGA_HOST_SURFACE_CACHE_BYTES) {
+ /* Adding this surface would exceed the cache size.
+ * Try to discard least recently used entries until we hit the
+ * new target cache size.
+ */
+ unsigned target_size = SVGA_HOST_SURFACE_CACHE_BYTES - surf_size;
+
+ svga_screen_cache_shrink(svgascreen, target_size);
+
+ if (cache->total_size > target_size) {
+ /* we weren't able to shrink the cache as much as we wanted so
+ * just discard this surface.
+ */
+ sws->surface_reference(sws, &handle, NULL);
+ pipe_mutex_unlock(cache->mutex);
+ return;
+ }
+ }
+
if(!LIST_IS_EMPTY(&cache->empty)) {
/* use the first empty entry */
entry = LIST_ENTRY(struct svga_host_surface_cache_entry, cache->empty.next, head);
@@ -136,6 +257,9 @@ svga_screen_cache_add(struct svga_screen *svgascreen,
entry = LIST_ENTRY(struct svga_host_surface_cache_entry, cache->unused.prev, head);
SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
"unref sid %p (make space)\n", entry->handle);
+
+ cache->total_size -= surface_size(&entry->key);
+
sws->surface_reference(sws, &entry->handle, NULL);
LIST_DEL(&entry->bucket_head);
@@ -150,6 +274,8 @@ svga_screen_cache_add(struct svga_screen *svgascreen,
SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
"cache sid %p\n", entry->handle);
LIST_ADD(&entry->head, &cache->validated);
+
+ cache->total_size += surf_size;
}
else {
/* Couldn't cache the buffer -- this really shouldn't happen */
@@ -216,6 +342,8 @@ svga_screen_cache_cleanup(struct svga_screen *svgascreen)
SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
"unref sid %p (shutdown)\n", cache->entries[i].handle);
sws->surface_reference(sws, &cache->entries[i].handle, NULL);
+
+ cache->total_size -= surface_size(&cache->entries[i].key);
}
if(cache->entries[i].fence)
@@ -232,6 +360,8 @@ svga_screen_cache_init(struct svga_screen *svgascreen)
struct svga_host_surface_cache *cache = &svgascreen->cache;
unsigned i;
+ assert(cache->total_size == 0);
+
pipe_mutex_init(cache->mutex);
for(i = 0; i < SVGA_HOST_SURFACE_CACHE_BUCKETS; ++i)
diff --git a/src/gallium/drivers/svga/svga_screen_cache.h b/src/gallium/drivers/svga/svga_screen_cache.h
index 62156e3f522..55e1c66a6e7 100644
--- a/src/gallium/drivers/svga/svga_screen_cache.h
+++ b/src/gallium/drivers/svga/svga_screen_cache.h
@@ -1,3 +1,4 @@
+
/**********************************************************
* Copyright 2008-2009 VMware, Inc. All rights reserved.
*
@@ -39,7 +40,7 @@
/* Guess the storage size of cached surfaces and try and keep it under
* this amount:
*/
-#define SVGA_HOST_SURFACE_CACHE_BYTES 16*1024*1024
+#define SVGA_HOST_SURFACE_CACHE_BYTES (16 * 1024 * 1024)
/* Maximum number of discrete surfaces in the cache:
*/
@@ -89,7 +90,7 @@ struct svga_host_surface_cache_entry
* Cache of the host surfaces.
*
* A cache entry can be in the following stages:
- * 1. empty
+ * 1. empty (entry->handle = NULL)
* 2. holding a buffer in a validate list
* 3. holding a flushed buffer (not in any validate list) with an active fence
* 4. holding a flushed buffer with an expired fence
@@ -117,6 +118,9 @@ struct svga_host_surface_cache
/** The actual storage for the entries */
struct svga_host_surface_cache_entry entries[SVGA_HOST_SURFACE_CACHE_SIZE];
+
+ /** Sum of sizes of all surfaces (in bytes) */
+ unsigned total_size;
};