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