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