pvr: Add support for fragment pass through shader

On the Rogue architecture add support for using a fragment passthrough
shader when there is no fragment shader present in a graphics
pipeline but the sample mask is required.

fix:
dEQP-VK.pipeline.monolithic.empty_fs.masked_samples

Backport-to: 26.0

Signed-off-by: Nick Hamilton <nick.hamilton@imgtec.com>
Co-authored-by: Simon Perretta <simon.perretta@imgtec.com>
Reviewed-by: Frank Binns <frank.binns@imgtec.com>
(cherry picked from commit 14508b4c9a)

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40092>
This commit is contained in:
Nick Hamilton 2026-02-19 10:48:40 +00:00 committed by Eric Engestrom
parent 23cd27b129
commit 6edfd32388
7 changed files with 85 additions and 7 deletions

View file

@ -454,7 +454,7 @@
"description": "pvr: Add support for fragment pass through shader",
"nominated": true,
"nomination_type": 4,
"resolution": 0,
"resolution": 1,
"main_sha": null,
"because_sha": null,
"notes": null

View file

@ -34,7 +34,6 @@ dEQP-VK.multiview.renderpass2.input_attachments.no_queries.5_10_5_10,Fail
dEQP-VK.multiview.renderpass2.input_attachments.no_queries.8_1_1_8,Fail
dEQP-VK.multiview.renderpass2.input_attachments.no_queries.8,Fail
dEQP-VK.multiview.renderpass2.input_attachments.no_queries.max_multi_view_view_count,Fail
dEQP-VK.pipeline.monolithic.empty_fs.masked_samples,Fail
dEQP-VK.pipeline.monolithic.stencil.no_stencil_att.dynamic_rendering.dynamic_enable.d24_unorm_s8_uint,Fail
dEQP-VK.pipeline.monolithic.stencil.no_stencil_att.dynamic_rendering.dynamic_enable.d32_sfloat_s8_uint,Fail
dEQP-VK.pipeline.monolithic.stencil.no_stencil_att.dynamic_rendering.static_enable.d24_unorm_s8_uint,Fail

View file

