zink: add 'optimal_keys' handling for shader keys

if it's known on init that there are no variants for nonseamless cubes,
or inlined values, or decomposed vertex attrs, then shader keys can be
compressed more optimally to reduce the work needed on program updates

more importantly, this reduces the total hash value for all the shader
modules to a single uint32_t (technically 24 bits), which is much better
than having to manage and incrementally add all the separte module hashes

...but for now using this is incompatible with gpl, so disable that

Reviewed-by: Adam Jackson <ajax@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18786>
This commit is contained in:
Mike Blumenkrantz 2022-09-01 15:56:06 -04:00 committed by Marge Bot
parent 1198568e32
commit 325c703624
8 changed files with 248 additions and 52 deletions

View file

@ -2111,25 +2111,27 @@ zink_shader_compile(struct zink_screen *screen, struct zink_shader *zs, nir_shad
/* TODO: use a separate mem ctx here for ralloc */
switch (zs->nir->info.stage) {
case MESA_SHADER_VERTEX: {
uint32_t decomposed_attrs = 0, decomposed_attrs_without_w = 0;
const struct zink_vs_key *vs_key = zink_vs_key(key);
switch (vs_key->size) {
case 4:
decomposed_attrs = vs_key->u32.decomposed_attrs;
decomposed_attrs_without_w = vs_key->u32.decomposed_attrs_without_w;
break;
case 2:
decomposed_attrs = vs_key->u16.decomposed_attrs;
decomposed_attrs_without_w = vs_key->u16.decomposed_attrs_without_w;
break;
case 1:
decomposed_attrs = vs_key->u8.decomposed_attrs;
decomposed_attrs_without_w = vs_key->u8.decomposed_attrs_without_w;
break;
default: break;
if (!screen->optimal_keys) {
uint32_t decomposed_attrs = 0, decomposed_attrs_without_w = 0;
const struct zink_vs_key *vs_key = zink_vs_key(key);
switch (vs_key->size) {
case 4:
decomposed_attrs = vs_key->u32.decomposed_attrs;
decomposed_attrs_without_w = vs_key->u32.decomposed_attrs_without_w;
break;
case 2:
decomposed_attrs = vs_key->u16.decomposed_attrs;
decomposed_attrs_without_w = vs_key->u16.decomposed_attrs_without_w;
break;
case 1:
decomposed_attrs = vs_key->u8.decomposed_attrs;
decomposed_attrs_without_w = vs_key->u8.decomposed_attrs_without_w;
break;
default: break;
}
if (decomposed_attrs || decomposed_attrs_without_w)
NIR_PASS_V(nir, decompose_attribs, decomposed_attrs, decomposed_attrs_without_w);
}
if (decomposed_attrs || decomposed_attrs_without_w)
NIR_PASS_V(nir, decompose_attribs, decomposed_attrs, decomposed_attrs_without_w);
FALLTHROUGH;
}
case MESA_SHADER_TESS_EVAL:

View file

@ -1303,6 +1303,7 @@ zink_set_inlinable_constants(struct pipe_context *pctx,
if (shader == MESA_SHADER_COMPUTE) {
key = &ctx->compute_pipeline_state.key;
} else {
assert(!zink_screen(pctx->screen)->optimal_keys);
key = &ctx->gfx_pipeline_state.shader_keys.key[shader];
}
inlinable_uniforms = key->base.inlined_uniform_values;
@ -1353,6 +1354,7 @@ invalidate_inlined_uniforms(struct zink_context *ctx, gl_shader_stage pstage)
if (pstage == MESA_SHADER_COMPUTE)
return;
assert(!zink_screen(ctx->base.screen)->optimal_keys);
struct zink_shader_key *key = &ctx->gfx_pipeline_state.shader_keys.key[pstage];
key->inline_uniforms = false;
}
@ -4678,14 +4680,15 @@ zink_context_create(struct pipe_screen *pscreen, void *priv, unsigned flags)
goto fail;
}
ctx->gfx_pipeline_state.shader_keys.last_vertex.key.vs_base.last_vertex_stage = true;
ctx->last_vertex_stage_dirty = true;
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_CTRL].key.tcs.patch_vertices = 1;
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_VERTEX].size = sizeof(struct zink_vs_key_base);
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_EVAL].size = sizeof(struct zink_vs_key_base);
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_CTRL].size = sizeof(struct zink_tcs_key);
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_GEOMETRY].size = sizeof(struct zink_vs_key_base);
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_FRAGMENT].size = sizeof(struct zink_fs_key);
zink_set_last_vertex_key(ctx)->last_vertex_stage = true;
zink_set_tcs_key_patches(ctx, 1);
if (!screen->optimal_keys) {
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_VERTEX].size = sizeof(struct zink_vs_key_base);
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_EVAL].size = sizeof(struct zink_vs_key_base);
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_CTRL].size = sizeof(struct zink_tcs_key);
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_GEOMETRY].size = sizeof(struct zink_vs_key_base);
ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_FRAGMENT].size = sizeof(struct zink_fs_key);
}
_mesa_hash_table_init(&ctx->framebuffer_cache, ctx, hash_framebuffer_imageless, equals_framebuffer_imageless);
if (!zink_init_render_pass(ctx))
goto fail;

