pvr, pco: add multiview compiler support, advertise extension

- Pass view index to fragment shader via linked flat varying.
- Use view index instead of layer id for input attachments when required.

Signed-off-by: Simon Perretta <simon.perretta@imgtec.com>
Acked-by: Erik Faye-Lund <erik.faye-lund@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37512>
This commit is contained in:
Simon Perretta 2025-08-30 22:06:52 +01:00 committed by Marge Bot
parent 9d48088428
commit cb6c921502
10 changed files with 167 additions and 8 deletions

View file

@ -452,7 +452,7 @@ Vulkan 1.1 -- all DONE: anv, hk, lvp, nvk, panvk/v10+, radv, tu, vn
VK_KHR_maintenance1 DONE (anv, dzn, hasvk, lvp, nvk, panvk, pvr, radv, tu, v3dv, vn)
VK_KHR_maintenance2 DONE (anv, dzn, hasvk, lvp, nvk, panvk, pvr, radv, tu, v3dv, vn)
VK_KHR_maintenance3 DONE (anv, dzn, hasvk, lvp, nvk, panvk, pvr, radv, tu, v3dv, vn)
VK_KHR_multiview DONE (anv, dzn, hasvk, lvp, nvk, panvk/v10+, radv, tu, v3dv, vn)
VK_KHR_multiview DONE (anv, dzn, hasvk, lvp, nvk, panvk/v10+, pvr, radv, tu, v3dv, vn)
VK_KHR_relaxed_block_layout DONE (anv, dzn, hasvk, lvp, nvk, panvk, radv, tu, v3dv, vn)
VK_KHR_sampler_ycbcr_conversion DONE (anv, hasvk, lvp, nvk, panvk/v10+, radv, tu, v3dv, vn)
VK_KHR_shader_draw_parameters DONE (anv, dzn, hasvk, lvp, nvk, panvk, radv, tu, vn)

View file

@ -34,7 +34,11 @@ const struct spirv_to_nir_options *pco_spirv_options(void);
const nir_shader_compiler_options *pco_nir_options(void);
void pco_preprocess_nir(pco_ctx *ctx, nir_shader *nir);
void pco_link_nir(pco_ctx *ctx, nir_shader *producer, nir_shader *consumer);
void pco_link_nir(pco_ctx *ctx,
nir_shader *producer,
nir_shader *consumer,
pco_data *producer_data,
pco_data *consumer_data);
void pco_rev_link_nir(pco_ctx *ctx, nir_shader *producer, nir_shader *consumer);
void pco_lower_nir(pco_ctx *ctx, nir_shader *nir, pco_data *data);
void pco_postprocess_nir(pco_ctx *ctx, nir_shader *nir, pco_data *data);

View file

