v3d: Blit for P030 format with BROADCOM_SAND128 modifier to P010 UIF

Implements the support to blit SAND modifier with columns 128-bytes-wide
support for P030 format to P010 with UIF layout. This allows sampling
from H265 10-bit frames exported by the video decoder on the Raspberry
Pi 4 devices.

When a DRM_FORMAT_MOD_BROADCOM_SAND128 is enabled with an imported P030
texture. The sand30 blit converts the Luma and Chroma planes to
a tiled P010 format that can be sampled using gallium YUV lowerings
without the interleaved 128-bytes-wide-columns.

This patch follows a similar approach to SAND8 blit but extracting luma
and chroma components from the DRM_FORMAT_P030 format. P030 is a two
plane YCbCr420 format where 3 10 bit components with 2 padding bits are
packed in 4 bytes.

index 0 = Y plane, [31:0] x:Y2:Y1:Y0 2:10:10:10 little endian
index 1 = Cr:Cb plane, [63:0] x:Cr2:Cb2:Cr1:x:Cb1:Cr0:Cb0
                       [2:10:10:10:2:10:10:10] little endian

After the sand30_blit is done, the shadow texture is an UIF tiled texture
with an R16_UNORM format for luma and R16G16_UNORM for chroma.

To reduce the number of texture-fetch operations during the blit, we
read pairs of 32-bit dwords. They include 6 10-bit unorm components.
And then we write 4 UNORM16 components from an uvec4 because our render
targets do not support writing to UNORM16 formats.

As sampling will be done using 16bpp (luma) and 32bpp (chroma), the
sand30_blit writes consider the different microtile layouts of UIF
format between 64, 32 and 16 bpp.

v2: Fixes save and recovery of constant buffers (Iago)
    Typo corrections. (Iago)
    Removed not needed return. (Iago)
    Added shader information about uniforms, ubos, inputs and outputs.

Reviewed-by: Iago Toral Quiroga <itoral@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/19639>
This commit is contained in:
Jose Maria Casanova Crespo
2022-11-09 12:39:01 +01:00
committed by José María Casanova Crespo
parent 4ea4147935
commit 653cf8c8d2
3 changed files with 333 additions and 0 deletions

View File