View file

@ -210,6 +210,98 @@ get_shader_module_for_stage(struct zink_context *ctx, struct zink_screen *screen
return NULL;
}
ALWAYS_INLINE static struct zink_shader_module *
create_shader_module_for_stage_optimal(struct zink_context *ctx, struct zink_screen *screen,
struct zink_shader *zs, struct zink_gfx_program *prog,
gl_shader_stage stage,
struct zink_gfx_pipeline_state *state)
{
VkShaderModule mod;
struct zink_shader_module *zm;
uint16_t *key;
unsigned mask = stage == MESA_SHADER_FRAGMENT ? BITFIELD_MASK(16) : BITFIELD_MASK(8);
if (zs == prog->last_vertex_stage) {
key = (uint16_t*)&state->shader_keys_optimal.key.vs_base;
} else if (stage == MESA_SHADER_FRAGMENT) {
key = (uint16_t*)&state->shader_keys_optimal.key.fs;
} else if (stage == MESA_SHADER_TESS_CTRL && zs->is_generated) {
key = (uint16_t*)&state->shader_keys_optimal.key.tcs;
} else {
key = NULL;
}
size_t key_size = sizeof(uint16_t);
zm = calloc(1, sizeof(struct zink_shader_module) + (key ? key_size : 0));
if (!zm) {
return NULL;
}
if (stage == MESA_SHADER_TESS_CTRL && zs->is_generated && zs->spirv) {
assert(ctx); //TODO async
struct zink_tcs_key *tcs = (struct zink_tcs_key*)key;
mod = zink_shader_tcs_compile(screen, zs, tcs->patch_vertices);
} else {
mod = zink_shader_compile(screen, zs, prog->nir[stage], (struct zink_shader_key*)key);
}
if (!mod) {
FREE(zm);
return NULL;
}
zm->shader = mod;
/* non-generated tcs won't use the shader key */
const bool is_nongenerated_tcs = stage == MESA_SHADER_TESS_CTRL && !zs->is_generated;
if (key && !is_nongenerated_tcs) {
zm->key_size = key_size;
uint16_t *data = (uint16_t*)zm->key;
/* sanitize actual key bits */
*data = (*key) & mask;
}
zm->default_variant = !util_dynarray_contains(&prog->shader_cache[stage][0][0], void*);
util_dynarray_append(&prog->shader_cache[stage][0][0], void*, zm);
return zm;
}
ALWAYS_INLINE static struct zink_shader_module *
get_shader_module_for_stage_optimal(struct zink_context *ctx, struct zink_screen *screen,
struct zink_shader *zs, struct zink_gfx_program *prog,
gl_shader_stage stage,
struct zink_gfx_pipeline_state *state)
{
/* non-generated tcs won't use the shader key */
const bool is_nongenerated_tcs = stage == MESA_SHADER_TESS_CTRL && !zs->is_generated;
uint16_t *key;
unsigned mask = stage == MESA_SHADER_FRAGMENT ? BITFIELD_MASK(16) : BITFIELD_MASK(8);
if (zs == prog->last_vertex_stage) {
key = (uint16_t*)&ctx->gfx_pipeline_state.shader_keys_optimal.key.vs_base;
} else if (stage == MESA_SHADER_FRAGMENT) {
key = (uint16_t*)&ctx->gfx_pipeline_state.shader_keys_optimal.key.fs;
} else if (stage == MESA_SHADER_TESS_CTRL && zs->is_generated) {
key = (uint16_t*)&ctx->gfx_pipeline_state.shader_keys_optimal.key.tcs;
} else {
key = NULL;
}
struct util_dynarray *shader_cache = &prog->shader_cache[stage][0][0];
unsigned count = util_dynarray_num_elements(shader_cache, struct zink_shader_module *);
struct zink_shader_module **pzm = shader_cache->data;
for (unsigned i = 0; i < count; i++) {
struct zink_shader_module *iter = pzm[i];
if (is_nongenerated_tcs) {
/* always match */
} else if (key) {
uint16_t val = (*key) & mask;
/* no key is bigger than uint16_t */
if (memcmp(iter->key, &val, sizeof(uint16_t)))
continue;
}
if (i > 0) {
struct zink_shader_module *zero = pzm[0];
pzm[0] = iter;
pzm[i] = zero;
}
return iter;
}
return NULL;
}
static void
zink_destroy_shader_module(struct zink_screen *screen, struct zink_shader_module *zm)
{
@ -278,6 +370,32 @@ update_gfx_shader_modules(struct zink_context *ctx,
}
}
ALWAYS_INLINE static void
update_gfx_shader_modules_optimal(struct zink_context *ctx,
struct zink_screen *screen,
struct zink_gfx_program *prog, uint32_t mask,
struct zink_gfx_pipeline_state *state)
{
assert(prog->modules[MESA_SHADER_VERTEX]);
for (unsigned i = 0; i < MESA_SHADER_COMPUTE; i++) {
if (!(mask & BITFIELD_BIT(i)))
continue;
assert(prog->shaders[i]);
struct zink_shader_module *zm = get_shader_module_for_stage_optimal(ctx, screen, prog->shaders[i], prog, i, state);
if (!zm)
zm = create_shader_module_for_stage_optimal(ctx, screen, prog->shaders[i], prog, i, state);
state->modules[i] = zm->shader;
if (prog->modules[i] == zm->shader)
continue;
state->modules_changed = true;
prog->modules[i] = zm->shader;
}
prog->last_variant_hash = state->shader_keys_optimal.key.val;
}
static void
generate_gfx_program_modules(struct zink_context *ctx, struct zink_screen *screen, struct zink_gfx_program *prog, struct zink_gfx_pipeline_state *state)
{
@ -306,12 +424,32 @@ generate_gfx_program_modules(struct zink_context *ctx, struct zink_screen *scree
variant_hash ^= prog->module_hash[i];
}
prog->last_variant_hash = variant_hash;
p_atomic_dec(&prog->base.reference.count);
state->modules_changed = true;
prog->last_variant_hash = variant_hash;
if (default_variants)
prog->default_variant_hash = prog->last_variant_hash;
}
static void
generate_gfx_program_modules_optimal(struct zink_context *ctx, struct zink_screen *screen, struct zink_gfx_program *prog, struct zink_gfx_pipeline_state *state)
{
assert(!prog->modules[MESA_SHADER_VERTEX]);
for (unsigned i = 0; i < MESA_SHADER_COMPUTE; i++) {
if (!(prog->stages_present & BITFIELD_BIT(i)))
continue;
assert(prog->shaders[i]);
struct zink_shader_module *zm = zm = create_shader_module_for_stage_optimal(ctx, screen, prog->shaders[i], prog, i, state);
state->modules[i] = zm->shader;
prog->modules[i] = zm->shader;
}
p_atomic_dec(&prog->base.reference.count);
state->modules_changed = true;
prog->last_variant_hash = state->shader_keys_optimal.key.val;
}
static uint32_t
@ -397,10 +535,16 @@ static void
update_gfx_program(struct zink_context *ctx, struct zink_gfx_program *prog)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
if (screen->info.have_EXT_non_seamless_cube_map)
update_gfx_program_nonseamless(ctx, prog, true);
else
update_gfx_program_nonseamless(ctx, prog, false);
if (screen->optimal_keys) {
update_gfx_shader_modules_optimal(ctx, screen, prog,
ctx->dirty_shader_stages & prog->stages_present,
&ctx->gfx_pipeline_state);
} else {
if (screen->info.have_EXT_non_seamless_cube_map)
update_gfx_program_nonseamless(ctx, prog, true);
else
update_gfx_program_nonseamless(ctx, prog, false);
}
}
void
@ -409,9 +553,11 @@ zink_gfx_program_update(struct zink_context *ctx)
if (ctx->last_vertex_stage_dirty) {
gl_shader_stage pstage = ctx->last_vertex_stage->nir->info.stage;
ctx->dirty_shader_stages |= BITFIELD_BIT(pstage);
memcpy(&ctx->gfx_pipeline_state.shader_keys.key[pstage].key.vs_base,
&ctx->gfx_pipeline_state.shader_keys.last_vertex.key.vs_base,
sizeof(struct zink_vs_key_base));
if (!zink_screen(ctx->base.screen)->optimal_keys) {
memcpy(&ctx->gfx_pipeline_state.shader_keys.key[pstage].key.vs_base,
&ctx->gfx_pipeline_state.shader_keys.last_vertex.key.vs_base,
sizeof(struct zink_vs_key_base));
}
ctx->last_vertex_stage_dirty = false;
}
unsigned bits = BITFIELD_MASK(MESA_SHADER_COMPUTE);
@ -435,7 +581,10 @@ zink_gfx_program_update(struct zink_context *ctx)
ctx->dirty_shader_stages |= bits;
prog = zink_create_gfx_program(ctx, ctx->gfx_stages, ctx->gfx_pipeline_state.dyn_state2.vertices_per_patch);
_mesa_hash_table_insert_pre_hashed(ht, hash, prog->shaders, prog);
generate_gfx_program_modules(ctx, zink_screen(ctx->base.screen), prog, &ctx->gfx_pipeline_state);
if (zink_screen(ctx->base.screen)->optimal_keys)
generate_gfx_program_modules_optimal(ctx, zink_screen(ctx->base.screen), prog, &ctx->gfx_pipeline_state);
else
generate_gfx_program_modules(ctx, zink_screen(ctx->base.screen), prog, &ctx->gfx_pipeline_state);
}
simple_mtx_unlock(&ctx->program_lock[zink_program_cache_stages(ctx->shader_stages)]);
if (prog && prog != ctx->curr_program)
@ -1085,12 +1234,14 @@ bind_last_vertex_stage(struct zink_context *ctx)
ctx->last_vertex_stage = ctx->gfx_stages[MESA_SHADER_VERTEX];
gl_shader_stage current = ctx->last_vertex_stage ? ctx->last_vertex_stage->nir->info.stage : MESA_SHADER_VERTEX;
if (old != current) {
if (old != MESA_SHADER_STAGES) {
memset(&ctx->gfx_pipeline_state.shader_keys.key[old].key.vs_base, 0, sizeof(struct zink_vs_key_base));
ctx->dirty_shader_stages |= BITFIELD_BIT(old);
} else {
/* always unset vertex shader values when changing to a non-vs last stage */
memset(&ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_VERTEX].key.vs_base, 0, sizeof(struct zink_vs_key_base));
if (!zink_screen(ctx->base.screen)->optimal_keys) {
if (old != MESA_SHADER_STAGES) {
memset(&ctx->gfx_pipeline_state.shader_keys.key[old].key.vs_base, 0, sizeof(struct zink_vs_key_base));
ctx->dirty_shader_stages |= BITFIELD_BIT(old);
} else {
/* always unset vertex shader values when changing to a non-vs last stage */
memset(&ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_VERTEX].key.vs_base, 0, sizeof(struct zink_vs_key_base));
}
}
unsigned num_viewports = ctx->vp_state.num_viewports;
@ -1349,6 +1500,8 @@ zink_program_init(struct zink_context *ctx)
STATIC_ASSERT(offsetof(struct zink_gfx_pipeline_state, modules) - offsetof(struct zink_gfx_pipeline_state, gkey) ==
offsetof(struct zink_gfx_library_key, modules) - offsetof(struct zink_gfx_library_key, hw_rast_state));
STATIC_ASSERT(sizeof(union zink_shader_key_optimal) == sizeof(uint32_t));
}
bool

