From 99330e50c9e08532f5d9c3568be938c7e9d8fd93 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 6 Jul 2020 15:32:19 +1000 Subject: [PATCH] llvmpipe: add reference counting to fragment shaders. Currently llvmpipe calls finish on the context when a shader variant has to be destroyed just in case the variant is currently in use by the setup engine. Fix this by reference counting the shaders, and reference counting the shader variants. Whenever a shader is used in the rasteriser backend, it is added to a reference list and removed when the rasterizer is finished with it. Reviewed-by: Roland Scheidegger Reviewed-by: Jose Fonseca Part-of: --- src/gallium/drivers/llvmpipe/lp_scene.c | 74 ++++++++++++++++++++++ src/gallium/drivers/llvmpipe/lp_scene.h | 9 +++ src/gallium/drivers/llvmpipe/lp_setup.c | 13 ++-- src/gallium/drivers/llvmpipe/lp_state_fs.c | 73 +++++++++++---------- src/gallium/drivers/llvmpipe/lp_state_fs.h | 37 ++++++++++- 5 files changed, 165 insertions(+), 41 deletions(-) diff --git a/src/gallium/drivers/llvmpipe/lp_scene.c b/src/gallium/drivers/llvmpipe/lp_scene.c index 59eed414550..539b84c6525 100644 --- a/src/gallium/drivers/llvmpipe/lp_scene.c +++ b/src/gallium/drivers/llvmpipe/lp_scene.c @@ -34,6 +34,8 @@ #include "lp_scene.h" #include "lp_fence.h" #include "lp_debug.h" +#include "lp_context.h" +#include "lp_state_fs.h" #define RESOURCE_REF_SZ 32 @@ -45,6 +47,14 @@ struct resource_ref { struct resource_ref *next; }; +#define SHADER_REF_SZ 32 +/** List of shader variant references */ +struct shader_ref { + struct lp_fragment_shader_variant *variant[SHADER_REF_SZ]; + int count; + struct shader_ref *next; +}; + /** * Create a new scene object. @@ -281,6 +291,22 @@ lp_scene_end_rasterization(struct lp_scene *scene ) j, scene->resource_reference_size); } + /* Decrement shader variant ref counts + */ + { + struct shader_ref *ref; + int i, j = 0; + + for (ref = scene->frag_shaders; ref; ref = ref->next) { + for (i = 0; i < ref->count; i++) { + if (LP_DEBUG & DEBUG_SETUP) + debug_printf("shader %d: %p\n", j, (void *) ref->variant[i]); + j++; + lp_fs_variant_reference(llvmpipe_context(scene->pipe), &ref->variant[i], NULL); + } + } + } + /* Free all scene data blocks: */ { @@ -299,6 +325,7 @@ lp_scene_end_rasterization(struct lp_scene *scene ) lp_fence_reference(&scene->fence, NULL); scene->resources = NULL; + scene->frag_shaders = NULL; scene->scene_size = 0; scene->resource_reference_size = 0; @@ -434,6 +461,53 @@ lp_scene_add_resource_reference(struct lp_scene *scene, } +/** + * Add a reference to a fragment shader variant + */ +boolean +lp_scene_add_frag_shader_reference(struct lp_scene *scene, + struct lp_fragment_shader_variant *variant) +{ + struct shader_ref *ref, **last = &scene->frag_shaders; + int i; + + /* Look at existing resource blocks: + */ + for (ref = scene->frag_shaders; ref; ref = ref->next) { + last = &ref->next; + + /* Search for this resource: + */ + for (i = 0; i < ref->count; i++) + if (ref->variant[i] == variant) + return TRUE; + + if (ref->count < SHADER_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); + } + + /* Append the reference to the reference block. + */ + lp_fs_variant_reference(llvmpipe_context(scene->pipe), &ref->variant[ref->count++], variant); + + return TRUE; +} + /** * Does this scene have a reference to the given resource? */ diff --git a/src/gallium/drivers/llvmpipe/lp_scene.h b/src/gallium/drivers/llvmpipe/lp_scene.h index 679e2c07b28..ba6b201395a 100644 --- a/src/gallium/drivers/llvmpipe/lp_scene.h +++ b/src/gallium/drivers/llvmpipe/lp_scene.h @@ -117,6 +117,8 @@ struct data_block_list { struct resource_ref; +struct shader_ref; + /** * All bins and bin data are contained here. * Per-bin data goes into the 'tile' bins. @@ -162,6 +164,9 @@ struct lp_scene { /** list of resources referenced by the scene commands */ struct resource_ref *resources; + /** list of frag shaders referenced by the scene commands */ + struct shader_ref *frag_shaders; + /** 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. @@ -209,6 +214,10 @@ boolean lp_scene_add_resource_reference(struct lp_scene *scene, boolean lp_scene_is_resource_referenced(const struct lp_scene *scene, const struct pipe_resource *resource ); +boolean lp_scene_add_frag_shader_reference(struct lp_scene *scene, + struct lp_fragment_shader_variant *variant); + + /** * Allocate space for a command/data in the bin's data buffer. diff --git a/src/gallium/drivers/llvmpipe/lp_setup.c b/src/gallium/drivers/llvmpipe/lp_setup.c index 550062ffcfc..57d0acb41b1 100644 --- a/src/gallium/drivers/llvmpipe/lp_setup.c +++ b/src/gallium/drivers/llvmpipe/lp_setup.c @@ -128,6 +128,7 @@ void lp_setup_reset( struct lp_setup_context *setup ) setup->constants[i].stored_size = 0; setup->constants[i].stored_data = NULL; } + setup->fs.stored = NULL; setup->dirty = ~0; @@ -624,7 +625,6 @@ lp_setup_set_fs_variant( struct lp_setup_context *setup, { LP_DBG(DEBUG_SETUP, "%s %p\n", __FUNCTION__, variant); - /* FIXME: reference count */ setup->fs.current.variant = variant; setup->dirty |= LP_SETUP_NEW_FS; @@ -1280,9 +1280,14 @@ try_update_scene_state( struct lp_setup_context *setup ) return FALSE; } - memcpy(stored, - &setup->fs.current, - sizeof setup->fs.current); + memcpy(&stored->jit_context, + &setup->fs.current.jit_context, + sizeof setup->fs.current.jit_context); + stored->variant = setup->fs.current.variant; + + if (!lp_scene_add_frag_shader_reference(scene, + setup->fs.current.variant)) + return FALSE; setup->fs.stored = stored; /* The scene now references the textures in the rasterization diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs.c b/src/gallium/drivers/llvmpipe/lp_state_fs.c index 5166238a118..efb6bee2511 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_fs.c +++ b/src/gallium/drivers/llvmpipe/lp_state_fs.c @@ -3481,7 +3481,9 @@ generate_variant(struct llvmpipe_context *lp, snprintf(module_name, sizeof(module_name), "fs%u_variant%u", shader->no, shader->variants_created); - variant->shader = shader; + pipe_reference_init(&variant->reference, 1); + lp_fs_reference(lp, &variant->shader, shader); + memcpy(&variant->key, key, shader->variant_key_size); if (shader->base.ir.nir) { @@ -3588,6 +3590,7 @@ llvmpipe_create_fs_state(struct pipe_context *pipe, if (!shader) return NULL; + pipe_reference_init(&shader->reference, 1); shader->no = fs_no++; make_empty_list(&shader->variants); @@ -3687,8 +3690,10 @@ llvmpipe_bind_fs_state(struct pipe_context *pipe, void *fs) draw_bind_fragment_shader(llvmpipe->draw, (lp_fs ? lp_fs->draw_data : NULL)); - llvmpipe->fs = lp_fs; + lp_fs_reference(llvmpipe, &llvmpipe->fs, lp_fs); + /* invalidate the setup link, NEW_FS will make it update */ + lp_setup_set_fs_variant(llvmpipe->setup, NULL); llvmpipe->dirty |= LP_NEW_FS; } @@ -3697,9 +3702,10 @@ llvmpipe_bind_fs_state(struct pipe_context *pipe, void *fs) * Remove shader variant from two lists: the shader's variant list * and the context's variant list. */ -static void -llvmpipe_remove_shader_variant(struct llvmpipe_context *lp, - struct lp_fragment_shader_variant *variant) + +static +void llvmpipe_remove_shader_variant(struct llvmpipe_context *lp, + struct lp_fragment_shader_variant *variant) { if ((LP_DEBUG & DEBUG_FS) || (gallivm_debug & GALLIVM_DEBUG_IR)) { debug_printf("llvmpipe: del fs #%u var %u v created %u v cached %u " @@ -3710,8 +3716,6 @@ llvmpipe_remove_shader_variant(struct llvmpipe_context *lp, lp->nr_fs_variants, variant->nr_instrs, lp->nr_fs_instrs); } - gallivm_destroy(variant->gallivm); - /* remove from shader's list */ remove_from_list(&variant->list_item_local); variant->shader->variants_cached--; @@ -3720,10 +3724,32 @@ llvmpipe_remove_shader_variant(struct llvmpipe_context *lp, remove_from_list(&variant->list_item_global); lp->nr_fs_variants--; lp->nr_fs_instrs -= variant->nr_instrs; +} + +void +llvmpipe_destroy_shader_variant(struct llvmpipe_context *lp, + struct lp_fragment_shader_variant *variant) +{ + gallivm_destroy(variant->gallivm); + + lp_fs_reference(lp, &variant->shader, NULL); FREE(variant); } +void +llvmpipe_destroy_fs(struct llvmpipe_context *llvmpipe, + struct lp_fragment_shader *shader) +{ + /* Delete draw module's data */ + draw_delete_fragment_shader(llvmpipe->draw, shader->draw_data); + + if (shader->base.ir.nir) + ralloc_free(shader->base.ir.nir); + assert(shader->variants_cached == 0); + FREE((void *) shader->base.tokens); + FREE(shader); +} static void llvmpipe_delete_fs_state(struct pipe_context *pipe, void *fs) @@ -3732,35 +3758,20 @@ llvmpipe_delete_fs_state(struct pipe_context *pipe, void *fs) struct lp_fragment_shader *shader = fs; struct lp_fs_variant_list_item *li; - assert(fs != llvmpipe->fs); - - /* - * XXX: we need to flush the context until we have some sort of reference - * counting in fragment shaders as they may still be binned - * Flushing alone might not sufficient we need to wait on it too. - */ - llvmpipe_finish(pipe, __FUNCTION__); - /* Delete all the variants */ li = first_elem(&shader->variants); while(!at_end(&shader->variants, li)) { struct lp_fs_variant_list_item *next = next_elem(li); + struct lp_fragment_shader_variant *variant; + variant = li->base; llvmpipe_remove_shader_variant(llvmpipe, li->base); + lp_fs_variant_reference(llvmpipe, &variant, NULL); li = next; } - /* Delete draw module's data */ - draw_delete_fragment_shader(llvmpipe->draw, shader->draw_data); - - if (shader->base.ir.nir) - ralloc_free(shader->base.ir.nir); - assert(shader->variants_cached == 0); - FREE((void *) shader->base.tokens); - FREE(shader); + lp_fs_reference(llvmpipe, &shader, NULL); } - - static void llvmpipe_set_constant_buffer(struct pipe_context *pipe, enum pipe_shader_type shader, uint index, @@ -4181,8 +4192,6 @@ llvmpipe_update_fs(struct llvmpipe_context *lp) if (variants_to_cull || lp->nr_fs_instrs >= LP_MAX_SHADER_INSTRUCTIONS) { - struct pipe_context *pipe = &lp->pipe; - if (gallivm_debug & GALLIVM_DEBUG_PERF) { debug_printf("Evicting FS: %u fs variants,\t%u total variants," "\t%u instrs,\t%u instrs/variant\n", @@ -4191,13 +4200,6 @@ llvmpipe_update_fs(struct llvmpipe_context *lp) lp->nr_fs_instrs / lp->nr_fs_variants); } - /* - * XXX: we need to flush the context until we have some sort of - * reference counting in fragment shaders as they may still be binned - * Flushing alone might not be sufficient we need to wait on it too. - */ - llvmpipe_finish(pipe, __FUNCTION__); - /* * We need to re-check lp->nr_fs_variants because an arbitrarliy large * number of shader variants (potentially all of them) could be @@ -4213,6 +4215,7 @@ llvmpipe_update_fs(struct llvmpipe_context *lp) assert(item); assert(item->base); llvmpipe_remove_shader_variant(lp, item->base); + lp_fs_variant_reference(lp, &item->base, NULL); } } diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs.h b/src/gallium/drivers/llvmpipe/lp_state_fs.h index 555bacff4ec..4dd48db356e 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_fs.h +++ b/src/gallium/drivers/llvmpipe/lp_state_fs.h @@ -36,7 +36,7 @@ #include "gallivm/lp_bld_sample.h" /* for struct lp_sampler_static_state */ #include "gallivm/lp_bld_tgsi.h" /* for lp_tgsi_info */ #include "lp_bld_interp.h" /* for struct lp_shader_input */ - +#include "util/u_inlines.h" struct tgsi_token; struct lp_fragment_shader; @@ -128,7 +128,7 @@ struct lp_fs_variant_list_item struct lp_fragment_shader_variant { - + struct pipe_reference reference; boolean opaque; struct gallivm_state *gallivm; @@ -160,6 +160,7 @@ struct lp_fragment_shader { struct pipe_shader_state base; + struct pipe_reference reference; struct lp_tgsi_info info; struct lp_fs_variant_list_item variants; @@ -180,4 +181,36 @@ struct lp_fragment_shader void lp_debug_fs_variant(struct lp_fragment_shader_variant *variant); +void +llvmpipe_destroy_fs(struct llvmpipe_context *llvmpipe, + struct lp_fragment_shader *shader); + +static inline void +lp_fs_reference(struct llvmpipe_context *llvmpipe, + struct lp_fragment_shader **ptr, + struct lp_fragment_shader *shader) +{ + struct lp_fragment_shader *old_ptr = *ptr; + if (pipe_reference(old_ptr ? &(*ptr)->reference : NULL, shader ? &shader->reference : NULL)) { + llvmpipe_destroy_fs(llvmpipe, old_ptr); + } + *ptr = shader; +} + +void +llvmpipe_destroy_shader_variant(struct llvmpipe_context *lp, + struct lp_fragment_shader_variant *variant); + +static inline void +lp_fs_variant_reference(struct llvmpipe_context *llvmpipe, + struct lp_fragment_shader_variant **ptr, + struct lp_fragment_shader_variant *variant) +{ + struct lp_fragment_shader_variant *old_ptr = *ptr; + if (pipe_reference(old_ptr ? &(*ptr)->reference : NULL, variant ? &variant->reference : NULL)) { + llvmpipe_destroy_shader_variant(llvmpipe, old_ptr); + } + *ptr = variant; +} + #endif /* LP_STATE_FS_H_ */ -- 2.30.2