@ -106,6 +106,8 @@ typedef struct _pco_fs_data {
pco_range sample_locations;
gl_varying_slot view_index_slot;
struct {
bool w; /** Whether the shader uses pos.w. */
bool z; /** Whether the shader uses pos.z */
@ -224,6 +226,7 @@ typedef struct _pco_common_data {
bool robust_buffer_access;
bool image_2d_view_of_3d;
bool multiview;
} pco_common_data;
/** PCO shader data. */

View file

@ -1756,6 +1756,9 @@ bool pco_legalize(pco_shader *shader);
bool pco_opt_comp_only_vecs(pco_shader *shader);
bool pco_nir_compute_instance_check(nir_shader *shader);
bool pco_nir_link_clip_cull_vars(nir_shader *producer, nir_shader *consumer);
bool pco_nir_link_multiview(nir_shader *producer,
nir_shader *consumer,
pco_data *consumer_data);
bool pco_nir_lower_algebraic(nir_shader *shader);
bool pco_nir_lower_algebraic_late(nir_shader *shader);
bool pco_nir_lower_atomics(nir_shader *shader, pco_data *data);

View file

@ -636,15 +636,27 @@ void pco_preprocess_nir(pco_ctx *ctx, nir_shader *nir)
}
}
static bool can_remove_var(nir_variable *var, UNUSED void *data)
{
return !var->data.always_active_io;
}
/**
* \brief Performs linking optimizations on consecutive NIR shader stages.
*
* \param[in] ctx PCO compiler context.
* \param[in,out] producer NIR producer shader.
* \param[in,out] consumer NIR consumer shader.
* \param[in,out] producer_data Producer shader data.
* \param[in,out] consumer_data Consumer shader data.
*/
void pco_link_nir(pco_ctx *ctx, nir_shader *producer, nir_shader *consumer)
void pco_link_nir(pco_ctx *ctx,
nir_shader *producer,
nir_shader *consumer,
pco_data *producer_data,
pco_data *consumer_data)
{
pco_nir_link_multiview(producer, consumer, consumer_data);
pco_nir_link_clip_cull_vars(producer, consumer);
nir_lower_io_array_vars_to_elements(producer, consumer);
@ -660,8 +672,12 @@ void pco_link_nir(pco_ctx *ctx, nir_shader *producer, nir_shader *consumer)
if (nir_link_opt_varyings(producer, consumer))
pco_nir_opt(ctx, consumer);
NIR_PASS(_, producer, nir_remove_dead_variables, nir_var_shader_out, NULL);
NIR_PASS(_, consumer, nir_remove_dead_variables, nir_var_shader_in, NULL);
nir_remove_dead_variables_options rdv = {
.can_remove_var = can_remove_var,
};
NIR_PASS(_, producer, nir_remove_dead_variables, nir_var_shader_out, &rdv);
NIR_PASS(_, consumer, nir_remove_dead_variables, nir_var_shader_in, &rdv);
bool progress = nir_remove_unused_varyings(producer, consumer);
nir_compact_varyings(producer, consumer, true);

View file

@ -1549,3 +1549,68 @@ bool pco_nir_lower_interpolation(nir_shader *shader, pco_fs_data *fs)
return progress;
}
static bool lower_load_view_index_fs(struct nir_builder *b,
nir_intrinsic_instr *intr,
void *cb_data)
{
if (intr->intrinsic != nir_intrinsic_load_view_index)
return false;
nir_variable *view_index_var = cb_data;
b->cursor = nir_before_instr(&intr->instr);
nir_def_replace(&intr->def, nir_load_var(b, view_index_var));
nir_instr_free(&intr->instr);
return true;
}
bool pco_nir_link_multiview(nir_shader *producer,
nir_shader *consumer,
pco_data *consumer_data)
{
if (producer->info.stage != MESA_SHADER_VERTEX ||
consumer->info.stage != MESA_SHADER_FRAGMENT ||
!consumer_data->common.multiview) {
return false;
}
/* Find unused varying slot for the view index. */
gl_varying_slot view_index_slot = VARYING_SLOT_VAR0;
nir_foreach_shader_out_variable (var, producer) {
view_index_slot = MAX2(view_index_slot, var->data.location + 1);
}
assert(view_index_slot < VARYING_SLOT_MAX);
consumer_data->fs.view_index_slot = view_index_slot;
/* Create output variable in the producer. */
nir_variable *view_index_var = nir_variable_create(producer,
nir_var_shader_out,
glsl_uint_type(),
"view_index");
view_index_var->data.location = view_index_slot;
view_index_var->data.interpolation = INTERP_MODE_FLAT;
view_index_var->data.always_active_io = true;
/* Store view index in the producer. */
nir_builder b = nir_builder_at(nir_after_block(
nir_impl_last_block(nir_shader_get_entrypoint(producer))));
nir_store_var(&b, view_index_var, nir_load_view_index(&b), 1);
/* Create input variable in the consumer. */
view_index_var = nir_variable_create(consumer,
nir_var_shader_in,
glsl_uint_type(),
"view_index");
view_index_var->data.location = view_index_slot;
view_index_var->data.interpolation = INTERP_MODE_FLAT;
view_index_var->data.always_active_io = true;
/* Lower view index loads in the consumer. */
nir_shader_intrinsics_pass(consumer,
lower_load_view_index_fs,
nir_metadata_all,
view_index_var);
return true;
}

View file

@ -1226,7 +1226,22 @@ static nir_def *lower_image(nir_builder *b, nir_instr *instr, void *cb_data)
frag_coords = nir_f2i32(b, frag_coords);
coords = nir_iadd(b, frag_coords, coords);
nir_def *layer = nir_load_layer_id(b); /* TODO: view id for multiview? */
nir_def *layer = nir_load_layer_id(b);
/* Use the view index instead if we're in multiview. */
if (data->common.multiview) {
assert(data->fs.view_index_slot >= VARYING_SLOT_VAR0 &&
data->fs.view_index_slot < VARYING_SLOT_MAX);
layer = nir_load_input(b,
1,
32,
nir_imm_int(b, 0),
.dest_type = nir_type_uint32,
.io_semantics = (nir_io_semantics){
.location = data->fs.view_index_slot,
.num_slots = 1,
});
}
coords = nir_pad_vector(b, coords, 3);
coords = nir_vector_insert_imm(b, coords, layer, 2);

View file

@ -2113,6 +2113,7 @@ static pco_instr *trans_intr(trans_ctx *tctx, nir_intrinsic_instr *intr)
case nir_intrinsic_load_base_instance:
case nir_intrinsic_load_base_vertex:
case nir_intrinsic_load_draw_id:
case nir_intrinsic_load_view_index:
/* Compute sysvals. */
case nir_intrinsic_load_local_invocation_index:

View file

