Home | History | Annotate | Download | only in svga
      1 /**********************************************************
      2  * Copyright 2008-2009 VMware, Inc.  All rights reserved.
      3  *
      4  * Permission is hereby granted, free of charge, to any person
      5  * obtaining a copy of this software and associated documentation
      6  * files (the "Software"), to deal in the Software without
      7  * restriction, including without limitation the rights to use, copy,
      8  * modify, merge, publish, distribute, sublicense, and/or sell copies
      9  * of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice shall be
     13  * included in all copies or substantial portions of the Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22  * SOFTWARE.
     23  *
     24  **********************************************************/
     25 
     26 #include "util/u_math.h"
     27 #include "util/u_memory.h"
     28 #include "util/u_hash.h"
     29 
     30 #include "svga_debug.h"
     31 #include "svga_format.h"
     32 #include "svga_winsys.h"
     33 #include "svga_screen.h"
     34 #include "svga_screen_cache.h"
     35 
     36 
     37 #define SVGA_SURFACE_CACHE_ENABLED 1
     38 
     39 
     40 /**
     41  * Return the size of the surface described by the key (in bytes).
     42  */
     43 static unsigned
     44 surface_size(const struct svga_host_surface_cache_key *key)
     45 {
     46    unsigned bw, bh, bpb, total_size, i;
     47 
     48    assert(key->numMipLevels > 0);
     49    assert(key->numFaces > 0);
     50 
     51    if (key->format == SVGA3D_BUFFER) {
     52       /* Special case: we don't want to count vertex/index buffers
     53        * against the cache size limit, so view them as zero-sized.
     54        */
     55       return 0;
     56    }
     57 
     58    svga_format_size(key->format, &bw, &bh, &bpb);
     59 
     60    total_size = 0;
     61 
     62    for (i = 0; i < key->numMipLevels; i++) {
     63       unsigned w = u_minify(key->size.width, i);
     64       unsigned h = u_minify(key->size.height, i);
     65       unsigned d = u_minify(key->size.depth, i);
     66       unsigned img_size = ((w + bw - 1) / bw) * ((h + bh - 1) / bh) * d * bpb;
     67       total_size += img_size;
     68    }
     69 
     70    total_size *= key->numFaces;
     71 
     72    return total_size;
     73 }
     74 
     75 
     76 /**
     77  * Compute the bucket for this key.
     78  */
     79 static INLINE unsigned
     80 svga_screen_cache_bucket(const struct svga_host_surface_cache_key *key)
     81 {
     82    return util_hash_crc32(key, sizeof *key) % SVGA_HOST_SURFACE_CACHE_BUCKETS;
     83 }
     84 
     85 
     86 /**
     87  * Search the cache for a surface that matches the key.  If a match is
     88  * found, remove it from the cache and return the surface pointer.
     89  * Return NULL otherwise.
     90  */
     91 static INLINE struct svga_winsys_surface *
     92 svga_screen_cache_lookup(struct svga_screen *svgascreen,
     93                          const struct svga_host_surface_cache_key *key)
     94 {
     95    struct svga_host_surface_cache *cache = &svgascreen->cache;
     96    struct svga_winsys_screen *sws = svgascreen->sws;
     97    struct svga_host_surface_cache_entry *entry;
     98    struct svga_winsys_surface *handle = NULL;
     99    struct list_head *curr, *next;
    100    unsigned bucket;
    101    unsigned tries = 0;
    102 
    103    assert(key->cachable);
    104 
    105    bucket = svga_screen_cache_bucket(key);
    106 
    107    pipe_mutex_lock(cache->mutex);
    108 
    109    curr = cache->bucket[bucket].next;
    110    next = curr->next;
    111    while (curr != &cache->bucket[bucket]) {
    112       ++tries;
    113 
    114       entry = LIST_ENTRY(struct svga_host_surface_cache_entry, curr, bucket_head);
    115 
    116       assert(entry->handle);
    117 
    118       if (memcmp(&entry->key, key, sizeof *key) == 0 &&
    119          sws->fence_signalled(sws, entry->fence, 0) == 0) {
    120          unsigned surf_size;
    121 
    122          assert(sws->surface_is_flushed(sws, entry->handle));
    123 
    124          handle = entry->handle; /* Reference is transfered here. */
    125          entry->handle = NULL;
    126 
    127          LIST_DEL(&entry->bucket_head);
    128 
    129          LIST_DEL(&entry->head);
    130 
    131          LIST_ADD(&entry->head, &cache->empty);
    132 
    133          /* update the cache size */
    134          surf_size = surface_size(&entry->key);
    135          assert(surf_size <= cache->total_size);
    136          if (surf_size > cache->total_size)
    137             cache->total_size = 0; /* should never happen, but be safe */
    138          else
    139             cache->total_size -= surf_size;
    140 
    141          break;
    142       }
    143 
    144       curr = next;
    145       next = curr->next;
    146    }
    147 
    148    pipe_mutex_unlock(cache->mutex);
    149 
    150    if (SVGA_DEBUG & DEBUG_DMA)
    151       debug_printf("%s: cache %s after %u tries (bucket %d)\n", __FUNCTION__,
    152                    handle ? "hit" : "miss", tries, bucket);
    153 
    154    return handle;
    155 }
    156 
    157 
    158 /**
    159  * Free the least recently used entries in the surface cache until the
    160  * cache size is <= the target size OR there are no unused entries left
    161  * to discard.  We don't do any flushing to try to free up additional
    162  * surfaces.
    163  */
    164 static void
    165 svga_screen_cache_shrink(struct svga_screen *svgascreen,
    166                          unsigned target_size)
    167 {
    168    struct svga_host_surface_cache *cache = &svgascreen->cache;
    169    struct svga_winsys_screen *sws = svgascreen->sws;
    170    struct svga_host_surface_cache_entry *entry = NULL, *next_entry;
    171 
    172    /* Walk over the list of unused buffers in reverse order: from oldest
    173     * to newest.
    174     */
    175    LIST_FOR_EACH_ENTRY_SAFE_REV(entry, next_entry, &cache->unused, head) {
    176       if (entry->key.format != SVGA3D_BUFFER) {
    177          /* we don't want to discard vertex/index buffers */
    178 
    179          cache->total_size -= surface_size(&entry->key);
    180 
    181          assert(entry->handle);
    182          sws->surface_reference(sws, &entry->handle, NULL);
    183 
    184          LIST_DEL(&entry->bucket_head);
    185          LIST_DEL(&entry->head);
    186          LIST_ADD(&entry->head, &cache->empty);
    187 
    188          if (cache->total_size <= target_size) {
    189             /* all done */
    190             break;
    191          }
    192       }
    193    }
    194 }
    195 
    196 
    197 /**
    198  * Transfers a handle reference.
    199  */
    200 static INLINE void
    201 svga_screen_cache_add(struct svga_screen *svgascreen,
    202                       const struct svga_host_surface_cache_key *key,
    203                       struct svga_winsys_surface **p_handle)
    204 {
    205    struct svga_host_surface_cache *cache = &svgascreen->cache;
    206    struct svga_winsys_screen *sws = svgascreen->sws;
    207    struct svga_host_surface_cache_entry *entry = NULL;
    208    struct svga_winsys_surface *handle = *p_handle;
    209    unsigned surf_size;
    210 
    211    assert(key->cachable);
    212 
    213    assert(handle);
    214    if (!handle)
    215       return;
    216 
    217    surf_size = surface_size(key);
    218 
    219    *p_handle = NULL;
    220    pipe_mutex_lock(cache->mutex);
    221 
    222    if (surf_size >= SVGA_HOST_SURFACE_CACHE_BYTES) {
    223       /* this surface is too large to cache, just free it */
    224       sws->surface_reference(sws, &handle, NULL);
    225       pipe_mutex_unlock(cache->mutex);
    226       return;
    227    }
    228 
    229    if (cache->total_size + surf_size > SVGA_HOST_SURFACE_CACHE_BYTES) {
    230       /* Adding this surface would exceed the cache size.
    231        * Try to discard least recently used entries until we hit the
    232        * new target cache size.
    233        */
    234       unsigned target_size = SVGA_HOST_SURFACE_CACHE_BYTES - surf_size;
    235 
    236       svga_screen_cache_shrink(svgascreen, target_size);
    237 
    238       if (cache->total_size > target_size) {
    239          /* we weren't able to shrink the cache as much as we wanted so
    240           * just discard this surface.
    241           */
    242          sws->surface_reference(sws, &handle, NULL);
    243          pipe_mutex_unlock(cache->mutex);
    244          return;
    245       }
    246    }
    247 
    248    if (!LIST_IS_EMPTY(&cache->empty)) {
    249       /* use the first empty entry */
    250       entry = LIST_ENTRY(struct svga_host_surface_cache_entry,
    251                          cache->empty.next, head);
    252 
    253       LIST_DEL(&entry->head);
    254    }
    255    else if (!LIST_IS_EMPTY(&cache->unused)) {
    256       /* free the last used buffer and reuse its entry */
    257       entry = LIST_ENTRY(struct svga_host_surface_cache_entry,
    258                          cache->unused.prev, head);
    259       SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
    260                "unref sid %p (make space)\n", entry->handle);
    261 
    262       cache->total_size -= surface_size(&entry->key);
    263 
    264       sws->surface_reference(sws, &entry->handle, NULL);
    265 
    266       LIST_DEL(&entry->bucket_head);
    267 
    268       LIST_DEL(&entry->head);
    269    }
    270 
    271    if (entry) {
    272       entry->handle = handle;
    273       memcpy(&entry->key, key, sizeof entry->key);
    274 
    275       SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
    276                "cache sid %p\n", entry->handle);
    277       LIST_ADD(&entry->head, &cache->validated);
    278 
    279       cache->total_size += surf_size;
    280    }
    281    else {
    282       /* Couldn't cache the buffer -- this really shouldn't happen */
    283       SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
    284                "unref sid %p (couldn't find space)\n", handle);
    285       sws->surface_reference(sws, &handle, NULL);
    286    }
    287 
    288    pipe_mutex_unlock(cache->mutex);
    289 }
    290 
    291 
    292 /**
    293  * Called during the screen flush to move all buffers not in a validate list
    294  * into the unused list.
    295  */
    296 void
    297 svga_screen_cache_flush(struct svga_screen *svgascreen,
    298                         struct pipe_fence_handle *fence)
    299 {
    300    struct svga_host_surface_cache *cache = &svgascreen->cache;
    301    struct svga_winsys_screen *sws = svgascreen->sws;
    302    struct svga_host_surface_cache_entry *entry;
    303    struct list_head *curr, *next;
    304    unsigned bucket;
    305 
    306    pipe_mutex_lock(cache->mutex);
    307 
    308    curr = cache->validated.next;
    309    next = curr->next;
    310    while (curr != &cache->validated) {
    311       entry = LIST_ENTRY(struct svga_host_surface_cache_entry, curr, head);
    312 
    313       assert(entry->handle);
    314 
    315       if (sws->surface_is_flushed(sws, entry->handle)) {
    316          LIST_DEL(&entry->head);
    317 
    318          svgascreen->sws->fence_reference(svgascreen->sws, &entry->fence, fence);
    319 
    320          LIST_ADD(&entry->head, &cache->unused);
    321 
    322          bucket = svga_screen_cache_bucket(&entry->key);
    323          LIST_ADD(&entry->bucket_head, &cache->bucket[bucket]);
    324       }
    325 
    326       curr = next;
    327       next = curr->next;
    328    }
    329 
    330    pipe_mutex_unlock(cache->mutex);
    331 }
    332 
    333 
    334 /**
    335  * Free all the surfaces in the cache.
    336  * Called when destroying the svga screen object.
    337  */
    338 void
    339 svga_screen_cache_cleanup(struct svga_screen *svgascreen)
    340 {
    341    struct svga_host_surface_cache *cache = &svgascreen->cache;
    342    struct svga_winsys_screen *sws = svgascreen->sws;
    343    unsigned i;
    344 
    345    for (i = 0; i < SVGA_HOST_SURFACE_CACHE_SIZE; ++i) {
    346       if (cache->entries[i].handle) {
    347 	 SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
    348                   "unref sid %p (shutdown)\n", cache->entries[i].handle);
    349 	 sws->surface_reference(sws, &cache->entries[i].handle, NULL);
    350 
    351          cache->total_size -= surface_size(&cache->entries[i].key);
    352       }
    353 
    354       if (cache->entries[i].fence)
    355          svgascreen->sws->fence_reference(svgascreen->sws,
    356                                           &cache->entries[i].fence, NULL);
    357    }
    358 
    359    pipe_mutex_destroy(cache->mutex);
    360 }
    361 
    362 
    363 enum pipe_error
    364 svga_screen_cache_init(struct svga_screen *svgascreen)
    365 {
    366    struct svga_host_surface_cache *cache = &svgascreen->cache;
    367    unsigned i;
    368 
    369    assert(cache->total_size == 0);
    370 
    371    pipe_mutex_init(cache->mutex);
    372 
    373    for (i = 0; i < SVGA_HOST_SURFACE_CACHE_BUCKETS; ++i)
    374       LIST_INITHEAD(&cache->bucket[i]);
    375 
    376    LIST_INITHEAD(&cache->unused);
    377 
    378    LIST_INITHEAD(&cache->validated);
    379 
    380    LIST_INITHEAD(&cache->empty);
    381    for (i = 0; i < SVGA_HOST_SURFACE_CACHE_SIZE; ++i)
    382       LIST_ADDTAIL(&cache->entries[i].head, &cache->empty);
    383 
    384    return PIPE_OK;
    385 }
    386 
    387 
    388 /**
    389  * Allocate a new host-side surface.  If the surface is marked as cachable,
    390  * first try re-using a surface in the cache of freed surfaces.  Otherwise,
    391  * allocate a new surface.
    392  */
    393 struct svga_winsys_surface *
    394 svga_screen_surface_create(struct svga_screen *svgascreen,
    395                            struct svga_host_surface_cache_key *key)
    396 {
    397    struct svga_winsys_screen *sws = svgascreen->sws;
    398    struct svga_winsys_surface *handle = NULL;
    399    boolean cachable = SVGA_SURFACE_CACHE_ENABLED && key->cachable;
    400 
    401    SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
    402             "%s sz %dx%dx%d mips %d faces %d cachable %d\n",
    403             __FUNCTION__,
    404             key->size.width,
    405             key->size.height,
    406             key->size.depth,
    407             key->numMipLevels,
    408             key->numFaces,
    409             key->cachable);
    410 
    411    if (cachable) {
    412       if (key->format == SVGA3D_BUFFER) {
    413          /* For buffers, round the buffer size up to the nearest power
    414           * of two to increase the probability of cache hits.  Keep
    415           * texture surface dimensions unchanged.
    416           */
    417          uint32_t size = 1;
    418          while (size < key->size.width)
    419             size <<= 1;
    420          key->size.width = size;
    421 	 /* Since we're reusing buffers we're effectively transforming all
    422 	  * of them into dynamic buffers.
    423 	  *
    424 	  * It would be nice to not cache long lived static buffers. But there
    425 	  * is no way to detect the long lived from short lived ones yet. A
    426 	  * good heuristic would be buffer size.
    427 	  */
    428 	 key->flags &= ~SVGA3D_SURFACE_HINT_STATIC;
    429 	 key->flags |= SVGA3D_SURFACE_HINT_DYNAMIC;
    430       }
    431 
    432       handle = svga_screen_cache_lookup(svgascreen, key);
    433       if (handle) {
    434          if (key->format == SVGA3D_BUFFER)
    435             SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
    436                      "reuse sid %p sz %d (buffer)\n", handle,
    437                      key->size.width);
    438          else
    439             SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
    440                      "reuse sid %p sz %dx%dx%d mips %d faces %d\n", handle,
    441                      key->size.width,
    442                      key->size.height,
    443                      key->size.depth,
    444                      key->numMipLevels,
    445                      key->numFaces);
    446       }
    447    }
    448 
    449    if (!handle) {
    450       handle = sws->surface_create(sws,
    451                                    key->flags,
    452                                    key->format,
    453                                    key->size,
    454                                    key->numFaces,
    455                                    key->numMipLevels);
    456       if (handle)
    457          SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
    458                   "  CREATE sid %p sz %dx%dx%d\n",
    459                   handle,
    460                   key->size.width,
    461                   key->size.height,
    462                   key->size.depth);
    463    }
    464 
    465    return handle;
    466 }
    467 
    468 
    469 /**
    470  * Release a surface.  We don't actually free the surface- we put
    471  * it into the cache of freed surfaces (if it's cachable).
    472  */
    473 void
    474 svga_screen_surface_destroy(struct svga_screen *svgascreen,
    475                             const struct svga_host_surface_cache_key *key,
    476                             struct svga_winsys_surface **p_handle)
    477 {
    478    struct svga_winsys_screen *sws = svgascreen->sws;
    479 
    480    /* We only set the cachable flag for surfaces of which we are the
    481     * exclusive owner.  So just hold onto our existing reference in
    482     * that case.
    483     */
    484    if (SVGA_SURFACE_CACHE_ENABLED && key->cachable) {
    485       svga_screen_cache_add(svgascreen, key, p_handle);
    486    }
    487    else {
    488       SVGA_DBG(DEBUG_DMA,
    489                "unref sid %p (uncachable)\n", *p_handle);
    490       sws->surface_reference(sws, p_handle, NULL);
    491    }
    492 }
    493