sp_tile_cache.c revision b1ed72ebe2599ec178f51d86fd42f26486b9a19b
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 * Render target tile caching.
30 *
31 * Author:
32 *    Brian Paul
33 */
34
35#include "pipe/p_inlines.h"
36#include "util/u_format.h"
37#include "util/u_memory.h"
38#include "util/u_tile.h"
39#include "sp_tile_cache.h"
40
41
42
43/**
44 * Return the position in the cache for the tile that contains win pos (x,y).
45 * We currently use a direct mapped cache so this is like a hack key.
46 * At some point we should investige something more sophisticated, like
47 * a LRU replacement policy.
48 */
49#define CACHE_POS(x, y) \
50   (((x) + (y) * 5) % NUM_ENTRIES)
51
52
53
54/**
55 * Is the tile at (x,y) in cleared state?
56 */
57static INLINE uint
58is_clear_flag_set(const uint *bitvec, union tile_address addr)
59{
60   int pos, bit;
61   pos = addr.bits.y * (MAX_WIDTH / TILE_SIZE) + addr.bits.x;
62   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
63   bit = bitvec[pos / 32] & (1 << (pos & 31));
64   return bit;
65}
66
67
68/**
69 * Mark the tile at (x,y) as not cleared.
70 */
71static INLINE void
72clear_clear_flag(uint *bitvec, union tile_address addr)
73{
74   int pos;
75   pos = addr.bits.y * (MAX_WIDTH / TILE_SIZE) + addr.bits.x;
76   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
77   bitvec[pos / 32] &= ~(1 << (pos & 31));
78}
79
80
81struct softpipe_tile_cache *
82sp_create_tile_cache( struct pipe_screen *screen )
83{
84   struct softpipe_tile_cache *tc;
85   uint pos;
86   int maxLevels, maxTexSize;
87
88   /* sanity checking: max sure MAX_WIDTH/HEIGHT >= largest texture image */
89   maxLevels = screen->get_param(screen, PIPE_CAP_MAX_TEXTURE_2D_LEVELS);
90   maxTexSize = 1 << (maxLevels - 1);
91   assert(MAX_WIDTH >= maxTexSize);
92
93   tc = CALLOC_STRUCT( softpipe_tile_cache );
94   if (tc) {
95      tc->screen = screen;
96      for (pos = 0; pos < NUM_ENTRIES; pos++) {
97         tc->entries[pos].addr.bits.invalid = 1;
98      }
99      tc->last_tile = &tc->entries[0]; /* any tile */
100
101      /* XXX this code prevents valgrind warnings about use of uninitialized
102       * memory in programs that don't clear the surface before rendering.
103       * However, it breaks clearing in other situations (such as in
104       * progs/tests/drawbuffers, see bug 24402).
105       */
106#if 0 && TILE_CLEAR_OPTIMIZATION
107      /* set flags to indicate all the tiles are cleared */
108      memset(tc->clear_flags, 255, sizeof(tc->clear_flags));
109#endif
110   }
111   return tc;
112}
113
114
115void
116sp_destroy_tile_cache(struct softpipe_tile_cache *tc)
117{
118   struct pipe_screen *screen;
119   uint pos;
120
121   for (pos = 0; pos < NUM_ENTRIES; pos++) {
122      /*assert(tc->entries[pos].x < 0);*/
123   }
124   if (tc->transfer) {
125      screen = tc->transfer->texture->screen;
126      screen->tex_transfer_destroy(tc->transfer);
127   }
128
129   FREE( tc );
130}
131
132
133/**
134 * Specify the surface to cache.
135 */
136void
137sp_tile_cache_set_surface(struct softpipe_tile_cache *tc,
138                          struct pipe_surface *ps)
139{
140   if (tc->transfer) {
141      struct pipe_screen *screen = tc->transfer->texture->screen;
142
143      if (ps == tc->surface)
144         return;
145
146      if (tc->transfer_map) {
147         screen->transfer_unmap(screen, tc->transfer);
148         tc->transfer_map = NULL;
149      }
150
151      screen->tex_transfer_destroy(tc->transfer);
152      tc->transfer = NULL;
153   }
154
155   tc->surface = ps;
156
157   if (ps) {
158      struct pipe_screen *screen = ps->texture->screen;
159
160      tc->transfer = screen->get_tex_transfer(screen, ps->texture, ps->face,
161                                              ps->level, ps->zslice,
162                                              PIPE_TRANSFER_READ_WRITE,
163                                              0, 0, ps->width, ps->height);
164
165      tc->depth_stencil = (ps->format == PIPE_FORMAT_S8Z24_UNORM ||
166                           ps->format == PIPE_FORMAT_X8Z24_UNORM ||
167                           ps->format == PIPE_FORMAT_Z24S8_UNORM ||
168                           ps->format == PIPE_FORMAT_Z24X8_UNORM ||
169                           ps->format == PIPE_FORMAT_Z16_UNORM ||
170                           ps->format == PIPE_FORMAT_Z32_UNORM ||
171                           ps->format == PIPE_FORMAT_S8_UNORM);
172   }
173}
174
175
176/**
177 * Return the transfer being cached.
178 */
179struct pipe_surface *
180sp_tile_cache_get_surface(struct softpipe_tile_cache *tc)
181{
182   return tc->surface;
183}
184
185
186void
187sp_tile_cache_map_transfers(struct softpipe_tile_cache *tc)
188{
189   if (tc->transfer && !tc->transfer_map)
190      tc->transfer_map = tc->screen->transfer_map(tc->screen, tc->transfer);
191}
192
193
194void
195sp_tile_cache_unmap_transfers(struct softpipe_tile_cache *tc)
196{
197   if (tc->transfer_map) {
198      tc->screen->transfer_unmap(tc->screen, tc->transfer);
199      tc->transfer_map = NULL;
200   }
201}
202
203
204/**
205 * Set pixels in a tile to the given clear color/value, float.
206 */
207static void
208clear_tile_rgba(struct softpipe_cached_tile *tile,
209                enum pipe_format format,
210                const float clear_value[4])
211{
212   if (clear_value[0] == 0.0 &&
213       clear_value[1] == 0.0 &&
214       clear_value[2] == 0.0 &&
215       clear_value[3] == 0.0) {
216      memset(tile->data.color, 0, sizeof(tile->data.color));
217   }
218   else {
219      uint i, j;
220      for (i = 0; i < TILE_SIZE; i++) {
221         for (j = 0; j < TILE_SIZE; j++) {
222            tile->data.color[i][j][0] = clear_value[0];
223            tile->data.color[i][j][1] = clear_value[1];
224            tile->data.color[i][j][2] = clear_value[2];
225            tile->data.color[i][j][3] = clear_value[3];
226         }
227      }
228   }
229}
230
231
232/**
233 * Set a tile to a solid value/color.
234 */
235static void
236clear_tile(struct softpipe_cached_tile *tile,
237           enum pipe_format format,
238           uint clear_value)
239{
240   uint i, j;
241
242   switch (util_format_get_blocksize(format)) {
243   case 1:
244      memset(tile->data.any, clear_value, TILE_SIZE * TILE_SIZE);
245      break;
246   case 2:
247      if (clear_value == 0) {
248         memset(tile->data.any, 0, 2 * TILE_SIZE * TILE_SIZE);
249      }
250      else {
251         for (i = 0; i < TILE_SIZE; i++) {
252            for (j = 0; j < TILE_SIZE; j++) {
253               tile->data.depth16[i][j] = (ushort) clear_value;
254            }
255         }
256      }
257      break;
258   case 4:
259      if (clear_value == 0) {
260         memset(tile->data.any, 0, 4 * TILE_SIZE * TILE_SIZE);
261      }
262      else {
263         for (i = 0; i < TILE_SIZE; i++) {
264            for (j = 0; j < TILE_SIZE; j++) {
265               tile->data.color32[i][j] = clear_value;
266            }
267         }
268      }
269      break;
270   default:
271      assert(0);
272   }
273}
274
275
276/**
277 * Actually clear the tiles which were flagged as being in a clear state.
278 */
279static void
280sp_tile_cache_flush_clear(struct softpipe_tile_cache *tc)
281{
282   struct pipe_transfer *pt = tc->transfer;
283   const uint w = tc->transfer->width;
284   const uint h = tc->transfer->height;
285   uint x, y;
286   uint numCleared = 0;
287
288   assert(pt->texture);
289   /* clear the scratch tile to the clear value */
290   clear_tile(&tc->tile, pt->texture->format, tc->clear_val);
291
292   /* push the tile to all positions marked as clear */
293   for (y = 0; y < h; y += TILE_SIZE) {
294      for (x = 0; x < w; x += TILE_SIZE) {
295         union tile_address addr = tile_address(x, y);
296
297         if (is_clear_flag_set(tc->clear_flags, addr)) {
298            pipe_put_tile_raw(pt,
299                              x, y, TILE_SIZE, TILE_SIZE,
300                              tc->tile.data.color32, 0/*STRIDE*/);
301
302            /* do this? */
303            clear_clear_flag(tc->clear_flags, addr);
304
305            numCleared++;
306         }
307      }
308   }
309#if 0
310   debug_printf("num cleared: %u\n", numCleared);
311#endif
312}
313
314
315/**
316 * Flush the tile cache: write all dirty tiles back to the transfer.
317 * any tiles "flagged" as cleared will be "really" cleared.
318 */
319void
320sp_flush_tile_cache(struct softpipe_tile_cache *tc)
321{
322   struct pipe_transfer *pt = tc->transfer;
323   int inuse = 0, pos;
324
325   if (pt) {
326      /* caching a drawing transfer */
327      for (pos = 0; pos < NUM_ENTRIES; pos++) {
328         struct softpipe_cached_tile *tile = tc->entries + pos;
329         if (!tile->addr.bits.invalid) {
330            if (tc->depth_stencil) {
331               pipe_put_tile_raw(pt,
332                                 tile->addr.bits.x * TILE_SIZE,
333                                 tile->addr.bits.y * TILE_SIZE,
334                                 TILE_SIZE, TILE_SIZE,
335                                 tile->data.depth32, 0/*STRIDE*/);
336            }
337            else {
338               pipe_put_tile_rgba(pt,
339                                  tile->addr.bits.x * TILE_SIZE,
340                                  tile->addr.bits.y * TILE_SIZE,
341                                  TILE_SIZE, TILE_SIZE,
342                                  (float *) tile->data.color);
343            }
344            tile->addr.bits.invalid = 1;  /* mark as empty */
345            inuse++;
346         }
347      }
348
349#if TILE_CLEAR_OPTIMIZATION
350      sp_tile_cache_flush_clear(tc);
351#endif
352   }
353
354#if 0
355   debug_printf("flushed tiles in use: %d\n", inuse);
356#endif
357}
358
359
360/**
361 * Get a tile from the cache.
362 * \param x, y  position of tile, in pixels
363 */
364struct softpipe_cached_tile *
365sp_find_cached_tile(struct softpipe_tile_cache *tc,
366                    union tile_address addr )
367{
368   struct pipe_transfer *pt = tc->transfer;
369
370   /* cache pos/entry: */
371   const int pos = CACHE_POS(addr.bits.x,
372                             addr.bits.y);
373   struct softpipe_cached_tile *tile = tc->entries + pos;
374
375   if (addr.value != tile->addr.value) {
376
377      assert(pt->texture);
378      if (tile->addr.bits.invalid == 0) {
379         /* put dirty tile back in framebuffer */
380         if (tc->depth_stencil) {
381            pipe_put_tile_raw(pt,
382                              tile->addr.bits.x * TILE_SIZE,
383                              tile->addr.bits.y * TILE_SIZE,
384                              TILE_SIZE, TILE_SIZE,
385                              tile->data.depth32, 0/*STRIDE*/);
386         }
387         else {
388            pipe_put_tile_rgba(pt,
389                               tile->addr.bits.x * TILE_SIZE,
390                               tile->addr.bits.y * TILE_SIZE,
391                               TILE_SIZE, TILE_SIZE,
392                               (float *) tile->data.color);
393         }
394      }
395
396      tile->addr = addr;
397
398      if (is_clear_flag_set(tc->clear_flags, addr)) {
399         /* don't get tile from framebuffer, just clear it */
400         if (tc->depth_stencil) {
401            clear_tile(tile, pt->texture->format, tc->clear_val);
402         }
403         else {
404            clear_tile_rgba(tile, pt->texture->format, tc->clear_color);
405         }
406         clear_clear_flag(tc->clear_flags, addr);
407      }
408      else {
409         /* get new tile data from transfer */
410         if (tc->depth_stencil) {
411            pipe_get_tile_raw(pt,
412                              tile->addr.bits.x * TILE_SIZE,
413                              tile->addr.bits.y * TILE_SIZE,
414                              TILE_SIZE, TILE_SIZE,
415                              tile->data.depth32, 0/*STRIDE*/);
416         }
417         else {
418            pipe_get_tile_rgba(pt,
419                               tile->addr.bits.x * TILE_SIZE,
420                               tile->addr.bits.y * TILE_SIZE,
421                               TILE_SIZE, TILE_SIZE,
422                               (float *) tile->data.color);
423         }
424      }
425   }
426
427   tc->last_tile = tile;
428   return tile;
429}
430
431
432
433
434
435/**
436 * When a whole surface is being cleared to a value we can avoid
437 * fetching tiles above.
438 * Save the color and set a 'clearflag' for each tile of the screen.
439 */
440void
441sp_tile_cache_clear(struct softpipe_tile_cache *tc, const float *rgba,
442                    uint clearValue)
443{
444   uint pos;
445
446   tc->clear_color[0] = rgba[0];
447   tc->clear_color[1] = rgba[1];
448   tc->clear_color[2] = rgba[2];
449   tc->clear_color[3] = rgba[3];
450
451   tc->clear_val = clearValue;
452
453#if TILE_CLEAR_OPTIMIZATION
454   /* set flags to indicate all the tiles are cleared */
455   memset(tc->clear_flags, 255, sizeof(tc->clear_flags));
456#else
457   /* disable the optimization */
458   memset(tc->clear_flags, 0, sizeof(tc->clear_flags));
459#endif
460
461   for (pos = 0; pos < NUM_ENTRIES; pos++) {
462      struct softpipe_cached_tile *tile = tc->entries + pos;
463      tile->addr.bits.invalid = 1;
464   }
465}
466