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