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