lp_scene.c revision 0639765b2850739af1678f10fc0c5706d5827776
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_math.h"
29#include "util/u_memory.h"
30#include "util/u_inlines.h"
31#include "util/u_simple_list.h"
32#include "util/u_surface.h"
33#include "lp_scene.h"
34#include "lp_scene_queue.h"
35#include "lp_debug.h"
36
37
38struct lp_scene *
39lp_scene_create( struct pipe_context *pipe,
40                 struct lp_scene_queue *queue )
41{
42   unsigned i, j;
43   struct lp_scene *scene = CALLOC_STRUCT(lp_scene);
44   if (!scene)
45      return NULL;
46
47   scene->pipe = pipe;
48   scene->empty_queue = queue;
49
50   for (i = 0; i < TILES_X; i++) {
51      for (j = 0; j < TILES_Y; j++) {
52         struct cmd_bin *bin = lp_scene_get_bin(scene, i, j);
53         bin->commands.head = bin->commands.tail = CALLOC_STRUCT(cmd_block);
54      }
55   }
56
57   scene->data.head =
58      scene->data.tail = CALLOC_STRUCT(data_block);
59
60   make_empty_list(&scene->textures);
61
62   pipe_mutex_init(scene->mutex);
63
64   return scene;
65}
66
67
68/**
69 * Free all data associated with the given scene, and free(scene).
70 */
71void
72lp_scene_destroy(struct lp_scene *scene)
73{
74   unsigned i, j;
75
76   lp_scene_reset(scene);
77
78   for (i = 0; i < TILES_X; i++)
79      for (j = 0; j < TILES_Y; j++) {
80         struct cmd_bin *bin = lp_scene_get_bin(scene, i, j);
81         assert(bin->commands.head == bin->commands.tail);
82         FREE(bin->commands.head);
83         bin->commands.head = NULL;
84         bin->commands.tail = NULL;
85      }
86
87   FREE(scene->data.head);
88   scene->data.head = NULL;
89
90   pipe_mutex_destroy(scene->mutex);
91
92   FREE(scene);
93}
94
95
96/**
97 * Check if the scene's bins are all empty.
98 * For debugging purposes.
99 */
100boolean
101lp_scene_is_empty(struct lp_scene *scene )
102{
103   unsigned x, y;
104
105   for (y = 0; y < TILES_Y; y++) {
106      for (x = 0; x < TILES_X; x++) {
107         const struct cmd_bin *bin = lp_scene_get_bin(scene, x, y);
108         const struct cmd_block_list *list = &bin->commands;
109         if (list->head != list->tail || list->head->count > 0) {
110            return FALSE;
111         }
112      }
113   }
114   return TRUE;
115}
116
117
118/* Free data for one particular bin.  May be called from the
119 * rasterizer thread(s).
120 */
121void
122lp_scene_bin_reset(struct lp_scene *scene, unsigned x, unsigned y)
123{
124   struct cmd_bin *bin = lp_scene_get_bin(scene, x, y);
125   struct cmd_block_list *list = &bin->commands;
126   struct cmd_block *block;
127   struct cmd_block *tmp;
128
129   assert(x < TILES_X);
130   assert(y < TILES_Y);
131
132   for (block = list->head; block != list->tail; block = tmp) {
133      tmp = block->next;
134      FREE(block);
135   }
136
137   assert(list->tail->next == NULL);
138   list->head = list->tail;
139   list->head->count = 0;
140}
141
142
143/**
144 * Free all the temporary data in a scene.  May be called from the
145 * rasterizer thread(s).
146 */
147void
148lp_scene_reset(struct lp_scene *scene )
149{
150   unsigned i, j;
151
152   /* Free all but last binner command lists:
153    */
154   for (i = 0; i < scene->tiles_x; i++) {
155      for (j = 0; j < scene->tiles_y; j++) {
156         lp_scene_bin_reset(scene, i, j);
157      }
158   }
159
160   assert(lp_scene_is_empty(scene));
161
162   /* Free all but last binned data block:
163    */
164   {
165      struct data_block_list *list = &scene->data;
166      struct data_block *block, *tmp;
167
168      for (block = list->head; block != list->tail; block = tmp) {
169         tmp = block->next;
170         FREE(block);
171      }
172
173      assert(list->tail->next == NULL);
174      list->head = list->tail;
175      list->head->used = 0;
176   }
177
178   /* Release texture refs
179    */
180   {
181      struct texture_ref *ref, *next, *ref_list = &scene->textures;
182      for (ref = ref_list->next; ref != ref_list; ref = next) {
183         next = next_elem(ref);
184         pipe_resource_reference(&ref->texture, NULL);
185         FREE(ref);
186      }
187      make_empty_list(ref_list);
188   }
189
190   scene->has_color_clear = FALSE;
191   scene->has_depth_clear = FALSE;
192}
193
194
195
196
197
198
199void
200lp_bin_new_cmd_block( struct cmd_block_list *list )
201{
202   struct cmd_block *block = MALLOC_STRUCT(cmd_block);
203   list->tail->next = block;
204   list->tail = block;
205   block->next = NULL;
206   block->count = 0;
207}
208
209
210void
211lp_bin_new_data_block( struct data_block_list *list )
212{
213   struct data_block *block = MALLOC_STRUCT(data_block);
214   list->tail->next = block;
215   list->tail = block;
216   block->next = NULL;
217   block->used = 0;
218}
219
220
221/** Return number of bytes used for all bin data within a scene */
222unsigned
223lp_scene_data_size( const struct lp_scene *scene )
224{
225   unsigned size = 0;
226   const struct data_block *block;
227   for (block = scene->data.head; block; block = block->next) {
228      size += block->used;
229   }
230   return size;
231}
232
233
234/** Return number of bytes used for a single bin */
235unsigned
236lp_scene_bin_size( const struct lp_scene *scene, unsigned x, unsigned y )
237{
238   struct cmd_bin *bin = lp_scene_get_bin((struct lp_scene *) scene, x, y);
239   const struct cmd_block *cmd;
240   unsigned size = 0;
241   for (cmd = bin->commands.head; cmd; cmd = cmd->next) {
242      size += (cmd->count *
243               (sizeof(lp_rast_cmd) + sizeof(union lp_rast_cmd_arg)));
244   }
245   return size;
246}
247
248
249/**
250 * Add a reference to a texture by the scene.
251 */
252void
253lp_scene_texture_reference( struct lp_scene *scene,
254                            struct pipe_resource *texture )
255{
256   struct texture_ref *ref = CALLOC_STRUCT(texture_ref);
257   if (ref) {
258      struct texture_ref *ref_list = &scene->textures;
259      pipe_resource_reference(&ref->texture, texture);
260      insert_at_tail(ref_list, ref);
261   }
262}
263
264
265/**
266 * Does this scene have a reference to the given texture?
267 */
268boolean
269lp_scene_is_resource_referenced( const struct lp_scene *scene,
270                                const struct pipe_resource *texture )
271{
272   const struct texture_ref *ref_list = &scene->textures;
273   const struct texture_ref *ref;
274   foreach (ref, ref_list) {
275      if (ref->texture == texture)
276         return TRUE;
277   }
278   return FALSE;
279}
280
281
282/**
283 * Return last command in the bin
284 */
285static lp_rast_cmd
286lp_get_last_command( const struct cmd_bin *bin )
287{
288   const struct cmd_block *tail = bin->commands.tail;
289   const unsigned i = tail->count;
290   if (i > 0)
291      return tail->cmd[i - 1];
292   else
293      return NULL;
294}
295
296
297/**
298 * Replace the arg of the last command in the bin.
299 */
300static void
301lp_replace_last_command_arg( struct cmd_bin *bin,
302                             const union lp_rast_cmd_arg arg )
303{
304   struct cmd_block *tail = bin->commands.tail;
305   const unsigned i = tail->count;
306   assert(i > 0);
307   tail->arg[i - 1] = arg;
308}
309
310
311
312/**
313 * Put a state-change command into all bins.
314 * If we find that the last command in a bin was also a state-change
315 * command, we can simply replace that one with the new one.
316 */
317void
318lp_scene_bin_state_command( struct lp_scene *scene,
319                            lp_rast_cmd cmd,
320                            const union lp_rast_cmd_arg arg )
321{
322   unsigned i, j;
323   for (i = 0; i < scene->tiles_x; i++) {
324      for (j = 0; j < scene->tiles_y; j++) {
325         struct cmd_bin *bin = lp_scene_get_bin(scene, i, j);
326         lp_rast_cmd last_cmd = lp_get_last_command(bin);
327         if (last_cmd == cmd) {
328            lp_replace_last_command_arg(bin, arg);
329         }
330         else {
331            lp_scene_bin_command( scene, i, j, cmd, arg );
332         }
333      }
334   }
335}
336
337
338/** advance curr_x,y to the next bin */
339static boolean
340next_bin(struct lp_scene *scene)
341{
342   scene->curr_x++;
343   if (scene->curr_x >= scene->tiles_x) {
344      scene->curr_x = 0;
345      scene->curr_y++;
346   }
347   if (scene->curr_y >= scene->tiles_y) {
348      /* no more bins */
349      return FALSE;
350   }
351   return TRUE;
352}
353
354
355void
356lp_scene_bin_iter_begin( struct lp_scene *scene )
357{
358   scene->curr_x = scene->curr_y = -1;
359}
360
361
362/**
363 * Return pointer to next bin to be rendered.
364 * The lp_scene::curr_x and ::curr_y fields will be advanced.
365 * Multiple rendering threads will call this function to get a chunk
366 * of work (a bin) to work on.
367 */
368struct cmd_bin *
369lp_scene_bin_iter_next( struct lp_scene *scene, int *bin_x, int *bin_y )
370{
371   struct cmd_bin *bin = NULL;
372
373   pipe_mutex_lock(scene->mutex);
374
375   if (scene->curr_x < 0) {
376      /* first bin */
377      scene->curr_x = 0;
378      scene->curr_y = 0;
379   }
380   else if (!next_bin(scene)) {
381      /* no more bins left */
382      goto end;
383   }
384
385   bin = lp_scene_get_bin(scene, scene->curr_x, scene->curr_y);
386   *bin_x = scene->curr_x;
387   *bin_y = scene->curr_y;
388
389end:
390   /*printf("return bin %p at %d, %d\n", (void *) bin, *bin_x, *bin_y);*/
391   pipe_mutex_unlock(scene->mutex);
392   return bin;
393}
394
395
396
397/**
398 * Prepare this scene for the rasterizer.
399 * Map the framebuffer surfaces.  Initialize the 'rast' state.
400 */
401static boolean
402lp_scene_map_buffers( struct lp_scene *scene )
403{
404   LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
405
406   /* XXX framebuffer surfaces are no longer mapped here */
407   /* XXX move all map/unmap stuff into rast module... */
408
409   return TRUE;
410}
411
412
413
414/**
415 * Called after rasterizer as finished rasterizing a scene.
416 *
417 * We want to call this from the pipe_context's current thread to
418 * avoid having to have mutexes on the transfer functions.
419 */
420static void
421lp_scene_unmap_buffers( struct lp_scene *scene )
422{
423#if 0
424   unsigned i;
425
426   for (i = 0; i < scene->fb.nr_cbufs; i++) {
427      if (scene->cbuf_map[i]) {
428         struct pipe_surface *cbuf = scene->fb.cbufs[i];
429         llvmpipe_resource_unmap(cbuf->texture,
430                                cbuf->face,
431                                cbuf->level,
432                                cbuf->zslice);
433         scene->cbuf_map[i] = NULL;
434      }
435   }
436
437   if (scene->zsbuf_map) {
438      struct pipe_surface *zsbuf = scene->fb.zsbuf;
439      llvmpipe_resource_unmap(zsbuf->texture,
440                             zsbuf->face,
441                             zsbuf->level,
442                             zsbuf->zslice);
443      scene->zsbuf_map = NULL;
444   }
445#endif
446
447   util_unreference_framebuffer_state( &scene->fb );
448}
449
450
451void lp_scene_begin_binning( struct lp_scene *scene,
452                             struct pipe_framebuffer_state *fb )
453{
454   assert(lp_scene_is_empty(scene));
455
456   util_copy_framebuffer_state(&scene->fb, fb);
457
458   scene->tiles_x = align(fb->width, TILE_SIZE) / TILE_SIZE;
459   scene->tiles_y = align(fb->height, TILE_SIZE) / TILE_SIZE;
460
461   assert(scene->tiles_x <= TILES_X);
462   assert(scene->tiles_y <= TILES_Y);
463}
464
465
466void lp_scene_rasterize( struct lp_scene *scene,
467                         struct lp_rasterizer *rast,
468                         boolean write_depth )
469{
470   if (0) {
471      unsigned x, y;
472      debug_printf("rasterize scene:\n");
473      debug_printf("  data size: %u\n", lp_scene_data_size(scene));
474      for (y = 0; y < scene->tiles_y; y++) {
475         for (x = 0; x < scene->tiles_x; x++) {
476            debug_printf("  bin %u, %u size: %u\n", x, y,
477                         lp_scene_bin_size(scene, x, y));
478         }
479      }
480   }
481
482   scene->write_depth = (scene->fb.zsbuf != NULL &&
483                         write_depth);
484
485   lp_scene_map_buffers( scene );
486
487   /* Enqueue the scene for rasterization, then immediately wait for
488    * it to finish.
489    */
490   lp_rast_queue_scene( rast, scene );
491
492   /* Currently just wait for the rasterizer to finish.  Some
493    * threading interactions need to be worked out, particularly once
494    * transfers become per-context:
495    */
496   lp_rast_finish( rast );
497   lp_scene_unmap_buffers( scene );
498   lp_scene_enqueue( scene->empty_queue, scene );
499}
500