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