pvr: workaround hardware clamping for YCBCR_IDENTITY conversion

The TPU clamps to 0..1 so we have to workaround in software on any hardware
that does not have XR clamp support.

Reviewed-by: Simon Perretta <simon.perretta@imgtec.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/39231>
This commit is contained in:
Ella Stanforth 2025-12-31 19:41:18 +00:00 committed by Marge Bot
parent 3204e8b1a2
commit 0a01f7aeeb
4 changed files with 158 additions and 0 deletions

View file

@ -34,6 +34,7 @@ pvr_files = files(
'pvr_formats.c',
'pvr_image.c',
'pvr_instance.c',
'pvr_nir_lower_ycbcr.c',
'pvr_physical_device.c',
'pvr_transfer_frag_store.c',
'pvr_query.c',

View file

@ -47,10 +47,12 @@
#include "pvr_entrypoints.h"
#include "pvr_hw_pass.h"
#include "pvr_macros.h"
#include "pvr_nir_lower_ycbcr.h"
#include "pvr_pass.h"
#include "pvr_pds.h"
#include "pvr_physical_device.h"
#include "pvr_robustness.h"
#include "pvr_sampler.h"
#include "pvr_types.h"
#include "pvr_usc.h"
@ -930,6 +932,40 @@ static void pvr_postprocess_shader_data(pco_data *data,
struct vk_pipeline_layout *layout,
struct usc_mrt_setup *setup);
struct lower_ycbcr_state {
uint32_t set_layout_count;
struct vk_descriptor_set_layout *const *set_layouts;
};
static const struct vk_ycbcr_conversion_state *
lookup_ycbcr_conversion(const void *_state,
uint32_t set,
uint32_t binding,
uint32_t array_index)
{
const struct lower_ycbcr_state *state = _state;
assert(set < state->set_layout_count);
assert(state->set_layouts[set] != NULL);
const struct pvr_descriptor_set_layout *set_layout =
vk_to_pvr_descriptor_set_layout(state->set_layouts[set]);
assert(binding < set_layout->binding_count);
const struct pvr_descriptor_set_layout_binding *bind_layout =
&set_layout->bindings[binding];
if (bind_layout->immutable_samplers == NULL)
return NULL;
array_index = MIN2(array_index, bind_layout->immutable_sampler_count - 1);
const struct pvr_sampler *sampler =
bind_layout->immutable_samplers[array_index];
return sampler && sampler->vk.ycbcr_conversion
? &sampler->vk.ycbcr_conversion->state
: NULL;
}
/******************************************************************************
Compute pipeline functions
******************************************************************************/
@ -983,6 +1019,16 @@ static VkResult pvr_compute_pipeline_compile(
layout,
NULL,
NULL);
const struct lower_ycbcr_state ycbcr_state = {
.set_layout_count = layout->set_count,
.set_layouts = layout->set_layouts,
};
NIR_PASS(_,
nir,
nir_pvr_lower_ycbcr_tex,
lookup_ycbcr_conversion,
&ycbcr_state);
pco_lower_nir(pco_ctx, nir, &shader_data);
pco_postprocess_nir(pco_ctx, nir, &shader_data);
pvr_postprocess_shader_data(&shader_data, nir, pCreateInfo, layout, NULL);
@ -2822,6 +2868,15 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device,
state,
&mrt_setup);
const struct lower_ycbcr_state ycbcr_state = {
.set_layout_count = layout->set_count,
.set_layouts = layout->set_layouts,
};
NIR_PASS(_,
nir_shaders[stage],
nir_pvr_lower_ycbcr_tex,
lookup_ycbcr_conversion,
&ycbcr_state);
pco_lower_nir(pco_ctx, nir_shaders[stage], &shader_data[stage]);
pco_postprocess_nir(pco_ctx, nir_shaders[stage], &shader_data[stage]);

View file

@ -0,0 +1,80 @@
/*
* Copyright © 2026 Imagination Technologies Ltd.
* Copyright © 2017 Intel Corporation
* SPDX-License-Identifier: MIT
*
* Based on vk_nir_convert_ycbcr.c.
*
* We need this to fixup YCBCR_IDENTITY conversion since the TPU clamps the
* output from csc into the range 0.0 to 1.0.
*/
#include "pvr_nir_lower_ycbcr.h"
#include "nir_builder.h"
#include "vk_ycbcr_conversion.h"
struct lower_ycbcr_tex_state {
nir_pvr_ycbcr_conversion_lookup_cb cb;
const void *cb_data;
};
static bool
lower_ycbcr_tex_instr(nir_builder *b, nir_tex_instr *tex, void *_state)
{
const struct lower_ycbcr_tex_state *state = _state;
/* For the following instructions, we don't apply any change and let the
* instruction apply to the first plane.
*/
if (tex->op == nir_texop_txs || tex->op == nir_texop_query_levels ||
tex->op == nir_texop_lod)
return false;
int deref_src_idx = nir_tex_instr_src_index(tex, nir_tex_src_texture_deref);
assert(deref_src_idx >= 0);
nir_deref_instr *deref = nir_src_as_deref(tex->src[deref_src_idx].src);
nir_variable *var = nir_deref_instr_get_variable(deref);
uint32_t set = var->data.descriptor_set;
uint32_t binding = var->data.binding;
assert(tex->texture_index == 0);
unsigned array_index = 0;
if (deref->deref_type != nir_deref_type_var) {
assert(deref->deref_type == nir_deref_type_array);
if (!nir_src_is_const(deref->arr.index))
return false;
array_index = nir_src_as_uint(deref->arr.index);
}
const struct vk_ycbcr_conversion_state *conversion =
state->cb(state->cb_data, set, binding, array_index);
if (conversion == NULL)
return false;
/* Range expansion fix only needed for YCbCr Identity */
if (conversion->ycbcr_model !=
VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY)
return false;
b->cursor = nir_after_instr(&tex->instr);
nir_def *result =
nir_fsub(b, &tex->def, nir_imm_vec4(b, 0.5, 0.0, 0.5, 0.0));
nir_def_rewrite_uses_after(&tex->def, result);
return true;
}
bool nir_pvr_lower_ycbcr_tex(nir_shader *nir,
nir_pvr_ycbcr_conversion_lookup_cb cb,
const void *cb_data)
{
struct lower_ycbcr_tex_state state = {
.cb = cb,
.cb_data = cb_data,
};
return nir_shader_tex_pass(nir,
lower_ycbcr_tex_instr,
nir_metadata_control_flow,
&state);
}

View file

@ -0,0 +1,22 @@
/*
* Copyright © 2026 Imagination Technologies Ltd.
* SPDX-License-Identifier: MIT
*/
#ifndef PVR_NIR_LOWER_YCBCR_H
#define PVR_NIR_LOWER_YCBCR_H
#include <stdbool.h>
#include <stdint.h>
typedef struct nir_shader nir_shader;
typedef const struct vk_ycbcr_conversion_state *(
*nir_pvr_ycbcr_conversion_lookup_cb)(const void *data,
uint32_t set,
uint32_t binding,
uint32_t array_index);
bool nir_pvr_lower_ycbcr_tex(nir_shader *nir,
nir_pvr_ycbcr_conversion_lookup_cb cb,
const void *cb_data);
#endif /* PVR_NIR_LOWER_YCBCR_H */