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 
41 static struct softpipe_cached_tile *
42 sp_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  */
59 static INLINE uint
is_clear_flag_set(const uint * bitvec,union tile_address addr)60 is_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  */
73 static INLINE void
clear_clear_flag(uint * bitvec,union tile_address addr)74 clear_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 
83 struct softpipe_tile_cache *
sp_create_tile_cache(struct pipe_context * pipe)84 sp_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 
131 void
sp_destroy_tile_cache(struct softpipe_tile_cache * tc)132 sp_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  */
155 void
sp_tile_cache_set_surface(struct softpipe_tile_cache * tc,struct pipe_surface * ps)156 sp_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  */
191 struct pipe_surface *
sp_tile_cache_get_surface(struct softpipe_tile_cache * tc)192 sp_tile_cache_get_surface(struct softpipe_tile_cache *tc)
193 {
194    return tc->surface;
195 }
196 
197 
198 void
sp_tile_cache_map_transfers(struct softpipe_tile_cache * tc)199 sp_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 
206 void
sp_tile_cache_unmap_transfers(struct softpipe_tile_cache * tc)207 sp_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  */
219 static void
clear_tile_rgba(struct softpipe_cached_tile * tile,enum pipe_format format,const union pipe_color_union * clear_value)220 clear_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  */
268 static void
clear_tile(struct softpipe_cached_tile * tile,enum pipe_format format,uint64_t clear_value)269 clear_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  */
324 static void
sp_tile_cache_flush_clear(struct softpipe_tile_cache * tc)325 sp_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 
387 static void
sp_flush_tile(struct softpipe_tile_cache * tc,unsigned pos)388 sp_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  */
430 void
sp_flush_tile_cache(struct softpipe_tile_cache * tc)431 sp_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 
461 static struct softpipe_cached_tile *
sp_alloc_tile(struct softpipe_tile_cache * tc)462 sp_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  */
498 struct softpipe_cached_tile *
sp_find_cached_tile(struct softpipe_tile_cache * tc,union tile_address addr)499 sp_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  */
613 void
sp_tile_cache_clear(struct softpipe_tile_cache * tc,const union pipe_color_union * color,uint64_t clearValue)614 sp_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