1bf215546Sopenharmony_ci/********************************************************** 2bf215546Sopenharmony_ci * Copyright 2008-2009 VMware, Inc. All rights reserved. 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person 5bf215546Sopenharmony_ci * obtaining a copy of this software and associated documentation 6bf215546Sopenharmony_ci * files (the "Software"), to deal in the Software without 7bf215546Sopenharmony_ci * restriction, including without limitation the rights to use, copy, 8bf215546Sopenharmony_ci * modify, merge, publish, distribute, sublicense, and/or sell copies 9bf215546Sopenharmony_ci * of the Software, and to permit persons to whom the Software is 10bf215546Sopenharmony_ci * furnished to do so, subject to the following conditions: 11bf215546Sopenharmony_ci * 12bf215546Sopenharmony_ci * The above copyright notice and this permission notice shall be 13bf215546Sopenharmony_ci * included in all copies or substantial portions of the Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16bf215546Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18bf215546Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19bf215546Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20bf215546Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21bf215546Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22bf215546Sopenharmony_ci * SOFTWARE. 23bf215546Sopenharmony_ci * 24bf215546Sopenharmony_ci **********************************************************/ 25bf215546Sopenharmony_ci 26bf215546Sopenharmony_ci#include "util/u_math.h" 27bf215546Sopenharmony_ci#include "util/u_memory.h" 28bf215546Sopenharmony_ci#include "util/crc32.h" 29bf215546Sopenharmony_ci 30bf215546Sopenharmony_ci#include "svga_debug.h" 31bf215546Sopenharmony_ci#include "svga_format.h" 32bf215546Sopenharmony_ci#include "svga_winsys.h" 33bf215546Sopenharmony_ci#include "svga_screen.h" 34bf215546Sopenharmony_ci#include "svga_screen_cache.h" 35bf215546Sopenharmony_ci#include "svga_context.h" 36bf215546Sopenharmony_ci#include "svga_cmd.h" 37bf215546Sopenharmony_ci 38bf215546Sopenharmony_ci#define SVGA_SURFACE_CACHE_ENABLED 1 39bf215546Sopenharmony_ci 40bf215546Sopenharmony_ci 41bf215546Sopenharmony_ci/** 42bf215546Sopenharmony_ci * Return the size of the surface described by the key (in bytes). 43bf215546Sopenharmony_ci */ 44bf215546Sopenharmony_ciunsigned 45bf215546Sopenharmony_cisvga_surface_size(const struct svga_host_surface_cache_key *key) 46bf215546Sopenharmony_ci{ 47bf215546Sopenharmony_ci unsigned bw, bh, bpb, total_size, i; 48bf215546Sopenharmony_ci 49bf215546Sopenharmony_ci assert(key->numMipLevels > 0); 50bf215546Sopenharmony_ci assert(key->numFaces > 0); 51bf215546Sopenharmony_ci assert(key->arraySize > 0); 52bf215546Sopenharmony_ci 53bf215546Sopenharmony_ci if (key->format == SVGA3D_BUFFER) { 54bf215546Sopenharmony_ci /* Special case: we don't want to count vertex/index buffers 55bf215546Sopenharmony_ci * against the cache size limit, so view them as zero-sized. 56bf215546Sopenharmony_ci */ 57bf215546Sopenharmony_ci return 0; 58bf215546Sopenharmony_ci } 59bf215546Sopenharmony_ci 60bf215546Sopenharmony_ci svga_format_size(key->format, &bw, &bh, &bpb); 61bf215546Sopenharmony_ci 62bf215546Sopenharmony_ci total_size = 0; 63bf215546Sopenharmony_ci 64bf215546Sopenharmony_ci for (i = 0; i < key->numMipLevels; i++) { 65bf215546Sopenharmony_ci unsigned w = u_minify(key->size.width, i); 66bf215546Sopenharmony_ci unsigned h = u_minify(key->size.height, i); 67bf215546Sopenharmony_ci unsigned d = u_minify(key->size.depth, i); 68bf215546Sopenharmony_ci unsigned img_size = ((w + bw - 1) / bw) * ((h + bh - 1) / bh) * d * bpb; 69bf215546Sopenharmony_ci total_size += img_size; 70bf215546Sopenharmony_ci } 71bf215546Sopenharmony_ci 72bf215546Sopenharmony_ci total_size *= key->numFaces * key->arraySize * MAX2(1, key->sampleCount); 73bf215546Sopenharmony_ci 74bf215546Sopenharmony_ci return total_size; 75bf215546Sopenharmony_ci} 76bf215546Sopenharmony_ci 77bf215546Sopenharmony_ci 78bf215546Sopenharmony_ci/** 79bf215546Sopenharmony_ci * Compute the bucket for this key. 80bf215546Sopenharmony_ci */ 81bf215546Sopenharmony_cistatic inline unsigned 82bf215546Sopenharmony_cisvga_screen_cache_bucket(const struct svga_host_surface_cache_key *key) 83bf215546Sopenharmony_ci{ 84bf215546Sopenharmony_ci return util_hash_crc32(key, sizeof *key) % SVGA_HOST_SURFACE_CACHE_BUCKETS; 85bf215546Sopenharmony_ci} 86bf215546Sopenharmony_ci 87bf215546Sopenharmony_ci 88bf215546Sopenharmony_ci/** 89bf215546Sopenharmony_ci * Search the cache for a surface that matches the key. If a match is 90bf215546Sopenharmony_ci * found, remove it from the cache and return the surface pointer. 91bf215546Sopenharmony_ci * Return NULL otherwise. 92bf215546Sopenharmony_ci */ 93bf215546Sopenharmony_cistatic struct svga_winsys_surface * 94bf215546Sopenharmony_cisvga_screen_cache_lookup(struct svga_screen *svgascreen, 95bf215546Sopenharmony_ci const struct svga_host_surface_cache_key *key) 96bf215546Sopenharmony_ci{ 97bf215546Sopenharmony_ci struct svga_host_surface_cache *cache = &svgascreen->cache; 98bf215546Sopenharmony_ci struct svga_winsys_screen *sws = svgascreen->sws; 99bf215546Sopenharmony_ci struct svga_host_surface_cache_entry *entry; 100bf215546Sopenharmony_ci struct svga_winsys_surface *handle = NULL; 101bf215546Sopenharmony_ci struct list_head *curr, *next; 102bf215546Sopenharmony_ci unsigned bucket; 103bf215546Sopenharmony_ci unsigned tries = 0; 104bf215546Sopenharmony_ci 105bf215546Sopenharmony_ci assert(key->cachable); 106bf215546Sopenharmony_ci 107bf215546Sopenharmony_ci bucket = svga_screen_cache_bucket(key); 108bf215546Sopenharmony_ci 109bf215546Sopenharmony_ci mtx_lock(&cache->mutex); 110bf215546Sopenharmony_ci 111bf215546Sopenharmony_ci curr = cache->bucket[bucket].next; 112bf215546Sopenharmony_ci next = curr->next; 113bf215546Sopenharmony_ci while (curr != &cache->bucket[bucket]) { 114bf215546Sopenharmony_ci ++tries; 115bf215546Sopenharmony_ci 116bf215546Sopenharmony_ci entry = list_entry(curr, struct svga_host_surface_cache_entry, bucket_head); 117bf215546Sopenharmony_ci 118bf215546Sopenharmony_ci assert(entry->handle); 119bf215546Sopenharmony_ci 120bf215546Sopenharmony_ci /* If the key matches and the fence is signalled (the surface is no 121bf215546Sopenharmony_ci * longer needed) the lookup was successful. We found a surface that 122bf215546Sopenharmony_ci * can be reused. 123bf215546Sopenharmony_ci * We unlink the surface from the cache entry and we add the entry to 124bf215546Sopenharmony_ci * the 'empty' list. 125bf215546Sopenharmony_ci */ 126bf215546Sopenharmony_ci if (memcmp(&entry->key, key, sizeof *key) == 0 && 127bf215546Sopenharmony_ci sws->fence_signalled(sws, entry->fence, 0) == 0) { 128bf215546Sopenharmony_ci unsigned surf_size; 129bf215546Sopenharmony_ci 130bf215546Sopenharmony_ci assert(sws->surface_is_flushed(sws, entry->handle)); 131bf215546Sopenharmony_ci 132bf215546Sopenharmony_ci handle = entry->handle; /* Reference is transfered here. */ 133bf215546Sopenharmony_ci entry->handle = NULL; 134bf215546Sopenharmony_ci 135bf215546Sopenharmony_ci /* Remove from hash table */ 136bf215546Sopenharmony_ci list_del(&entry->bucket_head); 137bf215546Sopenharmony_ci 138bf215546Sopenharmony_ci /* remove from LRU list */ 139bf215546Sopenharmony_ci list_del(&entry->head); 140bf215546Sopenharmony_ci 141bf215546Sopenharmony_ci /* Add the cache entry (but not the surface!) to the empty list */ 142bf215546Sopenharmony_ci list_add(&entry->head, &cache->empty); 143bf215546Sopenharmony_ci 144bf215546Sopenharmony_ci /* update the cache size */ 145bf215546Sopenharmony_ci surf_size = svga_surface_size(&entry->key); 146bf215546Sopenharmony_ci assert(surf_size <= cache->total_size); 147bf215546Sopenharmony_ci if (surf_size > cache->total_size) 148bf215546Sopenharmony_ci cache->total_size = 0; /* should never happen, but be safe */ 149bf215546Sopenharmony_ci else 150bf215546Sopenharmony_ci cache->total_size -= surf_size; 151bf215546Sopenharmony_ci 152bf215546Sopenharmony_ci break; 153bf215546Sopenharmony_ci } 154bf215546Sopenharmony_ci 155bf215546Sopenharmony_ci curr = next; 156bf215546Sopenharmony_ci next = curr->next; 157bf215546Sopenharmony_ci } 158bf215546Sopenharmony_ci 159bf215546Sopenharmony_ci mtx_unlock(&cache->mutex); 160bf215546Sopenharmony_ci 161bf215546Sopenharmony_ci if (SVGA_DEBUG & DEBUG_DMA) 162bf215546Sopenharmony_ci debug_printf("%s: cache %s after %u tries (bucket %d)\n", __FUNCTION__, 163bf215546Sopenharmony_ci handle ? "hit" : "miss", tries, bucket); 164bf215546Sopenharmony_ci 165bf215546Sopenharmony_ci return handle; 166bf215546Sopenharmony_ci} 167bf215546Sopenharmony_ci 168bf215546Sopenharmony_ci 169bf215546Sopenharmony_ci/** 170bf215546Sopenharmony_ci * Free the least recently used entries in the surface cache until the 171bf215546Sopenharmony_ci * cache size is <= the target size OR there are no unused entries left 172bf215546Sopenharmony_ci * to discard. We don't do any flushing to try to free up additional 173bf215546Sopenharmony_ci * surfaces. 174bf215546Sopenharmony_ci */ 175bf215546Sopenharmony_cistatic void 176bf215546Sopenharmony_cisvga_screen_cache_shrink(struct svga_screen *svgascreen, 177bf215546Sopenharmony_ci unsigned target_size) 178bf215546Sopenharmony_ci{ 179bf215546Sopenharmony_ci struct svga_host_surface_cache *cache = &svgascreen->cache; 180bf215546Sopenharmony_ci struct svga_winsys_screen *sws = svgascreen->sws; 181bf215546Sopenharmony_ci struct svga_host_surface_cache_entry *entry = NULL, *next_entry; 182bf215546Sopenharmony_ci 183bf215546Sopenharmony_ci /* Walk over the list of unused buffers in reverse order: from oldest 184bf215546Sopenharmony_ci * to newest. 185bf215546Sopenharmony_ci */ 186bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY_SAFE_REV(entry, next_entry, &cache->unused, head) { 187bf215546Sopenharmony_ci if (entry->key.format != SVGA3D_BUFFER) { 188bf215546Sopenharmony_ci /* we don't want to discard vertex/index buffers */ 189bf215546Sopenharmony_ci 190bf215546Sopenharmony_ci cache->total_size -= svga_surface_size(&entry->key); 191bf215546Sopenharmony_ci 192bf215546Sopenharmony_ci assert(entry->handle); 193bf215546Sopenharmony_ci sws->surface_reference(sws, &entry->handle, NULL); 194bf215546Sopenharmony_ci 195bf215546Sopenharmony_ci list_del(&entry->bucket_head); 196bf215546Sopenharmony_ci list_del(&entry->head); 197bf215546Sopenharmony_ci list_add(&entry->head, &cache->empty); 198bf215546Sopenharmony_ci 199bf215546Sopenharmony_ci if (cache->total_size <= target_size) { 200bf215546Sopenharmony_ci /* all done */ 201bf215546Sopenharmony_ci break; 202bf215546Sopenharmony_ci } 203bf215546Sopenharmony_ci } 204bf215546Sopenharmony_ci } 205bf215546Sopenharmony_ci} 206bf215546Sopenharmony_ci 207bf215546Sopenharmony_ci 208bf215546Sopenharmony_ci/** 209bf215546Sopenharmony_ci * Add a surface to the cache. This is done when the driver deletes 210bf215546Sopenharmony_ci * the surface. Note: transfers a handle reference. 211bf215546Sopenharmony_ci */ 212bf215546Sopenharmony_cistatic void 213bf215546Sopenharmony_cisvga_screen_cache_add(struct svga_screen *svgascreen, 214bf215546Sopenharmony_ci const struct svga_host_surface_cache_key *key, 215bf215546Sopenharmony_ci boolean to_invalidate, 216bf215546Sopenharmony_ci struct svga_winsys_surface **p_handle) 217bf215546Sopenharmony_ci{ 218bf215546Sopenharmony_ci struct svga_host_surface_cache *cache = &svgascreen->cache; 219bf215546Sopenharmony_ci struct svga_winsys_screen *sws = svgascreen->sws; 220bf215546Sopenharmony_ci struct svga_host_surface_cache_entry *entry = NULL; 221bf215546Sopenharmony_ci struct svga_winsys_surface *handle = *p_handle; 222bf215546Sopenharmony_ci unsigned surf_size; 223bf215546Sopenharmony_ci 224bf215546Sopenharmony_ci assert(key->cachable); 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_ci if (!handle) 227bf215546Sopenharmony_ci return; 228bf215546Sopenharmony_ci 229bf215546Sopenharmony_ci surf_size = svga_surface_size(key); 230bf215546Sopenharmony_ci 231bf215546Sopenharmony_ci *p_handle = NULL; 232bf215546Sopenharmony_ci mtx_lock(&cache->mutex); 233bf215546Sopenharmony_ci 234bf215546Sopenharmony_ci if (surf_size >= SVGA_HOST_SURFACE_CACHE_BYTES) { 235bf215546Sopenharmony_ci /* this surface is too large to cache, just free it */ 236bf215546Sopenharmony_ci sws->surface_reference(sws, &handle, NULL); 237bf215546Sopenharmony_ci mtx_unlock(&cache->mutex); 238bf215546Sopenharmony_ci return; 239bf215546Sopenharmony_ci } 240bf215546Sopenharmony_ci 241bf215546Sopenharmony_ci if (cache->total_size + surf_size > SVGA_HOST_SURFACE_CACHE_BYTES) { 242bf215546Sopenharmony_ci /* Adding this surface would exceed the cache size. 243bf215546Sopenharmony_ci * Try to discard least recently used entries until we hit the 244bf215546Sopenharmony_ci * new target cache size. 245bf215546Sopenharmony_ci */ 246bf215546Sopenharmony_ci unsigned target_size = SVGA_HOST_SURFACE_CACHE_BYTES - surf_size; 247bf215546Sopenharmony_ci 248bf215546Sopenharmony_ci svga_screen_cache_shrink(svgascreen, target_size); 249bf215546Sopenharmony_ci 250bf215546Sopenharmony_ci if (cache->total_size > target_size) { 251bf215546Sopenharmony_ci /* we weren't able to shrink the cache as much as we wanted so 252bf215546Sopenharmony_ci * just discard this surface. 253bf215546Sopenharmony_ci */ 254bf215546Sopenharmony_ci sws->surface_reference(sws, &handle, NULL); 255bf215546Sopenharmony_ci mtx_unlock(&cache->mutex); 256bf215546Sopenharmony_ci return; 257bf215546Sopenharmony_ci } 258bf215546Sopenharmony_ci } 259bf215546Sopenharmony_ci 260bf215546Sopenharmony_ci if (!list_is_empty(&cache->empty)) { 261bf215546Sopenharmony_ci /* An empty entry has no surface associated with it. 262bf215546Sopenharmony_ci * Use the first empty entry. 263bf215546Sopenharmony_ci */ 264bf215546Sopenharmony_ci entry = list_entry(cache->empty.next, 265bf215546Sopenharmony_ci struct svga_host_surface_cache_entry, 266bf215546Sopenharmony_ci head); 267bf215546Sopenharmony_ci 268bf215546Sopenharmony_ci /* Remove from LRU list */ 269bf215546Sopenharmony_ci list_del(&entry->head); 270bf215546Sopenharmony_ci } 271bf215546Sopenharmony_ci else if (!list_is_empty(&cache->unused)) { 272bf215546Sopenharmony_ci /* free the last used buffer and reuse its entry */ 273bf215546Sopenharmony_ci entry = list_entry(cache->unused.prev, 274bf215546Sopenharmony_ci struct svga_host_surface_cache_entry, 275bf215546Sopenharmony_ci head); 276bf215546Sopenharmony_ci SVGA_DBG(DEBUG_CACHE|DEBUG_DMA, 277bf215546Sopenharmony_ci "unref sid %p (make space)\n", entry->handle); 278bf215546Sopenharmony_ci 279bf215546Sopenharmony_ci cache->total_size -= svga_surface_size(&entry->key); 280bf215546Sopenharmony_ci 281bf215546Sopenharmony_ci sws->surface_reference(sws, &entry->handle, NULL); 282bf215546Sopenharmony_ci 283bf215546Sopenharmony_ci /* Remove from hash table */ 284bf215546Sopenharmony_ci list_del(&entry->bucket_head); 285bf215546Sopenharmony_ci 286bf215546Sopenharmony_ci /* Remove from LRU list */ 287bf215546Sopenharmony_ci list_del(&entry->head); 288bf215546Sopenharmony_ci } 289bf215546Sopenharmony_ci 290bf215546Sopenharmony_ci if (entry) { 291bf215546Sopenharmony_ci assert(entry->handle == NULL); 292bf215546Sopenharmony_ci entry->handle = handle; 293bf215546Sopenharmony_ci memcpy(&entry->key, key, sizeof entry->key); 294bf215546Sopenharmony_ci 295bf215546Sopenharmony_ci SVGA_DBG(DEBUG_CACHE|DEBUG_DMA, 296bf215546Sopenharmony_ci "cache sid %p\n", entry->handle); 297bf215546Sopenharmony_ci 298bf215546Sopenharmony_ci /* If we don't have gb objects, we don't need to invalidate. */ 299bf215546Sopenharmony_ci if (sws->have_gb_objects) { 300bf215546Sopenharmony_ci if (to_invalidate) 301bf215546Sopenharmony_ci list_add(&entry->head, &cache->validated); 302bf215546Sopenharmony_ci else 303bf215546Sopenharmony_ci list_add(&entry->head, &cache->invalidated); 304bf215546Sopenharmony_ci } 305bf215546Sopenharmony_ci else 306bf215546Sopenharmony_ci list_add(&entry->head, &cache->invalidated); 307bf215546Sopenharmony_ci 308bf215546Sopenharmony_ci cache->total_size += surf_size; 309bf215546Sopenharmony_ci } 310bf215546Sopenharmony_ci else { 311bf215546Sopenharmony_ci /* Couldn't cache the buffer -- this really shouldn't happen */ 312bf215546Sopenharmony_ci SVGA_DBG(DEBUG_CACHE|DEBUG_DMA, 313bf215546Sopenharmony_ci "unref sid %p (couldn't find space)\n", handle); 314bf215546Sopenharmony_ci sws->surface_reference(sws, &handle, NULL); 315bf215546Sopenharmony_ci } 316bf215546Sopenharmony_ci 317bf215546Sopenharmony_ci mtx_unlock(&cache->mutex); 318bf215546Sopenharmony_ci} 319bf215546Sopenharmony_ci 320bf215546Sopenharmony_ci 321bf215546Sopenharmony_ci/* Maximum number of invalidate surface commands in a command buffer */ 322bf215546Sopenharmony_ci# define SVGA_MAX_SURFACE_TO_INVALIDATE 1000 323bf215546Sopenharmony_ci 324bf215546Sopenharmony_ci/** 325bf215546Sopenharmony_ci * Called during the screen flush to move all buffers not in a validate list 326bf215546Sopenharmony_ci * into the unused list. 327bf215546Sopenharmony_ci */ 328bf215546Sopenharmony_civoid 329bf215546Sopenharmony_cisvga_screen_cache_flush(struct svga_screen *svgascreen, 330bf215546Sopenharmony_ci struct svga_context *svga, 331bf215546Sopenharmony_ci struct pipe_fence_handle *fence) 332bf215546Sopenharmony_ci{ 333bf215546Sopenharmony_ci struct svga_host_surface_cache *cache = &svgascreen->cache; 334bf215546Sopenharmony_ci struct svga_winsys_screen *sws = svgascreen->sws; 335bf215546Sopenharmony_ci struct svga_host_surface_cache_entry *entry; 336bf215546Sopenharmony_ci struct list_head *curr, *next; 337bf215546Sopenharmony_ci unsigned bucket; 338bf215546Sopenharmony_ci 339bf215546Sopenharmony_ci mtx_lock(&cache->mutex); 340bf215546Sopenharmony_ci 341bf215546Sopenharmony_ci /* Loop over entries in the invalidated list */ 342bf215546Sopenharmony_ci curr = cache->invalidated.next; 343bf215546Sopenharmony_ci next = curr->next; 344bf215546Sopenharmony_ci while (curr != &cache->invalidated) { 345bf215546Sopenharmony_ci entry = list_entry(curr, struct svga_host_surface_cache_entry, head); 346bf215546Sopenharmony_ci 347bf215546Sopenharmony_ci assert(entry->handle); 348bf215546Sopenharmony_ci 349bf215546Sopenharmony_ci if (sws->surface_is_flushed(sws, entry->handle)) { 350bf215546Sopenharmony_ci /* remove entry from the invalidated list */ 351bf215546Sopenharmony_ci list_del(&entry->head); 352bf215546Sopenharmony_ci 353bf215546Sopenharmony_ci sws->fence_reference(sws, &entry->fence, fence); 354bf215546Sopenharmony_ci 355bf215546Sopenharmony_ci /* Add entry to the unused list */ 356bf215546Sopenharmony_ci list_add(&entry->head, &cache->unused); 357bf215546Sopenharmony_ci 358bf215546Sopenharmony_ci /* Add entry to the hash table bucket */ 359bf215546Sopenharmony_ci bucket = svga_screen_cache_bucket(&entry->key); 360bf215546Sopenharmony_ci list_add(&entry->bucket_head, &cache->bucket[bucket]); 361bf215546Sopenharmony_ci } 362bf215546Sopenharmony_ci 363bf215546Sopenharmony_ci curr = next; 364bf215546Sopenharmony_ci next = curr->next; 365bf215546Sopenharmony_ci } 366bf215546Sopenharmony_ci 367bf215546Sopenharmony_ci unsigned nsurf = 0; 368bf215546Sopenharmony_ci curr = cache->validated.next; 369bf215546Sopenharmony_ci next = curr->next; 370bf215546Sopenharmony_ci while (curr != &cache->validated) { 371bf215546Sopenharmony_ci entry = list_entry(curr, struct svga_host_surface_cache_entry, head); 372bf215546Sopenharmony_ci 373bf215546Sopenharmony_ci assert(entry->handle); 374bf215546Sopenharmony_ci assert(svga_have_gb_objects(svga)); 375bf215546Sopenharmony_ci 376bf215546Sopenharmony_ci if (sws->surface_is_flushed(sws, entry->handle)) { 377bf215546Sopenharmony_ci /* remove entry from the validated list */ 378bf215546Sopenharmony_ci list_del(&entry->head); 379bf215546Sopenharmony_ci 380bf215546Sopenharmony_ci /* It is now safe to invalidate the surface content. 381bf215546Sopenharmony_ci * It will be done using the current context. 382bf215546Sopenharmony_ci */ 383bf215546Sopenharmony_ci if (SVGA_TRY(SVGA3D_InvalidateGBSurface(svga->swc, entry->handle)) 384bf215546Sopenharmony_ci != PIPE_OK) { 385bf215546Sopenharmony_ci ASSERTED enum pipe_error ret; 386bf215546Sopenharmony_ci 387bf215546Sopenharmony_ci /* Even though surface invalidation here is done after the command 388bf215546Sopenharmony_ci * buffer is flushed, it is still possible that it will 389bf215546Sopenharmony_ci * fail because there might be just enough of this command that is 390bf215546Sopenharmony_ci * filling up the command buffer, so in this case we will call 391bf215546Sopenharmony_ci * the winsys flush directly to flush the buffer. 392bf215546Sopenharmony_ci * Note, we don't want to call svga_context_flush() here because 393bf215546Sopenharmony_ci * this function itself is called inside svga_context_flush(). 394bf215546Sopenharmony_ci */ 395bf215546Sopenharmony_ci svga_retry_enter(svga); 396bf215546Sopenharmony_ci svga->swc->flush(svga->swc, NULL); 397bf215546Sopenharmony_ci nsurf = 0; 398bf215546Sopenharmony_ci ret = SVGA3D_InvalidateGBSurface(svga->swc, entry->handle); 399bf215546Sopenharmony_ci svga_retry_exit(svga); 400bf215546Sopenharmony_ci assert(ret == PIPE_OK); 401bf215546Sopenharmony_ci } 402bf215546Sopenharmony_ci 403bf215546Sopenharmony_ci /* add the entry to the invalidated list */ 404bf215546Sopenharmony_ci 405bf215546Sopenharmony_ci list_add(&entry->head, &cache->invalidated); 406bf215546Sopenharmony_ci nsurf++; 407bf215546Sopenharmony_ci } 408bf215546Sopenharmony_ci 409bf215546Sopenharmony_ci curr = next; 410bf215546Sopenharmony_ci next = curr->next; 411bf215546Sopenharmony_ci } 412bf215546Sopenharmony_ci 413bf215546Sopenharmony_ci mtx_unlock(&cache->mutex); 414bf215546Sopenharmony_ci 415bf215546Sopenharmony_ci /** 416bf215546Sopenharmony_ci * In some rare cases (when running ARK survival), we hit the max number 417bf215546Sopenharmony_ci * of surface relocations with invalidated surfaces during context flush. 418bf215546Sopenharmony_ci * So if the number of invalidated surface exceeds a certain limit (1000), 419bf215546Sopenharmony_ci * we'll do another winsys flush. 420bf215546Sopenharmony_ci */ 421bf215546Sopenharmony_ci if (nsurf > SVGA_MAX_SURFACE_TO_INVALIDATE) { 422bf215546Sopenharmony_ci svga->swc->flush(svga->swc, NULL); 423bf215546Sopenharmony_ci } 424bf215546Sopenharmony_ci} 425bf215546Sopenharmony_ci 426bf215546Sopenharmony_ci 427bf215546Sopenharmony_ci/** 428bf215546Sopenharmony_ci * Free all the surfaces in the cache. 429bf215546Sopenharmony_ci * Called when destroying the svga screen object. 430bf215546Sopenharmony_ci */ 431bf215546Sopenharmony_civoid 432bf215546Sopenharmony_cisvga_screen_cache_cleanup(struct svga_screen *svgascreen) 433bf215546Sopenharmony_ci{ 434bf215546Sopenharmony_ci struct svga_host_surface_cache *cache = &svgascreen->cache; 435bf215546Sopenharmony_ci struct svga_winsys_screen *sws = svgascreen->sws; 436bf215546Sopenharmony_ci unsigned i; 437bf215546Sopenharmony_ci 438bf215546Sopenharmony_ci for (i = 0; i < SVGA_HOST_SURFACE_CACHE_SIZE; ++i) { 439bf215546Sopenharmony_ci if (cache->entries[i].handle) { 440bf215546Sopenharmony_ci SVGA_DBG(DEBUG_CACHE|DEBUG_DMA, 441bf215546Sopenharmony_ci "unref sid %p (shutdown)\n", cache->entries[i].handle); 442bf215546Sopenharmony_ci sws->surface_reference(sws, &cache->entries[i].handle, NULL); 443bf215546Sopenharmony_ci 444bf215546Sopenharmony_ci cache->total_size -= svga_surface_size(&cache->entries[i].key); 445bf215546Sopenharmony_ci } 446bf215546Sopenharmony_ci 447bf215546Sopenharmony_ci if (cache->entries[i].fence) 448bf215546Sopenharmony_ci sws->fence_reference(sws, &cache->entries[i].fence, NULL); 449bf215546Sopenharmony_ci } 450bf215546Sopenharmony_ci 451bf215546Sopenharmony_ci mtx_destroy(&cache->mutex); 452bf215546Sopenharmony_ci} 453bf215546Sopenharmony_ci 454bf215546Sopenharmony_ci 455bf215546Sopenharmony_cienum pipe_error 456bf215546Sopenharmony_cisvga_screen_cache_init(struct svga_screen *svgascreen) 457bf215546Sopenharmony_ci{ 458bf215546Sopenharmony_ci struct svga_host_surface_cache *cache = &svgascreen->cache; 459bf215546Sopenharmony_ci unsigned i; 460bf215546Sopenharmony_ci 461bf215546Sopenharmony_ci assert(cache->total_size == 0); 462bf215546Sopenharmony_ci 463bf215546Sopenharmony_ci (void) mtx_init(&cache->mutex, mtx_plain); 464bf215546Sopenharmony_ci 465bf215546Sopenharmony_ci for (i = 0; i < SVGA_HOST_SURFACE_CACHE_BUCKETS; ++i) 466bf215546Sopenharmony_ci list_inithead(&cache->bucket[i]); 467bf215546Sopenharmony_ci 468bf215546Sopenharmony_ci list_inithead(&cache->unused); 469bf215546Sopenharmony_ci 470bf215546Sopenharmony_ci list_inithead(&cache->validated); 471bf215546Sopenharmony_ci 472bf215546Sopenharmony_ci list_inithead(&cache->invalidated); 473bf215546Sopenharmony_ci 474bf215546Sopenharmony_ci list_inithead(&cache->empty); 475bf215546Sopenharmony_ci for (i = 0; i < SVGA_HOST_SURFACE_CACHE_SIZE; ++i) 476bf215546Sopenharmony_ci list_addtail(&cache->entries[i].head, &cache->empty); 477bf215546Sopenharmony_ci 478bf215546Sopenharmony_ci return PIPE_OK; 479bf215546Sopenharmony_ci} 480bf215546Sopenharmony_ci 481bf215546Sopenharmony_ci 482bf215546Sopenharmony_ci/** 483bf215546Sopenharmony_ci * Allocate a new host-side surface. If the surface is marked as cachable, 484bf215546Sopenharmony_ci * first try re-using a surface in the cache of freed surfaces. Otherwise, 485bf215546Sopenharmony_ci * allocate a new surface. 486bf215546Sopenharmony_ci * \param bind_flags bitmask of PIPE_BIND_x flags 487bf215546Sopenharmony_ci * \param usage one of PIPE_USAGE_x values 488bf215546Sopenharmony_ci * \param validated return True if the surface is a reused surface 489bf215546Sopenharmony_ci */ 490bf215546Sopenharmony_cistruct svga_winsys_surface * 491bf215546Sopenharmony_cisvga_screen_surface_create(struct svga_screen *svgascreen, 492bf215546Sopenharmony_ci unsigned bind_flags, enum pipe_resource_usage usage, 493bf215546Sopenharmony_ci boolean *validated, 494bf215546Sopenharmony_ci struct svga_host_surface_cache_key *key) 495bf215546Sopenharmony_ci{ 496bf215546Sopenharmony_ci struct svga_winsys_screen *sws = svgascreen->sws; 497bf215546Sopenharmony_ci struct svga_winsys_surface *handle = NULL; 498bf215546Sopenharmony_ci boolean cachable = SVGA_SURFACE_CACHE_ENABLED && key->cachable; 499bf215546Sopenharmony_ci 500bf215546Sopenharmony_ci SVGA_DBG(DEBUG_CACHE|DEBUG_DMA, 501bf215546Sopenharmony_ci "%s sz %dx%dx%d mips %d faces %d arraySize %d cachable %d\n", 502bf215546Sopenharmony_ci __FUNCTION__, 503bf215546Sopenharmony_ci key->size.width, 504bf215546Sopenharmony_ci key->size.height, 505bf215546Sopenharmony_ci key->size.depth, 506bf215546Sopenharmony_ci key->numMipLevels, 507bf215546Sopenharmony_ci key->numFaces, 508bf215546Sopenharmony_ci key->arraySize, 509bf215546Sopenharmony_ci key->cachable); 510bf215546Sopenharmony_ci 511bf215546Sopenharmony_ci if (cachable) { 512bf215546Sopenharmony_ci /* Try to re-cycle a previously freed, cached surface */ 513bf215546Sopenharmony_ci if (key->format == SVGA3D_BUFFER) { 514bf215546Sopenharmony_ci SVGA3dSurfaceAllFlags hint_flag; 515bf215546Sopenharmony_ci 516bf215546Sopenharmony_ci /* For buffers, round the buffer size up to the nearest power 517bf215546Sopenharmony_ci * of two to increase the probability of cache hits. Keep 518bf215546Sopenharmony_ci * texture surface dimensions unchanged. 519bf215546Sopenharmony_ci */ 520bf215546Sopenharmony_ci uint32_t size = 1; 521bf215546Sopenharmony_ci while (size < key->size.width) 522bf215546Sopenharmony_ci size <<= 1; 523bf215546Sopenharmony_ci key->size.width = size; 524bf215546Sopenharmony_ci 525bf215546Sopenharmony_ci /* Determine whether the buffer is static or dynamic. 526bf215546Sopenharmony_ci * This is a bit of a heuristic which can be tuned as needed. 527bf215546Sopenharmony_ci */ 528bf215546Sopenharmony_ci if (usage == PIPE_USAGE_DEFAULT || 529bf215546Sopenharmony_ci usage == PIPE_USAGE_IMMUTABLE) { 530bf215546Sopenharmony_ci hint_flag = SVGA3D_SURFACE_HINT_STATIC; 531bf215546Sopenharmony_ci } 532bf215546Sopenharmony_ci else if (bind_flags & PIPE_BIND_INDEX_BUFFER) { 533bf215546Sopenharmony_ci /* Index buffers don't change too often. Mark them as static. 534bf215546Sopenharmony_ci */ 535bf215546Sopenharmony_ci hint_flag = SVGA3D_SURFACE_HINT_STATIC; 536bf215546Sopenharmony_ci } 537bf215546Sopenharmony_ci else { 538bf215546Sopenharmony_ci /* Since we're reusing buffers we're effectively transforming all 539bf215546Sopenharmony_ci * of them into dynamic buffers. 540bf215546Sopenharmony_ci * 541bf215546Sopenharmony_ci * It would be nice to not cache long lived static buffers. But there 542bf215546Sopenharmony_ci * is no way to detect the long lived from short lived ones yet. A 543bf215546Sopenharmony_ci * good heuristic would be buffer size. 544bf215546Sopenharmony_ci */ 545bf215546Sopenharmony_ci hint_flag = SVGA3D_SURFACE_HINT_DYNAMIC; 546bf215546Sopenharmony_ci } 547bf215546Sopenharmony_ci 548bf215546Sopenharmony_ci key->flags &= ~(SVGA3D_SURFACE_HINT_STATIC | 549bf215546Sopenharmony_ci SVGA3D_SURFACE_HINT_DYNAMIC); 550bf215546Sopenharmony_ci key->flags |= hint_flag; 551bf215546Sopenharmony_ci } 552bf215546Sopenharmony_ci 553bf215546Sopenharmony_ci handle = svga_screen_cache_lookup(svgascreen, key); 554bf215546Sopenharmony_ci if (handle) { 555bf215546Sopenharmony_ci if (key->format == SVGA3D_BUFFER) 556bf215546Sopenharmony_ci SVGA_DBG(DEBUG_CACHE|DEBUG_DMA, 557bf215546Sopenharmony_ci "reuse sid %p sz %d (buffer)\n", handle, 558bf215546Sopenharmony_ci key->size.width); 559bf215546Sopenharmony_ci else 560bf215546Sopenharmony_ci SVGA_DBG(DEBUG_CACHE|DEBUG_DMA, 561bf215546Sopenharmony_ci "reuse sid %p sz %dx%dx%d mips %d faces %d arraySize %d\n", handle, 562bf215546Sopenharmony_ci key->size.width, 563bf215546Sopenharmony_ci key->size.height, 564bf215546Sopenharmony_ci key->size.depth, 565bf215546Sopenharmony_ci key->numMipLevels, 566bf215546Sopenharmony_ci key->numFaces, 567bf215546Sopenharmony_ci key->arraySize); 568bf215546Sopenharmony_ci *validated = TRUE; 569bf215546Sopenharmony_ci } 570bf215546Sopenharmony_ci } 571bf215546Sopenharmony_ci 572bf215546Sopenharmony_ci if (!handle) { 573bf215546Sopenharmony_ci /* Unable to recycle surface, allocate a new one */ 574bf215546Sopenharmony_ci unsigned usage = 0; 575bf215546Sopenharmony_ci 576bf215546Sopenharmony_ci if (!key->cachable) 577bf215546Sopenharmony_ci usage |= SVGA_SURFACE_USAGE_SHARED; 578bf215546Sopenharmony_ci if (key->scanout) 579bf215546Sopenharmony_ci usage |= SVGA_SURFACE_USAGE_SCANOUT; 580bf215546Sopenharmony_ci if (key->coherent) 581bf215546Sopenharmony_ci usage |= SVGA_SURFACE_USAGE_COHERENT; 582bf215546Sopenharmony_ci 583bf215546Sopenharmony_ci handle = sws->surface_create(sws, 584bf215546Sopenharmony_ci key->flags, 585bf215546Sopenharmony_ci key->format, 586bf215546Sopenharmony_ci usage, 587bf215546Sopenharmony_ci key->size, 588bf215546Sopenharmony_ci key->numFaces * key->arraySize, 589bf215546Sopenharmony_ci key->numMipLevels, 590bf215546Sopenharmony_ci key->sampleCount); 591bf215546Sopenharmony_ci if (handle) 592bf215546Sopenharmony_ci SVGA_DBG(DEBUG_CACHE|DEBUG_DMA, 593bf215546Sopenharmony_ci " CREATE sid %p sz %dx%dx%d\n", 594bf215546Sopenharmony_ci handle, 595bf215546Sopenharmony_ci key->size.width, 596bf215546Sopenharmony_ci key->size.height, 597bf215546Sopenharmony_ci key->size.depth); 598bf215546Sopenharmony_ci 599bf215546Sopenharmony_ci *validated = FALSE; 600bf215546Sopenharmony_ci } 601bf215546Sopenharmony_ci 602bf215546Sopenharmony_ci return handle; 603bf215546Sopenharmony_ci} 604bf215546Sopenharmony_ci 605bf215546Sopenharmony_ci 606bf215546Sopenharmony_ci/** 607bf215546Sopenharmony_ci * Release a surface. We don't actually free the surface- we put 608bf215546Sopenharmony_ci * it into the cache of freed surfaces (if it's cachable). 609bf215546Sopenharmony_ci */ 610bf215546Sopenharmony_civoid 611bf215546Sopenharmony_cisvga_screen_surface_destroy(struct svga_screen *svgascreen, 612bf215546Sopenharmony_ci const struct svga_host_surface_cache_key *key, 613bf215546Sopenharmony_ci boolean to_invalidate, 614bf215546Sopenharmony_ci struct svga_winsys_surface **p_handle) 615bf215546Sopenharmony_ci{ 616bf215546Sopenharmony_ci struct svga_winsys_screen *sws = svgascreen->sws; 617bf215546Sopenharmony_ci 618bf215546Sopenharmony_ci /* We only set the cachable flag for surfaces of which we are the 619bf215546Sopenharmony_ci * exclusive owner. So just hold onto our existing reference in 620bf215546Sopenharmony_ci * that case. 621bf215546Sopenharmony_ci */ 622bf215546Sopenharmony_ci if (SVGA_SURFACE_CACHE_ENABLED && key->cachable) { 623bf215546Sopenharmony_ci svga_screen_cache_add(svgascreen, key, to_invalidate, p_handle); 624bf215546Sopenharmony_ci } 625bf215546Sopenharmony_ci else { 626bf215546Sopenharmony_ci SVGA_DBG(DEBUG_DMA, 627bf215546Sopenharmony_ci "unref sid %p (uncachable)\n", *p_handle); 628bf215546Sopenharmony_ci sws->surface_reference(sws, p_handle, NULL); 629bf215546Sopenharmony_ci } 630bf215546Sopenharmony_ci} 631bf215546Sopenharmony_ci 632bf215546Sopenharmony_ci 633bf215546Sopenharmony_ci/** 634bf215546Sopenharmony_ci * Print/dump the contents of the screen cache. For debugging. 635bf215546Sopenharmony_ci */ 636bf215546Sopenharmony_civoid 637bf215546Sopenharmony_cisvga_screen_cache_dump(const struct svga_screen *svgascreen) 638bf215546Sopenharmony_ci{ 639bf215546Sopenharmony_ci const struct svga_host_surface_cache *cache = &svgascreen->cache; 640bf215546Sopenharmony_ci unsigned bucket; 641bf215546Sopenharmony_ci unsigned count = 0; 642bf215546Sopenharmony_ci 643bf215546Sopenharmony_ci debug_printf("svga3d surface cache:\n"); 644bf215546Sopenharmony_ci for (bucket = 0; bucket < SVGA_HOST_SURFACE_CACHE_BUCKETS; bucket++) { 645bf215546Sopenharmony_ci struct list_head *curr; 646bf215546Sopenharmony_ci curr = cache->bucket[bucket].next; 647bf215546Sopenharmony_ci while (curr && curr != &cache->bucket[bucket]) { 648bf215546Sopenharmony_ci struct svga_host_surface_cache_entry *entry = 649bf215546Sopenharmony_ci list_entry(curr, struct svga_host_surface_cache_entry,bucket_head); 650bf215546Sopenharmony_ci if (entry->key.format == SVGA3D_BUFFER) { 651bf215546Sopenharmony_ci debug_printf(" %p: buffer %u bytes\n", 652bf215546Sopenharmony_ci entry->handle, 653bf215546Sopenharmony_ci entry->key.size.width); 654bf215546Sopenharmony_ci } 655bf215546Sopenharmony_ci else { 656bf215546Sopenharmony_ci debug_printf(" %p: %u x %u x %u format %u\n", 657bf215546Sopenharmony_ci entry->handle, 658bf215546Sopenharmony_ci entry->key.size.width, 659bf215546Sopenharmony_ci entry->key.size.height, 660bf215546Sopenharmony_ci entry->key.size.depth, 661bf215546Sopenharmony_ci entry->key.format); 662bf215546Sopenharmony_ci } 663bf215546Sopenharmony_ci curr = curr->next; 664bf215546Sopenharmony_ci count++; 665bf215546Sopenharmony_ci } 666bf215546Sopenharmony_ci } 667bf215546Sopenharmony_ci 668bf215546Sopenharmony_ci debug_printf("%u surfaces, %u bytes\n", count, cache->total_size); 669bf215546Sopenharmony_ci} 670