sp_tile_cache.c revision 6acd63a4980951727939c0dd545a0324965b3834
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 * Framebuffer/surface tile caching.
30 *
31 * Author:
32 *    Brian Paul
33 */
34
35#include "pipe/p_util.h"
36#include "pipe/p_inlines.h"
37#include "util/p_tile.h"
38#include "sp_context.h"
39#include "sp_surface.h"
40#include "sp_tile_cache.h"
41
42#define NUM_ENTRIES 30
43
44
45/** XXX move these */
46#define MAX_WIDTH 2048
47#define MAX_HEIGHT 2048
48
49
50struct softpipe_tile_cache
51{
52   struct pipe_surface *surface;  /**< the surface we're caching */
53   void *surface_map;
54   struct pipe_texture *texture;  /**< if caching a texture */
55   struct softpipe_cached_tile entries[NUM_ENTRIES];
56   uint clear_flags[(MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32];
57   float clear_color[4];
58   uint clear_val;
59   boolean depth_stencil; /** Is the surface a depth/stencil format? */
60
61   struct pipe_surface *tex_surf;
62   void *tex_surf_map;
63   int tex_face, tex_level, tex_z;
64
65   struct softpipe_cached_tile tile;  /**< scratch tile for clears */
66};
67
68
69/**
70 * Return the position in the cache for the tile that contains win pos (x,y).
71 * We currently use a direct mapped cache so this is like a hack key.
72 * At some point we should investige something more sophisticated, like
73 * a LRU replacement policy.
74 */
75#define CACHE_POS(x, y) \
76   (((x) / TILE_SIZE + ((y) / TILE_SIZE) * 5) % NUM_ENTRIES)
77
78
79
80/**
81 * Is the tile at (x,y) in cleared state?
82 */
83static INLINE uint
84is_clear_flag_set(const uint *bitvec, int x, int y)
85{
86   int pos, bit;
87   x /= TILE_SIZE;
88   y /= TILE_SIZE;
89   pos = y * (MAX_WIDTH / TILE_SIZE) + x;
90   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
91   bit = bitvec[pos / 32] & (1 << (pos & 31));
92   return bit;
93}
94
95
96/**
97 * Mark the tile at (x,y) as not cleared.
98 */
99static INLINE void
100clear_clear_flag(uint *bitvec, int x, int y)
101{
102   int pos;
103   x /= TILE_SIZE;
104   y /= TILE_SIZE;
105   pos = y * (MAX_WIDTH / TILE_SIZE) + x;
106   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
107   bitvec[pos / 32] &= ~(1 << (pos & 31));
108}
109
110
111struct softpipe_tile_cache *
112sp_create_tile_cache(void)
113{
114   struct softpipe_tile_cache *tc;
115   uint pos;
116
117   tc = CALLOC_STRUCT( softpipe_tile_cache );
118   if (tc) {
119      for (pos = 0; pos < NUM_ENTRIES; pos++) {
120         tc->entries[pos].x =
121         tc->entries[pos].y = -1;
122      }
123   }
124   return tc;
125}
126
127
128void
129sp_destroy_tile_cache(struct softpipe_tile_cache *tc)
130{
131   uint pos;
132
133   for (pos = 0; pos < NUM_ENTRIES; pos++) {
134      //assert(tc->entries[pos].x < 0);
135   }
136   if (tc->surface) {
137      pipe_surface_reference(&tc->surface, NULL);
138   }
139   if (tc->tex_surf) {
140      pipe_surface_reference(&tc->tex_surf, NULL);
141   }
142
143   FREE( tc );
144}
145
146
147/**
148 * Specify the surface to cache.
149 */
150void
151sp_tile_cache_set_surface(struct softpipe_tile_cache *tc,
152                          struct pipe_surface *ps)
153{
154   assert(!tc->texture);
155
156   if (tc->surface_map) {
157      /*assert(tc->surface != ps);*/
158      pipe_surface_unmap(tc->surface);
159   }
160
161   pipe_surface_reference(&tc->surface, ps);
162
163   if (ps) {
164      if (tc->surface_map)
165	 tc->surface_map = pipe_surface_map(ps);
166
167      tc->depth_stencil = (ps->format == PIPE_FORMAT_S8Z24_UNORM ||
168                           ps->format == PIPE_FORMAT_Z16_UNORM ||
169                           ps->format == PIPE_FORMAT_Z32_UNORM ||
170                           ps->format == PIPE_FORMAT_U_S8);
171   }
172}
173
174
175/**
176 * Return the surface being cached.
177 */
178struct pipe_surface *
179sp_tile_cache_get_surface(struct softpipe_tile_cache *tc)
180{
181   return tc->surface;
182}
183
184
185void
186sp_tile_cache_map_surfaces(struct softpipe_tile_cache *tc)
187{
188   if (tc->surface && !tc->surface_map)
189      tc->surface_map = pipe_surface_map(tc->surface);
190
191   if (tc->tex_surf && !tc->tex_surf_map)
192      tc->tex_surf_map = pipe_surface_map(tc->tex_surf);
193}
194
195
196void
197sp_tile_cache_unmap_surfaces(struct softpipe_tile_cache *tc)
198{
199   if (tc->surface_map) {
200      pipe_surface_unmap(tc->surface);
201      tc->surface_map = NULL;
202   }
203
204   if (tc->tex_surf_map) {
205      pipe_surface_unmap(tc->tex_surf);
206      tc->tex_surf_map = NULL;
207   }
208}
209
210
211/**
212 * Specify the texture to cache.
213 */
214void
215sp_tile_cache_set_texture(struct softpipe_tile_cache *tc,
216                          struct pipe_texture *texture)
217{
218   uint i;
219
220   assert(!tc->surface);
221
222   tc->texture = texture;
223
224   if (tc->tex_surf_map) {
225      pipe_surface_unmap(tc->tex_surf);
226      tc->tex_surf_map = NULL;
227   }
228   pipe_surface_reference(&tc->tex_surf, NULL);
229
230   /* mark as entries as invalid/empty */
231   /* XXX we should try to avoid this when the teximage hasn't changed */
232   for (i = 0; i < NUM_ENTRIES; i++) {
233      tc->entries[i].x = -1;
234   }
235
236   tc->tex_face = -1; /* any invalid value here */
237}
238
239
240/**
241 * Set pixels in a tile to the given clear color/value, float.
242 */
243static void
244clear_tile_rgba(struct softpipe_cached_tile *tile,
245                enum pipe_format format,
246                const float clear_value[4])
247{
248   if (clear_value[0] == 0.0 &&
249       clear_value[1] == 0.0 &&
250       clear_value[2] == 0.0 &&
251       clear_value[3] == 0.0) {
252      memset(tile->data.color, 0, sizeof(tile->data.color));
253   }
254   else {
255      uint i, j;
256      for (i = 0; i < TILE_SIZE; i++) {
257         for (j = 0; j < TILE_SIZE; j++) {
258            tile->data.color[i][j][0] = clear_value[0];
259            tile->data.color[i][j][1] = clear_value[1];
260            tile->data.color[i][j][2] = clear_value[2];
261            tile->data.color[i][j][3] = clear_value[3];
262         }
263      }
264   }
265}
266
267
268/**
269 * Set a tile to a solid value/color.
270 */
271static void
272clear_tile(struct softpipe_cached_tile *tile,
273           enum pipe_format format,
274           uint clear_value)
275{
276   uint i, j;
277
278   switch (pf_get_size(format)) {
279   case 1:
280      memset(tile->data.any, 0, TILE_SIZE * TILE_SIZE);
281      break;
282   case 2:
283      if (clear_value == 0) {
284         memset(tile->data.any, 0, 2 * TILE_SIZE * TILE_SIZE);
285      }
286      else {
287         for (i = 0; i < TILE_SIZE; i++) {
288            for (j = 0; j < TILE_SIZE; j++) {
289               tile->data.depth16[i][j] = (ushort) clear_value;
290            }
291         }
292      }
293      break;
294   case 4:
295      if (clear_value == 0) {
296         memset(tile->data.any, 0, 4 * TILE_SIZE * TILE_SIZE);
297      }
298      else {
299         for (i = 0; i < TILE_SIZE; i++) {
300            for (j = 0; j < TILE_SIZE; j++) {
301               tile->data.color32[i][j] = clear_value;
302            }
303         }
304      }
305      break;
306   default:
307      assert(0);
308   }
309}
310
311
312/**
313 * Actually clear the tiles which were flagged as being in a clear state.
314 */
315static void
316sp_tile_cache_flush_clear(struct pipe_context *pipe,
317                          struct softpipe_tile_cache *tc)
318{
319   struct pipe_surface *ps = tc->surface;
320   const uint w = tc->surface->width;
321   const uint h = tc->surface->height;
322   uint x, y;
323   uint numCleared = 0;
324
325   /* clear the scratch tile to the clear value */
326   clear_tile(&tc->tile, ps->format, tc->clear_val);
327
328   /* push the tile to all positions marked as clear */
329   for (y = 0; y < h; y += TILE_SIZE) {
330      for (x = 0; x < w; x += TILE_SIZE) {
331         if (is_clear_flag_set(tc->clear_flags, x, y)) {
332            pipe_put_tile_raw(pipe, ps,
333                           x, y, TILE_SIZE, TILE_SIZE,
334                           tc->tile.data.color32, 0/*STRIDE*/);
335
336            /* do this? */
337            clear_clear_flag(tc->clear_flags, x, y);
338
339            numCleared++;
340         }
341      }
342   }
343#if 0
344   debug_printf("num cleared: %u\n", numCleared);
345#endif
346}
347
348
349/**
350 * Flush the tile cache: write all dirty tiles back to the surface.
351 * any tiles "flagged" as cleared will be "really" cleared.
352 */
353void
354sp_flush_tile_cache(struct softpipe_context *softpipe,
355                    struct softpipe_tile_cache *tc)
356{
357   struct pipe_context *pipe = &softpipe->pipe;
358   struct pipe_surface *ps = tc->surface;
359   int inuse = 0, pos;
360
361   if (!ps || !ps->buffer)
362      return;
363
364   for (pos = 0; pos < NUM_ENTRIES; pos++) {
365      struct softpipe_cached_tile *tile = tc->entries + pos;
366      if (tile->x >= 0) {
367         if (tc->depth_stencil) {
368            pipe_put_tile_raw(pipe, ps,
369                           tile->x, tile->y, TILE_SIZE, TILE_SIZE,
370                           tile->data.depth32, 0/*STRIDE*/);
371         }
372         else {
373            pipe_put_tile_rgba(pipe, ps,
374                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
375                               (float *) tile->data.color);
376         }
377         tile->x = tile->y = -1;  /* mark as empty */
378         inuse++;
379      }
380   }
381
382#if TILE_CLEAR_OPTIMIZATION
383   sp_tile_cache_flush_clear(&softpipe->pipe, tc);
384#endif
385
386#if 0
387   debug_printf("flushed tiles in use: %d\n", inuse);
388#endif
389}
390
391
392/**
393 * Get a tile from the cache.
394 * \param x, y  position of tile, in pixels
395 */
396struct softpipe_cached_tile *
397sp_get_cached_tile(struct softpipe_context *softpipe,
398                   struct softpipe_tile_cache *tc, int x, int y)
399{
400   struct pipe_context *pipe = &softpipe->pipe;
401   struct pipe_surface *ps = tc->surface;
402
403   /* tile pos in framebuffer: */
404   const int tile_x = x & ~(TILE_SIZE - 1);
405   const int tile_y = y & ~(TILE_SIZE - 1);
406
407   /* cache pos/entry: */
408   const int pos = CACHE_POS(x, y);
409   struct softpipe_cached_tile *tile = tc->entries + pos;
410
411   if (tile_x != tile->x ||
412       tile_y != tile->y) {
413
414      if (tile->x != -1) {
415         /* put dirty tile back in framebuffer */
416         if (tc->depth_stencil) {
417            pipe_put_tile_raw(pipe, ps,
418                              tile->x, tile->y, TILE_SIZE, TILE_SIZE,
419                              tile->data.depth32, 0/*STRIDE*/);
420         }
421         else {
422            pipe_put_tile_rgba(pipe, ps,
423                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
424                               (float *) tile->data.color);
425         }
426      }
427
428      tile->x = tile_x;
429      tile->y = tile_y;
430
431      if (is_clear_flag_set(tc->clear_flags, x, y)) {
432         /* don't get tile from framebuffer, just clear it */
433         if (tc->depth_stencil) {
434            clear_tile(tile, ps->format, tc->clear_val);
435         }
436         else {
437            clear_tile_rgba(tile, ps->format, tc->clear_color);
438         }
439         clear_clear_flag(tc->clear_flags, x, y);
440      }
441      else {
442         /* get new tile data from surface */
443         if (tc->depth_stencil) {
444            pipe_get_tile_raw(pipe, ps,
445                              tile->x, tile->y, TILE_SIZE, TILE_SIZE,
446                              tile->data.depth32, 0/*STRIDE*/);
447         }
448         else {
449            pipe_get_tile_rgba(pipe, ps,
450                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
451                               (float *) tile->data.color);
452         }
453      }
454   }
455
456   return tile;
457}
458
459
460/**
461 * Given the texture face, level, zslice, x and y values, compute
462 * the cache entry position/index where we'd hope to find the
463 * cached texture tile.
464 * This is basically a direct-map cache.
465 * XXX There's probably lots of ways in which we can improve this.
466 */
467static INLINE uint
468tex_cache_pos(int x, int y, int z, int face, int level)
469{
470   uint entry = x + y * 5 + z * 4 + face + level;
471   return entry % NUM_ENTRIES;
472}
473
474
475/**
476 * Similar to sp_get_cached_tile() but for textures.
477 * Tiles are read-only and indexed with more params.
478 */
479const struct softpipe_cached_tile *
480sp_get_cached_tile_tex(struct pipe_context *pipe,
481                       struct softpipe_tile_cache *tc, int x, int y, int z,
482                       int face, int level)
483{
484   /* tile pos in framebuffer: */
485   const int tile_x = x & ~(TILE_SIZE - 1);
486   const int tile_y = y & ~(TILE_SIZE - 1);
487   /* cache pos/entry: */
488   const uint pos = tex_cache_pos(x / TILE_SIZE, y / TILE_SIZE, z,
489                                  face, level);
490   struct softpipe_cached_tile *tile = tc->entries + pos;
491
492   if (tile_x != tile->x ||
493       tile_y != tile->y ||
494       z != tile->z ||
495       face != tile->face ||
496       level != tile->level) {
497      /* cache miss */
498
499      /* check if we need to get a new surface */
500      if (!tc->tex_surf ||
501          tc->tex_face != face ||
502          tc->tex_level != level ||
503          tc->tex_z != z) {
504         /* get new surface (view into texture) */
505
506	 if (tc->tex_surf_map)
507            pipe_surface_unmap(tc->tex_surf);
508
509         tc->tex_surf = pipe->get_tex_surface(pipe, tc->texture, face, level, z);
510         tc->tex_surf_map = pipe_surface_map(tc->tex_surf);
511
512         tc->tex_face = face;
513         tc->tex_level = level;
514         tc->tex_z = z;
515      }
516
517      /* get tile from the surface (view into texture) */
518      pipe_get_tile_rgba(pipe, tc->tex_surf,
519                         tile_x, tile_y, TILE_SIZE, TILE_SIZE,
520                         (float *) tile->data.color);
521      tile->x = tile_x;
522      tile->y = tile_y;
523      tile->z = z;
524      tile->face = face;
525      tile->level = level;
526   }
527
528   return tile;
529}
530
531
532/**
533 * When a whole surface is being cleared to a value we can avoid
534 * fetching tiles above.
535 * Save the color and set a 'clearflag' for each tile of the screen.
536 */
537void
538sp_tile_cache_clear(struct softpipe_tile_cache *tc, uint clearValue)
539{
540   uint r, g, b, a;
541   uint pos;
542
543   tc->clear_val = clearValue;
544
545   switch (tc->surface->format) {
546   case PIPE_FORMAT_R8G8B8A8_UNORM:
547      r = (clearValue >> 24) & 0xff;
548      g = (clearValue >> 16) & 0xff;
549      b = (clearValue >>  8) & 0xff;
550      a = (clearValue      ) & 0xff;
551      break;
552   case PIPE_FORMAT_A8R8G8B8_UNORM:
553      r = (clearValue >> 16) & 0xff;
554      g = (clearValue >>  8) & 0xff;
555      b = (clearValue      ) & 0xff;
556      a = (clearValue >> 24) & 0xff;
557      break;
558   case PIPE_FORMAT_B8G8R8A8_UNORM:
559      r = (clearValue >>  8) & 0xff;
560      g = (clearValue >> 16) & 0xff;
561      b = (clearValue >> 24) & 0xff;
562      a = (clearValue      ) & 0xff;
563      break;
564   default:
565      r = g = b = a = 0;
566   }
567
568   tc->clear_color[0] = r / 255.0f;
569   tc->clear_color[1] = g / 255.0f;
570   tc->clear_color[2] = b / 255.0f;
571   tc->clear_color[3] = a / 255.0f;
572
573#if TILE_CLEAR_OPTIMIZATION
574   /* set flags to indicate all the tiles are cleared */
575   memset(tc->clear_flags, 255, sizeof(tc->clear_flags));
576#else
577   /* disable the optimization */
578   memset(tc->clear_flags, 0, sizeof(tc->clear_flags));
579#endif
580
581   for (pos = 0; pos < NUM_ENTRIES; pos++) {
582      struct softpipe_cached_tile *tile = tc->entries + pos;
583      tile->x = tile->y = -1;
584   }
585}
586