Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5563 | serge | 1 | /************************************************************************** |
2 | * |
||
3 | * Copyright 2009 VMware, Inc. |
||
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 VMWARE 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 | #include "util/u_framebuffer.h" |
||
29 | #include "util/u_math.h" |
||
30 | #include "util/u_memory.h" |
||
31 | #include "util/u_inlines.h" |
||
32 | #include "util/u_simple_list.h" |
||
33 | #include "util/u_format.h" |
||
34 | #include "lp_scene.h" |
||
35 | #include "lp_fence.h" |
||
36 | #include "lp_debug.h" |
||
37 | |||
38 | |||
39 | #define RESOURCE_REF_SZ 32 |
||
40 | |||
41 | /** List of resource references */ |
||
42 | struct resource_ref { |
||
43 | struct pipe_resource *resource[RESOURCE_REF_SZ]; |
||
44 | int count; |
||
45 | struct resource_ref *next; |
||
46 | }; |
||
47 | |||
48 | |||
49 | /** |
||
50 | * Create a new scene object. |
||
51 | * \param queue the queue to put newly rendered/emptied scenes into |
||
52 | */ |
||
53 | struct lp_scene * |
||
54 | lp_scene_create( struct pipe_context *pipe ) |
||
55 | { |
||
56 | struct lp_scene *scene = CALLOC_STRUCT(lp_scene); |
||
57 | if (!scene) |
||
58 | return NULL; |
||
59 | |||
60 | scene->pipe = pipe; |
||
61 | |||
62 | scene->data.head = |
||
63 | CALLOC_STRUCT(data_block); |
||
64 | |||
65 | pipe_mutex_init(scene->mutex); |
||
66 | |||
67 | #ifdef DEBUG |
||
68 | /* Do some scene limit sanity checks here */ |
||
69 | { |
||
70 | size_t maxBins = TILES_X * TILES_Y; |
||
71 | size_t maxCommandBytes = sizeof(struct cmd_block) * maxBins; |
||
72 | size_t maxCommandPlusData = maxCommandBytes + DATA_BLOCK_SIZE; |
||
73 | /* We'll need at least one command block per bin. Make sure that's |
||
74 | * less than the max allowed scene size. |
||
75 | */ |
||
76 | assert(maxCommandBytes < LP_SCENE_MAX_SIZE); |
||
77 | /* We'll also need space for at least one other data block */ |
||
78 | assert(maxCommandPlusData <= LP_SCENE_MAX_SIZE); |
||
79 | } |
||
80 | #endif |
||
81 | |||
82 | return scene; |
||
83 | } |
||
84 | |||
85 | |||
86 | /** |
||
87 | * Free all data associated with the given scene, and the scene itself. |
||
88 | */ |
||
89 | void |
||
90 | lp_scene_destroy(struct lp_scene *scene) |
||
91 | { |
||
92 | lp_fence_reference(&scene->fence, NULL); |
||
93 | pipe_mutex_destroy(scene->mutex); |
||
94 | assert(scene->data.head->next == NULL); |
||
95 | FREE(scene->data.head); |
||
96 | FREE(scene); |
||
97 | } |
||
98 | |||
99 | |||
100 | /** |
||
101 | * Check if the scene's bins are all empty. |
||
102 | * For debugging purposes. |
||
103 | */ |
||
104 | boolean |
||
105 | lp_scene_is_empty(struct lp_scene *scene ) |
||
106 | { |
||
107 | unsigned x, y; |
||
108 | |||
109 | for (y = 0; y < TILES_Y; y++) { |
||
110 | for (x = 0; x < TILES_X; x++) { |
||
111 | const struct cmd_bin *bin = lp_scene_get_bin(scene, x, y); |
||
112 | if (bin->head) { |
||
113 | return FALSE; |
||
114 | } |
||
115 | } |
||
116 | } |
||
117 | return TRUE; |
||
118 | } |
||
119 | |||
120 | |||
121 | /* Returns true if there has ever been a failed allocation attempt in |
||
122 | * this scene. Used in triangle emit to avoid having to check success |
||
123 | * at each bin. |
||
124 | */ |
||
125 | boolean |
||
126 | lp_scene_is_oom(struct lp_scene *scene) |
||
127 | { |
||
128 | return scene->alloc_failed; |
||
129 | } |
||
130 | |||
131 | |||
132 | /* Remove all commands from a bin. Tries to reuse some of the memory |
||
133 | * allocated to the bin, however. |
||
134 | */ |
||
135 | void |
||
136 | lp_scene_bin_reset(struct lp_scene *scene, unsigned x, unsigned y) |
||
137 | { |
||
138 | struct cmd_bin *bin = lp_scene_get_bin(scene, x, y); |
||
139 | |||
140 | bin->last_state = NULL; |
||
141 | bin->head = bin->tail; |
||
142 | if (bin->tail) { |
||
143 | bin->tail->next = NULL; |
||
144 | bin->tail->count = 0; |
||
145 | } |
||
146 | } |
||
147 | |||
148 | |||
149 | void |
||
150 | lp_scene_begin_rasterization(struct lp_scene *scene) |
||
151 | { |
||
152 | const struct pipe_framebuffer_state *fb = &scene->fb; |
||
153 | int i; |
||
154 | unsigned max_layer = ~0; |
||
155 | |||
156 | //LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__); |
||
157 | |||
158 | for (i = 0; i < scene->fb.nr_cbufs; i++) { |
||
159 | struct pipe_surface *cbuf = scene->fb.cbufs[i]; |
||
160 | if (llvmpipe_resource_is_texture(cbuf->texture)) { |
||
161 | scene->cbufs[i].stride = llvmpipe_resource_stride(cbuf->texture, |
||
162 | cbuf->u.tex.level); |
||
163 | scene->cbufs[i].layer_stride = llvmpipe_layer_stride(cbuf->texture, |
||
164 | cbuf->u.tex.level); |
||
165 | max_layer = MIN2(max_layer, cbuf->u.tex.last_layer - cbuf->u.tex.first_layer); |
||
166 | |||
167 | scene->cbufs[i].map = llvmpipe_resource_map(cbuf->texture, |
||
168 | cbuf->u.tex.level, |
||
169 | cbuf->u.tex.first_layer, |
||
170 | LP_TEX_USAGE_READ_WRITE); |
||
171 | } |
||
172 | else { |
||
173 | struct llvmpipe_resource *lpr = llvmpipe_resource(cbuf->texture); |
||
174 | unsigned pixstride = util_format_get_blocksize(cbuf->format); |
||
175 | scene->cbufs[i].stride = cbuf->texture->width0; |
||
176 | max_layer = 0; |
||
177 | |||
178 | scene->cbufs[i].map = lpr->data; |
||
179 | scene->cbufs[i].map += cbuf->u.buf.first_element * pixstride; |
||
180 | } |
||
181 | } |
||
182 | |||
183 | if (fb->zsbuf) { |
||
184 | struct pipe_surface *zsbuf = scene->fb.zsbuf; |
||
185 | scene->zsbuf.stride = llvmpipe_resource_stride(zsbuf->texture, zsbuf->u.tex.level); |
||
186 | scene->zsbuf.layer_stride = llvmpipe_layer_stride(zsbuf->texture, zsbuf->u.tex.level); |
||
187 | max_layer = MIN2(max_layer, zsbuf->u.tex.last_layer - zsbuf->u.tex.first_layer); |
||
188 | |||
189 | scene->zsbuf.map = llvmpipe_resource_map(zsbuf->texture, |
||
190 | zsbuf->u.tex.level, |
||
191 | zsbuf->u.tex.first_layer, |
||
192 | LP_TEX_USAGE_READ_WRITE); |
||
193 | } |
||
194 | |||
195 | scene->fb_max_layer = max_layer; |
||
196 | } |
||
197 | |||
198 | |||
199 | |||
200 | |||
201 | /** |
||
202 | * Free all the temporary data in a scene. |
||
203 | */ |
||
204 | void |
||
205 | lp_scene_end_rasterization(struct lp_scene *scene ) |
||
206 | { |
||
207 | int i, j; |
||
208 | |||
209 | /* Unmap color buffers */ |
||
210 | for (i = 0; i < scene->fb.nr_cbufs; i++) { |
||
211 | if (scene->cbufs[i].map) { |
||
212 | struct pipe_surface *cbuf = scene->fb.cbufs[i]; |
||
213 | if (llvmpipe_resource_is_texture(cbuf->texture)) { |
||
214 | llvmpipe_resource_unmap(cbuf->texture, |
||
215 | cbuf->u.tex.level, |
||
216 | cbuf->u.tex.first_layer); |
||
217 | } |
||
218 | scene->cbufs[i].map = NULL; |
||
219 | } |
||
220 | } |
||
221 | |||
222 | /* Unmap z/stencil buffer */ |
||
223 | if (scene->zsbuf.map) { |
||
224 | struct pipe_surface *zsbuf = scene->fb.zsbuf; |
||
225 | llvmpipe_resource_unmap(zsbuf->texture, |
||
226 | zsbuf->u.tex.level, |
||
227 | zsbuf->u.tex.first_layer); |
||
228 | scene->zsbuf.map = NULL; |
||
229 | } |
||
230 | |||
231 | /* Reset all command lists: |
||
232 | */ |
||
233 | for (i = 0; i < scene->tiles_x; i++) { |
||
234 | for (j = 0; j < scene->tiles_y; j++) { |
||
235 | struct cmd_bin *bin = lp_scene_get_bin(scene, i, j); |
||
236 | bin->head = NULL; |
||
237 | bin->tail = NULL; |
||
238 | bin->last_state = NULL; |
||
239 | } |
||
240 | } |
||
241 | |||
242 | /* If there are any bins which weren't cleared by the loop above, |
||
243 | * they will be caught (on debug builds at least) by this assert: |
||
244 | */ |
||
245 | assert(lp_scene_is_empty(scene)); |
||
246 | |||
247 | /* Decrement texture ref counts |
||
248 | */ |
||
249 | { |
||
250 | struct resource_ref *ref; |
||
251 | int i, j = 0; |
||
252 | |||
253 | for (ref = scene->resources; ref; ref = ref->next) { |
||
254 | for (i = 0; i < ref->count; i++) { |
||
255 | if (LP_DEBUG & DEBUG_SETUP) |
||
256 | debug_printf("resource %d: %p %dx%d sz %d\n", |
||
257 | j, |
||
258 | (void *) ref->resource[i], |
||
259 | ref->resource[i]->width0, |
||
260 | ref->resource[i]->height0, |
||
261 | llvmpipe_resource_size(ref->resource[i])); |
||
262 | j++; |
||
263 | pipe_resource_reference(&ref->resource[i], NULL); |
||
264 | } |
||
265 | } |
||
266 | |||
267 | if (LP_DEBUG & DEBUG_SETUP) |
||
268 | debug_printf("scene %d resources, sz %d\n", |
||
269 | j, scene->resource_reference_size); |
||
270 | } |
||
271 | |||
272 | /* Free all scene data blocks: |
||
273 | */ |
||
274 | { |
||
275 | struct data_block_list *list = &scene->data; |
||
276 | struct data_block *block, *tmp; |
||
277 | |||
278 | for (block = list->head->next; block; block = tmp) { |
||
279 | tmp = block->next; |
||
280 | FREE(block); |
||
281 | } |
||
282 | |||
283 | list->head->next = NULL; |
||
284 | list->head->used = 0; |
||
285 | } |
||
286 | |||
287 | lp_fence_reference(&scene->fence, NULL); |
||
288 | |||
289 | scene->resources = NULL; |
||
290 | scene->scene_size = 0; |
||
291 | scene->resource_reference_size = 0; |
||
292 | |||
293 | scene->has_depthstencil_clear = FALSE; |
||
294 | scene->alloc_failed = FALSE; |
||
295 | |||
296 | util_unreference_framebuffer_state( &scene->fb ); |
||
297 | } |
||
298 | |||
299 | |||
300 | |||
301 | |||
302 | |||
303 | |||
304 | struct cmd_block * |
||
305 | lp_scene_new_cmd_block( struct lp_scene *scene, |
||
306 | struct cmd_bin *bin ) |
||
307 | { |
||
308 | struct cmd_block *block = lp_scene_alloc(scene, sizeof(struct cmd_block)); |
||
309 | if (block) { |
||
310 | if (bin->tail) { |
||
311 | bin->tail->next = block; |
||
312 | bin->tail = block; |
||
313 | } |
||
314 | else { |
||
315 | bin->head = block; |
||
316 | bin->tail = block; |
||
317 | } |
||
318 | //memset(block, 0, sizeof *block); |
||
319 | block->next = NULL; |
||
320 | block->count = 0; |
||
321 | } |
||
322 | return block; |
||
323 | } |
||
324 | |||
325 | |||
326 | struct data_block * |
||
327 | lp_scene_new_data_block( struct lp_scene *scene ) |
||
328 | { |
||
329 | if (scene->scene_size + DATA_BLOCK_SIZE > LP_SCENE_MAX_SIZE) { |
||
330 | if (0) debug_printf("%s: failed\n", __FUNCTION__); |
||
331 | scene->alloc_failed = TRUE; |
||
332 | return NULL; |
||
333 | } |
||
334 | else { |
||
335 | struct data_block *block = MALLOC_STRUCT(data_block); |
||
336 | if (block == NULL) |
||
337 | return NULL; |
||
338 | |||
339 | scene->scene_size += sizeof *block; |
||
340 | |||
341 | block->used = 0; |
||
342 | block->next = scene->data.head; |
||
343 | scene->data.head = block; |
||
344 | |||
345 | return block; |
||
346 | } |
||
347 | } |
||
348 | |||
349 | |||
350 | /** |
||
351 | * Return number of bytes used for all bin data within a scene. |
||
352 | * This does not include resources (textures) referenced by the scene. |
||
353 | */ |
||
354 | static unsigned |
||
355 | lp_scene_data_size( const struct lp_scene *scene ) |
||
356 | { |
||
357 | unsigned size = 0; |
||
358 | const struct data_block *block; |
||
359 | for (block = scene->data.head; block; block = block->next) { |
||
360 | size += block->used; |
||
361 | } |
||
362 | return size; |
||
363 | } |
||
364 | |||
365 | |||
366 | |||
367 | /** |
||
368 | * Add a reference to a resource by the scene. |
||
369 | */ |
||
370 | boolean |
||
371 | lp_scene_add_resource_reference(struct lp_scene *scene, |
||
372 | struct pipe_resource *resource, |
||
373 | boolean initializing_scene) |
||
374 | { |
||
375 | struct resource_ref *ref, **last = &scene->resources; |
||
376 | int i; |
||
377 | |||
378 | /* Look at existing resource blocks: |
||
379 | */ |
||
380 | for (ref = scene->resources; ref; ref = ref->next) { |
||
381 | last = &ref->next; |
||
382 | |||
383 | /* Search for this resource: |
||
384 | */ |
||
385 | for (i = 0; i < ref->count; i++) |
||
386 | if (ref->resource[i] == resource) |
||
387 | return TRUE; |
||
388 | |||
389 | if (ref->count < RESOURCE_REF_SZ) { |
||
390 | /* If the block is half-empty, then append the reference here. |
||
391 | */ |
||
392 | break; |
||
393 | } |
||
394 | } |
||
395 | |||
396 | /* Create a new block if no half-empty block was found. |
||
397 | */ |
||
398 | if (!ref) { |
||
399 | assert(*last == NULL); |
||
400 | *last = lp_scene_alloc(scene, sizeof *ref); |
||
401 | if (*last == NULL) |
||
402 | return FALSE; |
||
403 | |||
404 | ref = *last; |
||
405 | memset(ref, 0, sizeof *ref); |
||
406 | } |
||
407 | |||
408 | /* Append the reference to the reference block. |
||
409 | */ |
||
410 | pipe_resource_reference(&ref->resource[ref->count++], resource); |
||
411 | scene->resource_reference_size += llvmpipe_resource_size(resource); |
||
412 | |||
413 | /* Heuristic to advise scene flushes. This isn't helpful in the |
||
414 | * initial setup of the scene, but after that point flush on the |
||
415 | * next resource added which exceeds 64MB in referenced texture |
||
416 | * data. |
||
417 | */ |
||
418 | if (!initializing_scene && |
||
419 | scene->resource_reference_size >= LP_SCENE_MAX_RESOURCE_SIZE) |
||
420 | return FALSE; |
||
421 | |||
422 | return TRUE; |
||
423 | } |
||
424 | |||
425 | |||
426 | /** |
||
427 | * Does this scene have a reference to the given resource? |
||
428 | */ |
||
429 | boolean |
||
430 | lp_scene_is_resource_referenced(const struct lp_scene *scene, |
||
431 | const struct pipe_resource *resource) |
||
432 | { |
||
433 | const struct resource_ref *ref; |
||
434 | int i; |
||
435 | |||
436 | for (ref = scene->resources; ref; ref = ref->next) { |
||
437 | for (i = 0; i < ref->count; i++) |
||
438 | if (ref->resource[i] == resource) |
||
439 | return TRUE; |
||
440 | } |
||
441 | |||
442 | return FALSE; |
||
443 | } |
||
444 | |||
445 | |||
446 | |||
447 | |||
448 | /** advance curr_x,y to the next bin */ |
||
449 | static boolean |
||
450 | next_bin(struct lp_scene *scene) |
||
451 | { |
||
452 | scene->curr_x++; |
||
453 | if (scene->curr_x >= scene->tiles_x) { |
||
454 | scene->curr_x = 0; |
||
455 | scene->curr_y++; |
||
456 | } |
||
457 | if (scene->curr_y >= scene->tiles_y) { |
||
458 | /* no more bins */ |
||
459 | return FALSE; |
||
460 | } |
||
461 | return TRUE; |
||
462 | } |
||
463 | |||
464 | |||
465 | void |
||
466 | lp_scene_bin_iter_begin( struct lp_scene *scene ) |
||
467 | { |
||
468 | scene->curr_x = scene->curr_y = -1; |
||
469 | } |
||
470 | |||
471 | |||
472 | /** |
||
473 | * Return pointer to next bin to be rendered. |
||
474 | * The lp_scene::curr_x and ::curr_y fields will be advanced. |
||
475 | * Multiple rendering threads will call this function to get a chunk |
||
476 | * of work (a bin) to work on. |
||
477 | */ |
||
478 | struct cmd_bin * |
||
479 | lp_scene_bin_iter_next( struct lp_scene *scene , int *x, int *y) |
||
480 | { |
||
481 | struct cmd_bin *bin = NULL; |
||
482 | |||
483 | pipe_mutex_lock(scene->mutex); |
||
484 | |||
485 | if (scene->curr_x < 0) { |
||
486 | /* first bin */ |
||
487 | scene->curr_x = 0; |
||
488 | scene->curr_y = 0; |
||
489 | } |
||
490 | else if (!next_bin(scene)) { |
||
491 | /* no more bins left */ |
||
492 | goto end; |
||
493 | } |
||
494 | |||
495 | bin = lp_scene_get_bin(scene, scene->curr_x, scene->curr_y); |
||
496 | *x = scene->curr_x; |
||
497 | *y = scene->curr_y; |
||
498 | |||
499 | end: |
||
500 | /*printf("return bin %p at %d, %d\n", (void *) bin, *bin_x, *bin_y);*/ |
||
501 | pipe_mutex_unlock(scene->mutex); |
||
502 | return bin; |
||
503 | } |
||
504 | |||
505 | |||
506 | void lp_scene_begin_binning( struct lp_scene *scene, |
||
507 | struct pipe_framebuffer_state *fb, boolean discard ) |
||
508 | { |
||
509 | assert(lp_scene_is_empty(scene)); |
||
510 | |||
511 | scene->discard = discard; |
||
512 | util_copy_framebuffer_state(&scene->fb, fb); |
||
513 | |||
514 | scene->tiles_x = align(fb->width, TILE_SIZE) / TILE_SIZE; |
||
515 | scene->tiles_y = align(fb->height, TILE_SIZE) / TILE_SIZE; |
||
516 | |||
517 | assert(scene->tiles_x <= TILES_X); |
||
518 | assert(scene->tiles_y <= TILES_Y); |
||
519 | } |
||
520 | |||
521 | |||
522 | void lp_scene_end_binning( struct lp_scene *scene ) |
||
523 | { |
||
524 | if (LP_DEBUG & DEBUG_SCENE) { |
||
525 | debug_printf("rasterize scene:\n"); |
||
526 | debug_printf(" scene_size: %u\n", |
||
527 | scene->scene_size); |
||
528 | debug_printf(" data size: %u\n", |
||
529 | lp_scene_data_size(scene)); |
||
530 | |||
531 | if (0) |
||
532 | lp_debug_bins( scene ); |
||
533 | } |
||
534 | }=>=>>>>>>>>>>>>=>> |