sp_tile_cache.c revision 2142bf5e17d699396772b90ee5b2592289334f28
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 * Texture tile caching. 30 * 31 * Author: 32 * Brian Paul 33 */ 34 35#include "pipe/p_inlines.h" 36#include "util/u_memory.h" 37#include "util/u_tile.h" 38#include "sp_context.h" 39#include "sp_surface.h" 40#include "sp_texture.h" 41#include "sp_tile_cache.h" 42 43#define NUM_ENTRIES 32 44 45 46/** XXX move these */ 47#define MAX_WIDTH 2048 48#define MAX_HEIGHT 2048 49 50 51struct softpipe_tile_cache 52{ 53 struct pipe_screen *screen; 54 struct pipe_surface *surface; /**< the surface we're caching */ 55 struct pipe_transfer *transfer; 56 void *transfer_map; 57 struct pipe_texture *texture; /**< if caching a texture */ 58 struct softpipe_cached_tile entries[NUM_ENTRIES]; 59 uint clear_flags[(MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32]; 60 float clear_color[4]; 61 uint clear_val; 62 boolean depth_stencil; /** Is the surface a depth/stencil format? */ 63 64 struct pipe_transfer *tex_trans; 65 void *tex_trans_map; 66 int tex_face, tex_level, tex_z; 67 68 struct softpipe_cached_tile tile; /**< scratch tile for clears */ 69}; 70 71 72/** 73 * Return the position in the cache for the tile that contains win pos (x,y). 74 * We currently use a direct mapped cache so this is like a hack key. 75 * At some point we should investige something more sophisticated, like 76 * a LRU replacement policy. 77 */ 78#define CACHE_POS(x, y) \ 79 (((x) / TILE_SIZE + ((y) / TILE_SIZE) * 5) % NUM_ENTRIES) 80 81 82 83/** 84 * Is the tile at (x,y) in cleared state? 85 */ 86static INLINE uint 87is_clear_flag_set(const uint *bitvec, int x, int y) 88{ 89 int pos, bit; 90 x /= TILE_SIZE; 91 y /= TILE_SIZE; 92 pos = y * (MAX_WIDTH / TILE_SIZE) + x; 93 assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32); 94 bit = bitvec[pos / 32] & (1 << (pos & 31)); 95 return bit; 96} 97 98 99/** 100 * Mark the tile at (x,y) as not cleared. 101 */ 102static INLINE void 103clear_clear_flag(uint *bitvec, int x, int y) 104{ 105 int pos; 106 x /= TILE_SIZE; 107 y /= TILE_SIZE; 108 pos = y * (MAX_WIDTH / TILE_SIZE) + x; 109 assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32); 110 bitvec[pos / 32] &= ~(1 << (pos & 31)); 111} 112 113 114struct softpipe_tile_cache * 115sp_create_tile_cache( struct pipe_screen *screen ) 116{ 117 struct softpipe_tile_cache *tc; 118 uint pos; 119 120 tc = CALLOC_STRUCT( softpipe_tile_cache ); 121 if (tc) { 122 tc->screen = screen; 123 for (pos = 0; pos < NUM_ENTRIES; pos++) { 124 tc->entries[pos].x = 125 tc->entries[pos].y = -1; 126 } 127 } 128 return tc; 129} 130 131 132void 133sp_destroy_tile_cache(struct softpipe_tile_cache *tc) 134{ 135 struct pipe_screen *screen; 136 uint pos; 137 138 for (pos = 0; pos < NUM_ENTRIES; pos++) { 139 /*assert(tc->entries[pos].x < 0);*/ 140 } 141 if (tc->transfer) { 142 screen = tc->transfer->texture->screen; 143 screen->tex_transfer_release(screen, &tc->transfer); 144 } 145 if (tc->tex_trans) { 146 screen = tc->tex_trans->texture->screen; 147 screen->tex_transfer_release(screen, &tc->tex_trans); 148 } 149 150 FREE( tc ); 151} 152 153 154/** 155 * Specify the surface to cache. 156 */ 157void 158sp_tile_cache_set_surface(struct softpipe_tile_cache *tc, 159 struct pipe_surface *ps) 160{ 161 struct pipe_screen *screen = ps->texture->screen; 162 163 assert(!tc->texture); 164 165 if (tc->transfer) { 166 if (ps == tc->surface) 167 return; 168 169 if (tc->transfer_map) { 170 tc->screen->transfer_unmap(tc->screen, tc->transfer); 171 tc->transfer_map = NULL; 172 } 173 174 screen->tex_transfer_release(screen, &tc->transfer); 175 } 176 177 tc->surface = ps; 178 179 tc->transfer = screen->get_tex_transfer(screen, ps->texture, ps->face, 180 ps->level, ps->zslice, 181 PIPE_TRANSFER_READ_WRITE, 182 0, 0, ps->width, ps->height); 183 184 tc->depth_stencil = (ps->format == PIPE_FORMAT_S8Z24_UNORM || 185 ps->format == PIPE_FORMAT_X8Z24_UNORM || 186 ps->format == PIPE_FORMAT_Z24S8_UNORM || 187 ps->format == PIPE_FORMAT_Z24X8_UNORM || 188 ps->format == PIPE_FORMAT_Z16_UNORM || 189 ps->format == PIPE_FORMAT_Z32_UNORM || 190 ps->format == PIPE_FORMAT_S8_UNORM); 191} 192 193 194/** 195 * Return the transfer being cached. 196 */ 197struct pipe_surface * 198sp_tile_cache_get_surface(struct softpipe_tile_cache *tc) 199{ 200 return tc->surface; 201} 202 203 204void 205sp_tile_cache_map_transfers(struct softpipe_tile_cache *tc) 206{ 207 if (tc->transfer && !tc->transfer_map) 208 tc->transfer_map = tc->screen->transfer_map(tc->screen, tc->transfer); 209 210 if (tc->tex_trans && !tc->tex_trans_map) 211 tc->tex_trans_map = tc->screen->transfer_map(tc->screen, tc->tex_trans); 212} 213 214 215void 216sp_tile_cache_unmap_transfers(struct softpipe_tile_cache *tc) 217{ 218 if (tc->transfer_map) { 219 tc->screen->transfer_unmap(tc->screen, tc->transfer); 220 tc->transfer_map = NULL; 221 } 222 223 if (tc->tex_trans_map) { 224 tc->screen->transfer_unmap(tc->screen, tc->tex_trans); 225 tc->tex_trans_map = NULL; 226 } 227} 228 229 230/** 231 * Specify the texture to cache. 232 */ 233void 234sp_tile_cache_set_texture(struct pipe_context *pipe, 235 struct softpipe_tile_cache *tc, 236 struct pipe_texture *texture) 237{ 238 struct pipe_screen *screen = texture->screen; 239 uint i; 240 241 assert(!tc->transfer); 242 243 pipe_texture_reference(&tc->texture, texture); 244 245 if (tc->tex_trans_map) { 246 tc->screen->transfer_unmap(tc->screen, tc->tex_trans); 247 tc->tex_trans_map = NULL; 248 } 249 screen->tex_transfer_release(screen, &tc->tex_trans); 250 251 /* mark as entries as invalid/empty */ 252 /* XXX we should try to avoid this when the teximage hasn't changed */ 253 for (i = 0; i < NUM_ENTRIES; i++) { 254 tc->entries[i].x = -1; 255 } 256 257 tc->tex_face = -1; /* any invalid value here */ 258} 259 260 261/** 262 * Set pixels in a tile to the given clear color/value, float. 263 */ 264static void 265clear_tile_rgba(struct softpipe_cached_tile *tile, 266 enum pipe_format format, 267 const float clear_value[4]) 268{ 269 if (clear_value[0] == 0.0 && 270 clear_value[1] == 0.0 && 271 clear_value[2] == 0.0 && 272 clear_value[3] == 0.0) { 273 memset(tile->data.color, 0, sizeof(tile->data.color)); 274 } 275 else { 276 uint i, j; 277 for (i = 0; i < TILE_SIZE; i++) { 278 for (j = 0; j < TILE_SIZE; j++) { 279 tile->data.color[i][j][0] = clear_value[0]; 280 tile->data.color[i][j][1] = clear_value[1]; 281 tile->data.color[i][j][2] = clear_value[2]; 282 tile->data.color[i][j][3] = clear_value[3]; 283 } 284 } 285 } 286} 287 288 289/** 290 * Set a tile to a solid value/color. 291 */ 292static void 293clear_tile(struct softpipe_cached_tile *tile, 294 enum pipe_format format, 295 uint clear_value) 296{ 297 uint i, j; 298 299 switch (pf_get_size(format)) { 300 case 1: 301 memset(tile->data.any, 0, TILE_SIZE * TILE_SIZE); 302 break; 303 case 2: 304 if (clear_value == 0) { 305 memset(tile->data.any, 0, 2 * 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.depth16[i][j] = (ushort) clear_value; 311 } 312 } 313 } 314 break; 315 case 4: 316 if (clear_value == 0) { 317 memset(tile->data.any, 0, 4 * TILE_SIZE * TILE_SIZE); 318 } 319 else { 320 for (i = 0; i < TILE_SIZE; i++) { 321 for (j = 0; j < TILE_SIZE; j++) { 322 tile->data.color32[i][j] = clear_value; 323 } 324 } 325 } 326 break; 327 default: 328 assert(0); 329 } 330} 331 332 333/** 334 * Actually clear the tiles which were flagged as being in a clear state. 335 */ 336static void 337sp_tile_cache_flush_clear(struct pipe_context *pipe, 338 struct softpipe_tile_cache *tc) 339{ 340 struct pipe_transfer *pt = tc->transfer; 341 const uint w = tc->transfer->width; 342 const uint h = tc->transfer->height; 343 uint x, y; 344 uint numCleared = 0; 345 346 /* clear the scratch tile to the clear value */ 347 clear_tile(&tc->tile, pt->format, tc->clear_val); 348 349 /* push the tile to all positions marked as clear */ 350 for (y = 0; y < h; y += TILE_SIZE) { 351 for (x = 0; x < w; x += TILE_SIZE) { 352 if (is_clear_flag_set(tc->clear_flags, x, y)) { 353 pipe_put_tile_raw(pt, 354 x, y, TILE_SIZE, TILE_SIZE, 355 tc->tile.data.color32, 0/*STRIDE*/); 356 357 /* do this? */ 358 clear_clear_flag(tc->clear_flags, x, y); 359 360 numCleared++; 361 } 362 } 363 } 364#if 0 365 debug_printf("num cleared: %u\n", numCleared); 366#endif 367} 368 369 370/** 371 * Flush the tile cache: write all dirty tiles back to the transfer. 372 * any tiles "flagged" as cleared will be "really" cleared. 373 */ 374void 375sp_flush_tile_cache(struct softpipe_context *softpipe, 376 struct softpipe_tile_cache *tc) 377{ 378 struct pipe_transfer *pt = tc->transfer; 379 int inuse = 0, pos; 380 381 if (pt) { 382 /* caching a drawing transfer */ 383 for (pos = 0; pos < NUM_ENTRIES; pos++) { 384 struct softpipe_cached_tile *tile = tc->entries + pos; 385 if (tile->x >= 0) { 386 if (tc->depth_stencil) { 387 pipe_put_tile_raw(pt, 388 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 389 tile->data.depth32, 0/*STRIDE*/); 390 } 391 else { 392 pipe_put_tile_rgba(pt, 393 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 394 (float *) tile->data.color); 395 } 396 tile->x = tile->y = -1; /* mark as empty */ 397 inuse++; 398 } 399 } 400 401#if TILE_CLEAR_OPTIMIZATION 402 sp_tile_cache_flush_clear(&softpipe->pipe, tc); 403#endif 404 } 405 else if (tc->texture) { 406 /* caching a texture, mark all entries as empty */ 407 for (pos = 0; pos < NUM_ENTRIES; pos++) { 408 tc->entries[pos].x = -1; 409 } 410 tc->tex_face = -1; 411 } 412 413#if 0 414 debug_printf("flushed tiles in use: %d\n", inuse); 415#endif 416} 417 418 419/** 420 * Get a tile from the cache. 421 * \param x, y position of tile, in pixels 422 */ 423struct softpipe_cached_tile * 424sp_get_cached_tile(struct softpipe_context *softpipe, 425 struct softpipe_tile_cache *tc, int x, int y) 426{ 427 struct pipe_transfer *pt = tc->transfer; 428 429 /* tile pos in framebuffer: */ 430 const int tile_x = x & ~(TILE_SIZE - 1); 431 const int tile_y = y & ~(TILE_SIZE - 1); 432 433 /* cache pos/entry: */ 434 const int pos = CACHE_POS(x, y); 435 struct softpipe_cached_tile *tile = tc->entries + pos; 436 437 if (tile_x != tile->x || 438 tile_y != tile->y) { 439 440 if (tile->x != -1) { 441 /* put dirty tile back in framebuffer */ 442 if (tc->depth_stencil) { 443 pipe_put_tile_raw(pt, 444 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 445 tile->data.depth32, 0/*STRIDE*/); 446 } 447 else { 448 pipe_put_tile_rgba(pt, 449 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 450 (float *) tile->data.color); 451 } 452 } 453 454 tile->x = tile_x; 455 tile->y = tile_y; 456 457 if (is_clear_flag_set(tc->clear_flags, x, y)) { 458 /* don't get tile from framebuffer, just clear it */ 459 if (tc->depth_stencil) { 460 clear_tile(tile, pt->format, tc->clear_val); 461 } 462 else { 463 clear_tile_rgba(tile, pt->format, tc->clear_color); 464 } 465 clear_clear_flag(tc->clear_flags, x, y); 466 } 467 else { 468 /* get new tile data from transfer */ 469 if (tc->depth_stencil) { 470 pipe_get_tile_raw(pt, 471 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 472 tile->data.depth32, 0/*STRIDE*/); 473 } 474 else { 475 pipe_get_tile_rgba(pt, 476 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 477 (float *) tile->data.color); 478 } 479 } 480 } 481 482 return tile; 483} 484 485 486/** 487 * Given the texture face, level, zslice, x and y values, compute 488 * the cache entry position/index where we'd hope to find the 489 * cached texture tile. 490 * This is basically a direct-map cache. 491 * XXX There's probably lots of ways in which we can improve this. 492 */ 493static INLINE uint 494tex_cache_pos(int x, int y, int z, int face, int level) 495{ 496 uint entry = x + y * 5 + z * 4 + face + level; 497 return entry % NUM_ENTRIES; 498} 499 500 501/** 502 * Similar to sp_get_cached_tile() but for textures. 503 * Tiles are read-only and indexed with more params. 504 */ 505const struct softpipe_cached_tile * 506sp_get_cached_tile_tex(struct softpipe_context *sp, 507 struct softpipe_tile_cache *tc, int x, int y, int z, 508 int face, int level) 509{ 510 struct pipe_screen *screen = sp->pipe.screen; 511 /* tile pos in framebuffer: */ 512 const int tile_x = x & ~(TILE_SIZE - 1); 513 const int tile_y = y & ~(TILE_SIZE - 1); 514 /* cache pos/entry: */ 515 const uint pos = tex_cache_pos(x / TILE_SIZE, y / TILE_SIZE, z, 516 face, level); 517 struct softpipe_cached_tile *tile = tc->entries + pos; 518 519 if (tc->texture) { 520 struct softpipe_texture *spt = softpipe_texture(tc->texture); 521 if (spt->modified) { 522 /* texture was modified, force a cache reload */ 523 tile->x = -1; 524 spt->modified = FALSE; 525 } 526 } 527 528 if (tile_x != tile->x || 529 tile_y != tile->y || 530 z != tile->z || 531 face != tile->face || 532 level != tile->level) { 533 /* cache miss */ 534 535 /* check if we need to get a new transfer */ 536 if (!tc->tex_trans || 537 tc->tex_face != face || 538 tc->tex_level != level || 539 tc->tex_z != z) { 540 /* get new transfer (view into texture) */ 541 542 if (tc->tex_trans_map) 543 tc->screen->transfer_unmap(tc->screen, tc->tex_trans); 544 screen->tex_transfer_release(screen, &tc->tex_trans); 545 546 tc->tex_trans = screen->get_tex_transfer(screen, tc->texture, face, level, z, 547 PIPE_TRANSFER_READ, 0, 0, 548 tc->texture->width[level], 549 tc->texture->height[level]); 550 tc->tex_trans_map = screen->transfer_map(screen, tc->tex_trans); 551 552 tc->tex_face = face; 553 tc->tex_level = level; 554 tc->tex_z = z; 555 } 556 557 /* get tile from the transfer (view into texture) */ 558 pipe_get_tile_rgba(tc->tex_trans, 559 tile_x, tile_y, TILE_SIZE, TILE_SIZE, 560 (float *) tile->data.color); 561 tile->x = tile_x; 562 tile->y = tile_y; 563 tile->z = z; 564 tile->face = face; 565 tile->level = level; 566 } 567 568 return tile; 569} 570 571 572/** 573 * When a whole surface is being cleared to a value we can avoid 574 * fetching tiles above. 575 * Save the color and set a 'clearflag' for each tile of the screen. 576 */ 577void 578sp_tile_cache_clear(struct softpipe_tile_cache *tc, uint clearValue) 579{ 580 uint r, g, b, a; 581 uint pos; 582 583 tc->clear_val = clearValue; 584 585 switch (tc->transfer->format) { 586 case PIPE_FORMAT_R8G8B8A8_UNORM: 587 r = (clearValue >> 24) & 0xff; 588 g = (clearValue >> 16) & 0xff; 589 b = (clearValue >> 8) & 0xff; 590 a = (clearValue ) & 0xff; 591 break; 592 case PIPE_FORMAT_A8R8G8B8_UNORM: 593 r = (clearValue >> 16) & 0xff; 594 g = (clearValue >> 8) & 0xff; 595 b = (clearValue ) & 0xff; 596 a = (clearValue >> 24) & 0xff; 597 break; 598 case PIPE_FORMAT_B8G8R8A8_UNORM: 599 r = (clearValue >> 8) & 0xff; 600 g = (clearValue >> 16) & 0xff; 601 b = (clearValue >> 24) & 0xff; 602 a = (clearValue ) & 0xff; 603 break; 604 default: 605 r = g = b = a = 0; 606 } 607 608 tc->clear_color[0] = r / 255.0f; 609 tc->clear_color[1] = g / 255.0f; 610 tc->clear_color[2] = b / 255.0f; 611 tc->clear_color[3] = a / 255.0f; 612 613#if TILE_CLEAR_OPTIMIZATION 614 /* set flags to indicate all the tiles are cleared */ 615 memset(tc->clear_flags, 255, sizeof(tc->clear_flags)); 616#else 617 /* disable the optimization */ 618 memset(tc->clear_flags, 0, sizeof(tc->clear_flags)); 619#endif 620 621 for (pos = 0; pos < NUM_ENTRIES; pos++) { 622 struct softpipe_cached_tile *tile = tc->entries + pos; 623 tile->x = tile->y = -1; 624 } 625} 626