Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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