View file

@ -224,19 +224,25 @@ static inline struct zink_fs_key *
zink_set_fs_key(struct zink_context *ctx)
{
ctx->dirty_shader_stages |= BITFIELD_BIT(MESA_SHADER_FRAGMENT);
return &ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_FRAGMENT].key.fs;
return zink_screen(ctx->base.screen)->optimal_keys ?
&ctx->gfx_pipeline_state.shader_keys_optimal.key.fs :
&ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_FRAGMENT].key.fs;
}
static inline const struct zink_fs_key *
zink_get_fs_key(struct zink_context *ctx)
{
return &ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_FRAGMENT].key.fs;
return zink_screen(ctx->base.screen)->optimal_keys ?
&ctx->gfx_pipeline_state.shader_keys_optimal.key.fs :
&ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_FRAGMENT].key.fs;
}
static inline bool
zink_set_tcs_key_patches(struct zink_context *ctx, uint8_t patch_vertices)
{
struct zink_tcs_key *tcs = &ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_CTRL].key.tcs;
struct zink_tcs_key *tcs = zink_screen(ctx->base.screen)->optimal_keys ?
&ctx->gfx_pipeline_state.shader_keys_optimal.key.tcs :
&ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_CTRL].key.tcs;
if (tcs->patch_vertices == patch_vertices)
return false;
ctx->dirty_shader_stages |= BITFIELD_BIT(MESA_SHADER_TESS_CTRL);
@ -247,7 +253,9 @@ zink_set_tcs_key_patches(struct zink_context *ctx, uint8_t patch_vertices)
static inline const struct zink_tcs_key *
zink_get_tcs_key(struct zink_context *ctx)
{
return &ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_CTRL].key.tcs;
return zink_screen(ctx->base.screen)->optimal_keys ?
&ctx->gfx_pipeline_state.shader_keys_optimal.key.tcs :
&ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_TESS_CTRL].key.tcs;
}
void
@ -257,12 +265,14 @@ static inline struct zink_vs_key *
zink_set_vs_key(struct zink_context *ctx)
{
ctx->dirty_shader_stages |= BITFIELD_BIT(MESA_SHADER_VERTEX);
assert(!zink_screen(ctx->base.screen)->optimal_keys);
return &ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_VERTEX].key.vs;
}
static inline const struct zink_vs_key *
zink_get_vs_key(struct zink_context *ctx)
{
assert(!zink_screen(ctx->base.screen)->optimal_keys);
return &ctx->gfx_pipeline_state.shader_keys.key[MESA_SHADER_VERTEX].key.vs;
}
@ -270,13 +280,17 @@ static inline struct zink_vs_key_base *
zink_set_last_vertex_key(struct zink_context *ctx)
{
ctx->last_vertex_stage_dirty = true;
return &ctx->gfx_pipeline_state.shader_keys.last_vertex.key.vs_base;
return zink_screen(ctx->base.screen)->optimal_keys ?
&ctx->gfx_pipeline_state.shader_keys_optimal.key.vs_base :
&ctx->gfx_pipeline_state.shader_keys.last_vertex.key.vs_base;
}
static inline const struct zink_vs_key_base *
zink_get_last_vertex_key(struct zink_context *ctx)
{
return &ctx->gfx_pipeline_state.shader_keys.last_vertex.key.vs_base;
return zink_screen(ctx->base.screen)->optimal_keys ?
&ctx->gfx_pipeline_state.shader_keys_optimal.key.vs_base :
&ctx->gfx_pipeline_state.shader_keys.last_vertex.key.vs_base;
}
static inline void
@ -295,6 +309,7 @@ zink_set_fs_point_coord_key(struct zink_context *ctx)
static inline const struct zink_shader_key_base *
zink_get_shader_key_base(struct zink_context *ctx, gl_shader_stage pstage)
{
assert(!zink_screen(ctx->base.screen)->optimal_keys);
return &ctx->gfx_pipeline_state.shader_keys.key[pstage].base;
}
@ -302,6 +317,7 @@ static inline struct zink_shader_key_base *
zink_set_shader_key_base(struct zink_context *ctx, gl_shader_stage pstage)
{
ctx->dirty_shader_stages |= BITFIELD_BIT(pstage);
assert(!zink_screen(ctx->base.screen)->optimal_keys);
return &ctx->gfx_pipeline_state.shader_keys.key[pstage].base;
}