@ -6548,6 +6548,21 @@ static void pvr_setup_output_select(struct pvr_cmd_buffer *const cmd_buffer)
}
}
static bool
pvr_needs_fs_passthrough(const struct vk_dynamic_graphics_state *dynamic_state)
{
if (dynamic_state->rs.rasterizer_discard_enable)
return false;
uint32_t full_sample_mask =
BITFIELD_MASK(dynamic_state->ms.rasterization_samples);
bool partial_sample_mask =
(dynamic_state->ms.sample_mask & full_sample_mask) != full_sample_mask;
return partial_sample_mask;
}
static void
pvr_setup_isp_faces_and_control(struct pvr_cmd_buffer *const cmd_buffer,
struct ROGUE_TA_STATE_ISPA *const ispa_out)
@ -6560,6 +6575,8 @@ pvr_setup_isp_faces_and_control(struct pvr_cmd_buffer *const cmd_buffer,
struct vk_dynamic_graphics_state *dynamic_state =
&cmd_buffer->vk.dynamic_graphics_state;
struct pvr_ppp_state *const ppp_state = &cmd_buffer->state.ppp_state;
bool skip_fs = fragment_shader_state->is_passthrough &&
!pvr_needs_fs_passthrough(dynamic_state);
const bool rasterizer_discard = dynamic_state->rs.rasterizer_discard_enable;
const uint32_t subpass_idx = pass_info->subpass_idx;
@ -6635,6 +6652,9 @@ pvr_setup_isp_faces_and_control(struct pvr_cmd_buffer *const cmd_buffer,
fragment_shader_state->pass_type == ROGUE_TA_PASSTYPE_OPAQUE)
ispa.passtype = ROGUE_TA_PASSTYPE_TRANSLUCENT;
if (skip_fs)
ispa.passtype = ROGUE_TA_PASSTYPE_OPAQUE;
ispa.objtype = obj_type;
/* Return unpacked ispa structure. dcmpmode, dwritedisable, passtype and
@ -6749,7 +6769,7 @@ pvr_setup_isp_faces_and_control(struct pvr_cmd_buffer *const cmd_buffer,
/* TODO: is shader_bo ever NULL? Figure out what to do. */
ispctl.tagwritedisable = rasterizer_discard ||
!fragment_shader_state->shader_bo;
!fragment_shader_state->shader_bo || skip_fs;
ispctl.two_sided = is_two_sided;
ispctl.bpres = header->pres_ispctl_fb || header->pres_ispctl_bb;
@ -7865,10 +7885,12 @@ pvr_emit_dirty_ppp_state(struct pvr_cmd_buffer *const cmd_buffer,
pvr_setup_isp_faces_and_control(cmd_buffer, NULL);
}
bool skip_fs = fragment_state->is_passthrough &&
!pvr_needs_fs_passthrough(dynamic_state);
if (!dynamic_state->rs.rasterizer_discard_enable &&
state->dirty.fragment_descriptors &&
state->gfx_pipeline->shader_state.fragment.shader_bo &&
!state->gfx_pipeline->fs_data.common.uses.empty) {
!state->gfx_pipeline->fs_data.common.uses.empty && !skip_fs) {
result = pvr_setup_fragment_state_pointers(cmd_buffer, sub_cmd);
if (result != VK_SUCCESS)
return result;
@ -7906,7 +7928,8 @@ pvr_emit_dirty_ppp_state(struct pvr_cmd_buffer *const cmd_buffer,
return result;
if (state->gfx_pipeline &&
fragment_state->pass_type == ROGUE_TA_PASSTYPE_DEPTH_FEEDBACK) {
fragment_state->pass_type == ROGUE_TA_PASSTYPE_DEPTH_FEEDBACK &&
!skip_fs) {
assert(state->current_sub_cmd->type == PVR_SUB_CMD_TYPE_GRAPHICS);
state->current_sub_cmd->gfx.has_depth_feedback = true;
}
@ -8235,7 +8258,12 @@ static VkResult pvr_validate_draw_state(struct pvr_cmd_buffer *cmd_buffer)
state->dirty.fragment_descriptors = true;
}
if (state->dirty.fragment_descriptors) {
const struct pvr_fragment_shader_state *const fragment_shader_state =
&gfx_pipeline->shader_state.fragment;
bool skip_fs = fragment_shader_state->is_passthrough &&
!pvr_needs_fs_passthrough(dynamic_state);
if (state->dirty.fragment_descriptors && !skip_fs) {
result = pvr_setup_descriptor_mappings(
cmd_buffer,
PVR_STAGE_ALLOCATION_FRAGMENT,

View file

@ -2591,7 +2591,9 @@ pvr_preprocess_shader_data(pco_data *data,
pvr_init_fs_blend(data, state->cb);
pvr_init_fs_tile_buffers(data);
data->fs.uses.alpha_to_coverage = state->ms->alpha_to_coverage_enable;
data->fs.uses.alpha_to_coverage = false;
if (state->ms)
data->fs.uses.alpha_to_coverage = state->ms->alpha_to_coverage_enable;
if (BITSET_TEST(state->dynamic, MESA_VK_DYNAMIC_CB_COLOR_WRITE_ENABLES) ||
(state->cb && state->cb->color_write_enables !=
@ -2715,6 +2717,14 @@ static void pvr_early_init_shader_data(pco_data *data,
}
}
static bool pvr_build_fs_passthrough(bool no_fragment_shader,
const struct pvr_device_info *dev_info)
{
bool is_arch_rogue = dev_info->ident.arch == PVR_DEVICE_ARCH_ROGUE;
return is_arch_rogue && no_fragment_shader;
}
/* Compiles and uploads shaders and PDS programs. */
static VkResult
pvr_graphics_pipeline_compile(struct pvr_device *const device,
@ -2761,6 +2771,7 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device,
struct pvr_pds_vertex_dma vtx_dma_descriptions[PVR_MAX_VERTEX_ATTRIB_DMAS];
uint32_t vtx_dma_count = 0;
bool build_fs_passthrough = false;
for (mesa_shader_stage stage = 0; stage < MESA_SHADER_STAGES; ++stage) {
size_t stage_index = gfx_pipeline->stage_indices[stage];
@ -2811,6 +2822,17 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device,
consumer = nir_shaders[stage];
}
/* Check if the fragment passthrough shader should be built. */
build_fs_passthrough =
pvr_build_fs_passthrough(!nir_shaders[MESA_SHADER_FRAGMENT],
&device->pdevice->dev_info);
if (build_fs_passthrough) {
nir_shader *fs_pass = pvr_usc_fs_pfo_passthrough_nir(pco_ctx);
ralloc_steal(shader_mem_ctx, fs_pass);
nir_shaders[MESA_SHADER_FRAGMENT] = fs_pass;
}
for (mesa_shader_stage stage = 0; stage < MESA_SHADER_STAGES; ++stage) {
if (!nir_shaders[stage])
continue;
@ -2874,6 +2896,7 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device,
if (*fs) {
pvr_fragment_state_save(gfx_pipeline, *fs);
fragment_state->is_passthrough = build_fs_passthrough;
pvr_graphics_pipeline_setup_fragment_coeff_program(
gfx_pipeline,

View file

@ -84,6 +84,8 @@ struct pvr_fragment_shader_state {
struct pvr_pds_kickusc_program pds_fragment_program;
uint32_t *pds_fragment_program_buffer;
bool is_passthrough;
};
struct pvr_pipeline {

View file

@ -52,6 +52,30 @@ static pco_shader *build_shader(pco_ctx *ctx, nir_shader *nir, pco_data *data)
return shader;
}
/**
* Generate a fragment passthrough shader that does a dummy store.
*
* \param ctx PCO context.
* \return The fragment passthrough shader.
*/
nir_shader *pvr_usc_fs_pfo_passthrough_nir(pco_ctx *ctx)
{
nir_builder b = nir_builder_init_simple_shader(MESA_SHADER_FRAGMENT,
pco_nir_options(),
"fs_pfo_passthrough");
/* Need to mark the shader as not internal for the compiler to
* emit the pfo code for the sample mask.
*/
b.shader->info.internal = false;
nir_frag_store_pco(&b, nir_imm_int(&b, 0), .base = 0);
nir_jump(&b, nir_jump_return);
pco_preprocess_nir(ctx, b.shader);
return b.shader;
}
/**
* Generate an end-of-tile shader.
*

View file

@ -37,6 +37,8 @@ struct pvr_eot_props {
uint64_t tile_buffer_addrs[PVR_MAX_COLOR_ATTACHMENTS];
};
nir_shader *pvr_usc_fs_pfo_passthrough_nir (pco_ctx *ctx);
pco_shader *pvr_usc_eot(pco_ctx *ctx,
struct pvr_eot_props *props,
const struct pvr_device_info *dev_info);