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