#include "util/u_memory.h"
#include "util/u_inlines.h"
#include "util/u_simple_list.h"
+#include "util/u_format.h"
#include "lp_scene.h"
-#include "lp_scene_queue.h"
#include "lp_fence.h"
+#include "lp_debug.h"
-/** List of texture references */
-struct texture_ref {
- struct pipe_resource *texture;
- struct texture_ref *prev, *next; /**< linked list w/ u_simple_list.h */
-};
+#define RESOURCE_REF_SZ 32
+/** List of resource references */
+struct resource_ref {
+ struct pipe_resource *resource[RESOURCE_REF_SZ];
+ int count;
+ struct resource_ref *next;
+};
/**
* \param queue the queue to put newly rendered/emptied scenes into
*/
struct lp_scene *
-lp_scene_create( struct pipe_context *pipe,
- struct lp_scene_queue *queue )
+lp_scene_create( struct pipe_context *pipe )
{
- unsigned i, j;
struct lp_scene *scene = CALLOC_STRUCT(lp_scene);
if (!scene)
return NULL;
scene->pipe = pipe;
- scene->empty_queue = queue;
-
- for (i = 0; i < TILES_X; i++) {
- for (j = 0; j < TILES_Y; j++) {
- struct cmd_bin *bin = lp_scene_get_bin(scene, i, j);
- bin->commands.head = bin->commands.tail = CALLOC_STRUCT(cmd_block);
- }
- }
scene->data.head =
- scene->data.tail = CALLOC_STRUCT(data_block);
-
- make_empty_list(&scene->resources);
+ CALLOC_STRUCT(data_block);
pipe_mutex_init(scene->mutex);
+#ifdef DEBUG
+ /* Do some scene limit sanity checks here */
+ {
+ size_t maxBins = TILES_X * TILES_Y;
+ size_t maxCommandBytes = sizeof(struct cmd_block) * maxBins;
+ size_t maxCommandPlusData = maxCommandBytes + DATA_BLOCK_SIZE;
+ /* We'll need at least one command block per bin. Make sure that's
+ * less than the max allowed scene size.
+ */
+ assert(maxCommandBytes < LP_SCENE_MAX_SIZE);
+ /* We'll also need space for at least one other data block */
+ assert(maxCommandPlusData <= LP_SCENE_MAX_SIZE);
+ }
+#endif
+
return scene;
}
void
lp_scene_destroy(struct lp_scene *scene)
{
- unsigned i, j;
-
- lp_scene_reset(scene);
-
- for (i = 0; i < TILES_X; i++)
- for (j = 0; j < TILES_Y; j++) {
- struct cmd_bin *bin = lp_scene_get_bin(scene, i, j);
- assert(bin->commands.head == bin->commands.tail);
- FREE(bin->commands.head);
- bin->commands.head = NULL;
- bin->commands.tail = NULL;
- }
-
- FREE(scene->data.head);
- scene->data.head = NULL;
-
+ lp_fence_reference(&scene->fence, NULL);
pipe_mutex_destroy(scene->mutex);
-
+ assert(scene->data.head->next == NULL);
+ FREE(scene->data.head);
FREE(scene);
}
for (y = 0; y < TILES_Y; y++) {
for (x = 0; x < TILES_X; x++) {
const struct cmd_bin *bin = lp_scene_get_bin(scene, x, y);
- const struct cmd_block_list *list = &bin->commands;
- if (list->head != list->tail || list->head->count > 0) {
+ if (bin->head) {
return FALSE;
}
}
}
-/* Free data for one particular bin. May be called from the
- * rasterizer thread(s).
+/* Returns true if there has ever been a failed allocation attempt in
+ * this scene. Used in triangle emit to avoid having to check success
+ * at each bin.
+ */
+boolean
+lp_scene_is_oom(struct lp_scene *scene)
+{
+ return scene->alloc_failed;
+}
+
+
+/* Remove all commands from a bin. Tries to reuse some of the memory
+ * allocated to the bin, however.
*/
void
lp_scene_bin_reset(struct lp_scene *scene, unsigned x, unsigned y)
{
struct cmd_bin *bin = lp_scene_get_bin(scene, x, y);
- struct cmd_block_list *list = &bin->commands;
- struct cmd_block *block;
- struct cmd_block *tmp;
- assert(x < TILES_X);
- assert(y < TILES_Y);
+ bin->last_state = NULL;
+ bin->head = bin->tail;
+ if (bin->tail) {
+ bin->tail->next = NULL;
+ bin->tail->count = 0;
+ }
+}
+
+
+void
+lp_scene_begin_rasterization(struct lp_scene *scene)
+{
+ const struct pipe_framebuffer_state *fb = &scene->fb;
+ int i;
+
+ //LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__);
- for (block = list->head; block != list->tail; block = tmp) {
- tmp = block->next;
- FREE(block);
+ for (i = 0; i < scene->fb.nr_cbufs; i++) {
+ struct pipe_surface *cbuf = scene->fb.cbufs[i];
+ if (llvmpipe_resource_is_texture(cbuf->texture)) {
+ scene->cbufs[i].stride = llvmpipe_resource_stride(cbuf->texture,
+ cbuf->u.tex.level);
+
+ scene->cbufs[i].map = llvmpipe_resource_map(cbuf->texture,
+ cbuf->u.tex.level,
+ cbuf->u.tex.first_layer,
+ LP_TEX_USAGE_READ_WRITE);
+ }
+ else {
+ struct llvmpipe_resource *lpr = llvmpipe_resource(cbuf->texture);
+ unsigned pixstride = util_format_get_blocksize(cbuf->format);
+ scene->cbufs[i].stride = cbuf->texture->width0;
+ scene->cbufs[i].map = lpr->data;
+ scene->cbufs[i].map += cbuf->u.buf.first_element * pixstride;
+ }
}
- assert(list->tail->next == NULL);
- list->head = list->tail;
- list->head->count = 0;
+ if (fb->zsbuf) {
+ struct pipe_surface *zsbuf = scene->fb.zsbuf;
+ scene->zsbuf.stride = llvmpipe_resource_stride(zsbuf->texture, zsbuf->u.tex.level);
+ scene->zsbuf.blocksize =
+ util_format_get_blocksize(zsbuf->texture->format);
+
+ scene->zsbuf.map = llvmpipe_resource_map(zsbuf->texture,
+ zsbuf->u.tex.level,
+ zsbuf->u.tex.first_layer,
+ LP_TEX_USAGE_READ_WRITE);
+ }
}
+
+
/**
- * Free all the temporary data in a scene. May be called from the
- * rasterizer thread(s).
+ * Free all the temporary data in a scene.
*/
void
-lp_scene_reset(struct lp_scene *scene )
+lp_scene_end_rasterization(struct lp_scene *scene )
{
- unsigned i, j;
+ int i, j;
+
+ /* Unmap color buffers */
+ for (i = 0; i < scene->fb.nr_cbufs; i++) {
+ if (scene->cbufs[i].map) {
+ struct pipe_surface *cbuf = scene->fb.cbufs[i];
+ if (llvmpipe_resource_is_texture(cbuf->texture)) {
+ llvmpipe_resource_unmap(cbuf->texture,
+ cbuf->u.tex.level,
+ cbuf->u.tex.first_layer);
+ }
+ scene->cbufs[i].map = NULL;
+ }
+ }
+
+ /* Unmap z/stencil buffer */
+ if (scene->zsbuf.map) {
+ struct pipe_surface *zsbuf = scene->fb.zsbuf;
+ llvmpipe_resource_unmap(zsbuf->texture,
+ zsbuf->u.tex.level,
+ zsbuf->u.tex.first_layer);
+ scene->zsbuf.map = NULL;
+ }
- /* Free all but last binner command lists:
+ /* Reset all command lists:
*/
- for (i = 0; i < TILES_X; i++) {
- for (j = 0; j < TILES_Y; j++) {
- lp_scene_bin_reset(scene, i, j);
+ for (i = 0; i < scene->tiles_x; i++) {
+ for (j = 0; j < scene->tiles_y; j++) {
+ struct cmd_bin *bin = lp_scene_get_bin(scene, i, j);
+ bin->head = NULL;
+ bin->tail = NULL;
+ bin->last_state = NULL;
}
}
+ /* If there are any bins which weren't cleared by the loop above,
+ * they will be caught (on debug builds at least) by this assert:
+ */
assert(lp_scene_is_empty(scene));
- /* Free all but last binned data block:
+ /* Decrement texture ref counts
*/
{
- struct data_block_list *list = &scene->data;
- struct data_block *block, *tmp;
-
- for (block = list->head; block != list->tail; block = tmp) {
- tmp = block->next;
- FREE(block);
+ struct resource_ref *ref;
+ int i, j = 0;
+
+ for (ref = scene->resources; ref; ref = ref->next) {
+ for (i = 0; i < ref->count; i++) {
+ if (LP_DEBUG & DEBUG_SETUP)
+ debug_printf("resource %d: %p %dx%d sz %d\n",
+ j,
+ (void *) ref->resource[i],
+ ref->resource[i]->width0,
+ ref->resource[i]->height0,
+ llvmpipe_resource_size(ref->resource[i]));
+ j++;
+ pipe_resource_reference(&ref->resource[i], NULL);
+ }
}
-
- assert(list->tail->next == NULL);
- list->head = list->tail;
- list->head->used = 0;
+
+ if (LP_DEBUG & DEBUG_SETUP)
+ debug_printf("scene %d resources, sz %d\n",
+ j, scene->resource_reference_size);
}
- /* Release texture refs
+ /* Free all scene data blocks:
*/
{
- struct resource_ref *ref, *next, *ref_list = &scene->resources;
- for (ref = ref_list->next; ref != ref_list; ref = next) {
- next = next_elem(ref);
- pipe_resource_reference(&ref->resource, NULL);
- FREE(ref);
+ struct data_block_list *list = &scene->data;
+ struct data_block *block, *tmp;
+
+ for (block = list->head->next; block; block = tmp) {
+ tmp = block->next;
+ FREE(block);
}
- make_empty_list(ref_list);
+
+ list->head->next = NULL;
+ list->head->used = 0;
}
lp_fence_reference(&scene->fence, NULL);
+ scene->resources = NULL;
scene->scene_size = 0;
+ scene->resource_reference_size = 0;
- scene->has_color_clear = FALSE;
scene->has_depthstencil_clear = FALSE;
+ scene->alloc_failed = FALSE;
+
+ util_unreference_framebuffer_state( &scene->fb );
}
struct cmd_block *
-lp_bin_new_cmd_block( struct cmd_block_list *list )
+lp_scene_new_cmd_block( struct lp_scene *scene,
+ struct cmd_bin *bin )
{
- struct cmd_block *block = MALLOC_STRUCT(cmd_block);
+ struct cmd_block *block = lp_scene_alloc(scene, sizeof(struct cmd_block));
if (block) {
- list->tail->next = block;
- list->tail = block;
+ if (bin->tail) {
+ bin->tail->next = block;
+ bin->tail = block;
+ }
+ else {
+ bin->head = block;
+ bin->tail = block;
+ }
+ //memset(block, 0, sizeof *block);
block->next = NULL;
block->count = 0;
}
struct data_block *
-lp_bin_new_data_block( struct data_block_list *list )
+lp_scene_new_data_block( struct lp_scene *scene )
{
- struct data_block *block = MALLOC_STRUCT(data_block);
- if (block) {
- list->tail->next = block;
- list->tail = block;
- block->next = NULL;
+ if (scene->scene_size + DATA_BLOCK_SIZE > LP_SCENE_MAX_SIZE) {
+ if (0) debug_printf("%s: failed\n", __FUNCTION__);
+ scene->alloc_failed = TRUE;
+ return NULL;
+ }
+ else {
+ struct data_block *block = MALLOC_STRUCT(data_block);
+ if (block == NULL)
+ return NULL;
+
+ scene->scene_size += sizeof *block;
+
block->used = 0;
+ block->next = scene->data.head;
+ scene->data.head = block;
+
+ return block;
}
- return block;
}
* Return number of bytes used for all bin data within a scene.
* This does not include resources (textures) referenced by the scene.
*/
-unsigned
+static unsigned
lp_scene_data_size( const struct lp_scene *scene )
{
unsigned size = 0;
}
-/** Return number of bytes used for a single bin */
-unsigned
-lp_scene_bin_size( const struct lp_scene *scene, unsigned x, unsigned y )
-{
- struct cmd_bin *bin = lp_scene_get_bin((struct lp_scene *) scene, x, y);
- const struct cmd_block *cmd;
- unsigned size = 0;
- for (cmd = bin->commands.head; cmd; cmd = cmd->next) {
- size += (cmd->count *
- (sizeof(lp_rast_cmd) + sizeof(union lp_rast_cmd_arg)));
- }
- return size;
-}
-
/**
* Add a reference to a resource by the scene.
*/
-void
+boolean
lp_scene_add_resource_reference(struct lp_scene *scene,
- struct pipe_resource *resource)
+ struct pipe_resource *resource,
+ boolean initializing_scene)
{
- struct resource_ref *ref = CALLOC_STRUCT(resource_ref);
- if (ref) {
- struct resource_ref *ref_list = &scene->resources;
- pipe_resource_reference(&ref->resource, resource);
- insert_at_tail(ref_list, ref);
+ struct resource_ref *ref, **last = &scene->resources;
+ int i;
+
+ /* Look at existing resource blocks:
+ */
+ for (ref = scene->resources; ref; ref = ref->next) {
+ last = &ref->next;
+
+ /* Search for this resource:
+ */
+ for (i = 0; i < ref->count; i++)
+ if (ref->resource[i] == resource)
+ return TRUE;
+
+ if (ref->count < RESOURCE_REF_SZ) {
+ /* If the block is half-empty, then append the reference here.
+ */
+ break;
+ }
+ }
+
+ /* Create a new block if no half-empty block was found.
+ */
+ if (!ref) {
+ assert(*last == NULL);
+ *last = lp_scene_alloc(scene, sizeof *ref);
+ if (*last == NULL)
+ return FALSE;
+
+ ref = *last;
+ memset(ref, 0, sizeof *ref);
}
- scene->scene_size += llvmpipe_resource_size(resource);
+ /* Append the reference to the reference block.
+ */
+ pipe_resource_reference(&ref->resource[ref->count++], resource);
+ scene->resource_reference_size += llvmpipe_resource_size(resource);
+
+ /* Heuristic to advise scene flushes. This isn't helpful in the
+ * initial setup of the scene, but after that point flush on the
+ * next resource added which exceeds 64MB in referenced texture
+ * data.
+ */
+ if (!initializing_scene &&
+ scene->resource_reference_size >= LP_SCENE_MAX_RESOURCE_SIZE)
+ return FALSE;
+
+ return TRUE;
}
lp_scene_is_resource_referenced(const struct lp_scene *scene,
const struct pipe_resource *resource)
{
- const struct resource_ref *ref_list = &scene->resources;
const struct resource_ref *ref;
- foreach (ref, ref_list) {
- if (ref->resource == resource)
- return TRUE;
- }
- return FALSE;
-}
-
-
-/**
- * Return last command in the bin
- */
-static lp_rast_cmd
-lp_get_last_command( const struct cmd_bin *bin )
-{
- const struct cmd_block *tail = bin->commands.tail;
- const unsigned i = tail->count;
- if (i > 0)
- return tail->cmd[i - 1];
- else
- return NULL;
-}
+ int i;
+ for (ref = scene->resources; ref; ref = ref->next) {
+ for (i = 0; i < ref->count; i++)
+ if (ref->resource[i] == resource)
+ return TRUE;
+ }
-/**
- * Replace the arg of the last command in the bin.
- */
-static void
-lp_replace_last_command_arg( struct cmd_bin *bin,
- const union lp_rast_cmd_arg arg )
-{
- struct cmd_block *tail = bin->commands.tail;
- const unsigned i = tail->count;
- assert(i > 0);
- tail->arg[i - 1] = arg;
+ return FALSE;
}
-/**
- * Put a state-change command into all bins.
- * If we find that the last command in a bin was also a state-change
- * command, we can simply replace that one with the new one.
- */
-void
-lp_scene_bin_state_command( struct lp_scene *scene,
- lp_rast_cmd cmd,
- const union lp_rast_cmd_arg arg )
-{
- unsigned i, j;
- for (i = 0; i < scene->tiles_x; i++) {
- for (j = 0; j < scene->tiles_y; j++) {
- struct cmd_bin *bin = lp_scene_get_bin(scene, i, j);
- lp_rast_cmd last_cmd = lp_get_last_command(bin);
- if (last_cmd == cmd) {
- lp_replace_last_command_arg(bin, arg);
- }
- else {
- lp_scene_bin_command( scene, i, j, cmd, arg );
- }
- }
- }
-}
-
/** advance curr_x,y to the next bin */
static boolean
* of work (a bin) to work on.
*/
struct cmd_bin *
-lp_scene_bin_iter_next( struct lp_scene *scene, int *bin_x, int *bin_y )
+lp_scene_bin_iter_next( struct lp_scene *scene , int *x, int *y)
{
struct cmd_bin *bin = NULL;
}
bin = lp_scene_get_bin(scene, scene->curr_x, scene->curr_y);
- *bin_x = scene->curr_x;
- *bin_y = scene->curr_y;
+ *x = scene->curr_x;
+ *y = scene->curr_y;
end:
/*printf("return bin %p at %d, %d\n", (void *) bin, *bin_x, *bin_y);*/
void lp_scene_begin_binning( struct lp_scene *scene,
- struct pipe_framebuffer_state *fb )
+ struct pipe_framebuffer_state *fb, boolean discard )
{
assert(lp_scene_is_empty(scene));
+ scene->discard = discard;
util_copy_framebuffer_state(&scene->fb, fb);
scene->tiles_x = align(fb->width, TILE_SIZE) / TILE_SIZE;
}
-void lp_scene_rasterize( struct lp_scene *scene,
- struct lp_rasterizer *rast )
+void lp_scene_end_binning( struct lp_scene *scene )
{
- if (0) {
- unsigned x, y;
+ if (LP_DEBUG & DEBUG_SCENE) {
debug_printf("rasterize scene:\n");
- debug_printf(" data size: %u\n", lp_scene_data_size(scene));
- for (y = 0; y < scene->tiles_y; y++) {
- for (x = 0; x < scene->tiles_x; x++) {
- debug_printf(" bin %u, %u size: %u\n", x, y,
- lp_scene_bin_size(scene, x, y));
- }
- }
- }
-
- /* Enqueue the scene for rasterization, then immediately wait for
- * it to finish.
- */
- lp_rast_queue_scene( rast, scene );
-
- /* Currently just wait for the rasterizer to finish. Some
- * threading interactions need to be worked out, particularly once
- * transfers become per-context:
- */
- lp_rast_finish( rast );
-
- util_unreference_framebuffer_state( &scene->fb );
+ debug_printf(" scene_size: %u\n",
+ scene->scene_size);
+ debug_printf(" data size: %u\n",
+ lp_scene_data_size(scene));
- /* put scene into the empty list */
- lp_scene_enqueue( scene->empty_queue, scene );
+ if (0)
+ lp_debug_bins( scene );
+ }
}