sp_tile_cache.c revision 2142bf5e17d699396772b90ee5b2592289334f28
1/**************************************************************************
2 *
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/**
29 * Texture tile caching.
30 *
31 * Author:
32 *    Brian Paul
33 */
34
35#include "pipe/p_inlines.h"
36#include "util/u_memory.h"
37#include "util/u_tile.h"
38#include "sp_context.h"
39#include "sp_surface.h"
40#include "sp_texture.h"
41#include "sp_tile_cache.h"
42
43#define NUM_ENTRIES 32
44
45
46/** XXX move these */
47#define MAX_WIDTH 2048
48#define MAX_HEIGHT 2048
49
50
51struct softpipe_tile_cache
52{
53   struct pipe_screen *screen;
54   struct pipe_surface *surface;  /**< the surface we're caching */
55   struct pipe_transfer *transfer;
56   void *transfer_map;
57   struct pipe_texture *texture;  /**< if caching a texture */
58   struct softpipe_cached_tile entries[NUM_ENTRIES];
59   uint clear_flags[(MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32];
60   float clear_color[4];
61   uint clear_val;
62   boolean depth_stencil; /** Is the surface a depth/stencil format? */
63
64   struct pipe_transfer *tex_trans;
65   void *tex_trans_map;
66   int tex_face, tex_level, tex_z;
67
68   struct softpipe_cached_tile tile;  /**< scratch tile for clears */
69};
70
71
72/**
73 * Return the position in the cache for the tile that contains win pos (x,y).
74 * We currently use a direct mapped cache so this is like a hack key.
75 * At some point we should investige something more sophisticated, like
76 * a LRU replacement policy.
77 */
78#define CACHE_POS(x, y) \
79   (((x) / TILE_SIZE + ((y) / TILE_SIZE) * 5) % NUM_ENTRIES)
80
81
82
83/**
84 * Is the tile at (x,y) in cleared state?
85 */
86static INLINE uint
87is_clear_flag_set(const uint *bitvec, int x, int y)
88{
89   int pos, bit;
90   x /= TILE_SIZE;
91   y /= TILE_SIZE;
92   pos = y * (MAX_WIDTH / TILE_SIZE) + x;
93   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
94   bit = bitvec[pos / 32] & (1 << (pos & 31));
95   return bit;
96}
97
98
99/**
100 * Mark the tile at (x,y) as not cleared.
101 */
102static INLINE void
103clear_clear_flag(uint *bitvec, int x, int y)
104{
105   int pos;
106   x /= TILE_SIZE;
107   y /= TILE_SIZE;
108   pos = y * (MAX_WIDTH / TILE_SIZE) + x;
109   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
110   bitvec[pos / 32] &= ~(1 << (pos & 31));
111}
112
113
114struct softpipe_tile_cache *
115sp_create_tile_cache( struct pipe_screen *screen )
116{
117   struct softpipe_tile_cache *tc;
118   uint pos;
119
120   tc = CALLOC_STRUCT( softpipe_tile_cache );
121   if (tc) {
122      tc->screen = screen;
123      for (pos = 0; pos < NUM_ENTRIES; pos++) {
124         tc->entries[pos].x =
125         tc->entries[pos].y = -1;
126      }
127   }
128   return tc;
129}
130
131
132void
133sp_destroy_tile_cache(struct softpipe_tile_cache *tc)
134{
135   struct pipe_screen *screen;
136   uint pos;
137
138   for (pos = 0; pos < NUM_ENTRIES; pos++) {
139      /*assert(tc->entries[pos].x < 0);*/
140   }
141   if (tc->transfer) {
142      screen = tc->transfer->texture->screen;
143      screen->tex_transfer_release(screen, &tc->transfer);
144   }
145   if (tc->tex_trans) {
146      screen = tc->tex_trans->texture->screen;
147      screen->tex_transfer_release(screen, &tc->tex_trans);
148   }
149
150   FREE( tc );
151}
152
153
154/**
155 * Specify the surface to cache.
156 */
157void
158sp_tile_cache_set_surface(struct softpipe_tile_cache *tc,
159                          struct pipe_surface *ps)
160{
161   struct pipe_screen *screen = ps->texture->screen;
162
163   assert(!tc->texture);
164
165   if (tc->transfer) {
166      if (ps == tc->surface)
167         return;
168
169      if (tc->transfer_map) {
170         tc->screen->transfer_unmap(tc->screen, tc->transfer);
171         tc->transfer_map = NULL;
172      }
173
174      screen->tex_transfer_release(screen, &tc->transfer);
175   }
176
177   tc->surface = ps;
178
179   tc->transfer = screen->get_tex_transfer(screen, ps->texture, ps->face,
180                                           ps->level, ps->zslice,
181                                           PIPE_TRANSFER_READ_WRITE,
182                                           0, 0, ps->width, ps->height);
183
184   tc->depth_stencil = (ps->format == PIPE_FORMAT_S8Z24_UNORM ||
185                        ps->format == PIPE_FORMAT_X8Z24_UNORM ||
186                        ps->format == PIPE_FORMAT_Z24S8_UNORM ||
187                        ps->format == PIPE_FORMAT_Z24X8_UNORM ||
188                        ps->format == PIPE_FORMAT_Z16_UNORM ||
189                        ps->format == PIPE_FORMAT_Z32_UNORM ||
190                        ps->format == PIPE_FORMAT_S8_UNORM);
191}
192
193
194/**
195 * Return the transfer being cached.
196 */
197struct pipe_surface *
198sp_tile_cache_get_surface(struct softpipe_tile_cache *tc)
199{
200   return tc->surface;
201}
202
203
204void
205sp_tile_cache_map_transfers(struct softpipe_tile_cache *tc)
206{
207   if (tc->transfer && !tc->transfer_map)
208      tc->transfer_map = tc->screen->transfer_map(tc->screen, tc->transfer);
209
210   if (tc->tex_trans && !tc->tex_trans_map)
211      tc->tex_trans_map = tc->screen->transfer_map(tc->screen, tc->tex_trans);
212}
213
214
215void
216sp_tile_cache_unmap_transfers(struct softpipe_tile_cache *tc)
217{
218   if (tc->transfer_map) {
219      tc->screen->transfer_unmap(tc->screen, tc->transfer);
220      tc->transfer_map = NULL;
221   }
222
223   if (tc->tex_trans_map) {
224      tc->screen->transfer_unmap(tc->screen, tc->tex_trans);
225      tc->tex_trans_map = NULL;
226   }
227}
228
229
230/**
231 * Specify the texture to cache.
232 */
233void
234sp_tile_cache_set_texture(struct pipe_context *pipe,
235                          struct softpipe_tile_cache *tc,
236                          struct pipe_texture *texture)
237{
238   struct pipe_screen *screen = texture->screen;
239   uint i;
240
241   assert(!tc->transfer);
242
243   pipe_texture_reference(&tc->texture, texture);
244
245   if (tc->tex_trans_map) {
246      tc->screen->transfer_unmap(tc->screen, tc->tex_trans);
247      tc->tex_trans_map = NULL;
248   }
249   screen->tex_transfer_release(screen, &tc->tex_trans);
250
251   /* mark as entries as invalid/empty */
252   /* XXX we should try to avoid this when the teximage hasn't changed */
253   for (i = 0; i < NUM_ENTRIES; i++) {
254      tc->entries[i].x = -1;
255   }
256
257   tc->tex_face = -1; /* any invalid value here */
258}
259
260
261/**
262 * Set pixels in a tile to the given clear color/value, float.
263 */
264static void
265clear_tile_rgba(struct softpipe_cached_tile *tile,
266                enum pipe_format format,
267                const float clear_value[4])
268{
269   if (clear_value[0] == 0.0 &&
270       clear_value[1] == 0.0 &&
271       clear_value[2] == 0.0 &&
272       clear_value[3] == 0.0) {
273      memset(tile->data.color, 0, sizeof(tile->data.color));
274   }
275   else {
276      uint i, j;
277      for (i = 0; i < TILE_SIZE; i++) {
278         for (j = 0; j < TILE_SIZE; j++) {
279            tile->data.color[i][j][0] = clear_value[0];
280            tile->data.color[i][j][1] = clear_value[1];
281            tile->data.color[i][j][2] = clear_value[2];
282            tile->data.color[i][j][3] = clear_value[3];
283         }
284      }
285   }
286}
287
288
289/**
290 * Set a tile to a solid value/color.
291 */
292static void
293clear_tile(struct softpipe_cached_tile *tile,
294           enum pipe_format format,
295           uint clear_value)
296{
297   uint i, j;
298
299   switch (pf_get_size(format)) {
300   case 1:
301      memset(tile->data.any, 0, TILE_SIZE * TILE_SIZE);
302      break;
303   case 2:
304      if (clear_value == 0) {
305         memset(tile->data.any, 0, 2 * TILE_SIZE * TILE_SIZE);
306      }
307      else {
308         for (i = 0; i < TILE_SIZE; i++) {
309            for (j = 0; j < TILE_SIZE; j++) {
310               tile->data.depth16[i][j] = (ushort) clear_value;
311            }
312         }
313      }
314      break;
315   case 4:
316      if (clear_value == 0) {
317         memset(tile->data.any, 0, 4 * TILE_SIZE * TILE_SIZE);
318      }
319      else {
320         for (i = 0; i < TILE_SIZE; i++) {
321            for (j = 0; j < TILE_SIZE; j++) {
322               tile->data.color32[i][j] = clear_value;
323            }
324         }
325      }
326      break;
327   default:
328      assert(0);
329   }
330}
331
332
333/**
334 * Actually clear the tiles which were flagged as being in a clear state.
335 */
336static void
337sp_tile_cache_flush_clear(struct pipe_context *pipe,
338                          struct softpipe_tile_cache *tc)
339{
340   struct pipe_transfer *pt = tc->transfer;
341   const uint w = tc->transfer->width;
342   const uint h = tc->transfer->height;
343   uint x, y;
344   uint numCleared = 0;
345
346   /* clear the scratch tile to the clear value */
347   clear_tile(&tc->tile, pt->format, tc->clear_val);
348
349   /* push the tile to all positions marked as clear */
350   for (y = 0; y < h; y += TILE_SIZE) {
351      for (x = 0; x < w; x += TILE_SIZE) {
352         if (is_clear_flag_set(tc->clear_flags, x, y)) {
353            pipe_put_tile_raw(pt,
354                              x, y, TILE_SIZE, TILE_SIZE,
355                              tc->tile.data.color32, 0/*STRIDE*/);
356
357            /* do this? */
358            clear_clear_flag(tc->clear_flags, x, y);
359
360            numCleared++;
361         }
362      }
363   }
364#if 0
365   debug_printf("num cleared: %u\n", numCleared);
366#endif
367}
368
369
370/**
371 * Flush the tile cache: write all dirty tiles back to the transfer.
372 * any tiles "flagged" as cleared will be "really" cleared.
373 */
374void
375sp_flush_tile_cache(struct softpipe_context *softpipe,
376                    struct softpipe_tile_cache *tc)
377{
378   struct pipe_transfer *pt = tc->transfer;
379   int inuse = 0, pos;
380
381   if (pt) {
382      /* caching a drawing transfer */
383      for (pos = 0; pos < NUM_ENTRIES; pos++) {
384         struct softpipe_cached_tile *tile = tc->entries + pos;
385         if (tile->x >= 0) {
386            if (tc->depth_stencil) {
387               pipe_put_tile_raw(pt,
388                                 tile->x, tile->y, TILE_SIZE, TILE_SIZE,
389                                 tile->data.depth32, 0/*STRIDE*/);
390            }
391            else {
392               pipe_put_tile_rgba(pt,
393                                  tile->x, tile->y, TILE_SIZE, TILE_SIZE,
394                                  (float *) tile->data.color);
395            }
396            tile->x = tile->y = -1;  /* mark as empty */
397            inuse++;
398         }
399      }
400
401#if TILE_CLEAR_OPTIMIZATION
402      sp_tile_cache_flush_clear(&softpipe->pipe, tc);
403#endif
404   }
405   else if (tc->texture) {
406      /* caching a texture, mark all entries as empty */
407      for (pos = 0; pos < NUM_ENTRIES; pos++) {
408         tc->entries[pos].x = -1;
409      }
410      tc->tex_face = -1;
411   }
412
413#if 0
414   debug_printf("flushed tiles in use: %d\n", inuse);
415#endif
416}
417
418
419/**
420 * Get a tile from the cache.
421 * \param x, y  position of tile, in pixels
422 */
423struct softpipe_cached_tile *
424sp_get_cached_tile(struct softpipe_context *softpipe,
425                   struct softpipe_tile_cache *tc, int x, int y)
426{
427   struct pipe_transfer *pt = tc->transfer;
428
429   /* tile pos in framebuffer: */
430   const int tile_x = x & ~(TILE_SIZE - 1);
431   const int tile_y = y & ~(TILE_SIZE - 1);
432
433   /* cache pos/entry: */
434   const int pos = CACHE_POS(x, y);
435   struct softpipe_cached_tile *tile = tc->entries + pos;
436
437   if (tile_x != tile->x ||
438       tile_y != tile->y) {
439
440      if (tile->x != -1) {
441         /* put dirty tile back in framebuffer */
442         if (tc->depth_stencil) {
443            pipe_put_tile_raw(pt,
444                              tile->x, tile->y, TILE_SIZE, TILE_SIZE,
445                              tile->data.depth32, 0/*STRIDE*/);
446         }
447         else {
448            pipe_put_tile_rgba(pt,
449                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
450                               (float *) tile->data.color);
451         }
452      }
453
454      tile->x = tile_x;
455      tile->y = tile_y;
456
457      if (is_clear_flag_set(tc->clear_flags, x, y)) {
458         /* don't get tile from framebuffer, just clear it */
459         if (tc->depth_stencil) {
460            clear_tile(tile, pt->format, tc->clear_val);
461         }
462         else {
463            clear_tile_rgba(tile, pt->format, tc->clear_color);
464         }
465         clear_clear_flag(tc->clear_flags, x, y);
466      }
467      else {
468         /* get new tile data from transfer */
469         if (tc->depth_stencil) {
470            pipe_get_tile_raw(pt,
471                              tile->x, tile->y, TILE_SIZE, TILE_SIZE,
472                              tile->data.depth32, 0/*STRIDE*/);
473         }
474         else {
475            pipe_get_tile_rgba(pt,
476                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
477                               (float *) tile->data.color);
478         }
479      }
480   }
481
482   return tile;
483}
484
485
486/**
487 * Given the texture face, level, zslice, x and y values, compute
488 * the cache entry position/index where we'd hope to find the
489 * cached texture tile.
490 * This is basically a direct-map cache.
491 * XXX There's probably lots of ways in which we can improve this.
492 */
493static INLINE uint
494tex_cache_pos(int x, int y, int z, int face, int level)
495{
496   uint entry = x + y * 5 + z * 4 + face + level;
497   return entry % NUM_ENTRIES;
498}
499
500
501/**
502 * Similar to sp_get_cached_tile() but for textures.
503 * Tiles are read-only and indexed with more params.
504 */
505const struct softpipe_cached_tile *
506sp_get_cached_tile_tex(struct softpipe_context *sp,
507                       struct softpipe_tile_cache *tc, int x, int y, int z,
508                       int face, int level)
509{
510   struct pipe_screen *screen = sp->pipe.screen;
511   /* tile pos in framebuffer: */
512   const int tile_x = x & ~(TILE_SIZE - 1);
513   const int tile_y = y & ~(TILE_SIZE - 1);
514   /* cache pos/entry: */
515   const uint pos = tex_cache_pos(x / TILE_SIZE, y / TILE_SIZE, z,
516                                  face, level);
517   struct softpipe_cached_tile *tile = tc->entries + pos;
518
519   if (tc->texture) {
520      struct softpipe_texture *spt = softpipe_texture(tc->texture);
521      if (spt->modified) {
522         /* texture was modified, force a cache reload */
523         tile->x = -1;
524         spt->modified = FALSE;
525      }
526   }
527
528   if (tile_x != tile->x ||
529       tile_y != tile->y ||
530       z != tile->z ||
531       face != tile->face ||
532       level != tile->level) {
533      /* cache miss */
534
535      /* check if we need to get a new transfer */
536      if (!tc->tex_trans ||
537          tc->tex_face != face ||
538          tc->tex_level != level ||
539          tc->tex_z != z) {
540         /* get new transfer (view into texture) */
541
542	 if (tc->tex_trans_map)
543            tc->screen->transfer_unmap(tc->screen, tc->tex_trans);
544         screen->tex_transfer_release(screen, &tc->tex_trans);
545
546         tc->tex_trans = screen->get_tex_transfer(screen, tc->texture, face, level, z,
547                                                  PIPE_TRANSFER_READ, 0, 0,
548                                                  tc->texture->width[level],
549                                                  tc->texture->height[level]);
550         tc->tex_trans_map = screen->transfer_map(screen, tc->tex_trans);
551
552         tc->tex_face = face;
553         tc->tex_level = level;
554         tc->tex_z = z;
555      }
556
557      /* get tile from the transfer (view into texture) */
558      pipe_get_tile_rgba(tc->tex_trans,
559                         tile_x, tile_y, TILE_SIZE, TILE_SIZE,
560                         (float *) tile->data.color);
561      tile->x = tile_x;
562      tile->y = tile_y;
563      tile->z = z;
564      tile->face = face;
565      tile->level = level;
566   }
567
568   return tile;
569}
570
571
572/**
573 * When a whole surface is being cleared to a value we can avoid
574 * fetching tiles above.
575 * Save the color and set a 'clearflag' for each tile of the screen.
576 */
577void
578sp_tile_cache_clear(struct softpipe_tile_cache *tc, uint clearValue)
579{
580   uint r, g, b, a;
581   uint pos;
582
583   tc->clear_val = clearValue;
584
585   switch (tc->transfer->format) {
586   case PIPE_FORMAT_R8G8B8A8_UNORM:
587      r = (clearValue >> 24) & 0xff;
588      g = (clearValue >> 16) & 0xff;
589      b = (clearValue >>  8) & 0xff;
590      a = (clearValue      ) & 0xff;
591      break;
592   case PIPE_FORMAT_A8R8G8B8_UNORM:
593      r = (clearValue >> 16) & 0xff;
594      g = (clearValue >>  8) & 0xff;
595      b = (clearValue      ) & 0xff;
596      a = (clearValue >> 24) & 0xff;
597      break;
598   case PIPE_FORMAT_B8G8R8A8_UNORM:
599      r = (clearValue >>  8) & 0xff;
600      g = (clearValue >> 16) & 0xff;
601      b = (clearValue >> 24) & 0xff;
602      a = (clearValue      ) & 0xff;
603      break;
604   default:
605      r = g = b = a = 0;
606   }
607
608   tc->clear_color[0] = r / 255.0f;
609   tc->clear_color[1] = g / 255.0f;
610   tc->clear_color[2] = b / 255.0f;
611   tc->clear_color[3] = a / 255.0f;
612
613#if TILE_CLEAR_OPTIMIZATION
614   /* set flags to indicate all the tiles are cleared */
615   memset(tc->clear_flags, 255, sizeof(tc->clear_flags));
616#else
617   /* disable the optimization */
618   memset(tc->clear_flags, 0, sizeof(tc->clear_flags));
619#endif
620
621   for (pos = 0; pos < NUM_ENTRIES; pos++) {
622      struct softpipe_cached_tile *tile = tc->entries + pos;
623      tile->x = tile->y = -1;
624   }
625}
626