iris: Refcount shader variants
There is a small gap of time where the currently bound uncompiled shaders, and compiled shader variant, are out of sync. Specifically, between pipe->bind_*_state() and the next draw. Currently, shaders variants live entirely within a single context, and when deleting an iris_uncompiled_shader, we check if any of its variants are currently bound, and defer deleting those until the next iris_update_compiled_shaders() hook runs and binds new shaders to replace them. (This is due to the time gap between binding new uncompiled shaders, and updating variants at draw time when we have the required NOS in place.) This works pretty well in a single context world. But as we move to share compiled shader variants across multiple contexts, it breaks down. When deleting a shader, we can't look at all contexts to see if its variants are bound anywhere. We can't even quantify whether those contexts will run a future draw any time soon, to update and unbind. One fairly crazy solution would be to delete the variants anyway, and leave the stale pointers to dead variants in place. This requires removing any code that compares old and new variants. Today, we do that sometimes for seeing if the old/new shaders toggled some feature. Worse than that, though, we don't just have to avoid dereferences, we'd have to avoid pointer comparisons. If we free a variant, and quickly allocate a new variant, malloc may return the same pointer. If it's for the same shader stage, we may get a new different program that has the same pointer as a previously bound stale one, causing us to think nothing had changed when we really needed to do updates. Again, this is doable, but leaves the code fragile - we'd have to guard against future patches adding such checks back in. So, don't do that. Instead, do basic reference counting. When a variant is bound in a context, up the reference. When it's unbound, decrement it. When it hits zero, we know it's not bound anywhere and is safe to delete, with no stale references. This ends up being reasonably cheap anyway, since the atomic is usually uncontested. Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7668>
This commit is contained in:
parent
bef0af3f21
commit
578cd00d93
|
@ -429,7 +429,7 @@ struct iris_binding_table {
|
||||||
* (iris_uncompiled_shader), due to state-based recompiles (brw_*_prog_key).
|
* (iris_uncompiled_shader), due to state-based recompiles (brw_*_prog_key).
|
||||||
*/
|
*/
|
||||||
struct iris_compiled_shader {
|
struct iris_compiled_shader {
|
||||||
struct list_head link;
|
struct pipe_reference ref;
|
||||||
|
|
||||||
/** Reference to the uploaded assembly. */
|
/** Reference to the uploaded assembly. */
|
||||||
struct iris_state_ref assembly;
|
struct iris_state_ref assembly;
|
||||||
|
@ -615,9 +615,6 @@ struct iris_context {
|
||||||
bool constrained;
|
bool constrained;
|
||||||
} urb;
|
} urb;
|
||||||
|
|
||||||
/** List of shader variants whose deletion has been deferred for now */
|
|
||||||
struct list_head deleted_variants[MESA_SHADER_STAGES];
|
|
||||||
|
|
||||||
struct u_upload_mgr *uploader;
|
struct u_upload_mgr *uploader;
|
||||||
struct hash_table *cache;
|
struct hash_table *cache;
|
||||||
|
|
||||||
|
@ -911,8 +908,20 @@ struct iris_compiled_shader *iris_upload_shader(struct iris_context *ice,
|
||||||
const void *iris_find_previous_compile(const struct iris_context *ice,
|
const void *iris_find_previous_compile(const struct iris_context *ice,
|
||||||
enum iris_program_cache_id cache_id,
|
enum iris_program_cache_id cache_id,
|
||||||
unsigned program_string_id);
|
unsigned program_string_id);
|
||||||
void iris_delete_shader_variants(struct iris_context *ice,
|
void iris_delete_shader_variant(struct iris_compiled_shader *shader);
|
||||||
struct iris_uncompiled_shader *ish);
|
|
||||||
|
static inline void
|
||||||
|
iris_shader_variant_reference(struct iris_compiled_shader **dst,
|
||||||
|
struct iris_compiled_shader *src)
|
||||||
|
{
|
||||||
|
struct iris_compiled_shader *old_dst = *dst;
|
||||||
|
|
||||||
|
if (pipe_reference(old_dst ? &old_dst->ref: NULL, src ? &src->ref : NULL))
|
||||||
|
iris_delete_shader_variant(old_dst);
|
||||||
|
|
||||||
|
*dst = src;
|
||||||
|
}
|
||||||
|
|
||||||
bool iris_blorp_lookup_shader(struct blorp_batch *blorp_batch,
|
bool iris_blorp_lookup_shader(struct blorp_batch *blorp_batch,
|
||||||
const void *key,
|
const void *key,
|
||||||
uint32_t key_size,
|
uint32_t key_size,
|
||||||
|
|
|
@ -1213,7 +1213,8 @@ iris_update_compiled_vs(struct iris_context *ice)
|
||||||
shader = iris_compile_vs(ice, ish, &key);
|
shader = iris_compile_vs(ice, ish, &key);
|
||||||
|
|
||||||
if (old != shader) {
|
if (old != shader) {
|
||||||
ice->shaders.prog[IRIS_CACHE_VS] = shader;
|
iris_shader_variant_reference(&ice->shaders.prog[MESA_SHADER_VERTEX],
|
||||||
|
shader);
|
||||||
ice->state.dirty |= IRIS_DIRTY_VF_SGVS;
|
ice->state.dirty |= IRIS_DIRTY_VF_SGVS;
|
||||||
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_VS |
|
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_VS |
|
||||||
IRIS_STAGE_DIRTY_BINDINGS_VS |
|
IRIS_STAGE_DIRTY_BINDINGS_VS |
|
||||||
|
@ -1411,7 +1412,8 @@ iris_update_compiled_tcs(struct iris_context *ice)
|
||||||
shader = iris_compile_tcs(ice, tcs, &key);
|
shader = iris_compile_tcs(ice, tcs, &key);
|
||||||
|
|
||||||
if (old != shader) {
|
if (old != shader) {
|
||||||
ice->shaders.prog[IRIS_CACHE_TCS] = shader;
|
iris_shader_variant_reference(&ice->shaders.prog[MESA_SHADER_TESS_CTRL],
|
||||||
|
shader);
|
||||||
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_TCS |
|
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_TCS |
|
||||||
IRIS_STAGE_DIRTY_BINDINGS_TCS |
|
IRIS_STAGE_DIRTY_BINDINGS_TCS |
|
||||||
IRIS_STAGE_DIRTY_CONSTANTS_TCS;
|
IRIS_STAGE_DIRTY_CONSTANTS_TCS;
|
||||||
|
@ -1525,7 +1527,8 @@ iris_update_compiled_tes(struct iris_context *ice)
|
||||||
shader = iris_compile_tes(ice, ish, &key);
|
shader = iris_compile_tes(ice, ish, &key);
|
||||||
|
|
||||||
if (old != shader) {
|
if (old != shader) {
|
||||||
ice->shaders.prog[IRIS_CACHE_TES] = shader;
|
iris_shader_variant_reference(&ice->shaders.prog[MESA_SHADER_TESS_EVAL],
|
||||||
|
shader);
|
||||||
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_TES |
|
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_TES |
|
||||||
IRIS_STAGE_DIRTY_BINDINGS_TES |
|
IRIS_STAGE_DIRTY_BINDINGS_TES |
|
||||||
IRIS_STAGE_DIRTY_CONSTANTS_TES;
|
IRIS_STAGE_DIRTY_CONSTANTS_TES;
|
||||||
|
@ -1647,7 +1650,8 @@ iris_update_compiled_gs(struct iris_context *ice)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old != shader) {
|
if (old != shader) {
|
||||||
ice->shaders.prog[IRIS_CACHE_GS] = shader;
|
iris_shader_variant_reference(&ice->shaders.prog[MESA_SHADER_GEOMETRY],
|
||||||
|
shader);
|
||||||
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_GS |
|
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_GS |
|
||||||
IRIS_STAGE_DIRTY_BINDINGS_GS |
|
IRIS_STAGE_DIRTY_BINDINGS_GS |
|
||||||
IRIS_STAGE_DIRTY_CONSTANTS_GS;
|
IRIS_STAGE_DIRTY_CONSTANTS_GS;
|
||||||
|
@ -1763,7 +1767,8 @@ iris_update_compiled_fs(struct iris_context *ice)
|
||||||
if (old != shader) {
|
if (old != shader) {
|
||||||
// XXX: only need to flag CLIP if barycentric has NONPERSPECTIVE
|
// XXX: only need to flag CLIP if barycentric has NONPERSPECTIVE
|
||||||
// toggles. might be able to avoid flagging SBE too.
|
// toggles. might be able to avoid flagging SBE too.
|
||||||
ice->shaders.prog[IRIS_CACHE_FS] = shader;
|
iris_shader_variant_reference(&ice->shaders.prog[MESA_SHADER_FRAGMENT],
|
||||||
|
shader);
|
||||||
ice->state.dirty |= IRIS_DIRTY_WM |
|
ice->state.dirty |= IRIS_DIRTY_WM |
|
||||||
IRIS_DIRTY_CLIP |
|
IRIS_DIRTY_CLIP |
|
||||||
IRIS_DIRTY_SBE;
|
IRIS_DIRTY_SBE;
|
||||||
|
@ -1858,8 +1863,8 @@ iris_update_compiled_shaders(struct iris_context *ice)
|
||||||
iris_update_compiled_tcs(ice);
|
iris_update_compiled_tcs(ice);
|
||||||
iris_update_compiled_tes(ice);
|
iris_update_compiled_tes(ice);
|
||||||
} else {
|
} else {
|
||||||
ice->shaders.prog[IRIS_CACHE_TCS] = NULL;
|
iris_shader_variant_reference(&ice->shaders.prog[MESA_SHADER_TESS_CTRL], NULL);
|
||||||
ice->shaders.prog[IRIS_CACHE_TES] = NULL;
|
iris_shader_variant_reference(&ice->shaders.prog[MESA_SHADER_TESS_EVAL], NULL);
|
||||||
ice->state.stage_dirty |=
|
ice->state.stage_dirty |=
|
||||||
IRIS_STAGE_DIRTY_TCS | IRIS_STAGE_DIRTY_TES |
|
IRIS_STAGE_DIRTY_TCS | IRIS_STAGE_DIRTY_TES |
|
||||||
IRIS_STAGE_DIRTY_BINDINGS_TCS | IRIS_STAGE_DIRTY_BINDINGS_TES |
|
IRIS_STAGE_DIRTY_BINDINGS_TCS | IRIS_STAGE_DIRTY_BINDINGS_TES |
|
||||||
|
@ -2005,7 +2010,8 @@ iris_update_compiled_cs(struct iris_context *ice)
|
||||||
shader = iris_compile_cs(ice, ish, &key);
|
shader = iris_compile_cs(ice, ish, &key);
|
||||||
|
|
||||||
if (old != shader) {
|
if (old != shader) {
|
||||||
ice->shaders.prog[IRIS_CACHE_CS] = shader;
|
iris_shader_variant_reference(&ice->shaders.prog[MESA_SHADER_COMPUTE],
|
||||||
|
shader);
|
||||||
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_CS |
|
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_CS |
|
||||||
IRIS_STAGE_DIRTY_BINDINGS_CS |
|
IRIS_STAGE_DIRTY_BINDINGS_CS |
|
||||||
IRIS_STAGE_DIRTY_CONSTANTS_CS;
|
IRIS_STAGE_DIRTY_CONSTANTS_CS;
|
||||||
|
@ -2409,8 +2415,6 @@ iris_delete_shader_state(struct pipe_context *ctx, void *state, gl_shader_stage
|
||||||
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_UNCOMPILED_VS << stage;
|
ice->state.stage_dirty |= IRIS_STAGE_DIRTY_UNCOMPILED_VS << stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
iris_delete_shader_variants(ice, ish);
|
|
||||||
|
|
||||||
ralloc_free(ish->nir);
|
ralloc_free(ish->nir);
|
||||||
free(ish);
|
free(ish);
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,58 +117,12 @@ iris_find_previous_compile(const struct iris_context *ice,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
iris_delete_shader_variants(struct iris_context *ice,
|
iris_delete_shader_variant(struct iris_compiled_shader *shader)
|
||||||
struct iris_uncompiled_shader *ish)
|
|
||||||
{
|
{
|
||||||
struct hash_table *cache = ice->shaders.cache;
|
pipe_resource_reference(&shader->assembly.res, NULL);
|
||||||
gl_shader_stage stage = ish->nir->info.stage;
|
ralloc_free(shader);
|
||||||
enum iris_program_cache_id cache_id = stage;
|
|
||||||
|
|
||||||
hash_table_foreach(cache, entry) {
|
|
||||||
const struct keybox *keybox = entry->key;
|
|
||||||
const struct brw_base_prog_key *key = (const void *)keybox->data;
|
|
||||||
|
|
||||||
if (keybox->cache_id == cache_id &&
|
|
||||||
key->program_string_id == ish->program_id) {
|
|
||||||
struct iris_compiled_shader *shader = entry->data;
|
|
||||||
|
|
||||||
_mesa_hash_table_remove(cache, entry);
|
|
||||||
|
|
||||||
/* Shader variants may still be bound in the context even after
|
|
||||||
* the API-facing shader has been deleted. In particular, a draw
|
|
||||||
* may not have triggered iris_update_compiled_shaders() yet. In
|
|
||||||
* that case, we may be referring to that shader's VUE map, stream
|
|
||||||
* output settings, and so on. We also like to compare the old and
|
|
||||||
* new shader programs when swapping them out to flag dirty state.
|
|
||||||
*
|
|
||||||
* So, it's hazardous to delete a bound shader variant. We avoid
|
|
||||||
* doing so, choosing to instead move "deleted" shader variants to
|
|
||||||
* a list, deferring the actual deletion until they're not bound.
|
|
||||||
*
|
|
||||||
* For simplicity, we always move deleted variants to the list,
|
|
||||||
* even if we could delete them immediately. We'll then process
|
|
||||||
* the list, catching both these variants and any others.
|
|
||||||
*/
|
|
||||||
list_addtail(&shader->link, &ice->shaders.deleted_variants[stage]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process any pending deferred variant deletions. */
|
|
||||||
list_for_each_entry_safe(struct iris_compiled_shader, shader,
|
|
||||||
&ice->shaders.deleted_variants[stage], link) {
|
|
||||||
/* If the shader is still bound, defer deletion. */
|
|
||||||
if (ice->shaders.prog[stage] == shader)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
list_del(&shader->link);
|
|
||||||
|
|
||||||
/* Actually delete the variant. */
|
|
||||||
pipe_resource_reference(&shader->assembly.res, NULL);
|
|
||||||
ralloc_free(shader);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct iris_compiled_shader *
|
struct iris_compiled_shader *
|
||||||
iris_upload_shader(struct iris_context *ice,
|
iris_upload_shader(struct iris_context *ice,
|
||||||
enum iris_program_cache_id cache_id,
|
enum iris_program_cache_id cache_id,
|
||||||
|
@ -189,6 +143,8 @@ iris_upload_shader(struct iris_context *ice,
|
||||||
rzalloc_size(cache, sizeof(struct iris_compiled_shader) +
|
rzalloc_size(cache, sizeof(struct iris_compiled_shader) +
|
||||||
screen->vtbl.derived_program_state_size(cache_id));
|
screen->vtbl.derived_program_state_size(cache_id));
|
||||||
|
|
||||||
|
pipe_reference_init(&shader->ref, 1);
|
||||||
|
|
||||||
shader->assembly.res = NULL;
|
shader->assembly.res = NULL;
|
||||||
u_upload_alloc(ice->shaders.uploader, 0, prog_data->program_size, 64,
|
u_upload_alloc(ice->shaders.uploader, 0, prog_data->program_size, 64,
|
||||||
&shader->assembly.offset, &shader->assembly.res,
|
&shader->assembly.offset, &shader->assembly.res,
|
||||||
|
@ -213,8 +169,6 @@ iris_upload_shader(struct iris_context *ice,
|
||||||
brw_write_shader_relocs(&screen->devinfo, shader->map, prog_data,
|
brw_write_shader_relocs(&screen->devinfo, shader->map, prog_data,
|
||||||
reloc_values, ARRAY_SIZE(reloc_values));
|
reloc_values, ARRAY_SIZE(reloc_values));
|
||||||
|
|
||||||
list_inithead(&shader->link);
|
|
||||||
|
|
||||||
shader->prog_data = prog_data;
|
shader->prog_data = prog_data;
|
||||||
shader->streamout = streamout;
|
shader->streamout = streamout;
|
||||||
shader->system_values = system_values;
|
shader->system_values = system_values;
|
||||||
|
@ -304,26 +258,18 @@ iris_init_program_cache(struct iris_context *ice)
|
||||||
ice->shaders.uploader =
|
ice->shaders.uploader =
|
||||||
u_upload_create(&ice->ctx, 16384, PIPE_BIND_CUSTOM, PIPE_USAGE_IMMUTABLE,
|
u_upload_create(&ice->ctx, 16384, PIPE_BIND_CUSTOM, PIPE_USAGE_IMMUTABLE,
|
||||||
IRIS_RESOURCE_FLAG_SHADER_MEMZONE);
|
IRIS_RESOURCE_FLAG_SHADER_MEMZONE);
|
||||||
|
|
||||||
for (int i = 0; i < MESA_SHADER_STAGES; i++)
|
|
||||||
list_inithead(&ice->shaders.deleted_variants[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
iris_destroy_program_cache(struct iris_context *ice)
|
iris_destroy_program_cache(struct iris_context *ice)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < MESA_SHADER_STAGES; i++) {
|
for (int i = 0; i < MESA_SHADER_STAGES; i++) {
|
||||||
ice->shaders.prog[i] = NULL;
|
iris_shader_variant_reference(&ice->shaders.prog[i], NULL);
|
||||||
|
|
||||||
list_for_each_entry_safe(struct iris_compiled_shader, shader,
|
|
||||||
&ice->shaders.deleted_variants[i], link) {
|
|
||||||
pipe_resource_reference(&shader->assembly.res, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash_table_foreach(ice->shaders.cache, entry) {
|
hash_table_foreach(ice->shaders.cache, entry) {
|
||||||
struct iris_compiled_shader *shader = entry->data;
|
struct iris_compiled_shader *shader = entry->data;
|
||||||
pipe_resource_reference(&shader->assembly.res, NULL);
|
iris_delete_shader_variant(shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
u_upload_destroy(ice->shaders.uploader);
|
u_upload_destroy(ice->shaders.uploader);
|
||||||
|
|
Loading…
Reference in New Issue