sp_tile_cache.c revision 479d929530ce40a39d9310576b97cb46fab214de
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->texture == tc->transfer->texture && 167 ps->face == tc->transfer->face && 168 ps->level == tc->transfer->level && 169 ps->zslice == tc->transfer->zslice) 170 return; 171 172 if (tc->transfer_map) { 173 tc->screen->transfer_unmap(tc->screen, tc->transfer); 174 tc->transfer_map = NULL; 175 } 176 177 screen->tex_transfer_release(screen, &tc->transfer); 178 } 179 180 tc->transfer = screen->get_tex_transfer(screen, ps->texture, ps->face, 181 ps->level, ps->zslice, 182 PIPE_TRANSFER_READ_WRITE, 183 0, 0, ps->width, ps->height); 184 185 tc->depth_stencil = (ps->format == PIPE_FORMAT_S8Z24_UNORM || 186 ps->format == PIPE_FORMAT_X8Z24_UNORM || 187 ps->format == PIPE_FORMAT_Z24S8_UNORM || 188 ps->format == PIPE_FORMAT_Z24X8_UNORM || 189 ps->format == PIPE_FORMAT_Z16_UNORM || 190 ps->format == PIPE_FORMAT_Z32_UNORM || 191 ps->format == PIPE_FORMAT_S8_UNORM); 192} 193 194 195/** 196 * Return the transfer being cached. 197 */ 198struct pipe_surface * 199sp_tile_cache_get_surface(struct softpipe_tile_cache *tc) 200{ 201 return tc->surface; 202} 203 204 205void 206sp_tile_cache_map_transfers(struct softpipe_tile_cache *tc) 207{ 208 if (tc->transfer && !tc->transfer_map) 209 tc->transfer_map = tc->screen->transfer_map(tc->screen, tc->transfer); 210 211 if (tc->tex_trans && !tc->tex_trans_map) 212 tc->tex_trans_map = tc->screen->transfer_map(tc->screen, tc->tex_trans); 213} 214 215 216void 217sp_tile_cache_unmap_transfers(struct softpipe_tile_cache *tc) 218{ 219 if (tc->transfer_map) { 220 tc->screen->transfer_unmap(tc->screen, tc->transfer); 221 tc->transfer_map = NULL; 222 } 223 224 if (tc->tex_trans_map) { 225 tc->screen->transfer_unmap(tc->screen, tc->tex_trans); 226 tc->tex_trans_map = NULL; 227 } 228} 229 230 231/** 232 * Specify the texture to cache. 233 */ 234void 235sp_tile_cache_set_texture(struct pipe_context *pipe, 236 struct softpipe_tile_cache *tc, 237 struct pipe_texture *texture) 238{ 239 struct pipe_screen *screen = texture->screen; 240 uint i; 241 242 assert(!tc->transfer); 243 244 pipe_texture_reference(&tc->texture, texture); 245 246 if (tc->tex_trans_map) { 247 tc->screen->transfer_unmap(tc->screen, tc->tex_trans); 248 tc->tex_trans_map = NULL; 249 } 250 screen->tex_transfer_release(screen, &tc->tex_trans); 251 252 /* mark as entries as invalid/empty */ 253 /* XXX we should try to avoid this when the teximage hasn't changed */ 254 for (i = 0; i < NUM_ENTRIES; i++) { 255 tc->entries[i].x = -1; 256 } 257 258 tc->tex_face = -1; /* any invalid value here */ 259} 260 261 262/** 263 * Set pixels in a tile to the given clear color/value, float. 264 */ 265static void 266clear_tile_rgba(struct softpipe_cached_tile *tile, 267 enum pipe_format format, 268 const float clear_value[4]) 269{ 270 if (clear_value[0] == 0.0 && 271 clear_value[1] == 0.0 && 272 clear_value[2] == 0.0 && 273 clear_value[3] == 0.0) { 274 memset(tile->data.color, 0, sizeof(tile->data.color)); 275 } 276 else { 277 uint i, j; 278 for (i = 0; i < TILE_SIZE; i++) { 279 for (j = 0; j < TILE_SIZE; j++) { 280 tile->data.color[i][j][0] = clear_value[0]; 281 tile->data.color[i][j][1] = clear_value[1]; 282 tile->data.color[i][j][2] = clear_value[2]; 283 tile->data.color[i][j][3] = clear_value[3]; 284 } 285 } 286 } 287} 288 289 290/** 291 * Set a tile to a solid value/color. 292 */ 293static void 294clear_tile(struct softpipe_cached_tile *tile, 295 enum pipe_format format, 296 uint clear_value) 297{ 298 uint i, j; 299 300 switch (pf_get_size(format)) { 301 case 1: 302 memset(tile->data.any, 0, TILE_SIZE * TILE_SIZE); 303 break; 304 case 2: 305 if (clear_value == 0) { 306 memset(tile->data.any, 0, 2 * TILE_SIZE * TILE_SIZE); 307 } 308 else { 309 for (i = 0; i < TILE_SIZE; i++) { 310 for (j = 0; j < TILE_SIZE; j++) { 311 tile->data.depth16[i][j] = (ushort) clear_value; 312 } 313 } 314 } 315 break; 316 case 4: 317 if (clear_value == 0) { 318 memset(tile->data.any, 0, 4 * TILE_SIZE * TILE_SIZE); 319 } 320 else { 321 for (i = 0; i < TILE_SIZE; i++) { 322 for (j = 0; j < TILE_SIZE; j++) { 323 tile->data.color32[i][j] = clear_value; 324 } 325 } 326 } 327 break; 328 default: 329 assert(0); 330 } 331} 332 333 334/** 335 * Actually clear the tiles which were flagged as being in a clear state. 336 */ 337static void 338sp_tile_cache_flush_clear(struct pipe_context *pipe, 339 struct softpipe_tile_cache *tc) 340{ 341 struct pipe_transfer *pt = tc->transfer; 342 const uint w = tc->transfer->width; 343 const uint h = tc->transfer->height; 344 uint x, y; 345 uint numCleared = 0; 346 347 /* clear the scratch tile to the clear value */ 348 clear_tile(&tc->tile, pt->format, tc->clear_val); 349 350 /* push the tile to all positions marked as clear */ 351 for (y = 0; y < h; y += TILE_SIZE) { 352 for (x = 0; x < w; x += TILE_SIZE) { 353 if (is_clear_flag_set(tc->clear_flags, x, y)) { 354 pipe_put_tile_raw(pt, 355 x, y, TILE_SIZE, TILE_SIZE, 356 tc->tile.data.color32, 0/*STRIDE*/); 357 358 /* do this? */ 359 clear_clear_flag(tc->clear_flags, x, y); 360 361 numCleared++; 362 } 363 } 364 } 365#if 0 366 debug_printf("num cleared: %u\n", numCleared); 367#endif 368} 369 370 371/** 372 * Flush the tile cache: write all dirty tiles back to the transfer. 373 * any tiles "flagged" as cleared will be "really" cleared. 374 */ 375void 376sp_flush_tile_cache(struct softpipe_context *softpipe, 377 struct softpipe_tile_cache *tc) 378{ 379 struct pipe_transfer *pt = tc->transfer; 380 int inuse = 0, pos; 381 382 if (pt) { 383 /* caching a drawing transfer */ 384 for (pos = 0; pos < NUM_ENTRIES; pos++) { 385 struct softpipe_cached_tile *tile = tc->entries + pos; 386 if (tile->x >= 0) { 387 if (tc->depth_stencil) { 388 pipe_put_tile_raw(pt, 389 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 390 tile->data.depth32, 0/*STRIDE*/); 391 } 392 else { 393 pipe_put_tile_rgba(pt, 394 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 395 (float *) tile->data.color); 396 } 397 tile->x = tile->y = -1; /* mark as empty */ 398 inuse++; 399 } 400 } 401 402#if TILE_CLEAR_OPTIMIZATION 403 sp_tile_cache_flush_clear(&softpipe->pipe, tc); 404#endif 405 } 406 else if (tc->texture) { 407 /* caching a texture, mark all entries as empty */ 408 for (pos = 0; pos < NUM_ENTRIES; pos++) { 409 tc->entries[pos].x = -1; 410 } 411 tc->tex_face = -1; 412 } 413 414#if 0 415 debug_printf("flushed tiles in use: %d\n", inuse); 416#endif 417} 418 419 420/** 421 * Get a tile from the cache. 422 * \param x, y position of tile, in pixels 423 */ 424struct softpipe_cached_tile * 425sp_get_cached_tile(struct softpipe_context *softpipe, 426 struct softpipe_tile_cache *tc, int x, int y) 427{ 428 struct pipe_transfer *pt = tc->transfer; 429 430 /* tile pos in framebuffer: */ 431 const int tile_x = x & ~(TILE_SIZE - 1); 432 const int tile_y = y & ~(TILE_SIZE - 1); 433 434 /* cache pos/entry: */ 435 const int pos = CACHE_POS(x, y); 436 struct softpipe_cached_tile *tile = tc->entries + pos; 437 438 if (tile_x != tile->x || 439 tile_y != tile->y) { 440 441 if (tile->x != -1) { 442 /* put dirty tile back in framebuffer */ 443 if (tc->depth_stencil) { 444 pipe_put_tile_raw(pt, 445 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 446 tile->data.depth32, 0/*STRIDE*/); 447 } 448 else { 449 pipe_put_tile_rgba(pt, 450 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 451 (float *) tile->data.color); 452 } 453 } 454 455 tile->x = tile_x; 456 tile->y = tile_y; 457 458 if (is_clear_flag_set(tc->clear_flags, x, y)) { 459 /* don't get tile from framebuffer, just clear it */ 460 if (tc->depth_stencil) { 461 clear_tile(tile, pt->format, tc->clear_val); 462 } 463 else { 464 clear_tile_rgba(tile, pt->format, tc->clear_color); 465 } 466 clear_clear_flag(tc->clear_flags, x, y); 467 } 468 else { 469 /* get new tile data from transfer */ 470 if (tc->depth_stencil) { 471 pipe_get_tile_raw(pt, 472 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 473 tile->data.depth32, 0/*STRIDE*/); 474 } 475 else { 476 pipe_get_tile_rgba(pt, 477 tile->x, tile->y, TILE_SIZE, TILE_SIZE, 478 (float *) tile->data.color); 479 } 480 } 481 } 482 483 return tile; 484} 485 486 487/** 488 * Given the texture face, level, zslice, x and y values, compute 489 * the cache entry position/index where we'd hope to find the 490 * cached texture tile. 491 * This is basically a direct-map cache. 492 * XXX There's probably lots of ways in which we can improve this. 493 */ 494static INLINE uint 495tex_cache_pos(int x, int y, int z, int face, int level) 496{ 497 uint entry = x + y * 5 + z * 4 + face + level; 498 return entry % NUM_ENTRIES; 499} 500 501 502/** 503 * Similar to sp_get_cached_tile() but for textures. 504 * Tiles are read-only and indexed with more params. 505 */ 506const struct softpipe_cached_tile * 507sp_get_cached_tile_tex(struct softpipe_context *sp, 508 struct softpipe_tile_cache *tc, int x, int y, int z, 509 int face, int level) 510{ 511 struct pipe_screen *screen = sp->pipe.screen; 512 /* tile pos in framebuffer: */ 513 const int tile_x = x & ~(TILE_SIZE - 1); 514 const int tile_y = y & ~(TILE_SIZE - 1); 515 /* cache pos/entry: */ 516 const uint pos = tex_cache_pos(x / TILE_SIZE, y / TILE_SIZE, z, 517 face, level); 518 struct softpipe_cached_tile *tile = tc->entries + pos; 519 520 if (tc->texture) { 521 struct softpipe_texture *spt = softpipe_texture(tc->texture); 522 if (spt->modified) { 523 /* texture was modified, force a cache reload */ 524 tile->x = -1; 525 spt->modified = FALSE; 526 } 527 } 528 529 if (tile_x != tile->x || 530 tile_y != tile->y || 531 z != tile->z || 532 face != tile->face || 533 level != tile->level) { 534 /* cache miss */ 535 536 /* check if we need to get a new transfer */ 537 if (!tc->tex_trans || 538 tc->tex_face != face || 539 tc->tex_level != level || 540 tc->tex_z != z) { 541 /* get new transfer (view into texture) */ 542 543 if (tc->tex_trans_map) 544 tc->screen->transfer_unmap(tc->screen, tc->tex_trans); 545 screen->tex_transfer_release(screen, &tc->tex_trans); 546 547 tc->tex_trans = screen->get_tex_transfer(screen, tc->texture, face, level, z, 548 PIPE_TRANSFER_READ, 0, 0, 549 tc->texture->width[level], 550 tc->texture->height[level]); 551 tc->tex_trans_map = screen->transfer_map(screen, tc->tex_trans); 552 553 tc->tex_face = face; 554 tc->tex_level = level; 555 tc->tex_z = z; 556 } 557 558 /* get tile from the transfer (view into texture) */ 559 pipe_get_tile_rgba(tc->tex_trans, 560 tile_x, tile_y, TILE_SIZE, TILE_SIZE, 561 (float *) tile->data.color); 562 tile->x = tile_x; 563 tile->y = tile_y; 564 tile->z = z; 565 tile->face = face; 566 tile->level = level; 567 } 568 569 return tile; 570} 571 572 573/** 574 * When a whole surface is being cleared to a value we can avoid 575 * fetching tiles above. 576 * Save the color and set a 'clearflag' for each tile of the screen. 577 */ 578void 579sp_tile_cache_clear(struct softpipe_tile_cache *tc, uint clearValue) 580{ 581 uint r, g, b, a; 582 uint pos; 583 584 tc->clear_val = clearValue; 585 586 switch (tc->transfer->format) { 587 case PIPE_FORMAT_R8G8B8A8_UNORM: 588 r = (clearValue >> 24) & 0xff; 589 g = (clearValue >> 16) & 0xff; 590 b = (clearValue >> 8) & 0xff; 591 a = (clearValue ) & 0xff; 592 break; 593 case PIPE_FORMAT_A8R8G8B8_UNORM: 594 r = (clearValue >> 16) & 0xff; 595 g = (clearValue >> 8) & 0xff; 596 b = (clearValue ) & 0xff; 597 a = (clearValue >> 24) & 0xff; 598 break; 599 case PIPE_FORMAT_B8G8R8A8_UNORM: 600 r = (clearValue >> 8) & 0xff; 601 g = (clearValue >> 16) & 0xff; 602 b = (clearValue >> 24) & 0xff; 603 a = (clearValue ) & 0xff; 604 break; 605 default: 606 r = g = b = a = 0; 607 } 608 609 tc->clear_color[0] = r / 255.0f; 610 tc->clear_color[1] = g / 255.0f; 611 tc->clear_color[2] = b / 255.0f; 612 tc->clear_color[3] = a / 255.0f; 613 614#if TILE_CLEAR_OPTIMIZATION 615 /* set flags to indicate all the tiles are cleared */ 616 memset(tc->clear_flags, 255, sizeof(tc->clear_flags)); 617#else 618 /* disable the optimization */ 619 memset(tc->clear_flags, 0, sizeof(tc->clear_flags)); 620#endif 621 622 for (pos = 0; pos < NUM_ENTRIES; pos++) { 623 struct softpipe_cached_tile *tile = tc->entries + pos; 624 tile->x = tile->y = -1; 625 } 626} 627