From 0a01f7aeeba693b24d08e2005f90b1e905fa551f Mon Sep 17 00:00:00 2001 From: Ella Stanforth Date: Wed, 31 Dec 2025 19:41:18 +0000 Subject: [PATCH] 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 Part-of: --- src/imagination/vulkan/meson.build | 1 + src/imagination/vulkan/pvr_arch_pipeline.c | 55 ++++++++++++++ src/imagination/vulkan/pvr_nir_lower_ycbcr.c | 80 ++++++++++++++++++++ src/imagination/vulkan/pvr_nir_lower_ycbcr.h | 22 ++++++ 4 files changed, 158 insertions(+) create mode 100644 src/imagination/vulkan/pvr_nir_lower_ycbcr.c create mode 100644 src/imagination/vulkan/pvr_nir_lower_ycbcr.h diff --git a/src/imagination/vulkan/meson.build b/src/imagination/vulkan/meson.build index bab40bee13d..d94d5c40254 100644 --- a/src/imagination/vulkan/meson.build +++ b/src/imagination/vulkan/meson.build @@ -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', diff --git a/src/imagination/vulkan/pvr_arch_pipeline.c b/src/imagination/vulkan/pvr_arch_pipeline.c index b352eae0d39..e3969335f80 100644 --- a/src/imagination/vulkan/pvr_arch_pipeline.c +++ b/src/imagination/vulkan/pvr_arch_pipeline.c @@ -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]); diff --git a/src/imagination/vulkan/pvr_nir_lower_ycbcr.c b/src/imagination/vulkan/pvr_nir_lower_ycbcr.c new file mode 100644 index 00000000000..beb5b7bcf42 --- /dev/null +++ b/src/imagination/vulkan/pvr_nir_lower_ycbcr.c @@ -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); +} diff --git a/src/imagination/vulkan/pvr_nir_lower_ycbcr.h b/src/imagination/vulkan/pvr_nir_lower_ycbcr.h new file mode 100644 index 00000000000..3ba798a02b1 --- /dev/null +++ b/src/imagination/vulkan/pvr_nir_lower_ycbcr.h @@ -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 +#include + +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 */