spirv2dxil: Implement lowering for multiview

D3D's view instancing is an optional feature, and even when it's
supported, it only goes up to 4 views, where Vulkan requires a
minimum of 6 supported views. So, we need to have a path for handling
the cases where we can't use the native feature.

In this mode, pass the view ID as a runtime var. The caller is then
responsible for looping the draw calls and filling out the constant
buffer value correctly for each draw. When we get to the last pre-rast
stage, we'll additionally want to write out gl_Layer to select the
right RTV array slice. Lastly, for the fragment shader, if there's
any input attachments, those get loaded using the RTV slice instead
of the view ID. RTV slice input into the PS is done with a signature
entry (which must be output from the previous stage) rather than a
system value.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/20650>
This commit is contained in:
Jesse Natalie 2023-01-11 10:53:45 -08:00 committed by Marge Bot
parent 2d56b3214d
commit 95a90b359a
2 changed files with 88 additions and 1 deletions

View file

@ -153,6 +153,11 @@ lower_shader_system_values(struct nir_builder *builder, nir_instr *instr,
case nir_intrinsic_load_draw_id:
offset = offsetof(struct dxil_spirv_vertex_runtime_data, draw_id);
break;
case nir_intrinsic_load_view_index:
if (!conf->lower_view_index)
return false;
offset = offsetof(struct dxil_spirv_vertex_runtime_data, view_index);
break;
default:
return false;
}
@ -733,6 +738,78 @@ dxil_spirv_compute_pntc(nir_shader *nir)
pos);
}
static bool
lower_view_index_to_rt_layer_instr(nir_builder *b, nir_instr *instr, void *data)
{
if (instr->type != nir_instr_type_intrinsic)
return false;
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
if (intr->intrinsic != nir_intrinsic_store_deref)
return false;
nir_variable *var = nir_intrinsic_get_var(intr, 0);
if (!var ||
var->data.mode != nir_var_shader_out ||
var->data.location != VARYING_SLOT_LAYER)
return false;
b->cursor = nir_before_instr(instr);
nir_ssa_def *layer = intr->src[1].ssa;
nir_ssa_def *new_layer = nir_iadd(b, layer,
nir_load_view_index(b));
nir_instr_rewrite_src_ssa(instr, &intr->src[1], new_layer);
return true;
}
static bool
add_layer_write(nir_builder *b, nir_instr *instr, void *data)
{
if (instr) {
if (instr->type != nir_instr_type_intrinsic)
return false;
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
if (intr->intrinsic != nir_intrinsic_emit_vertex &&
intr->intrinsic != nir_intrinsic_emit_vertex_with_counter)
return false;
b->cursor = nir_before_instr(instr);
}
nir_variable *var = (nir_variable *)data;
nir_store_var(b, var, nir_load_view_index(b), 0x1);
return true;
}
static void
lower_view_index_to_rt_layer(nir_shader *nir)
{
bool existing_write =
nir_shader_instructions_pass(nir,
lower_view_index_to_rt_layer_instr,
nir_metadata_block_index |
nir_metadata_dominance |
nir_metadata_loop_analysis, NULL);
if (existing_write)
return;
nir_variable *var = nir_variable_create(nir, nir_var_shader_out,
glsl_uint_type(), "gl_Layer");
var->data.location = VARYING_SLOT_LAYER;
var->data.interpolation = INTERP_MODE_FLAT;
if (nir->info.stage == MESA_SHADER_GEOMETRY) {
nir_shader_instructions_pass(nir,
add_layer_write,
nir_metadata_block_index |
nir_metadata_dominance |
nir_metadata_loop_analysis, var);
} else {
nir_function_impl *func = nir_shader_get_entrypoint(nir);
nir_builder b;
nir_builder_init(&b, func);
b.cursor = nir_after_block(nir_impl_last_block(func));
add_layer_write(&b, NULL, var);
}
}
void
dxil_spirv_nir_link(nir_shader *nir, nir_shader *prev_stage_nir,
const struct dxil_spirv_runtime_conf *conf,
@ -806,6 +883,9 @@ dxil_spirv_nir_passes(nir_shader *nir,
ARRAY_SIZE(system_values));
}
if (conf->lower_view_index_to_rt_layer)
NIR_PASS_V(nir, lower_view_index_to_rt_layer);
*requires_runtime_data = false;
NIR_PASS(*requires_runtime_data, nir,
dxil_spirv_nir_lower_shader_system_values,
@ -815,7 +895,8 @@ dxil_spirv_nir_passes(nir_shader *nir,
NIR_PASS_V(nir, nir_lower_input_attachments,
&(nir_input_attachment_options){
.use_fragcoord_sysval = false,
.use_layer_id_sysval = true,
.use_layer_id_sysval = !conf->lower_view_index,
.use_view_id_for_layer = !conf->lower_view_index,
});
/* This will lower load_helper to a memoized is_helper if needed; otherwise, load_helper

View file

@ -114,6 +114,7 @@ struct dxil_spirv_vertex_runtime_data {
uint32_t draw_id;
float viewport_width;
float viewport_height;
uint32_t view_index;
};
enum dxil_spirv_yz_flip_mode {
@ -161,6 +162,11 @@ struct dxil_spirv_runtime_conf {
// Force sample rate shading on a fragment shader
bool force_sample_rate_shading;
// View index needs to be lowered to a UBO lookup
bool lower_view_index;
// View index also needs to be forwarded to RT layer output
bool lower_view_index_to_rt_layer;
};
struct dxil_spirv_debug_options {