sp_tile_cache.c revision d5640a2dbdc4454d0405f2cd5b18fc49b1ca7694
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 pipe_context *pipe,
216                          struct softpipe_tile_cache *tc,
217                          struct pipe_texture *texture)
218{
219   uint i;
220
221   assert(!tc->surface);
222
223   pipe_texture_reference(pipe, &tc->texture, texture);
224
225   if (tc->tex_surf_map) {
226      pipe_surface_unmap(tc->tex_surf);
227      tc->tex_surf_map = NULL;
228   }
229   pipe_surface_reference(&tc->tex_surf, NULL);
230
231   /* mark as entries as invalid/empty */
232   /* XXX we should try to avoid this when the teximage hasn't changed */
233   for (i = 0; i < NUM_ENTRIES; i++) {
234      tc->entries[i].x = -1;
235   }
236
237   tc->tex_face = -1; /* any invalid value here */
238}
239
240
241/**
242 * Set pixels in a tile to the given clear color/value, float.
243 */
244static void
245clear_tile_rgba(struct softpipe_cached_tile *tile,
246                enum pipe_format format,
247                const float clear_value[4])
248{
249   if (clear_value[0] == 0.0 &&
250       clear_value[1] == 0.0 &&
251       clear_value[2] == 0.0 &&
252       clear_value[3] == 0.0) {
253      memset(tile->data.color, 0, sizeof(tile->data.color));
254   }
255   else {
256      uint i, j;
257      for (i = 0; i < TILE_SIZE; i++) {
258         for (j = 0; j < TILE_SIZE; j++) {
259            tile->data.color[i][j][0] = clear_value[0];
260            tile->data.color[i][j][1] = clear_value[1];
261            tile->data.color[i][j][2] = clear_value[2];
262            tile->data.color[i][j][3] = clear_value[3];
263         }
264      }
265   }
266}
267
268
269/**
270 * Set a tile to a solid value/color.
271 */
272static void
273clear_tile(struct softpipe_cached_tile *tile,
274           enum pipe_format format,
275           uint clear_value)
276{
277   uint i, j;
278
279   switch (pf_get_size(format)) {
280   case 1:
281      memset(tile->data.any, 0, TILE_SIZE * TILE_SIZE);
282      break;
283   case 2:
284      if (clear_value == 0) {
285         memset(tile->data.any, 0, 2 * TILE_SIZE * TILE_SIZE);
286      }
287      else {
288         for (i = 0; i < TILE_SIZE; i++) {
289            for (j = 0; j < TILE_SIZE; j++) {
290               tile->data.depth16[i][j] = (ushort) clear_value;
291            }
292         }
293      }
294      break;
295   case 4:
296      if (clear_value == 0) {
297         memset(tile->data.any, 0, 4 * TILE_SIZE * TILE_SIZE);
298      }
299      else {
300         for (i = 0; i < TILE_SIZE; i++) {
301            for (j = 0; j < TILE_SIZE; j++) {
302               tile->data.color32[i][j] = clear_value;
303            }
304         }
305      }
306      break;
307   default:
308      assert(0);
309   }
310}
311
312
313/**
314 * Actually clear the tiles which were flagged as being in a clear state.
315 */
316static void
317sp_tile_cache_flush_clear(struct pipe_context *pipe,
318                          struct softpipe_tile_cache *tc)
319{
320   struct pipe_surface *ps = tc->surface;
321   const uint w = tc->surface->width;
322   const uint h = tc->surface->height;
323   uint x, y;
324   uint numCleared = 0;
325
326   /* clear the scratch tile to the clear value */
327   clear_tile(&tc->tile, ps->format, tc->clear_val);
328
329   /* push the tile to all positions marked as clear */
330   for (y = 0; y < h; y += TILE_SIZE) {
331      for (x = 0; x < w; x += TILE_SIZE) {
332         if (is_clear_flag_set(tc->clear_flags, x, y)) {
333            pipe_put_tile_raw(pipe, ps,
334                           x, y, TILE_SIZE, TILE_SIZE,
335                           tc->tile.data.color32, 0/*STRIDE*/);
336
337            /* do this? */
338            clear_clear_flag(tc->clear_flags, x, y);
339
340            numCleared++;
341         }
342      }
343   }
344#if 0
345   debug_printf("num cleared: %u\n", numCleared);
346#endif
347}
348
349
350/**
351 * Flush the tile cache: write all dirty tiles back to the surface.
352 * any tiles "flagged" as cleared will be "really" cleared.
353 */
354void
355sp_flush_tile_cache(struct softpipe_context *softpipe,
356                    struct softpipe_tile_cache *tc)
357{
358   struct pipe_context *pipe = &softpipe->pipe;
359   struct pipe_surface *ps = tc->surface;
360   int inuse = 0, pos;
361
362   if (ps && ps->buffer) {
363      /* caching a drawing surface */
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   else if (tc->texture) {
387      /* caching a texture, mark all entries as embpy */
388      for (pos = 0; pos < NUM_ENTRIES; pos++) {
389         tc->entries[pos].x = -1;
390      }
391      tc->tex_face = -1;
392   }
393
394#if 0
395   debug_printf("flushed tiles in use: %d\n", inuse);
396#endif
397}
398
399
400/**
401 * Get a tile from the cache.
402 * \param x, y  position of tile, in pixels
403 */
404struct softpipe_cached_tile *
405sp_get_cached_tile(struct softpipe_context *softpipe,
406                   struct softpipe_tile_cache *tc, int x, int y)
407{
408   struct pipe_context *pipe = &softpipe->pipe;
409   struct pipe_surface *ps = tc->surface;
410
411   /* tile pos in framebuffer: */
412   const int tile_x = x & ~(TILE_SIZE - 1);
413   const int tile_y = y & ~(TILE_SIZE - 1);
414
415   /* cache pos/entry: */
416   const int pos = CACHE_POS(x, y);
417   struct softpipe_cached_tile *tile = tc->entries + pos;
418
419   if (tile_x != tile->x ||
420       tile_y != tile->y) {
421
422      if (tile->x != -1) {
423         /* put dirty tile back in framebuffer */
424         if (tc->depth_stencil) {
425            pipe_put_tile_raw(pipe, ps,
426                              tile->x, tile->y, TILE_SIZE, TILE_SIZE,
427                              tile->data.depth32, 0/*STRIDE*/);
428         }
429         else {
430            pipe_put_tile_rgba(pipe, ps,
431                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
432                               (float *) tile->data.color);
433         }
434      }
435
436      tile->x = tile_x;
437      tile->y = tile_y;
438
439      if (is_clear_flag_set(tc->clear_flags, x, y)) {
440         /* don't get tile from framebuffer, just clear it */
441         if (tc->depth_stencil) {
442            clear_tile(tile, ps->format, tc->clear_val);
443         }
444         else {
445            clear_tile_rgba(tile, ps->format, tc->clear_color);
446         }
447         clear_clear_flag(tc->clear_flags, x, y);
448      }
449      else {
450         /* get new tile data from surface */
451         if (tc->depth_stencil) {
452            pipe_get_tile_raw(pipe, ps,
453                              tile->x, tile->y, TILE_SIZE, TILE_SIZE,
454                              tile->data.depth32, 0/*STRIDE*/);
455         }
456         else {
457            pipe_get_tile_rgba(pipe, ps,
458                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
459                               (float *) tile->data.color);
460         }
461      }
462   }
463
464   return tile;
465}
466
467
468/**
469 * Given the texture face, level, zslice, x and y values, compute
470 * the cache entry position/index where we'd hope to find the
471 * cached texture tile.
472 * This is basically a direct-map cache.
473 * XXX There's probably lots of ways in which we can improve this.
474 */
475static INLINE uint
476tex_cache_pos(int x, int y, int z, int face, int level)
477{
478   uint entry = x + y * 5 + z * 4 + face + level;
479   return entry % NUM_ENTRIES;
480}
481
482
483/**
484 * Similar to sp_get_cached_tile() but for textures.
485 * Tiles are read-only and indexed with more params.
486 */
487const struct softpipe_cached_tile *
488sp_get_cached_tile_tex(struct pipe_context *pipe,
489                       struct softpipe_tile_cache *tc, int x, int y, int z,
490                       int face, int level)
491{
492   /* tile pos in framebuffer: */
493   const int tile_x = x & ~(TILE_SIZE - 1);
494   const int tile_y = y & ~(TILE_SIZE - 1);
495   /* cache pos/entry: */
496   const uint pos = tex_cache_pos(x / TILE_SIZE, y / TILE_SIZE, z,
497                                  face, level);
498   struct softpipe_cached_tile *tile = tc->entries + pos;
499
500   if (tile_x != tile->x ||
501       tile_y != tile->y ||
502       z != tile->z ||
503       face != tile->face ||
504       level != tile->level) {
505      /* cache miss */
506
507      /* check if we need to get a new surface */
508      if (!tc->tex_surf ||
509          tc->tex_face != face ||
510          tc->tex_level != level ||
511          tc->tex_z != z) {
512         /* get new surface (view into texture) */
513
514	 if (tc->tex_surf_map)
515            pipe_surface_unmap(tc->tex_surf);
516
517         tc->tex_surf = pipe->get_tex_surface(pipe, tc->texture, face, level, z);
518         tc->tex_surf_map = pipe_surface_map(tc->tex_surf);
519
520         tc->tex_face = face;
521         tc->tex_level = level;
522         tc->tex_z = z;
523      }
524
525      /* get tile from the surface (view into texture) */
526      pipe_get_tile_rgba(pipe, tc->tex_surf,
527                         tile_x, tile_y, TILE_SIZE, TILE_SIZE,
528                         (float *) tile->data.color);
529      tile->x = tile_x;
530      tile->y = tile_y;
531      tile->z = z;
532      tile->face = face;
533      tile->level = level;
534   }
535
536   return tile;
537}
538
539
540/**
541 * When a whole surface is being cleared to a value we can avoid
542 * fetching tiles above.
543 * Save the color and set a 'clearflag' for each tile of the screen.
544 */
545void
546sp_tile_cache_clear(struct softpipe_tile_cache *tc, uint clearValue)
547{
548   uint r, g, b, a;
549   uint pos;
550
551   tc->clear_val = clearValue;
552
553   switch (tc->surface->format) {
554   case PIPE_FORMAT_R8G8B8A8_UNORM:
555      r = (clearValue >> 24) & 0xff;
556      g = (clearValue >> 16) & 0xff;
557      b = (clearValue >>  8) & 0xff;
558      a = (clearValue      ) & 0xff;
559      break;
560   case PIPE_FORMAT_A8R8G8B8_UNORM:
561      r = (clearValue >> 16) & 0xff;
562      g = (clearValue >>  8) & 0xff;
563      b = (clearValue      ) & 0xff;
564      a = (clearValue >> 24) & 0xff;
565      break;
566   case PIPE_FORMAT_B8G8R8A8_UNORM:
567      r = (clearValue >>  8) & 0xff;
568      g = (clearValue >> 16) & 0xff;
569      b = (clearValue >> 24) & 0xff;
570      a = (clearValue      ) & 0xff;
571      break;
572   default:
573      r = g = b = a = 0;
574   }
575
576   tc->clear_color[0] = r / 255.0f;
577   tc->clear_color[1] = g / 255.0f;
578   tc->clear_color[2] = b / 255.0f;
579   tc->clear_color[3] = a / 255.0f;
580
581#if TILE_CLEAR_OPTIMIZATION
582   /* set flags to indicate all the tiles are cleared */
583   memset(tc->clear_flags, 255, sizeof(tc->clear_flags));
584#else
585   /* disable the optimization */
586   memset(tc->clear_flags, 0, sizeof(tc->clear_flags));
587#endif
588
589   for (pos = 0; pos < NUM_ENTRIES; pos++) {
590      struct softpipe_cached_tile *tile = tc->entries + pos;
591      tile->x = tile->y = -1;
592   }
593}
594