From 6419ecd02ce43a2614822e228f306d4db589f317 Mon Sep 17 00:00:00 2001 From: Keith Whitwell Date: Fri, 27 Aug 2010 17:49:40 +0100 Subject: [PATCH] llvmpipe: enforce fixed memory limit on scenes --- src/gallium/drivers/llvmpipe/lp_rast.c | 11 +- src/gallium/drivers/llvmpipe/lp_rast.h | 1 + src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h | 5 + src/gallium/drivers/llvmpipe/lp_scene.c | 227 +++++++++++-------- src/gallium/drivers/llvmpipe/lp_scene.h | 140 ++++++------ src/gallium/drivers/llvmpipe/lp_setup.c | 280 +++++++++++++----------- src/gallium/drivers/llvmpipe/lp_setup.h | 2 - src/gallium/drivers/llvmpipe/lp_setup_context.h | 5 +- src/gallium/drivers/llvmpipe/lp_setup_line.c | 28 ++- src/gallium/drivers/llvmpipe/lp_setup_point.c | 24 +- src/gallium/drivers/llvmpipe/lp_setup_tri.c | 77 ++++--- 11 files changed, 459 insertions(+), 341 deletions(-) diff --git a/src/gallium/drivers/llvmpipe/lp_rast.c b/src/gallium/drivers/llvmpipe/lp_rast.c index d8dc24e..c022153 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast.c +++ b/src/gallium/drivers/llvmpipe/lp_rast.c @@ -369,6 +369,11 @@ lp_rast_shade_tile(struct lp_rasterizer_task *task, const unsigned tile_x = task->x, tile_y = task->y; unsigned x, y; + if (inputs->disable) { + /* This command was partially binned and has been disabled */ + return; + } + LP_DBG(DEBUG_RAST, "%s\n", __FUNCTION__); /* render the whole 64x64 tile in 4x4 chunks */ @@ -664,10 +669,6 @@ rasterize_bin(struct lp_rasterizer_task *task, } lp_rast_tile_end(task); - - /* Free data for this bin. - */ - lp_scene_bin_reset( task->rast->curr_scene, x, y); } @@ -727,7 +728,7 @@ static boolean is_empty_bin( const struct cmd_bin *bin ) { if (0) debug_bin(bin); - return bin->commands.head->count == 0; + return bin->commands.head == NULL; } diff --git a/src/gallium/drivers/llvmpipe/lp_rast.h b/src/gallium/drivers/llvmpipe/lp_rast.h index d6e53f3..f43e714 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast.h +++ b/src/gallium/drivers/llvmpipe/lp_rast.h @@ -80,6 +80,7 @@ struct lp_rast_state { struct lp_rast_shader_inputs { float facing; /** Positive for front-facing, negative for back-facing */ boolean opaque:1; /** Is opaque */ + boolean disable:1; /** Partially binned, disable this command */ float (*a0)[4]; float (*dadx)[4]; diff --git a/src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h b/src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h index 99a0bae..eca25bb 100644 --- a/src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h +++ b/src/gallium/drivers/llvmpipe/lp_rast_tri_tmp.h @@ -165,6 +165,11 @@ TAG(lp_rast_triangle)(struct lp_rasterizer_task *task, outmask = 0; /* outside one or more trivial reject planes */ partmask = 0; /* outside one or more trivial accept planes */ + if (tri->inputs.disable) { + /* This triangle was partially binned and has been disabled */ + return; + } + while (plane_mask) { int i = ffs(plane_mask) - 1; plane[j] = tri->plane[i]; diff --git a/src/gallium/drivers/llvmpipe/lp_scene.c b/src/gallium/drivers/llvmpipe/lp_scene.c index 51389c2..67bc747 100644 --- a/src/gallium/drivers/llvmpipe/lp_scene.c +++ b/src/gallium/drivers/llvmpipe/lp_scene.c @@ -31,16 +31,19 @@ #include "util/u_inlines.h" #include "util/u_simple_list.h" #include "lp_scene.h" +#include "lp_debug.h" #include "lp_scene_queue.h" #include "lp_fence.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; +}; /** @@ -51,7 +54,6 @@ struct lp_scene * lp_scene_create( struct pipe_context *pipe, struct lp_scene_queue *queue ) { - unsigned i, j; struct lp_scene *scene = CALLOC_STRUCT(lp_scene); if (!scene) return NULL; @@ -59,18 +61,6 @@ lp_scene_create( struct pipe_context *pipe, 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); - pipe_mutex_init(scene->mutex); return scene; @@ -83,24 +73,9 @@ lp_scene_create( struct pipe_context *pipe, 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; - pipe_mutex_destroy(scene->mutex); - FREE(scene); } @@ -118,7 +93,7 @@ lp_scene_is_empty(struct lp_scene *scene ) 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 (list->head) { return FALSE; } } @@ -127,28 +102,31 @@ lp_scene_is_empty(struct lp_scene *scene ) } -/* 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/rectangle 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); - - for (block = list->head; block != list->tail; block = tmp) { - tmp = block->next; - FREE(block); - } - - assert(list->tail->next == NULL); list->head = list->tail; - list->head->count = 0; + if (list->tail) { + list->tail->next = NULL; + list->tail->count = 0; + } } @@ -159,13 +137,14 @@ lp_scene_bin_reset(struct lp_scene *scene, unsigned x, unsigned y) void lp_scene_reset(struct lp_scene *scene ) { - unsigned i, j; + int i, j; - /* Free all but last binner command lists: + /* Reset all command lists: */ for (i = 0; i < scene->tiles_x; i++) { for (j = 0; j < scene->tiles_y; j++) { - lp_scene_bin_reset(scene, i, j); + struct cmd_bin *bin = lp_scene_get_bin(scene, i, j); + memset(bin, 0, sizeof *bin); } } @@ -174,39 +153,53 @@ lp_scene_reset(struct lp_scene *scene ) */ assert(lp_scene_is_empty(scene)); - /* Free all but last binned data block: + /* Decrement texture ref counts + */ + { + 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, + 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); + } + } + + if (LP_DEBUG & DEBUG_SETUP) + debug_printf("scene %d resources, sz %d\n", + j, scene->resource_reference_size); + } + + /* Free all scene data blocks: */ { struct data_block_list *list = &scene->data; struct data_block *block, *tmp; - for (block = list->head; block != list->tail; block = tmp) { + for (block = list->head; block; block = tmp) { tmp = block->next; FREE(block); } - - assert(list->tail->next == NULL); - list->head = list->tail; - list->head->used = 0; - } - /* Release texture refs - */ - { - 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); - } - make_empty_list(ref_list); + list->head = NULL; } lp_fence_reference(&scene->fence, NULL); + scene->resources = NULL; scene->scene_size = 0; + scene->resource_reference_size = 0; scene->has_depthstencil_clear = FALSE; + scene->alloc_failed = FALSE; } @@ -215,12 +208,20 @@ lp_scene_reset(struct lp_scene *scene ) 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->commands.tail) { + bin->commands.tail->next = block; + bin->commands.tail = block; + } + else { + bin->commands.head = block; + bin->commands.tail = block; + } + //memset(block, 0, sizeof *block); block->next = NULL; block->count = 0; } @@ -229,16 +230,26 @@ lp_bin_new_cmd_block( struct cmd_block_list *list ) 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; } @@ -246,7 +257,7 @@ lp_bin_new_data_block( struct data_block_list *list ) * 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; @@ -259,7 +270,7 @@ lp_scene_data_size( const struct lp_scene *scene ) /** Return number of bytes used for a single bin */ -unsigned +static 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); @@ -276,18 +287,47 @@ lp_scene_bin_size( const struct lp_scene *scene, unsigned x, unsigned y ) /** * 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 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) { + + /* Search for this resource: + */ + for (i = 0; i < ref->count; i++) + if (ref->resource[i] == resource) + return TRUE; + + /* If the block is half-empty, this is the last block. Append + * the reference here. + */ + if (ref->count < RESOURCE_REF_SZ) + goto add_new_ref; + + last = &ref->next; } - scene->scene_size += llvmpipe_resource_size(resource); + /* Otherwise, need to create a new block: + */ + *last = lp_scene_alloc(scene, sizeof(struct resource_ref)); + if (*last) { + ref = *last; + memset(ref, 0, sizeof *ref); + goto add_new_ref; + } + + return FALSE; + +add_new_ref: + pipe_resource_reference(&ref->resource[ref->count++], resource); + scene->resource_reference_size += llvmpipe_resource_size(resource); + return scene->resource_reference_size < LP_SCENE_MAX_RESOURCE_SIZE; } @@ -298,12 +338,15 @@ boolean 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; + int i; + + for (ref = scene->resources; ref; ref = ref->next) { + for (i = 0; i < ref->count; i++) + if (ref->resource[i] == resource) + return TRUE; } + return FALSE; } diff --git a/src/gallium/drivers/llvmpipe/lp_scene.h b/src/gallium/drivers/llvmpipe/lp_scene.h index e933a84..d4f7b04 100644 --- a/src/gallium/drivers/llvmpipe/lp_scene.h +++ b/src/gallium/drivers/llvmpipe/lp_scene.h @@ -48,9 +48,17 @@ struct lp_scene_queue; #define TILES_Y (LP_MAX_HEIGHT / TILE_SIZE) -#define CMD_BLOCK_MAX 128 -#define DATA_BLOCK_SIZE (16 * 1024 - sizeof(unsigned) - sizeof(void *)) - +#define CMD_BLOCK_MAX 16 +#define DATA_BLOCK_SIZE (64 * 1024 - 2 * sizeof(void *)) + +/* Scene temporary storage is clamped to this size: + */ +#define LP_SCENE_MAX_SIZE (1024*1024) + +/* The maximum amount of texture storage referenced by a scene is + * clamped ot this size: + */ +#define LP_SCENE_MAX_RESOURCE_SIZE (64*1024*1024) /* switch to a non-pointer value for this: @@ -65,16 +73,18 @@ struct cmd_block { struct cmd_block *next; }; +struct cmd_block_list { + struct cmd_block *head; + struct cmd_block *tail; +}; + struct data_block { ubyte data[DATA_BLOCK_SIZE]; unsigned used; struct data_block *next; }; -struct cmd_block_list { - struct cmd_block *head; - struct cmd_block *tail; -}; + /** * For each screen tile we have one of these bins. @@ -85,22 +95,17 @@ struct cmd_bin { /** - * This stores bulk data which is shared by all bins within a scene. + * This stores bulk data which is used for all memory allocations + * within a scene. + * * Examples include triangle data and state data. The commands in * the per-tile bins will point to chunks of data in this structure. */ struct data_block_list { struct data_block *head; - struct data_block *tail; -}; - - -/** List of resource references */ -struct resource_ref { - struct pipe_resource *resource; - struct resource_ref *prev, *next; /**< linked list w/ u_simple_list.h */ }; +struct resource_ref; /** * All bins and bin data are contained here. @@ -118,13 +123,21 @@ struct lp_scene { struct pipe_framebuffer_state fb; /** list of resources referenced by the scene commands */ - struct resource_ref resources; + struct resource_ref *resources; - /** Approx memory used by the scene (in bytes). This includes the - * shared and per-tile bins plus any referenced resources/textures. + /** Total memory used by the scene (in bytes). This sums all the + * data blocks and counts all bins, state, resource references and + * other random allocations within the scene. */ unsigned scene_size; + /** Sum of sizes of all resources referenced by the scene. Sums + * all the textures read by the scene: + */ + unsigned resource_reference_size; + + boolean alloc_failed; + boolean has_depthstencil_clear; /** @@ -151,22 +164,18 @@ struct lp_scene *lp_scene_create(struct pipe_context *pipe, void lp_scene_destroy(struct lp_scene *scene); - - boolean lp_scene_is_empty(struct lp_scene *scene ); +boolean lp_scene_is_oom(struct lp_scene *scene ); void lp_scene_reset(struct lp_scene *scene ); -struct data_block *lp_bin_new_data_block( struct data_block_list *list ); - -struct cmd_block *lp_bin_new_cmd_block( struct cmd_block_list *list ); +struct data_block *lp_scene_new_data_block( struct lp_scene *scene ); -unsigned lp_scene_data_size( const struct lp_scene *scene ); +struct cmd_block *lp_scene_new_cmd_block( struct lp_scene *scene, + struct cmd_bin *bin ); -unsigned lp_scene_bin_size( const struct lp_scene *scene, unsigned x, unsigned y ); - -void lp_scene_add_resource_reference(struct lp_scene *scene, +boolean lp_scene_add_resource_reference(struct lp_scene *scene, struct pipe_resource *resource); boolean lp_scene_is_resource_referenced(const struct lp_scene *scene, @@ -181,21 +190,21 @@ static INLINE void * lp_scene_alloc( struct lp_scene *scene, unsigned size) { struct data_block_list *list = &scene->data; - struct data_block *tail = list->tail; + struct data_block *block = list->head; - if (tail->used + size > DATA_BLOCK_SIZE) { - tail = lp_bin_new_data_block( list ); - if (!tail) { + assert(size <= DATA_BLOCK_SIZE); + + if (block == NULL || block->used + size > DATA_BLOCK_SIZE) { + block = lp_scene_new_data_block( scene ); + if (!block) { /* out of memory */ return NULL; } } - scene->scene_size += size; - { - ubyte *data = tail->data + tail->used; - tail->used += size; + ubyte *data = block->data + block->used; + block->used += size; return data; } } @@ -209,20 +218,19 @@ lp_scene_alloc_aligned( struct lp_scene *scene, unsigned size, unsigned alignment ) { struct data_block_list *list = &scene->data; - struct data_block *tail = list->tail; + struct data_block *block = list->head; - if (tail->used + size + alignment - 1 > DATA_BLOCK_SIZE) { - tail = lp_bin_new_data_block( list ); - if (!tail) + if (block == NULL || + block->used + size + alignment - 1 > DATA_BLOCK_SIZE) { + block = lp_scene_new_data_block( scene ); + if (!block) return NULL; } - scene->scene_size += size; - { - ubyte *data = tail->data + tail->used; + ubyte *data = block->data + block->used; unsigned offset = (((uintptr_t)data + alignment - 1) & ~(alignment - 1)) - (uintptr_t)data; - tail->used += offset + size; + block->used += offset + size; return data + offset; } } @@ -234,9 +242,8 @@ static INLINE void lp_scene_putback_data( struct lp_scene *scene, unsigned size) { struct data_block_list *list = &scene->data; - scene->scene_size -= size; - assert(list->tail->used >= size); - list->tail->used -= size; + assert(list->head && list->head->used >= size); + list->head->used -= size; } @@ -255,24 +262,22 @@ lp_scene_bin_reset(struct lp_scene *scene, unsigned x, unsigned y); /* Add a command to bin[x][y]. */ -static INLINE void +static INLINE boolean lp_scene_bin_command( struct lp_scene *scene, unsigned x, unsigned y, lp_rast_cmd cmd, union lp_rast_cmd_arg arg ) { struct cmd_bin *bin = lp_scene_get_bin(scene, x, y); - struct cmd_block_list *list = &bin->commands; - struct cmd_block *tail = list->tail; + struct cmd_block *tail = bin->commands.tail; assert(x < scene->tiles_x); assert(y < scene->tiles_y); - if (tail->count == CMD_BLOCK_MAX) { - tail = lp_bin_new_cmd_block( list ); + if (tail == NULL || tail->count == CMD_BLOCK_MAX) { + tail = lp_scene_new_cmd_block( scene, bin ); if (!tail) { - /* out of memory - simply ignore this command (for now) */ - return; + return FALSE; } assert(tail->count == 0); } @@ -283,27 +288,28 @@ lp_scene_bin_command( struct lp_scene *scene, tail->arg[i] = arg; tail->count++; } + + return TRUE; } /* Add a command to all active bins. */ -static INLINE void +static INLINE boolean lp_scene_bin_everywhere( 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++) - lp_scene_bin_command( scene, i, j, cmd, arg ); -} - + for (i = 0; i < scene->tiles_x; i++) { + for (j = 0; j < scene->tiles_y; j++) { + if (!lp_scene_bin_command( scene, i, j, cmd, arg )) + return FALSE; + } + } -void -lp_scene_bin_state_command( struct lp_scene *scene, - lp_rast_cmd cmd, - const union lp_rast_cmd_arg arg ); + return TRUE; +} static INLINE unsigned @@ -329,11 +335,5 @@ lp_scene_begin_binning( struct lp_scene *scene, struct pipe_framebuffer_state *fb ); -static INLINE unsigned -lp_scene_get_size(const struct lp_scene *scene) -{ - return scene->scene_size; -} - #endif /* LP_BIN_H */ diff --git a/src/gallium/drivers/llvmpipe/lp_setup.c b/src/gallium/drivers/llvmpipe/lp_setup.c index 3bef620..476d23f 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup.c +++ b/src/gallium/drivers/llvmpipe/lp_setup.c @@ -70,26 +70,6 @@ lp_setup_get_current_scene(struct lp_setup_context *setup) } -/** - * Check if the size of the current scene has exceeded the limit. - * If so, flush/render it. - */ -static void -setup_check_scene_size_and_flush(struct lp_setup_context *setup) -{ - if (setup->scene) { - struct lp_scene *scene = lp_setup_get_current_scene(setup); - unsigned size = lp_scene_get_size(scene); - - if (size > LP_MAX_SCENE_SIZE) { - /*printf("LLVMPIPE: scene size = %u, flushing.\n", size);*/ - set_scene_state( setup, SETUP_FLUSHED ); - /*assert(lp_scene_get_size(scene) == 0);*/ - } - } -} - - static void first_triangle( struct lp_setup_context *setup, const float (*v0)[4], @@ -191,9 +171,10 @@ begin_binning( struct lp_setup_context *setup ) if (setup->fb.nr_cbufs) { if (setup->clear.flags & PIPE_CLEAR_COLOR) { - lp_scene_bin_everywhere( scene, - lp_rast_clear_color, - setup->clear.color ); + ok = lp_scene_bin_everywhere( scene, + lp_rast_clear_color, + setup->clear.color ); + assert(ok); } } @@ -201,18 +182,20 @@ begin_binning( struct lp_setup_context *setup ) if (setup->clear.flags & PIPE_CLEAR_DEPTHSTENCIL) { if (!need_zsload) scene->has_depthstencil_clear = TRUE; - lp_scene_bin_everywhere( scene, - lp_rast_clear_zstencil, - lp_rast_arg_clearzs( - setup->clear.zsmask, - setup->clear.zsvalue)); + ok = lp_scene_bin_everywhere( scene, + lp_rast_clear_zstencil, + lp_rast_arg_clearzs( + setup->clear.zsmask, + setup->clear.zsvalue)); + assert(ok); } } if (setup->active_query) { - lp_scene_bin_everywhere( scene, - lp_rast_restart_query, - lp_rast_arg_query(setup->active_query) ); + ok = lp_scene_bin_everywhere( scene, + lp_rast_restart_query, + lp_rast_arg_query(setup->active_query) ); + assert(ok); } @@ -254,7 +237,6 @@ set_scene_state( struct lp_setup_context *setup, /* wait for a free/empty scene */ setup->scene = lp_scene_dequeue(setup->empty_scenes, TRUE); - assert(lp_scene_is_empty(setup->scene)); lp_scene_begin_binning(setup->scene, &setup->fb ); break; @@ -333,12 +315,12 @@ lp_setup_bind_framebuffer( struct lp_setup_context *setup, } -void -lp_setup_clear( struct lp_setup_context *setup, - const float *color, - double depth, - unsigned stencil, - unsigned flags ) +static boolean +lp_setup_try_clear( struct lp_setup_context *setup, + const float *color, + double depth, + unsigned stencil, + unsigned flags ) { struct lp_scene *scene = lp_setup_get_current_scene(setup); uint32_t zsmask = 0; @@ -371,15 +353,17 @@ lp_setup_clear( struct lp_setup_context *setup, * a common usage. */ if (flags & PIPE_CLEAR_COLOR) { - lp_scene_bin_everywhere( scene, - lp_rast_clear_color, - setup->clear.color ); + if (!lp_scene_bin_everywhere( scene, + lp_rast_clear_color, + setup->clear.color )) + return FALSE; } if (flags & PIPE_CLEAR_DEPTHSTENCIL) { - lp_scene_bin_everywhere( scene, - lp_rast_clear_zstencil, - lp_rast_arg_clearzs(zsmask, zsvalue) ); + if (!lp_scene_bin_everywhere( scene, + lp_rast_clear_zstencil, + lp_rast_arg_clearzs(zsmask, zsvalue) )) + return FALSE; } } else { @@ -404,38 +388,27 @@ lp_setup_clear( struct lp_setup_context *setup, sizeof clear_color); } } -} + return TRUE; +} -/** - * Emit a fence. - */ -struct pipe_fence_handle * -lp_setup_fence( struct lp_setup_context *setup ) +void +lp_setup_clear( struct lp_setup_context *setup, + const float *color, + double depth, + unsigned stencil, + unsigned flags ) { - if (setup->scene == NULL) - return NULL; - else if (setup->num_threads == 0) - return NULL; - else - { - struct lp_scene *scene = lp_setup_get_current_scene(setup); - const unsigned rank = setup->num_threads; + if (!lp_setup_try_clear( setup, color, depth, stencil, flags )) { + lp_setup_flush(setup, 0, NULL, __FUNCTION__); - set_scene_state( setup, SETUP_ACTIVE ); - - assert(scene->fence == NULL); + if (!lp_setup_try_clear( setup, color, depth, stencil, flags )) + assert(0); + } +} - /* The caller gets a reference, we keep a copy too, so need to - * bump the refcount: - */ - lp_fence_reference(&scene->fence, lp_fence_create(rank)); - LP_DBG(DEBUG_SETUP, "%s rank %u\n", __FUNCTION__, rank); - return (struct pipe_fence_handle *) scene->fence; - } -} void @@ -698,58 +671,34 @@ lp_setup_is_resource_referenced( const struct lp_setup_context *setup, /** * Called by vbuf code when we're about to draw something. */ -void -lp_setup_update_state( struct lp_setup_context *setup ) +static boolean +try_update_scene_state( struct lp_setup_context *setup ) { struct lp_scene *scene; LP_DBG(DEBUG_SETUP, "%s\n", __FUNCTION__); - setup_check_scene_size_and_flush(setup); - scene = lp_setup_get_current_scene(setup); assert(setup->fs.current.variant); - /* Some of the 'draw' pipeline stages may have changed some driver state. - * Make sure we've processed those state changes before anything else. - * - * XXX this is the only place where llvmpipe_context is used in the - * setup code. This may get refactored/changed... - */ - { - struct llvmpipe_context *lp = llvmpipe_context(scene->pipe); - - /* Will probably need to move this somewhere else, just need - * to know about vertex shader point size attribute. - */ - setup->psize = lp->psize_slot; - - if (lp->dirty) { - llvmpipe_update_derived(lp); - } - assert(lp->dirty == 0); - } - if(setup->dirty & LP_SETUP_NEW_BLEND_COLOR) { uint8_t *stored; unsigned i, j; stored = lp_scene_alloc_aligned(scene, 4 * 16, 16); - - if (stored) { - /* smear each blend color component across 16 ubyte elements */ - for (i = 0; i < 4; ++i) { - uint8_t c = float_to_ubyte(setup->blend_color.current.color[i]); - for (j = 0; j < 16; ++j) - stored[i*16 + j] = c; - } - - setup->blend_color.stored = stored; - - setup->fs.current.jit_context.blend_color = setup->blend_color.stored; + if (!stored) + return FALSE; + + /* smear each blend color component across 16 ubyte elements */ + for (i = 0; i < 4; ++i) { + uint8_t c = float_to_ubyte(setup->blend_color.current.color[i]); + for (j = 0; j < 16; ++j) + stored[i*16 + j] = c; } + setup->blend_color.stored = stored; + setup->fs.current.jit_context.blend_color = setup->blend_color.stored; setup->dirty |= LP_SETUP_NEW_FS; } @@ -770,13 +719,14 @@ lp_setup_update_state( struct lp_setup_context *setup ) void *stored; stored = lp_scene_alloc(scene, current_size); - if(stored) { - memcpy(stored, - current_data, - current_size); - setup->constants.stored_size = current_size; - setup->constants.stored_data = stored; - } + if (!stored) + return FALSE; + + memcpy(stored, + current_data, + current_size); + setup->constants.stored_size = current_size; + setup->constants.stored_data = stored; } } else { @@ -789,31 +739,37 @@ lp_setup_update_state( struct lp_setup_context *setup ) } - if(setup->dirty & LP_SETUP_NEW_FS) { - if(!setup->fs.stored || - memcmp(setup->fs.stored, - &setup->fs.current, - sizeof setup->fs.current) != 0) { + if (setup->dirty & LP_SETUP_NEW_FS) { + if (!setup->fs.stored || + memcmp(setup->fs.stored, + &setup->fs.current, + sizeof setup->fs.current) != 0) + { + struct lp_rast_state *stored; + uint i; + /* The fs state that's been stored in the scene is different from * the new, current state. So allocate a new lp_rast_state object * and append it to the bin's setup data buffer. */ - uint i; - struct lp_rast_state *stored = - (struct lp_rast_state *) lp_scene_alloc(scene, sizeof *stored); - if(stored) { - memcpy(stored, - &setup->fs.current, - sizeof setup->fs.current); - setup->fs.stored = stored; - } + stored = (struct lp_rast_state *) lp_scene_alloc(scene, sizeof *stored); + if (!stored) + return FALSE; + memcpy(stored, + &setup->fs.current, + sizeof setup->fs.current); + setup->fs.stored = stored; + /* The scene now references the textures in the rasterization * state record. Note that now. */ for (i = 0; i < Elements(setup->fs.current_tex); i++) { - if (setup->fs.current_tex[i]) - lp_scene_add_resource_reference(scene, setup->fs.current_tex[i]); + if (setup->fs.current_tex[i]) { + if (!lp_scene_add_resource_reference(scene, + setup->fs.current_tex[i])) + return FALSE; + } } } } @@ -829,6 +785,40 @@ lp_setup_update_state( struct lp_setup_context *setup ) setup->dirty = 0; assert(setup->fs.stored); + return TRUE; +} + +void +lp_setup_update_state( struct lp_setup_context *setup ) +{ + /* Some of the 'draw' pipeline stages may have changed some driver state. + * Make sure we've processed those state changes before anything else. + * + * XXX this is the only place where llvmpipe_context is used in the + * setup code. This may get refactored/changed... + */ + { + struct llvmpipe_context *lp = llvmpipe_context(setup->pipe); + if (lp->dirty) { + llvmpipe_update_derived(lp); + } + + /* Will probably need to move this somewhere else, just need + * to know about vertex shader point size attribute. + */ + setup->psize = lp->psize_slot; + + assert(lp->dirty == 0); + } + + /* XXX: only call into update_scene_state() if we already have a + * scene: + */ + if (!try_update_scene_state(setup)) { + set_scene_state( setup, SETUP_FLUSHED ); + if (!try_update_scene_state(setup)) + assert(0); + } } @@ -881,6 +871,10 @@ lp_setup_create( struct pipe_context *pipe, return NULL; lp_setup_init_vbuf(setup); + + /* Used only in update_state(): + */ + setup->pipe = pipe; setup->empty_scenes = lp_scene_queue_create(); if (!setup->empty_scenes) @@ -932,9 +926,18 @@ lp_setup_begin_query(struct lp_setup_context *setup, assert(setup->active_query == NULL); if (setup->scene) { - lp_scene_bin_everywhere(setup->scene, - lp_rast_begin_query, - lp_rast_arg_query(pq)); + if (!lp_scene_bin_everywhere(setup->scene, + lp_rast_begin_query, + lp_rast_arg_query(pq))) { + lp_setup_flush_and_restart(setup); + + if (!lp_scene_bin_everywhere(setup->scene, + lp_rast_begin_query, + lp_rast_arg_query(pq))) { + assert(0); + return; + } + } } setup->active_query = pq; @@ -963,7 +966,9 @@ lp_setup_end_query(struct lp_setup_context *setup, struct llvmpipe_query *pq) */ lp_fence_reference(&pq->fence, setup->scene->fence); - lp_scene_bin_everywhere(setup->scene, lp_rast_end_query, dummy); + if (!lp_scene_bin_everywhere(setup->scene, lp_rast_end_query, dummy)) { + lp_setup_flush(setup, 0, NULL, __FUNCTION__); + } } else { lp_fence_reference(&pq->fence, setup->last_fence); @@ -971,3 +976,14 @@ lp_setup_end_query(struct lp_setup_context *setup, struct llvmpipe_query *pq) } +void +lp_setup_flush_and_restart(struct lp_setup_context *setup) +{ + if (0) debug_printf("%s\n", __FUNCTION__); + + assert(setup->state == SETUP_ACTIVE); + set_scene_state(setup, SETUP_FLUSHED); + set_scene_state(setup, SETUP_EMPTY); + set_scene_state(setup, SETUP_ACTIVE); + lp_setup_update_state(setup); +} diff --git a/src/gallium/drivers/llvmpipe/lp_setup.h b/src/gallium/drivers/llvmpipe/lp_setup.h index 821ebb1..b5ae4ec 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup.h +++ b/src/gallium/drivers/llvmpipe/lp_setup.h @@ -78,8 +78,6 @@ lp_setup_clear(struct lp_setup_context *setup, unsigned clear_stencil, unsigned flags); -struct pipe_fence_handle * -lp_setup_fence( struct lp_setup_context *setup ); void diff --git a/src/gallium/drivers/llvmpipe/lp_setup_context.h b/src/gallium/drivers/llvmpipe/lp_setup_context.h index fa4e5e9..3126cf1 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_context.h +++ b/src/gallium/drivers/llvmpipe/lp_setup_context.h @@ -70,6 +70,7 @@ struct lp_setup_context { struct vbuf_render base; + struct pipe_context *pipe; struct vertex_info *vertex_info; uint prim; uint vertex_size; @@ -186,11 +187,13 @@ lp_setup_alloc_triangle(struct lp_scene *scene, unsigned nr_planes, unsigned *tri_size); -void +boolean lp_setup_bin_triangle( struct lp_setup_context *setup, struct lp_rast_triangle *tri, const struct u_rect *bbox, int nr_planes ); +void lp_setup_flush_and_restart(struct lp_setup_context *setup); + #endif diff --git a/src/gallium/drivers/llvmpipe/lp_setup_line.c b/src/gallium/drivers/llvmpipe/lp_setup_line.c index ce2da55..c9d3d7a 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_line.c +++ b/src/gallium/drivers/llvmpipe/lp_setup_line.c @@ -263,8 +263,8 @@ static INLINE float fracf(float f) -static void -lp_setup_line( struct lp_setup_context *setup, +static boolean +try_setup_line( struct lp_setup_context *setup, const float (*v1)[4], const float (*v2)[4]) { @@ -536,13 +536,13 @@ lp_setup_line( struct lp_setup_context *setup, bbox.y1 < bbox.y0) { if (0) debug_printf("empty bounding box\n"); LP_COUNT(nr_culled_tris); - return; + return TRUE; } if (!u_rect_test_intersection(&setup->draw_region, &bbox)) { if (0) debug_printf("offscreen\n"); LP_COUNT(nr_culled_tris); - return; + return TRUE; } u_rect_find_intersection(&setup->draw_region, &bbox); @@ -552,7 +552,7 @@ lp_setup_line( struct lp_setup_context *setup, nr_planes, &tri_bytes); if (!line) - return; + return FALSE; #ifdef DEBUG line->v[0][0] = v1[0][0]; @@ -687,9 +687,23 @@ lp_setup_line( struct lp_setup_context *setup, line->plane[7].eo = 0; } - lp_setup_bin_triangle(setup, line, &bbox, nr_planes); + return lp_setup_bin_triangle(setup, line, &bbox, nr_planes); } - + + +static void lp_setup_line( struct lp_setup_context *setup, + const float (*v0)[4], + const float (*v1)[4] ) +{ + if (!try_setup_line( setup, v0, v1 )) + { + lp_setup_flush_and_restart(setup); + + if (!try_setup_line( setup, v0, v1 )) + assert(0); + } +} + void lp_setup_choose_line( struct lp_setup_context *setup ) { diff --git a/src/gallium/drivers/llvmpipe/lp_setup_point.c b/src/gallium/drivers/llvmpipe/lp_setup_point.c index 6ae318d..fd6b53e 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_point.c +++ b/src/gallium/drivers/llvmpipe/lp_setup_point.c @@ -210,8 +210,9 @@ subpixel_snap(float a) } -static void lp_setup_point( struct lp_setup_context *setup, - const float (*v0)[4] ) +static boolean +try_setup_point( struct lp_setup_context *setup, + const float (*v0)[4] ) { /* x/y positions in fixed point */ const int sizeAttr = setup->psize; @@ -259,7 +260,7 @@ static void lp_setup_point( struct lp_setup_context *setup, if (!u_rect_test_intersection(&setup->draw_region, &bbox)) { if (0) debug_printf("offscreen\n"); LP_COUNT(nr_culled_tris); - return; + return TRUE; } u_rect_find_intersection(&setup->draw_region, &bbox); @@ -269,7 +270,7 @@ static void lp_setup_point( struct lp_setup_context *setup, nr_planes, &bytes); if (!point) - return; + return FALSE; #ifdef DEBUG point->v[0][0] = v0[0][0]; @@ -315,7 +316,20 @@ static void lp_setup_point( struct lp_setup_context *setup, point->plane[3].eo = 0; } - lp_setup_bin_triangle(setup, point, &bbox, nr_planes); + return lp_setup_bin_triangle(setup, point, &bbox, nr_planes); +} + + +static void lp_setup_point( struct lp_setup_context *setup, + const float (*v0)[4] ) +{ + if (!try_setup_point( setup, v0 )) + { + lp_setup_flush_and_restart(setup); + + if (!try_setup_point( setup, v0 )) + assert(0); + } } diff --git a/src/gallium/drivers/llvmpipe/lp_setup_tri.c b/src/gallium/drivers/llvmpipe/lp_setup_tri.c index e5aacbc..d8dd9ab 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup_tri.c +++ b/src/gallium/drivers/llvmpipe/lp_setup_tri.c @@ -179,7 +179,7 @@ lp_rast_cmd lp_rast_tri_tab[9] = { * * \param tx, ty the tile position in tiles, not pixels */ -static void +static boolean lp_setup_whole_tile(struct lp_setup_context *setup, const struct lp_rast_shader_inputs *inputs, int tx, int ty) @@ -198,14 +198,14 @@ lp_setup_whole_tile(struct lp_setup_context *setup, } LP_COUNT(nr_shade_opaque_64); - lp_scene_bin_command( scene, tx, ty, - lp_rast_shade_tile_opaque, - lp_rast_arg_inputs(inputs) ); + return lp_scene_bin_command( scene, tx, ty, + lp_rast_shade_tile_opaque, + lp_rast_arg_inputs(inputs) ); } else { LP_COUNT(nr_shade_64); - lp_scene_bin_command( scene, tx, ty, - lp_rast_shade_tile, - lp_rast_arg_inputs(inputs) ); + return lp_scene_bin_command( scene, tx, ty, + lp_rast_shade_tile, + lp_rast_arg_inputs(inputs) ); } } @@ -215,7 +215,7 @@ lp_setup_whole_tile(struct lp_setup_context *setup, * framebuffer tiles are touched. Put the triangle in the scene's * bins for the tiles which we overlap. */ -static void +static boolean do_triangle_ccw(struct lp_setup_context *setup, const float (*v0)[4], const float (*v1)[4], @@ -280,13 +280,13 @@ do_triangle_ccw(struct lp_setup_context *setup, bbox.y1 < bbox.y0) { if (0) debug_printf("empty bounding box\n"); LP_COUNT(nr_culled_tris); - return; + return FALSE; } if (!u_rect_test_intersection(&setup->draw_region, &bbox)) { if (0) debug_printf("offscreen\n"); LP_COUNT(nr_culled_tris); - return; + return FALSE; } u_rect_find_intersection(&setup->draw_region, &bbox); @@ -296,7 +296,7 @@ do_triangle_ccw(struct lp_setup_context *setup, nr_planes, &tri_bytes); if (!tri) - return; + return FALSE; #ifdef DEBUG tri->v[0][0] = v0[0][0]; @@ -327,7 +327,7 @@ do_triangle_ccw(struct lp_setup_context *setup, if (area <= 0) { lp_scene_putback_data( scene, tri_bytes ); LP_COUNT(nr_culled_tris); - return; + return TRUE; } @@ -356,6 +356,7 @@ do_triangle_ccw(struct lp_setup_context *setup, tri->inputs.facing = frontfacing ? 1.0F : -1.0F; tri->inputs.opaque = variant->opaque; + tri->inputs.disable = FALSE; tri->inputs.state = setup->fs.stored; @@ -460,11 +461,11 @@ do_triangle_ccw(struct lp_setup_context *setup, tri->plane[6].eo = 0; } - lp_setup_bin_triangle( setup, tri, &bbox, nr_planes ); + return lp_setup_bin_triangle( setup, tri, &bbox, nr_planes ); } -void +boolean lp_setup_bin_triangle( struct lp_setup_context *setup, struct lp_rast_triangle *tri, const struct u_rect *bbox, @@ -494,10 +495,9 @@ lp_setup_bin_triangle( struct lp_setup_context *setup, */ int mask = (ix0 & 3) | ((iy0 & 3) << 4); - lp_scene_bin_command( scene, ix0/4, iy0/4, - lp_rast_triangle_3_16, - lp_rast_arg_triangle(tri, mask) ); - return; + return lp_scene_bin_command( scene, ix0/4, iy0/4, + lp_rast_triangle_3_16, + lp_rast_arg_triangle(tri, mask) ); } } @@ -520,9 +520,9 @@ lp_setup_bin_triangle( struct lp_setup_context *setup, { /* Triangle is contained in a single tile: */ - lp_scene_bin_command( scene, ix0, iy0, - lp_rast_tri_tab[nr_planes], - lp_rast_arg_triangle(tri, (1<inputs, x, y); + if (!lp_setup_whole_tile(setup, &tri->inputs, x, y)) + goto fail; } /* Iterate cx values across the region: @@ -608,6 +610,16 @@ lp_setup_bin_triangle( struct lp_setup_context *setup, c[i] += ystep[i]; } } + + return TRUE; + +fail: + /* Need to disable any partially binned triangle. This is easier + * than trying to locate all the triangle, shade-tile, etc, + * commands which may have been binned. + */ + tri->inputs.disable = TRUE; + return FALSE; } @@ -619,7 +631,13 @@ static void triangle_cw( struct lp_setup_context *setup, const float (*v1)[4], const float (*v2)[4] ) { - do_triangle_ccw( setup, v1, v0, v2, !setup->ccw_is_frontface ); + if (!do_triangle_ccw( setup, v1, v0, v2, !setup->ccw_is_frontface )) + { + lp_setup_flush_and_restart(setup); + + if (!do_triangle_ccw( setup, v1, v0, v2, !setup->ccw_is_frontface )) + assert(0); + } } @@ -631,7 +649,12 @@ static void triangle_ccw( struct lp_setup_context *setup, const float (*v1)[4], const float (*v2)[4] ) { - do_triangle_ccw( setup, v0, v1, v2, setup->ccw_is_frontface ); + if (!do_triangle_ccw( setup, v0, v1, v2, setup->ccw_is_frontface )) + { + lp_setup_flush_and_restart(setup); + if (!do_triangle_ccw( setup, v0, v1, v2, setup->ccw_is_frontface )) + assert(0); + } } -- 2.7.4