@ -189,6 +189,7 @@ static void pvr_physical_device_get_supported_extensions(
.KHR_maintenance1 = true,
.KHR_maintenance2 = true,
.KHR_maintenance3 = true,
.KHR_multiview = true,
.KHR_present_id2 = PVR_USE_WSI_PLATFORM,
.KHR_present_wait2 = PVR_USE_WSI_PLATFORM,
.KHR_separate_depth_stencil_layouts = true,
@ -293,6 +294,11 @@ static void pvr_physical_device_get_supported_features(
/* Vulkan 1.2 / VK_KHR_imageless_framebuffer */
.imagelessFramebuffer = true,
/* Vulkan 1.1 / VK_KHR_multiview */
.multiview = true,
.multiviewGeometryShader = false,
.multiviewTessellationShader = false,
/* Vulkan 1.2 / VK_KHR_timeline_semaphore */
.timelineSemaphore = true,
@ -586,6 +592,10 @@ static bool pvr_physical_device_get_properties(
.maxPerSetDescriptors = PVR_MAX_DESCRIPTORS_PER_SET,
.maxMemoryAllocationSize = max_memory_alloc_size,
/* Vulkan 1.1 / VK_KHR_multiview */
.maxMultiviewViewCount = PVR_MAX_MULTIVIEW,
.maxMultiviewInstanceIndex = (1 << 27) - 1,
/* Vulkan 1.2 / VK_KHR_driver_properties */
.driverID = VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA,
.driverName = "Imagination open-source Mesa driver",

View file

@ -400,6 +400,11 @@ static VkResult pvr_pds_vertex_attrib_programs_create_and_upload(
input.draw_index_register = sys_vals[SYSTEM_VALUE_DRAW_ID].start;
}
if (sys_vals[SYSTEM_VALUE_VIEW_INDEX].count > 0) {
input.flags |= PVR_PDS_VERTEX_FLAGS_VIEW_INDEX_REQUIRED;
input.view_index_register = sys_vals[SYSTEM_VALUE_VIEW_INDEX].start;
}
pvr_pds_setup_doutu(&input.usc_task_control,
0,
usc_temp_count,
@ -930,6 +935,10 @@ static void pvr_pipeline_finish(struct pvr_device *device,
vk_object_base_finish(&pipeline->base);
}
static void pvr_early_init_shader_data(pco_data *data,
nir_shader *nir,
const void *pCreateInfo);
static void
pvr_preprocess_shader_data(pco_data *data,
nir_shader *nir,
@ -987,6 +996,7 @@ static VkResult pvr_compute_pipeline_compile(
if (result != VK_SUCCESS)
goto err_free_build_context;
pvr_early_init_shader_data(&shader_data, nir, pCreateInfo);
pco_preprocess_nir(pco_ctx, nir);
pvr_preprocess_shader_data(&shader_data, nir, pCreateInfo, layout, NULL);
pco_lower_nir(pco_ctx, nir, &shader_data);
@ -1651,7 +1661,7 @@ static void pvr_alloc_vs_sysvals(pco_data *data, nir_shader *nir)
gl_system_value sys_vals[] = {
SYSTEM_VALUE_VERTEX_ID, SYSTEM_VALUE_INSTANCE_ID,
SYSTEM_VALUE_BASE_INSTANCE, SYSTEM_VALUE_BASE_VERTEX,
SYSTEM_VALUE_DRAW_ID,
SYSTEM_VALUE_DRAW_ID, SYSTEM_VALUE_VIEW_INDEX,
};
for (unsigned u = 0; u < ARRAY_SIZE(sys_vals); ++u) {
@ -2570,6 +2580,29 @@ static void pvr_postprocess_shader_data(pco_data *data,
/* TODO: common things, like large constants being put into shareds. */
}
static void pvr_early_init_shader_data(pco_data *data,
nir_shader *nir,
const void *pCreateInfo)
{
const VkGraphicsPipelineCreateInfo *pGraphicsCreateInfo = pCreateInfo;
switch (nir->info.stage) {
case MESA_SHADER_VERTEX:
case MESA_SHADER_FRAGMENT: {
VK_FROM_HANDLE(pvr_render_pass, pass, pGraphicsCreateInfo->renderPass);
data->common.multiview = pass->multiview_enabled;
break;
}
case MESA_SHADER_COMPUTE:
break;
default:
UNREACHABLE("Unsupported stage.");
}
}
/* Compiles and uploads shaders and PDS programs. */
static VkResult
pvr_graphics_pipeline_compile(struct pvr_device *const device,
@ -2594,6 +2627,7 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device,
nir_shader *producer = NULL;
nir_shader *consumer = NULL;
pco_data shader_data[MESA_SHADER_STAGES] = { 0 };
pco_data *producer_data = NULL;
nir_shader *nir_shaders[MESA_SHADER_STAGES] = { 0 };
pco_shader *pco_shaders[MESA_SHADER_STAGES] = { 0 };
pco_shader **vs = &pco_shaders[MESA_SHADER_VERTEX];
@ -2621,6 +2655,9 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device,
if (result != VK_SUCCESS)
goto err_free_build_context;
pvr_early_init_shader_data(&shader_data[stage],
nir_shaders[stage],
pCreateInfo);
pco_preprocess_nir(pco_ctx, nir_shaders[stage]);
}
@ -2629,9 +2666,14 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device,
continue;
if (producer)
pco_link_nir(pco_ctx, producer, nir_shaders[stage]);
pco_link_nir(pco_ctx,
producer,
nir_shaders[stage],
producer_data,
&shader_data[stage]);
producer = nir_shaders[stage];
producer_data = &shader_data[stage];
}
for (mesa_shader_stage stage = MESA_SHADER_STAGES; stage-- > 0;) {