Path: blob/21.2-virgl/src/gallium/drivers/svga/svga_screen_cache.c
4570 views
/**********************************************************1* Copyright 2008-2009 VMware, Inc. All rights reserved.2*3* Permission is hereby granted, free of charge, to any person4* obtaining a copy of this software and associated documentation5* files (the "Software"), to deal in the Software without6* restriction, including without limitation the rights to use, copy,7* modify, merge, publish, distribute, sublicense, and/or sell copies8* of the Software, and to permit persons to whom the Software is9* furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice shall be12* included in all copies or substantial portions of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS18* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN19* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*23**********************************************************/2425#include "util/u_math.h"26#include "util/u_memory.h"27#include "util/crc32.h"2829#include "svga_debug.h"30#include "svga_format.h"31#include "svga_winsys.h"32#include "svga_screen.h"33#include "svga_screen_cache.h"34#include "svga_context.h"35#include "svga_cmd.h"3637#define SVGA_SURFACE_CACHE_ENABLED 1383940/**41* Return the size of the surface described by the key (in bytes).42*/43unsigned44svga_surface_size(const struct svga_host_surface_cache_key *key)45{46unsigned bw, bh, bpb, total_size, i;4748assert(key->numMipLevels > 0);49assert(key->numFaces > 0);50assert(key->arraySize > 0);5152if (key->format == SVGA3D_BUFFER) {53/* Special case: we don't want to count vertex/index buffers54* against the cache size limit, so view them as zero-sized.55*/56return 0;57}5859svga_format_size(key->format, &bw, &bh, &bpb);6061total_size = 0;6263for (i = 0; i < key->numMipLevels; i++) {64unsigned w = u_minify(key->size.width, i);65unsigned h = u_minify(key->size.height, i);66unsigned d = u_minify(key->size.depth, i);67unsigned img_size = ((w + bw - 1) / bw) * ((h + bh - 1) / bh) * d * bpb;68total_size += img_size;69}7071total_size *= key->numFaces * key->arraySize * MAX2(1, key->sampleCount);7273return total_size;74}757677/**78* Compute the bucket for this key.79*/80static inline unsigned81svga_screen_cache_bucket(const struct svga_host_surface_cache_key *key)82{83return util_hash_crc32(key, sizeof *key) % SVGA_HOST_SURFACE_CACHE_BUCKETS;84}858687/**88* Search the cache for a surface that matches the key. If a match is89* found, remove it from the cache and return the surface pointer.90* Return NULL otherwise.91*/92static struct svga_winsys_surface *93svga_screen_cache_lookup(struct svga_screen *svgascreen,94const struct svga_host_surface_cache_key *key)95{96struct svga_host_surface_cache *cache = &svgascreen->cache;97struct svga_winsys_screen *sws = svgascreen->sws;98struct svga_host_surface_cache_entry *entry;99struct svga_winsys_surface *handle = NULL;100struct list_head *curr, *next;101unsigned bucket;102unsigned tries = 0;103104assert(key->cachable);105106bucket = svga_screen_cache_bucket(key);107108mtx_lock(&cache->mutex);109110curr = cache->bucket[bucket].next;111next = curr->next;112while (curr != &cache->bucket[bucket]) {113++tries;114115entry = LIST_ENTRY(struct svga_host_surface_cache_entry, curr, bucket_head);116117assert(entry->handle);118119/* If the key matches and the fence is signalled (the surface is no120* longer needed) the lookup was successful. We found a surface that121* can be reused.122* We unlink the surface from the cache entry and we add the entry to123* the 'empty' list.124*/125if (memcmp(&entry->key, key, sizeof *key) == 0 &&126sws->fence_signalled(sws, entry->fence, 0) == 0) {127unsigned surf_size;128129assert(sws->surface_is_flushed(sws, entry->handle));130131handle = entry->handle; /* Reference is transfered here. */132entry->handle = NULL;133134/* Remove from hash table */135list_del(&entry->bucket_head);136137/* remove from LRU list */138list_del(&entry->head);139140/* Add the cache entry (but not the surface!) to the empty list */141list_add(&entry->head, &cache->empty);142143/* update the cache size */144surf_size = svga_surface_size(&entry->key);145assert(surf_size <= cache->total_size);146if (surf_size > cache->total_size)147cache->total_size = 0; /* should never happen, but be safe */148else149cache->total_size -= surf_size;150151break;152}153154curr = next;155next = curr->next;156}157158mtx_unlock(&cache->mutex);159160if (SVGA_DEBUG & DEBUG_DMA)161debug_printf("%s: cache %s after %u tries (bucket %d)\n", __FUNCTION__,162handle ? "hit" : "miss", tries, bucket);163164return handle;165}166167168/**169* Free the least recently used entries in the surface cache until the170* cache size is <= the target size OR there are no unused entries left171* to discard. We don't do any flushing to try to free up additional172* surfaces.173*/174static void175svga_screen_cache_shrink(struct svga_screen *svgascreen,176unsigned target_size)177{178struct svga_host_surface_cache *cache = &svgascreen->cache;179struct svga_winsys_screen *sws = svgascreen->sws;180struct svga_host_surface_cache_entry *entry = NULL, *next_entry;181182/* Walk over the list of unused buffers in reverse order: from oldest183* to newest.184*/185LIST_FOR_EACH_ENTRY_SAFE_REV(entry, next_entry, &cache->unused, head) {186if (entry->key.format != SVGA3D_BUFFER) {187/* we don't want to discard vertex/index buffers */188189cache->total_size -= svga_surface_size(&entry->key);190191assert(entry->handle);192sws->surface_reference(sws, &entry->handle, NULL);193194list_del(&entry->bucket_head);195list_del(&entry->head);196list_add(&entry->head, &cache->empty);197198if (cache->total_size <= target_size) {199/* all done */200break;201}202}203}204}205206207/**208* Add a surface to the cache. This is done when the driver deletes209* the surface. Note: transfers a handle reference.210*/211static void212svga_screen_cache_add(struct svga_screen *svgascreen,213const struct svga_host_surface_cache_key *key,214struct svga_winsys_surface **p_handle)215{216struct svga_host_surface_cache *cache = &svgascreen->cache;217struct svga_winsys_screen *sws = svgascreen->sws;218struct svga_host_surface_cache_entry *entry = NULL;219struct svga_winsys_surface *handle = *p_handle;220unsigned surf_size;221222assert(key->cachable);223224if (!handle)225return;226227surf_size = svga_surface_size(key);228229*p_handle = NULL;230mtx_lock(&cache->mutex);231232if (surf_size >= SVGA_HOST_SURFACE_CACHE_BYTES) {233/* this surface is too large to cache, just free it */234sws->surface_reference(sws, &handle, NULL);235mtx_unlock(&cache->mutex);236return;237}238239if (cache->total_size + surf_size > SVGA_HOST_SURFACE_CACHE_BYTES) {240/* Adding this surface would exceed the cache size.241* Try to discard least recently used entries until we hit the242* new target cache size.243*/244unsigned target_size = SVGA_HOST_SURFACE_CACHE_BYTES - surf_size;245246svga_screen_cache_shrink(svgascreen, target_size);247248if (cache->total_size > target_size) {249/* we weren't able to shrink the cache as much as we wanted so250* just discard this surface.251*/252sws->surface_reference(sws, &handle, NULL);253mtx_unlock(&cache->mutex);254return;255}256}257258if (!list_is_empty(&cache->empty)) {259/* An empty entry has no surface associated with it.260* Use the first empty entry.261*/262entry = LIST_ENTRY(struct svga_host_surface_cache_entry,263cache->empty.next, head);264265/* Remove from LRU list */266list_del(&entry->head);267}268else if (!list_is_empty(&cache->unused)) {269/* free the last used buffer and reuse its entry */270entry = LIST_ENTRY(struct svga_host_surface_cache_entry,271cache->unused.prev, head);272SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,273"unref sid %p (make space)\n", entry->handle);274275cache->total_size -= svga_surface_size(&entry->key);276277sws->surface_reference(sws, &entry->handle, NULL);278279/* Remove from hash table */280list_del(&entry->bucket_head);281282/* Remove from LRU list */283list_del(&entry->head);284}285286if (entry) {287assert(entry->handle == NULL);288entry->handle = handle;289memcpy(&entry->key, key, sizeof entry->key);290291SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,292"cache sid %p\n", entry->handle);293294/* If we don't have gb objects, we don't need to invalidate. */295if (sws->have_gb_objects)296list_add(&entry->head, &cache->validated);297else298list_add(&entry->head, &cache->invalidated);299300cache->total_size += surf_size;301}302else {303/* Couldn't cache the buffer -- this really shouldn't happen */304SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,305"unref sid %p (couldn't find space)\n", handle);306sws->surface_reference(sws, &handle, NULL);307}308309mtx_unlock(&cache->mutex);310}311312313/* Maximum number of invalidate surface commands in a command buffer */314# define SVGA_MAX_SURFACE_TO_INVALIDATE 1000315316/**317* Called during the screen flush to move all buffers not in a validate list318* into the unused list.319*/320void321svga_screen_cache_flush(struct svga_screen *svgascreen,322struct svga_context *svga,323struct pipe_fence_handle *fence)324{325struct svga_host_surface_cache *cache = &svgascreen->cache;326struct svga_winsys_screen *sws = svgascreen->sws;327struct svga_host_surface_cache_entry *entry;328struct list_head *curr, *next;329unsigned bucket;330331mtx_lock(&cache->mutex);332333/* Loop over entries in the invalidated list */334curr = cache->invalidated.next;335next = curr->next;336while (curr != &cache->invalidated) {337entry = LIST_ENTRY(struct svga_host_surface_cache_entry, curr, head);338339assert(entry->handle);340341if (sws->surface_is_flushed(sws, entry->handle)) {342/* remove entry from the invalidated list */343list_del(&entry->head);344345sws->fence_reference(sws, &entry->fence, fence);346347/* Add entry to the unused list */348list_add(&entry->head, &cache->unused);349350/* Add entry to the hash table bucket */351bucket = svga_screen_cache_bucket(&entry->key);352list_add(&entry->bucket_head, &cache->bucket[bucket]);353}354355curr = next;356next = curr->next;357}358359unsigned nsurf = 0;360curr = cache->validated.next;361next = curr->next;362while (curr != &cache->validated) {363entry = LIST_ENTRY(struct svga_host_surface_cache_entry, curr, head);364365assert(entry->handle);366assert(svga_have_gb_objects(svga));367368if (sws->surface_is_flushed(sws, entry->handle)) {369/* remove entry from the validated list */370list_del(&entry->head);371372/* It is now safe to invalidate the surface content.373* It will be done using the current context.374*/375if (SVGA_TRY(SVGA3D_InvalidateGBSurface(svga->swc, entry->handle))376!= PIPE_OK) {377ASSERTED enum pipe_error ret;378379/* Even though surface invalidation here is done after the command380* buffer is flushed, it is still possible that it will381* fail because there might be just enough of this command that is382* filling up the command buffer, so in this case we will call383* the winsys flush directly to flush the buffer.384* Note, we don't want to call svga_context_flush() here because385* this function itself is called inside svga_context_flush().386*/387svga_retry_enter(svga);388svga->swc->flush(svga->swc, NULL);389nsurf = 0;390ret = SVGA3D_InvalidateGBSurface(svga->swc, entry->handle);391svga_retry_exit(svga);392assert(ret == PIPE_OK);393}394395/* add the entry to the invalidated list */396397list_add(&entry->head, &cache->invalidated);398nsurf++;399}400401curr = next;402next = curr->next;403}404405mtx_unlock(&cache->mutex);406407/**408* In some rare cases (when running ARK survival), we hit the max number409* of surface relocations with invalidated surfaces during context flush.410* So if the number of invalidated surface exceeds a certain limit (1000),411* we'll do another winsys flush.412*/413if (nsurf > SVGA_MAX_SURFACE_TO_INVALIDATE) {414svga->swc->flush(svga->swc, NULL);415}416}417418419/**420* Free all the surfaces in the cache.421* Called when destroying the svga screen object.422*/423void424svga_screen_cache_cleanup(struct svga_screen *svgascreen)425{426struct svga_host_surface_cache *cache = &svgascreen->cache;427struct svga_winsys_screen *sws = svgascreen->sws;428unsigned i;429430for (i = 0; i < SVGA_HOST_SURFACE_CACHE_SIZE; ++i) {431if (cache->entries[i].handle) {432SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,433"unref sid %p (shutdown)\n", cache->entries[i].handle);434sws->surface_reference(sws, &cache->entries[i].handle, NULL);435436cache->total_size -= svga_surface_size(&cache->entries[i].key);437}438439if (cache->entries[i].fence)440sws->fence_reference(sws, &cache->entries[i].fence, NULL);441}442443mtx_destroy(&cache->mutex);444}445446447enum pipe_error448svga_screen_cache_init(struct svga_screen *svgascreen)449{450struct svga_host_surface_cache *cache = &svgascreen->cache;451unsigned i;452453assert(cache->total_size == 0);454455(void) mtx_init(&cache->mutex, mtx_plain);456457for (i = 0; i < SVGA_HOST_SURFACE_CACHE_BUCKETS; ++i)458list_inithead(&cache->bucket[i]);459460list_inithead(&cache->unused);461462list_inithead(&cache->validated);463464list_inithead(&cache->invalidated);465466list_inithead(&cache->empty);467for (i = 0; i < SVGA_HOST_SURFACE_CACHE_SIZE; ++i)468list_addtail(&cache->entries[i].head, &cache->empty);469470return PIPE_OK;471}472473474/**475* Allocate a new host-side surface. If the surface is marked as cachable,476* first try re-using a surface in the cache of freed surfaces. Otherwise,477* allocate a new surface.478* \param bind_flags bitmask of PIPE_BIND_x flags479* \param usage one of PIPE_USAGE_x values480* \param validated return True if the surface is a reused surface481*/482struct svga_winsys_surface *483svga_screen_surface_create(struct svga_screen *svgascreen,484unsigned bind_flags, enum pipe_resource_usage usage,485boolean *validated,486struct svga_host_surface_cache_key *key)487{488struct svga_winsys_screen *sws = svgascreen->sws;489struct svga_winsys_surface *handle = NULL;490boolean cachable = SVGA_SURFACE_CACHE_ENABLED && key->cachable;491492SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,493"%s sz %dx%dx%d mips %d faces %d arraySize %d cachable %d\n",494__FUNCTION__,495key->size.width,496key->size.height,497key->size.depth,498key->numMipLevels,499key->numFaces,500key->arraySize,501key->cachable);502503if (cachable) {504/* Try to re-cycle a previously freed, cached surface */505if (key->format == SVGA3D_BUFFER) {506SVGA3dSurfaceAllFlags hint_flag;507508/* For buffers, round the buffer size up to the nearest power509* of two to increase the probability of cache hits. Keep510* texture surface dimensions unchanged.511*/512uint32_t size = 1;513while (size < key->size.width)514size <<= 1;515key->size.width = size;516517/* Determine whether the buffer is static or dynamic.518* This is a bit of a heuristic which can be tuned as needed.519*/520if (usage == PIPE_USAGE_DEFAULT ||521usage == PIPE_USAGE_IMMUTABLE) {522hint_flag = SVGA3D_SURFACE_HINT_STATIC;523}524else if (bind_flags & PIPE_BIND_INDEX_BUFFER) {525/* Index buffers don't change too often. Mark them as static.526*/527hint_flag = SVGA3D_SURFACE_HINT_STATIC;528}529else {530/* Since we're reusing buffers we're effectively transforming all531* of them into dynamic buffers.532*533* It would be nice to not cache long lived static buffers. But there534* is no way to detect the long lived from short lived ones yet. A535* good heuristic would be buffer size.536*/537hint_flag = SVGA3D_SURFACE_HINT_DYNAMIC;538}539540key->flags &= ~(SVGA3D_SURFACE_HINT_STATIC |541SVGA3D_SURFACE_HINT_DYNAMIC);542key->flags |= hint_flag;543}544545handle = svga_screen_cache_lookup(svgascreen, key);546if (handle) {547if (key->format == SVGA3D_BUFFER)548SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,549"reuse sid %p sz %d (buffer)\n", handle,550key->size.width);551else552SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,553"reuse sid %p sz %dx%dx%d mips %d faces %d arraySize %d\n", handle,554key->size.width,555key->size.height,556key->size.depth,557key->numMipLevels,558key->numFaces,559key->arraySize);560*validated = TRUE;561}562}563564if (!handle) {565/* Unable to recycle surface, allocate a new one */566unsigned usage = 0;567568if (!key->cachable)569usage |= SVGA_SURFACE_USAGE_SHARED;570if (key->scanout)571usage |= SVGA_SURFACE_USAGE_SCANOUT;572if (key->coherent)573usage |= SVGA_SURFACE_USAGE_COHERENT;574575handle = sws->surface_create(sws,576key->flags,577key->format,578usage,579key->size,580key->numFaces * key->arraySize,581key->numMipLevels,582key->sampleCount);583if (handle)584SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,585" CREATE sid %p sz %dx%dx%d\n",586handle,587key->size.width,588key->size.height,589key->size.depth);590591*validated = FALSE;592}593594return handle;595}596597598/**599* Release a surface. We don't actually free the surface- we put600* it into the cache of freed surfaces (if it's cachable).601*/602void603svga_screen_surface_destroy(struct svga_screen *svgascreen,604const struct svga_host_surface_cache_key *key,605struct svga_winsys_surface **p_handle)606{607struct svga_winsys_screen *sws = svgascreen->sws;608609/* We only set the cachable flag for surfaces of which we are the610* exclusive owner. So just hold onto our existing reference in611* that case.612*/613if (SVGA_SURFACE_CACHE_ENABLED && key->cachable) {614svga_screen_cache_add(svgascreen, key, p_handle);615}616else {617SVGA_DBG(DEBUG_DMA,618"unref sid %p (uncachable)\n", *p_handle);619sws->surface_reference(sws, p_handle, NULL);620}621}622623624/**625* Print/dump the contents of the screen cache. For debugging.626*/627void628svga_screen_cache_dump(const struct svga_screen *svgascreen)629{630const struct svga_host_surface_cache *cache = &svgascreen->cache;631unsigned bucket;632unsigned count = 0;633634debug_printf("svga3d surface cache:\n");635for (bucket = 0; bucket < SVGA_HOST_SURFACE_CACHE_BUCKETS; bucket++) {636struct list_head *curr;637curr = cache->bucket[bucket].next;638while (curr && curr != &cache->bucket[bucket]) {639struct svga_host_surface_cache_entry *entry =640LIST_ENTRY(struct svga_host_surface_cache_entry,641curr, bucket_head);642if (entry->key.format == SVGA3D_BUFFER) {643debug_printf(" %p: buffer %u bytes\n",644entry->handle,645entry->key.size.width);646}647else {648debug_printf(" %p: %u x %u x %u format %u\n",649entry->handle,650entry->key.size.width,651entry->key.size.height,652entry->key.size.depth,653entry->key.format);654}655curr = curr->next;656count++;657}658}659660debug_printf("%u surfaces, %u bytes\n", count, cache->total_size);661}662663664