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 "util/u_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
41static struct softpipe_cached_tile *
42sp_alloc_tile(struct softpipe_tile_cache *tc);
43
44
45/**
46 * Return the position in the cache for the tile that contains win pos (x,y).
47 * We currently use a direct mapped cache so this is like a hack key.
48 * At some point we should investige something more sophisticated, like
49 * a LRU replacement policy.
50 */
51#define CACHE_POS(x, y) \
52   (((x) + (y) * 5) % NUM_ENTRIES)
53
54
55
56/**
57 * Is the tile at (x,y) in cleared state?
58 */
59static INLINE uint
60is_clear_flag_set(const uint *bitvec, union tile_address addr)
61{
62   int pos, bit;
63   pos = addr.bits.y * (MAX_WIDTH / TILE_SIZE) + addr.bits.x;
64   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
65   bit = bitvec[pos / 32] & (1 << (pos & 31));
66   return bit;
67}
68
69
70/**
71 * Mark the tile at (x,y) as not cleared.
72 */
73static INLINE void
74clear_clear_flag(uint *bitvec, union tile_address addr)
75{
76   int pos;
77   pos = addr.bits.y * (MAX_WIDTH / TILE_SIZE) + addr.bits.x;
78   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
79   bitvec[pos / 32] &= ~(1 << (pos & 31));
80}
81
82
83struct softpipe_tile_cache *
84sp_create_tile_cache( struct pipe_context *pipe )
85{
86   struct softpipe_tile_cache *tc;
87   uint pos;
88   int maxLevels, maxTexSize;
89
90   /* sanity checking: max sure MAX_WIDTH/HEIGHT >= largest texture image */
91   maxLevels = pipe->screen->get_param(pipe->screen, PIPE_CAP_MAX_TEXTURE_2D_LEVELS);
92   maxTexSize = 1 << (maxLevels - 1);
93   assert(MAX_WIDTH >= maxTexSize);
94
95   assert(sizeof(union tile_address) == 4);
96
97   assert((TILE_SIZE << TILE_ADDR_BITS) >= MAX_WIDTH);
98
99   tc = CALLOC_STRUCT( softpipe_tile_cache );
100   if (tc) {
101      tc->pipe = pipe;
102      for (pos = 0; pos < NUM_ENTRIES; pos++) {
103         tc->tile_addrs[pos].bits.invalid = 1;
104      }
105      tc->last_tile_addr.bits.invalid = 1;
106
107      /* this allocation allows us to guarantee that allocation
108       * failures are never fatal later
109       */
110      tc->tile = MALLOC_STRUCT( softpipe_cached_tile );
111      if (!tc->tile)
112      {
113         FREE(tc);
114         return NULL;
115      }
116
117      /* XXX this code prevents valgrind warnings about use of uninitialized
118       * memory in programs that don't clear the surface before rendering.
119       * However, it breaks clearing in other situations (such as in
120       * progs/tests/drawbuffers, see bug 24402).
121       */
122#if 0
123      /* set flags to indicate all the tiles are cleared */
124      memset(tc->clear_flags, 255, sizeof(tc->clear_flags));
125#endif
126   }
127   return tc;
128}
129
130
131void
132sp_destroy_tile_cache(struct softpipe_tile_cache *tc)
133{
134   if (tc) {
135      uint pos;
136
137      for (pos = 0; pos < NUM_ENTRIES; pos++) {
138         /*assert(tc->entries[pos].x < 0);*/
139         FREE( tc->entries[pos] );
140      }
141      FREE( tc->tile );
142
143      if (tc->transfer) {
144         tc->pipe->transfer_destroy(tc->pipe, tc->transfer);
145      }
146
147      FREE( tc );
148   }
149}
150
151
152/**
153 * Specify the surface to cache.
154 */
155void
156sp_tile_cache_set_surface(struct softpipe_tile_cache *tc,
157                          struct pipe_surface *ps)
158{
159   struct pipe_context *pipe = tc->pipe;
160
161   if (tc->transfer) {
162      if (ps == tc->surface)
163         return;
164
165      if (tc->transfer_map) {
166         pipe->transfer_unmap(pipe, tc->transfer);
167         tc->transfer_map = NULL;
168      }
169
170      pipe->transfer_destroy(pipe, tc->transfer);
171      tc->transfer = NULL;
172   }
173
174   tc->surface = ps;
175
176   if (ps) {
177      tc->transfer = pipe_get_transfer(pipe, ps->texture,
178                                       ps->u.tex.level, ps->u.tex.first_layer,
179                                       PIPE_TRANSFER_READ_WRITE |
180                                       PIPE_TRANSFER_UNSYNCHRONIZED,
181                                       0, 0, ps->width, ps->height);
182
183      tc->depth_stencil = util_format_is_depth_or_stencil(ps->format);
184   }
185}
186
187
188/**
189 * Return the transfer being cached.
190 */
191struct pipe_surface *
192sp_tile_cache_get_surface(struct softpipe_tile_cache *tc)
193{
194   return tc->surface;
195}
196
197
198void
199sp_tile_cache_map_transfers(struct softpipe_tile_cache *tc)
200{
201   if (tc->transfer && !tc->transfer_map)
202      tc->transfer_map = tc->pipe->transfer_map(tc->pipe, tc->transfer);
203}
204
205
206void
207sp_tile_cache_unmap_transfers(struct softpipe_tile_cache *tc)
208{
209   if (tc->transfer_map) {
210      tc->pipe->transfer_unmap(tc->pipe, tc->transfer);
211      tc->transfer_map = NULL;
212   }
213}
214
215
216/**
217 * Set pixels in a tile to the given clear color/value, float.
218 */
219static void
220clear_tile_rgba(struct softpipe_cached_tile *tile,
221                enum pipe_format format,
222                const union pipe_color_union *clear_value)
223{
224   if (clear_value->f[0] == 0.0 &&
225       clear_value->f[1] == 0.0 &&
226       clear_value->f[2] == 0.0 &&
227       clear_value->f[3] == 0.0) {
228      memset(tile->data.color, 0, sizeof(tile->data.color));
229   }
230   else {
231      uint i, j;
232
233      if (util_format_is_pure_uint(format)) {
234         for (i = 0; i < TILE_SIZE; i++) {
235            for (j = 0; j < TILE_SIZE; j++) {
236               tile->data.colorui128[i][j][0] = clear_value->ui[0];
237               tile->data.colorui128[i][j][1] = clear_value->ui[1];
238               tile->data.colorui128[i][j][2] = clear_value->ui[2];
239               tile->data.colorui128[i][j][3] = clear_value->ui[3];
240            }
241         }
242      } else if (util_format_is_pure_sint(format)) {
243         for (i = 0; i < TILE_SIZE; i++) {
244            for (j = 0; j < TILE_SIZE; j++) {
245               tile->data.colori128[i][j][0] = clear_value->i[0];
246               tile->data.colori128[i][j][1] = clear_value->i[1];
247               tile->data.colori128[i][j][2] = clear_value->i[2];
248               tile->data.colori128[i][j][3] = clear_value->i[3];
249            }
250         }
251      } else {
252         for (i = 0; i < TILE_SIZE; i++) {
253            for (j = 0; j < TILE_SIZE; j++) {
254               tile->data.color[i][j][0] = clear_value->f[0];
255               tile->data.color[i][j][1] = clear_value->f[1];
256               tile->data.color[i][j][2] = clear_value->f[2];
257               tile->data.color[i][j][3] = clear_value->f[3];
258            }
259         }
260      }
261   }
262}
263
264
265/**
266 * Set a tile to a solid value/color.
267 */
268static void
269clear_tile(struct softpipe_cached_tile *tile,
270           enum pipe_format format,
271           uint64_t clear_value)
272{
273   uint i, j;
274
275   switch (util_format_get_blocksize(format)) {
276   case 1:
277      memset(tile->data.any, clear_value, TILE_SIZE * TILE_SIZE);
278      break;
279   case 2:
280      if (clear_value == 0) {
281         memset(tile->data.any, 0, 2 * TILE_SIZE * TILE_SIZE);
282      }
283      else {
284         for (i = 0; i < TILE_SIZE; i++) {
285            for (j = 0; j < TILE_SIZE; j++) {
286               tile->data.depth16[i][j] = (ushort) clear_value;
287            }
288         }
289      }
290      break;
291   case 4:
292      if (clear_value == 0) {
293         memset(tile->data.any, 0, 4 * TILE_SIZE * TILE_SIZE);
294      }
295      else {
296         for (i = 0; i < TILE_SIZE; i++) {
297            for (j = 0; j < TILE_SIZE; j++) {
298               tile->data.depth32[i][j] = clear_value;
299            }
300         }
301      }
302      break;
303   case 8:
304      if (clear_value == 0) {
305         memset(tile->data.any, 0, 8 * 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.depth64[i][j] = clear_value;
311            }
312         }
313      }
314      break;
315   default:
316      assert(0);
317   }
318}
319
320
321/**
322 * Actually clear the tiles which were flagged as being in a clear state.
323 */
324static void
325sp_tile_cache_flush_clear(struct softpipe_tile_cache *tc)
326{
327   struct pipe_transfer *pt = tc->transfer;
328   const uint w = tc->transfer->box.width;
329   const uint h = tc->transfer->box.height;
330   uint x, y;
331   uint numCleared = 0;
332
333   assert(pt->resource);
334   if (!tc->tile)
335      tc->tile = sp_alloc_tile(tc);
336
337   /* clear the scratch tile to the clear value */
338   if (tc->depth_stencil) {
339      clear_tile(tc->tile, pt->resource->format, tc->clear_val);
340   } else {
341      clear_tile_rgba(tc->tile, pt->resource->format, &tc->clear_color);
342   }
343
344   /* push the tile to all positions marked as clear */
345   for (y = 0; y < h; y += TILE_SIZE) {
346      for (x = 0; x < w; x += TILE_SIZE) {
347         union tile_address addr = tile_address(x, y);
348
349         if (is_clear_flag_set(tc->clear_flags, addr)) {
350            /* write the scratch tile to the surface */
351            if (tc->depth_stencil) {
352               pipe_put_tile_raw(tc->pipe,
353                                 pt,
354                                 x, y, TILE_SIZE, TILE_SIZE,
355                                 tc->tile->data.any, 0/*STRIDE*/);
356            }
357            else {
358               if (util_format_is_pure_uint(tc->surface->format)) {
359                  pipe_put_tile_ui_format(tc->pipe, pt,
360                                          x, y, TILE_SIZE, TILE_SIZE,
361                                          pt->resource->format,
362                                          (unsigned *) tc->tile->data.colorui128);
363               } else if (util_format_is_pure_sint(tc->surface->format)) {
364                  pipe_put_tile_i_format(tc->pipe, pt,
365                                         x, y, TILE_SIZE, TILE_SIZE,
366                                         pt->resource->format,
367                                         (int *) tc->tile->data.colori128);
368               } else {
369                  pipe_put_tile_rgba(tc->pipe, pt,
370                                     x, y, TILE_SIZE, TILE_SIZE,
371                                     (float *) tc->tile->data.color);
372               }
373            }
374            numCleared++;
375         }
376      }
377   }
378
379   /* reset all clear flags to zero */
380   memset(tc->clear_flags, 0, sizeof(tc->clear_flags));
381
382#if 0
383   debug_printf("num cleared: %u\n", numCleared);
384#endif
385}
386
387static void
388sp_flush_tile(struct softpipe_tile_cache* tc, unsigned pos)
389{
390   if (!tc->tile_addrs[pos].bits.invalid) {
391      if (tc->depth_stencil) {
392         pipe_put_tile_raw(tc->pipe, tc->transfer,
393                           tc->tile_addrs[pos].bits.x * TILE_SIZE,
394                           tc->tile_addrs[pos].bits.y * TILE_SIZE,
395                           TILE_SIZE, TILE_SIZE,
396                           tc->entries[pos]->data.depth32, 0/*STRIDE*/);
397      }
398      else {
399         if (util_format_is_pure_uint(tc->surface->format)) {
400            pipe_put_tile_ui_format(tc->pipe, tc->transfer,
401                                    tc->tile_addrs[pos].bits.x * TILE_SIZE,
402                                    tc->tile_addrs[pos].bits.y * TILE_SIZE,
403                                    TILE_SIZE, TILE_SIZE,
404                                    tc->surface->format,
405                                    (unsigned *) tc->entries[pos]->data.colorui128);
406         } else if (util_format_is_pure_sint(tc->surface->format)) {
407            pipe_put_tile_i_format(tc->pipe, tc->transfer,
408                                   tc->tile_addrs[pos].bits.x * TILE_SIZE,
409                                   tc->tile_addrs[pos].bits.y * TILE_SIZE,
410                                   TILE_SIZE, TILE_SIZE,
411                                   tc->surface->format,
412                                   (int *) tc->entries[pos]->data.colori128);
413         } else {
414            pipe_put_tile_rgba_format(tc->pipe, tc->transfer,
415                                      tc->tile_addrs[pos].bits.x * TILE_SIZE,
416                                      tc->tile_addrs[pos].bits.y * TILE_SIZE,
417                                      TILE_SIZE, TILE_SIZE,
418                                      tc->surface->format,
419                                      (float *) tc->entries[pos]->data.color);
420         }
421      }
422      tc->tile_addrs[pos].bits.invalid = 1;  /* mark as empty */
423   }
424}
425
426/**
427 * Flush the tile cache: write all dirty tiles back to the transfer.
428 * any tiles "flagged" as cleared will be "really" cleared.
429 */
430void
431sp_flush_tile_cache(struct softpipe_tile_cache *tc)
432{
433   struct pipe_transfer *pt = tc->transfer;
434   int inuse = 0, pos;
435
436   if (pt) {
437      /* caching a drawing transfer */
438      for (pos = 0; pos < NUM_ENTRIES; pos++) {
439         struct softpipe_cached_tile *tile = tc->entries[pos];
440         if (!tile)
441         {
442            assert(tc->tile_addrs[pos].bits.invalid);
443            continue;
444         }
445
446         sp_flush_tile(tc, pos);
447         ++inuse;
448      }
449
450      sp_tile_cache_flush_clear(tc);
451
452
453      tc->last_tile_addr.bits.invalid = 1;
454   }
455
456#if 0
457   debug_printf("flushed tiles in use: %d\n", inuse);
458#endif
459}
460
461static struct softpipe_cached_tile *
462sp_alloc_tile(struct softpipe_tile_cache *tc)
463{
464   struct softpipe_cached_tile * tile = MALLOC_STRUCT(softpipe_cached_tile);
465   if (!tile)
466   {
467      /* in this case, steal an existing tile */
468      if (!tc->tile)
469      {
470         unsigned pos;
471         for (pos = 0; pos < NUM_ENTRIES; ++pos) {
472            if (!tc->entries[pos])
473               continue;
474
475            sp_flush_tile(tc, pos);
476            tc->tile = tc->entries[pos];
477            tc->entries[pos] = NULL;
478            break;
479         }
480
481         /* this should never happen */
482         if (!tc->tile)
483            abort();
484      }
485
486      tile = tc->tile;
487      tc->tile = NULL;
488
489      tc->last_tile_addr.bits.invalid = 1;
490   }
491   return tile;
492}
493
494/**
495 * Get a tile from the cache.
496 * \param x, y  position of tile, in pixels
497 */
498struct softpipe_cached_tile *
499sp_find_cached_tile(struct softpipe_tile_cache *tc,
500                    union tile_address addr )
501{
502   struct pipe_transfer *pt = tc->transfer;
503   /* cache pos/entry: */
504   const int pos = CACHE_POS(addr.bits.x,
505                             addr.bits.y);
506   struct softpipe_cached_tile *tile = tc->entries[pos];
507
508   if (!tile) {
509      tile = sp_alloc_tile(tc);
510      tc->entries[pos] = tile;
511   }
512
513   if (addr.value != tc->tile_addrs[pos].value) {
514
515      assert(pt->resource);
516      if (tc->tile_addrs[pos].bits.invalid == 0) {
517         /* put dirty tile back in framebuffer */
518         if (tc->depth_stencil) {
519            pipe_put_tile_raw(tc->pipe, pt,
520                              tc->tile_addrs[pos].bits.x * TILE_SIZE,
521                              tc->tile_addrs[pos].bits.y * TILE_SIZE,
522                              TILE_SIZE, TILE_SIZE,
523                              tile->data.depth32, 0/*STRIDE*/);
524         }
525         else {
526            if (util_format_is_pure_uint(tc->surface->format)) {
527               pipe_put_tile_ui_format(tc->pipe, pt,
528                                      tc->tile_addrs[pos].bits.x * TILE_SIZE,
529                                      tc->tile_addrs[pos].bits.y * TILE_SIZE,
530                                      TILE_SIZE, TILE_SIZE,
531                                      tc->surface->format,
532                                      (unsigned *) tile->data.colorui128);
533            } else if (util_format_is_pure_sint(tc->surface->format)) {
534               pipe_put_tile_i_format(tc->pipe, pt,
535                                      tc->tile_addrs[pos].bits.x * TILE_SIZE,
536                                      tc->tile_addrs[pos].bits.y * TILE_SIZE,
537                                      TILE_SIZE, TILE_SIZE,
538                                      tc->surface->format,
539                                      (int *) tile->data.colori128);
540            } else {
541               pipe_put_tile_rgba_format(tc->pipe, pt,
542                                         tc->tile_addrs[pos].bits.x * TILE_SIZE,
543                                         tc->tile_addrs[pos].bits.y * TILE_SIZE,
544                                         TILE_SIZE, TILE_SIZE,
545                                         tc->surface->format,
546                                         (float *) tile->data.color);
547            }
548         }
549      }
550
551      tc->tile_addrs[pos] = addr;
552
553      if (is_clear_flag_set(tc->clear_flags, addr)) {
554         /* don't get tile from framebuffer, just clear it */
555         if (tc->depth_stencil) {
556            clear_tile(tile, pt->resource->format, tc->clear_val);
557         }
558         else {
559            clear_tile_rgba(tile, pt->resource->format, &tc->clear_color);
560         }
561         clear_clear_flag(tc->clear_flags, addr);
562      }
563      else {
564         /* get new tile data from transfer */
565         if (tc->depth_stencil) {
566            pipe_get_tile_raw(tc->pipe, pt,
567                              tc->tile_addrs[pos].bits.x * TILE_SIZE,
568                              tc->tile_addrs[pos].bits.y * TILE_SIZE,
569                              TILE_SIZE, TILE_SIZE,
570                              tile->data.depth32, 0/*STRIDE*/);
571         }
572         else {
573            if (util_format_is_pure_uint(tc->surface->format)) {
574               pipe_get_tile_ui_format(tc->pipe, pt,
575                                         tc->tile_addrs[pos].bits.x * TILE_SIZE,
576                                         tc->tile_addrs[pos].bits.y * TILE_SIZE,
577                                         TILE_SIZE, TILE_SIZE,
578                                         tc->surface->format,
579                                         (unsigned *) tile->data.colorui128);
580            } else if (util_format_is_pure_sint(tc->surface->format)) {
581               pipe_get_tile_i_format(tc->pipe, pt,
582                                         tc->tile_addrs[pos].bits.x * TILE_SIZE,
583                                         tc->tile_addrs[pos].bits.y * TILE_SIZE,
584                                         TILE_SIZE, TILE_SIZE,
585                                         tc->surface->format,
586                                         (int *) tile->data.colori128);
587            } else {
588               pipe_get_tile_rgba_format(tc->pipe, pt,
589                                         tc->tile_addrs[pos].bits.x * TILE_SIZE,
590                                         tc->tile_addrs[pos].bits.y * TILE_SIZE,
591                                         TILE_SIZE, TILE_SIZE,
592                                         tc->surface->format,
593                                         (float *) tile->data.color);
594            }
595         }
596      }
597   }
598
599   tc->last_tile = tile;
600   tc->last_tile_addr = addr;
601   return tile;
602}
603
604
605
606
607
608/**
609 * When a whole surface is being cleared to a value we can avoid
610 * fetching tiles above.
611 * Save the color and set a 'clearflag' for each tile of the screen.
612 */
613void
614sp_tile_cache_clear(struct softpipe_tile_cache *tc,
615                    const union pipe_color_union *color,
616                    uint64_t clearValue)
617{
618   uint pos;
619
620   tc->clear_color = *color;
621
622   tc->clear_val = clearValue;
623
624   /* set flags to indicate all the tiles are cleared */
625   memset(tc->clear_flags, 255, sizeof(tc->clear_flags));
626
627   for (pos = 0; pos < NUM_ENTRIES; pos++) {
628      tc->tile_addrs[pos].bits.invalid = 1;
629   }
630   tc->last_tile_addr.bits.invalid = 1;
631}
632