Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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 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 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.    if (!handle)
  214.       return;
  215.    
  216.    surf_size = surface_size(key);
  217.  
  218.    *p_handle = NULL;
  219.    pipe_mutex_lock(cache->mutex);
  220.    
  221.    if (surf_size >= SVGA_HOST_SURFACE_CACHE_BYTES) {
  222.       /* this surface is too large to cache, just free it */
  223.       sws->surface_reference(sws, &handle, NULL);
  224.       pipe_mutex_unlock(cache->mutex);
  225.       return;
  226.    }
  227.  
  228.    if (cache->total_size + surf_size > SVGA_HOST_SURFACE_CACHE_BYTES) {
  229.       /* Adding this surface would exceed the cache size.
  230.        * Try to discard least recently used entries until we hit the
  231.        * new target cache size.
  232.        */
  233.       unsigned target_size = SVGA_HOST_SURFACE_CACHE_BYTES - surf_size;
  234.  
  235.       svga_screen_cache_shrink(svgascreen, target_size);
  236.  
  237.       if (cache->total_size > target_size) {
  238.          /* we weren't able to shrink the cache as much as we wanted so
  239.           * just discard this surface.
  240.           */
  241.          sws->surface_reference(sws, &handle, NULL);
  242.          pipe_mutex_unlock(cache->mutex);
  243.          return;
  244.       }
  245.    }
  246.  
  247.    if (!LIST_IS_EMPTY(&cache->empty)) {
  248.       /* use the first empty entry */
  249.       entry = LIST_ENTRY(struct svga_host_surface_cache_entry,
  250.                          cache->empty.next, head);
  251.  
  252.       LIST_DEL(&entry->head);
  253.    }
  254.    else if (!LIST_IS_EMPTY(&cache->unused)) {
  255.       /* free the last used buffer and reuse its entry */
  256.       entry = LIST_ENTRY(struct svga_host_surface_cache_entry,
  257.                          cache->unused.prev, head);
  258.       SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
  259.                "unref sid %p (make space)\n", entry->handle);
  260.  
  261.       cache->total_size -= surface_size(&entry->key);
  262.  
  263.       sws->surface_reference(sws, &entry->handle, NULL);
  264.  
  265.       LIST_DEL(&entry->bucket_head);
  266.  
  267.       LIST_DEL(&entry->head);
  268.    }
  269.  
  270.    if (entry) {
  271.       entry->handle = handle;
  272.       memcpy(&entry->key, key, sizeof entry->key);
  273.  
  274.       SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
  275.                "cache sid %p\n", entry->handle);
  276.       LIST_ADD(&entry->head, &cache->validated);
  277.  
  278.       cache->total_size += surf_size;
  279.    }
  280.    else {
  281.       /* Couldn't cache the buffer -- this really shouldn't happen */
  282.       SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
  283.                "unref sid %p (couldn't find space)\n", handle);
  284.       sws->surface_reference(sws, &handle, NULL);
  285.    }
  286.  
  287.    pipe_mutex_unlock(cache->mutex);
  288. }
  289.  
  290.  
  291. /**
  292.  * Called during the screen flush to move all buffers not in a validate list
  293.  * into the unused list.
  294.  */
  295. void
  296. svga_screen_cache_flush(struct svga_screen *svgascreen,
  297.                         struct pipe_fence_handle *fence)
  298. {
  299.    struct svga_host_surface_cache *cache = &svgascreen->cache;
  300.    struct svga_winsys_screen *sws = svgascreen->sws;
  301.    struct svga_host_surface_cache_entry *entry;
  302.    struct list_head *curr, *next;
  303.    unsigned bucket;
  304.  
  305.    pipe_mutex_lock(cache->mutex);
  306.  
  307.    curr = cache->validated.next;
  308.    next = curr->next;
  309.    while (curr != &cache->validated) {
  310.       entry = LIST_ENTRY(struct svga_host_surface_cache_entry, curr, head);
  311.  
  312.       assert(entry->handle);
  313.  
  314.       if (sws->surface_is_flushed(sws, entry->handle)) {
  315.          LIST_DEL(&entry->head);
  316.  
  317.          svgascreen->sws->fence_reference(svgascreen->sws, &entry->fence, fence);
  318.  
  319.          LIST_ADD(&entry->head, &cache->unused);
  320.  
  321.          bucket = svga_screen_cache_bucket(&entry->key);
  322.          LIST_ADD(&entry->bucket_head, &cache->bucket[bucket]);
  323.       }
  324.  
  325.       curr = next;
  326.       next = curr->next;
  327.    }
  328.  
  329.    pipe_mutex_unlock(cache->mutex);
  330. }
  331.  
  332.  
  333. /**
  334.  * Free all the surfaces in the cache.
  335.  * Called when destroying the svga screen object.
  336.  */
  337. void
  338. svga_screen_cache_cleanup(struct svga_screen *svgascreen)
  339. {
  340.    struct svga_host_surface_cache *cache = &svgascreen->cache;
  341.    struct svga_winsys_screen *sws = svgascreen->sws;
  342.    unsigned i;
  343.  
  344.    for (i = 0; i < SVGA_HOST_SURFACE_CACHE_SIZE; ++i) {
  345.       if (cache->entries[i].handle) {
  346.          SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
  347.                   "unref sid %p (shutdown)\n", cache->entries[i].handle);
  348.          sws->surface_reference(sws, &cache->entries[i].handle, NULL);
  349.  
  350.          cache->total_size -= surface_size(&cache->entries[i].key);
  351.       }
  352.  
  353.       if (cache->entries[i].fence)
  354.          svgascreen->sws->fence_reference(svgascreen->sws,
  355.                                           &cache->entries[i].fence, NULL);
  356.    }
  357.  
  358.    pipe_mutex_destroy(cache->mutex);
  359. }
  360.  
  361.  
  362. enum pipe_error
  363. svga_screen_cache_init(struct svga_screen *svgascreen)
  364. {
  365.    struct svga_host_surface_cache *cache = &svgascreen->cache;
  366.    unsigned i;
  367.  
  368.    assert(cache->total_size == 0);
  369.  
  370.    pipe_mutex_init(cache->mutex);
  371.  
  372.    for (i = 0; i < SVGA_HOST_SURFACE_CACHE_BUCKETS; ++i)
  373.       LIST_INITHEAD(&cache->bucket[i]);
  374.  
  375.    LIST_INITHEAD(&cache->unused);
  376.  
  377.    LIST_INITHEAD(&cache->validated);
  378.  
  379.    LIST_INITHEAD(&cache->empty);
  380.    for (i = 0; i < SVGA_HOST_SURFACE_CACHE_SIZE; ++i)
  381.       LIST_ADDTAIL(&cache->entries[i].head, &cache->empty);
  382.  
  383.    return PIPE_OK;
  384. }
  385.  
  386.  
  387. /**
  388.  * Allocate a new host-side surface.  If the surface is marked as cachable,
  389.  * first try re-using a surface in the cache of freed surfaces.  Otherwise,
  390.  * allocate a new surface.
  391.  */
  392. struct svga_winsys_surface *
  393. svga_screen_surface_create(struct svga_screen *svgascreen,
  394.                            struct svga_host_surface_cache_key *key)
  395. {
  396.    struct svga_winsys_screen *sws = svgascreen->sws;
  397.    struct svga_winsys_surface *handle = NULL;
  398.    boolean cachable = SVGA_SURFACE_CACHE_ENABLED && key->cachable;
  399.  
  400.    SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
  401.             "%s sz %dx%dx%d mips %d faces %d cachable %d\n",
  402.             __FUNCTION__,
  403.             key->size.width,
  404.             key->size.height,
  405.             key->size.depth,
  406.             key->numMipLevels,
  407.             key->numFaces,
  408.             key->cachable);
  409.  
  410.    if (cachable) {
  411.       if (key->format == SVGA3D_BUFFER) {
  412.          /* For buffers, round the buffer size up to the nearest power
  413.           * of two to increase the probability of cache hits.  Keep
  414.           * texture surface dimensions unchanged.
  415.           */
  416.          uint32_t size = 1;
  417.          while (size < key->size.width)
  418.             size <<= 1;
  419.          key->size.width = size;
  420.          /* Since we're reusing buffers we're effectively transforming all
  421.           * of them into dynamic buffers.
  422.           *
  423.           * It would be nice to not cache long lived static buffers. But there
  424.           * is no way to detect the long lived from short lived ones yet. A
  425.           * good heuristic would be buffer size.
  426.           */
  427.          key->flags &= ~SVGA3D_SURFACE_HINT_STATIC;
  428.          key->flags |= SVGA3D_SURFACE_HINT_DYNAMIC;
  429.       }
  430.  
  431.       handle = svga_screen_cache_lookup(svgascreen, key);
  432.       if (handle) {
  433.          if (key->format == SVGA3D_BUFFER)
  434.             SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
  435.                      "reuse sid %p sz %d (buffer)\n", handle,
  436.                      key->size.width);
  437.          else
  438.             SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
  439.                      "reuse sid %p sz %dx%dx%d mips %d faces %d\n", handle,
  440.                      key->size.width,
  441.                      key->size.height,
  442.                      key->size.depth,
  443.                      key->numMipLevels,
  444.                      key->numFaces);
  445.       }
  446.    }
  447.  
  448.    if (!handle) {
  449.       handle = sws->surface_create(sws,
  450.                                    key->flags,
  451.                                    key->format,
  452.                                    key->cachable ?
  453.                                    0 : SVGA_SURFACE_USAGE_SHARED,
  454.                                    key->size,
  455.                                    key->numFaces,
  456.                                    key->numMipLevels);
  457.       if (handle)
  458.          SVGA_DBG(DEBUG_CACHE|DEBUG_DMA,
  459.                   "  CREATE sid %p sz %dx%dx%d\n",
  460.                   handle,
  461.                   key->size.width,
  462.                   key->size.height,
  463.                   key->size.depth);
  464.    }
  465.  
  466.    return handle;
  467. }
  468.  
  469.  
  470. /**
  471.  * Release a surface.  We don't actually free the surface- we put
  472.  * it into the cache of freed surfaces (if it's cachable).
  473.  */
  474. void
  475. svga_screen_surface_destroy(struct svga_screen *svgascreen,
  476.                             const struct svga_host_surface_cache_key *key,
  477.                             struct svga_winsys_surface **p_handle)
  478. {
  479.    struct svga_winsys_screen *sws = svgascreen->sws;
  480.  
  481.    /* We only set the cachable flag for surfaces of which we are the
  482.     * exclusive owner.  So just hold onto our existing reference in
  483.     * that case.
  484.     */
  485.    if (SVGA_SURFACE_CACHE_ENABLED && key->cachable) {
  486.       svga_screen_cache_add(svgascreen, key, p_handle);
  487.    }
  488.    else {
  489.       SVGA_DBG(DEBUG_DMA,
  490.                "unref sid %p (uncachable)\n", *p_handle);
  491.       sws->surface_reference(sws, p_handle, NULL);
  492.    }
  493. }
  494.  
  495.  
  496. /**
  497.  * Print/dump the contents of the screen cache.  For debugging.
  498.  */
  499. void
  500. svga_screen_cache_dump(const struct svga_screen *svgascreen)
  501. {
  502.    const struct svga_host_surface_cache *cache = &svgascreen->cache;
  503.    unsigned bucket;
  504.    unsigned count = 0;
  505.  
  506.    debug_printf("svga3d surface cache:\n");
  507.    for (bucket = 0; bucket < SVGA_HOST_SURFACE_CACHE_BUCKETS; bucket++) {
  508.       struct list_head *curr;
  509.       curr = cache->bucket[bucket].next;
  510.       while (curr && curr != &cache->bucket[bucket]) {
  511.          struct svga_host_surface_cache_entry *entry =
  512.             LIST_ENTRY(struct svga_host_surface_cache_entry,
  513.                        curr, bucket_head);
  514.          if (entry->key.format != 37) {
  515.             debug_printf("  %u x %u x %u format %u\n",
  516.                          entry->key.size.width,
  517.                          entry->key.size.height,
  518.                          entry->key.size.depth,
  519.                          entry->key.format);
  520.          }
  521.          curr = curr->next;
  522.          count++;
  523.       }
  524.    }
  525.  
  526.    debug_printf("%u surfaces, %u bytes\n", count, cache->total_size);
  527. }
  528.