@@ -839,6 +839,331 @@ v3d_sand8_blit(struct pipe_context *pctx, struct pipe_blit_info *info)
}
/**
* Creates the VS of the custom blit shader to convert YUV plane from
* the P030 format with BROADCOM_SAND_COL128 modifier to UIF tiled P010
* format.
* This vertex shader is mostly a pass-through VS.
*/
static void *
v3d_get_sand30_vs(struct pipe_context *pctx)
{
struct v3d_context *v3d = v3d_context(pctx);
struct pipe_screen *pscreen = pctx->screen;
if (v3d->sand30_blit_vs)
return v3d->sand30_blit_vs;
const struct nir_shader_compiler_options *options =
pscreen->get_compiler_options(pscreen,
PIPE_SHADER_IR_NIR,
PIPE_SHADER_VERTEX);
nir_builder b = nir_builder_init_simple_shader(MESA_SHADER_VERTEX,
options,
"sand30_blit_vs");
const struct glsl_type *vec4 = glsl_vec4_type();
nir_variable *pos_in = nir_variable_create(b.shader,
nir_var_shader_in,
vec4, "pos");
nir_variable *pos_out = nir_variable_create(b.shader,
nir_var_shader_out,
vec4, "gl_Position");
pos_out->data.location = VARYING_SLOT_POS;
nir_store_var(&b, pos_out, nir_load_var(&b, pos_in), 0xf);
struct pipe_shader_state shader_tmpl = {
.type = PIPE_SHADER_IR_NIR,
.ir.nir = b.shader,
};
v3d->sand30_blit_vs = pctx->create_vs_state(pctx, &shader_tmpl);
return v3d->sand30_blit_vs;
}
/**
* Given an uvec2 value with rgb10a2 components, it extracts four 10-bit
* components, then converts them from unorm10 to unorm16 and returns them
* in an uvec4. The start parameter defines where the sequence of 4 values
* begins.
*/
static nir_ssa_def *
extract_unorm_2xrgb10a2_component_to_4xunorm16(nir_builder *b,
nir_ssa_def *value,
nir_ssa_def *start)
{
const unsigned mask = BITFIELD_MASK(10);
nir_ssa_def *shiftw0 = nir_imul_imm(b, start, 10);
nir_ssa_def *word0 = nir_iand_imm(b, nir_channel(b, value, 0),
BITFIELD_MASK(30));
nir_ssa_def *finalword0 = nir_ushr(b, word0, shiftw0);
nir_ssa_def *word1 = nir_channel(b, value, 1);
nir_ssa_def *shiftw0tow1 = nir_isub(b, nir_imm_int(b, 30), shiftw0);
nir_ssa_def *word1toword0 = nir_ishl(b, word1, shiftw0tow1);
finalword0 = nir_ior(b, finalword0, word1toword0);
nir_ssa_def *finalword1 = nir_ushr(b, word1, shiftw0);
nir_ssa_def *val0 = nir_ishl_imm(b, nir_iand_imm(b, finalword0,
mask), 6);
nir_ssa_def *val1 = nir_ishr_imm(b, nir_iand_imm(b, finalword0,
mask << 10), 4);
nir_ssa_def *val2 = nir_ishr_imm(b, nir_iand_imm(b, finalword0,
mask << 20), 14);
nir_ssa_def *val3 = nir_ishl_imm(b, nir_iand_imm(b, finalword1,
mask), 6);
return nir_vec4(b, val0, val1, val2, val3);
}
/**
* Creates the FS of the custom blit shader to convert YUV plane from
* the P030 format with BROADCOM_SAND_COL128 modifier to UIF tiled P10
* format a 16-bit representation per component.
*
* The result texture is equivalent to a chroma (cpp=4) or luma (cpp=2)
* plane for a P010 format without the SAND128 modifier.
*/
static void *
v3d_get_sand30_fs(struct pipe_context *pctx)
{
struct v3d_context *v3d = v3d_context(pctx);
struct pipe_screen *pscreen = pctx->screen;
if (v3d->sand30_blit_fs)
return v3d->sand30_blit_fs;
const struct nir_shader_compiler_options *options =
pscreen->get_compiler_options(pscreen,
PIPE_SHADER_IR_NIR,
PIPE_SHADER_FRAGMENT);
nir_builder b = nir_builder_init_simple_shader(MESA_SHADER_FRAGMENT,
options,
"sand30_blit_fs");
b.shader->info.num_ubos = 1;
b.shader->num_outputs = 1;
b.shader->num_inputs = 1;
b.shader->num_uniforms = 1;
const struct glsl_type *vec4 = glsl_vec4_type();
const struct glsl_type *glsl_uint = glsl_uint_type();
const struct glsl_type *glsl_uvec4 = glsl_vector_type(GLSL_TYPE_UINT,
4);
nir_variable *color_out = nir_variable_create(b.shader,
nir_var_shader_out,
glsl_uvec4, "f_color");
color_out->data.location = FRAG_RESULT_COLOR;
nir_variable *pos_in =
nir_variable_create(b.shader, nir_var_shader_in, vec4, "pos");
pos_in->data.location = VARYING_SLOT_POS;
nir_ssa_def *pos = nir_load_var(&b, pos_in);
nir_ssa_def *zero = nir_imm_int(&b, 0);
nir_ssa_def *three = nir_imm_int(&b, 3);
/* With a SAND128 stripe, in 128-bytes with rgb10a2 format we have 96
* 10-bit values. So, it represents 96 pixels for Y plane and 48 pixels
* for UV frame, but as we are reading 4 10-bit-values at a time we
* will have 24 groups (pixels) of 4 10-bit values.
*/
nir_ssa_def *pixels_stripe = nir_imm_int(&b, 24);
nir_ssa_def *x = nir_f2i32(&b, nir_channel(&b, pos, 0));
nir_ssa_def *y = nir_f2i32(&b, nir_channel(&b, pos, 1));
/* UIF tiled format is composed by UIF blocks. Each block has four 64
* byte microtiles. Inside each microtile pixels are stored in raster
* format. But microtiles have different dimensions based in the bits
* per pixel of the image.
*
* 16bpp microtile dimensions are 8x4
* 32bpp microtile dimensions are 4x4
* 64bpp microtile dimensions are 4x2
*
* As we are reading and writing with 64bpp to optimize the number of
* texture operations during the blit, we adjust the offsets so when
* the microtile is sampled using the 16bpp (luma) and the 32bpp
* (chroma) the expected pixels are in the correct position, that
* would be different if we were using a 64bpp sampling.
*
* For luma 8x4 16bpp and chroma 4x4 32bpp luma raster order is
* incompatible with 4x2 64bpp. 16bpp has 16 bytes per line, 32bpp has
* also 16byte per line. But 64bpp has 32 bytes per line. So if we
* read a 16bpp or 32bpp texture that was written as 64bpp texture,
* pixels would be misplaced.
*
* inter/intra_utile_x_offsets takes care of mapping the offsets
* between microtiles to deal with this issue for luma and chroma
* planes.
*
* We reduce the luma and chroma planes to the same blit case
* because 16bpp and 32bpp have compatible microtile raster layout.
* So just doubling the width of the chroma plane before calling the
* blit makes them equivalent.
*/
nir_variable *stride_in =
nir_variable_create(b.shader, nir_var_uniform,
glsl_uint, "sand30_stride");
nir_ssa_def *stride =
nir_load_uniform(&b, 1, 32, zero,
.base = stride_in->data.driver_location,
.range = 4,
.dest_type = nir_type_uint32);
nir_ssa_def *real_x = nir_ior(&b, nir_iand_imm(&b, x, 1),
nir_ishl_imm(&b,nir_ushr_imm(&b, x, 2),
1));
nir_ssa_def *x_pos_in_stripe = nir_umod(&b, real_x, pixels_stripe);
nir_ssa_def *component = nir_umod(&b, real_x, three);
nir_ssa_def *intra_utile_x_offset = nir_ishl_imm(&b, component, 2);
nir_ssa_def *inter_utile_x_offset =
nir_ishl_imm(&b, nir_udiv_imm(&b, x_pos_in_stripe, 3), 4);
nir_ssa_def *stripe_offset=
nir_ishl_imm(&b,
nir_imul(&b,
nir_udiv(&b, real_x, pixels_stripe),
stride),
7);
nir_ssa_def *x_offset = nir_iadd(&b, stripe_offset,
nir_iadd(&b, intra_utile_x_offset,
inter_utile_x_offset));
nir_ssa_def *y_offset =
nir_iadd(&b, nir_ishl_imm(&b, nir_iand_imm(&b, x, 2), 6),
nir_ishl_imm(&b, y, 8));
nir_ssa_def *ubo_offset = nir_iadd(&b, x_offset, y_offset);
nir_ssa_def *load = nir_load_ubo(&b, 2, 32, zero, ubo_offset,
.align_mul = 8,
.align_offset = 0,
.range_base = 0,
.range = ~0);
nir_ssa_def *output =
extract_unorm_2xrgb10a2_component_to_4xunorm16(&b, load,
component);
nir_store_var(&b, color_out,
output,
0xf);
struct pipe_shader_state shader_tmpl = {
.type = PIPE_SHADER_IR_NIR,
.ir.nir = b.shader,
};
v3d->sand30_blit_fs = pctx->create_fs_state(pctx, &shader_tmpl);
return v3d->sand30_blit_fs;
}
/**
* Turns P030 with SAND30 format modifier from raster-order with interleaved
* luma and chroma 128-byte-wide-columns to a P010 UIF tiled format for luma
* and chroma.
*/
static void
v3d_sand30_blit(struct pipe_context *pctx, struct pipe_blit_info *info)
{
struct v3d_context *v3d = v3d_context(pctx);
struct v3d_resource *src = v3d_resource(info->src.resource);
ASSERTED struct v3d_resource *dst = v3d_resource(info->dst.resource);
if (!src->sand_col128_stride)
return;
if (src->tiled)
return;
if (src->base.format != PIPE_FORMAT_R16_UNORM &&
src->base.format != PIPE_FORMAT_R16G16_UNORM)
return;
if (!(info->mask & PIPE_MASK_RGBA))
return;
assert(dst->base.format == src->base.format);
assert(dst->tiled);
assert(info->src.box.x == 0 && info->dst.box.x == 0);
assert(info->src.box.y == 0 && info->dst.box.y == 0);
assert(info->src.box.width == info->dst.box.width);
assert(info->src.box.height == info->dst.box.height);
v3d_blitter_save(v3d, true);
struct pipe_surface dst_tmpl;
util_blitter_default_dst_texture(&dst_tmpl, info->dst.resource,
info->dst.level, info->dst.box.z);
dst_tmpl.format = PIPE_FORMAT_R16G16B16A16_UINT;
struct pipe_surface *dst_surf =
pctx->create_surface(pctx, info->dst.resource, &dst_tmpl);
if (!dst_surf) {
fprintf(stderr, "Failed to create YUV dst surface\n");
util_blitter_unset_running_flag(v3d->blitter);
return;
}
uint32_t sand30_stride = src->sand_col128_stride;
/* Adjust the dimensions of dst luma/chroma to match src
* size now we are using a cpp=8 format. Next dimension take into
* account the UIF microtile layouts.
*/
dst_surf->height /= 2;
dst_surf->width = align(dst_surf->width, 8);
if (src->cpp == 2)
dst_surf->width /= 2;
/* Set the constant buffer. */
struct pipe_constant_buffer cb_uniforms = {
.user_buffer = &sand30_stride,
.buffer_size = sizeof(sand30_stride),
};
pctx->set_constant_buffer(pctx, PIPE_SHADER_FRAGMENT, 0, false,
&cb_uniforms);
struct pipe_constant_buffer saved_fs_cb1 = { 0 };
pipe_resource_reference(&saved_fs_cb1.buffer,
v3d->constbuf[PIPE_SHADER_FRAGMENT].cb[1].buffer);
memcpy(&saved_fs_cb1, &v3d->constbuf[PIPE_SHADER_FRAGMENT].cb[1],
sizeof(struct pipe_constant_buffer));
struct pipe_constant_buffer cb_src = {
.buffer = info->src.resource,
.buffer_offset = src->slices[info->src.level].offset,
.buffer_size = (src->bo->size -
src->slices[info->src.level].offset),
};
pctx->set_constant_buffer(pctx, PIPE_SHADER_FRAGMENT, 1, false,
&cb_src);
/* Unbind the textures, to make sure we don't try to recurse into the
* shadow blit.
*/
pctx->set_sampler_views(pctx, PIPE_SHADER_FRAGMENT, 0, 0, 0, false,
NULL);
pctx->bind_sampler_states(pctx, PIPE_SHADER_FRAGMENT, 0, 0, NULL);
util_blitter_custom_shader(v3d->blitter, dst_surf,
v3d_get_sand30_vs(pctx),
v3d_get_sand30_fs(pctx));
util_blitter_restore_textures(v3d->blitter);
util_blitter_restore_constant_buffer_state(v3d->blitter);
/* Restore cb1 (util_blitter doesn't handle this one). */
pctx->set_constant_buffer(pctx, PIPE_SHADER_FRAGMENT, 1, true,
&saved_fs_cb1);
pipe_surface_reference(&dst_surf, NULL);
info->mask &= ~PIPE_MASK_RGBA;
return;
}
/* Optimal hardware path for blitting pixels.
* Scaling, format conversion, up- and downsampling (resolve) are allowed.
*/
@@ -848,6 +1173,8 @@ v3d_blit(struct pipe_context *pctx, const struct pipe_blit_info *blit_info)
struct v3d_context *v3d = v3d_context(pctx);
struct pipe_blit_info info = *blit_info;
v3d_sand30_blit(pctx, &info);
v3d_sand8_blit(pctx, &info);
v3d_tfu_blit(pctx, &info);

View File

@@ -293,6 +293,10 @@ v3d_context_destroy(struct pipe_context *pctx)
pctx->delete_fs_state(pctx, v3d->sand8_blit_fs_luma);
if (v3d->sand8_blit_fs_chroma)
pctx->delete_fs_state(pctx, v3d->sand8_blit_fs_chroma);
if (v3d->sand30_blit_vs)
pctx->delete_vs_state(pctx, v3d->sand30_blit_vs);
if (v3d->sand30_blit_fs)
pctx->delete_fs_state(pctx, v3d->sand30_blit_fs);
v3d_program_fini(pctx);

View File

@@ -552,6 +552,8 @@ struct v3d_context {
struct pipe_shader_state *sand8_blit_vs;
struct pipe_shader_state *sand8_blit_fs_luma;
struct pipe_shader_state *sand8_blit_fs_chroma;
struct pipe_shader_state *sand30_blit_vs;
struct pipe_shader_state *sand30_blit_fs;
/** @{ Current pipeline state objects */
struct pipe_scissor_state scissor;