View file

@ -2534,6 +2534,11 @@ zink_internal_create_screen(const struct pipe_screen_config *config)
goto fail;
}
if (!screen->driver_workarounds.force_pipeline_library)
screen->optimal_keys = !screen->need_decompose_attrs && screen->info.have_EXT_non_seamless_cube_map && !screen->driconf.inline_uniforms;
if (screen->optimal_keys)
screen->driver_workarounds.force_pipeline_library = false;
return screen;
fail:

View file

@ -93,6 +93,15 @@ struct zink_shader_key {
uint32_t size;
};
union zink_shader_key_optimal {
struct {
struct zink_vs_key_base vs_base;
struct zink_tcs_key tcs;
struct zink_fs_key fs;
};
uint32_t val;
};
static inline const struct zink_fs_key *
zink_fs_key(const struct zink_shader_key *key)
{

View file

@ -185,6 +185,9 @@ zink_bind_vertex_elements_state(struct pipe_context *pctx,
ctx->vertex_state_changed = !zink_screen(pctx->screen)->info.have_EXT_vertex_input_dynamic_state;
ctx->vertex_buffers_dirty = ctx->element_state->hw_state.num_bindings > 0;
}
state->element_state = &ctx->element_state->hw_state;
if (zink_screen(pctx->screen)->optimal_keys)
return;
const struct zink_vs_key *vs = zink_get_vs_key(ctx);
uint32_t decomposed_attrs = 0, decomposed_attrs_without_w = 0;
switch (vs->size) {
@ -224,7 +227,6 @@ zink_bind_vertex_elements_state(struct pipe_context *pctx,
key->key.vs.size = size;
key->size += 2 * size;
}
state->element_state = &ctx->element_state->hw_state;
} else {
state->element_state = NULL;
ctx->vertex_buffers_dirty = false;

View file

@ -683,10 +683,15 @@ struct zink_gfx_pipeline_state {
struct zink_vertex_elements_hw_state *element_state;
bool sample_locations_enabled;
uint8_t has_points; //either gs outputs points or prim type is points
struct {
struct zink_shader_key key[5];
struct zink_shader_key last_vertex;
} shader_keys;
union {
struct {
struct zink_shader_key key[5];
struct zink_shader_key last_vertex;
} shader_keys;
struct {
union zink_shader_key_optimal key;
} shader_keys_optimal;
};
struct zink_blend_state *blend_state;
struct zink_render_pass *render_pass;
struct zink_render_pass *next_render_pass; //will be used next time rp is begun
@ -1108,6 +1113,7 @@ struct zink_screen {
struct zink_device_info info;
struct nir_shader_compiler_options nir_options;
bool optimal_keys;
bool have_X8_D24_UNORM_PACK32;
bool have_D24_UNORM_S8_UINT;
bool have_D32_SFLOAT_S8_UINT;