android_external_ffmpeg/libavutil/rpi_sand_fns.c
2025-06-24 22:46:48 -05:00

449 lines
16 KiB
C

/*
Copyright (c) 2018 Raspberry Pi (Trading) Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Authors: John Cox
*/
#include "config.h"
#include <stdint.h>
#include <string.h>
#include "rpi_sand_fns.h"
#include "avassert.h"
#include "frame.h"
#if ARCH_ARM && HAVE_NEON
#include "libavutil/arm/cpu.h"
#include "libavutil/arm/rpi_sand_neon.h"
#define HAVE_SAND_ASM 1
#elif ARCH_AARCH64 && HAVE_NEON
#include "libavutil/aarch64/cpu.h"
#include "libavutil/aarch64/rpi_sand_neon.h"
#define HAVE_SAND_ASM 1
#else
#define HAVE_SAND_ASM 0
#endif
#define PW 1
#include "rpi_sand_fn_pw.c"
#undef PW
#define PW 2
#include "rpi_sand_fn_pw.c"
#undef PW
#if 1
// Simple round
static void cpy16_to_8(uint8_t * dst, const uint8_t * _src, unsigned int n, const unsigned int shr)
{
const unsigned int rnd = (1 << shr) >> 1;
const uint16_t * src = (const uint16_t *)_src;
for (; n != 0; --n) {
*dst++ = (*src++ + rnd) >> shr;
}
}
#else
// Dithered variation
static void cpy16_to_8(uint8_t * dst, const uint8_t * _src, unsigned int n, const unsigned int shr)
{
unsigned int rnd = (1 << shr) >> 1;
const unsigned int mask = ((1 << shr) - 1);
const uint16_t * src = (const uint16_t *)_src;
for (; n != 0; --n) {
rnd = *src++ + (rnd & mask);
*dst++ = rnd >> shr;
}
}
#endif
// Fetches a single patch - offscreen fixup not done here
// w <= stride1
// unclipped
// _x & _w in pixels, strides in bytes
void av_rpi_sand30_to_planar_y16(uint8_t * dst, const unsigned int dst_stride,
const uint8_t * src,
unsigned int stride1, unsigned int stride2,
unsigned int _x, unsigned int y,
unsigned int _w, unsigned int h)
{
const unsigned int x0 = (_x / 3) * 4; // Byte offset of the word
const unsigned int xskip0 = _x - (x0 >> 2) * 3;
const unsigned int x1 = ((_x + _w) / 3) * 4;
const unsigned int xrem1 = _x + _w - (x1 >> 2) * 3;
const unsigned int mask = stride1 - 1;
const uint8_t * p0 = src + (x0 & mask) + y * stride1 + (x0 & ~mask) * stride2;
const unsigned int slice_inc = ((stride2 - 1) * stride1) >> 2; // RHS of a stripe to LHS of next in words
#if HAVE_SAND_ASM
if (_x == 0 && have_neon(av_get_cpu_flags())) {
ff_rpi_sand30_lines_to_planar_y16(dst, dst_stride, src, stride1, stride2, _x, y, _w, h);
return;
}
#endif
if (x0 == x1) {
// *******************
// Partial single word xfer
return;
}
for (unsigned int i = 0; i != h; ++i, dst += dst_stride, p0 += stride1)
{
unsigned int x = x0;
const uint32_t * p = (const uint32_t *)p0;
uint16_t * d = (uint16_t *)dst;
if (xskip0 != 0) {
const uint32_t p3 = *p++;
if (xskip0 == 1)
*d++ = (p3 >> 10) & 0x3ff;
*d++ = (p3 >> 20) & 0x3ff;
if (((x += 4) & mask) == 0)
p += slice_inc;
}
while (x != x1) {
const uint32_t p3 = *p++;
*d++ = p3 & 0x3ff;
*d++ = (p3 >> 10) & 0x3ff;
*d++ = (p3 >> 20) & 0x3ff;
if (((x += 4) & mask) == 0)
p += slice_inc;
}
if (xrem1 != 0) {
const uint32_t p3 = *p;
*d++ = p3 & 0x3ff;
if (xrem1 == 2)
*d++ = (p3 >> 10) & 0x3ff;
}
}
}
void av_rpi_sand30_to_planar_c16(uint8_t * dst_u, const unsigned int dst_stride_u,
uint8_t * dst_v, const unsigned int dst_stride_v,
const uint8_t * src,
unsigned int stride1, unsigned int stride2,
unsigned int _x, unsigned int y,
unsigned int _w, unsigned int h)
{
const unsigned int x0 = (_x / 3) * 8; // Byte offset of the word
const unsigned int xskip0 = _x - (x0 >> 3) * 3;
const unsigned int x1 = ((_x + _w) / 3) * 8;
const unsigned int xrem1 = _x + _w - (x1 >> 3) * 3;
const unsigned int mask = stride1 - 1;
const uint8_t * p0 = src + (x0 & mask) + y * stride1 + (x0 & ~mask) * stride2;
const unsigned int slice_inc = ((stride2 - 1) * stride1) >> 2; // RHS of a stripe to LHS of next in words
#if HAVE_SAND_ASM
if (_x == 0 && have_neon(av_get_cpu_flags())) {
ff_rpi_sand30_lines_to_planar_c16(dst_u, dst_stride_u, dst_v, dst_stride_v,
src, stride1, stride2, _x, y, _w, h);
return;
}
#endif
if (x0 == x1) {
// *******************
// Partial single word xfer
return;
}
for (unsigned int i = 0; i != h; ++i, dst_u += dst_stride_u, dst_v += dst_stride_v, p0 += stride1)
{
unsigned int x = x0;
const uint32_t * p = (const uint32_t *)p0;
uint16_t * du = (uint16_t *)dst_u;
uint16_t * dv = (uint16_t *)dst_v;
if (xskip0 != 0) {
const uint32_t p3a = *p++;
const uint32_t p3b = *p++;
if (xskip0 == 1)
{
*du++ = (p3a >> 20) & 0x3ff;
*dv++ = (p3b >> 0) & 0x3ff;
}
*du++ = (p3b >> 10) & 0x3ff;
*dv++ = (p3b >> 20) & 0x3ff;
if (((x += 8) & mask) == 0)
p += slice_inc;
}
while (x != x1) {
const uint32_t p3a = *p++;
const uint32_t p3b = *p++;
*du++ = p3a & 0x3ff;
*dv++ = (p3a >> 10) & 0x3ff;
*du++ = (p3a >> 20) & 0x3ff;
*dv++ = p3b & 0x3ff;
*du++ = (p3b >> 10) & 0x3ff;
*dv++ = (p3b >> 20) & 0x3ff;
if (((x += 8) & mask) == 0)
p += slice_inc;
}
if (xrem1 != 0) {
const uint32_t p3a = *p++;
const uint32_t p3b = *p++;
*du++ = p3a & 0x3ff;
*dv++ = (p3a >> 10) & 0x3ff;
if (xrem1 == 2)
{
*du++ = (p3a >> 20) & 0x3ff;
*dv++ = p3b & 0x3ff;
}
}
}
}
// Fetches a single patch - offscreen fixup not done here
// w <= stride1
// single lose bottom 2 bits truncation
// _x & _w in pixels, strides in bytes
void av_rpi_sand30_to_planar_y8(uint8_t * dst, const unsigned int dst_stride,
const uint8_t * src,
unsigned int stride1, unsigned int stride2,
unsigned int _x, unsigned int y,
unsigned int _w, unsigned int h)
{
const unsigned int x0 = (_x / 3) * 4; // Byte offset of the word
const unsigned int xskip0 = _x - (x0 >> 2) * 3;
const unsigned int x1 = ((_x + _w) / 3) * 4;
const unsigned int xrem1 = _x + _w - (x1 >> 2) * 3;
const unsigned int mask = stride1 - 1;
const uint8_t * p0 = src + (x0 & mask) + y * stride1 + (x0 & ~mask) * stride2;
const unsigned int slice_inc = ((stride2 - 1) * stride1) >> 2; // RHS of a stripe to LHS of next in words
#if HAVE_SAND_ASM
if (_x == 0) {
ff_rpi_sand30_lines_to_planar_y8(dst, dst_stride, src, stride1, stride2, _x, y, _w, h);
return;
}
#endif
if (x0 == x1) {
// *******************
// Partial single word xfer
return;
}
for (unsigned int i = 0; i != h; ++i, dst += dst_stride, p0 += stride1)
{
unsigned int x = x0;
const uint32_t * p = (const uint32_t *)p0;
uint8_t * d = dst;
if (xskip0 != 0) {
const uint32_t p3 = *p++;
if (xskip0 == 1)
*d++ = (p3 >> 12) & 0xff;
*d++ = (p3 >> 22) & 0xff;
if (((x += 4) & mask) == 0)
p += slice_inc;
}
while (x != x1) {
const uint32_t p3 = *p++;
*d++ = (p3 >> 2) & 0xff;
*d++ = (p3 >> 12) & 0xff;
*d++ = (p3 >> 22) & 0xff;
if (((x += 4) & mask) == 0)
p += slice_inc;
}
if (xrem1 != 0) {
const uint32_t p3 = *p;
*d++ = (p3 >> 2) & 0xff;
if (xrem1 == 2)
*d++ = (p3 >> 12) & 0xff;
}
}
}
// w/h in pixels
void av_rpi_sand16_to_sand8(uint8_t * dst, const unsigned int dst_stride1, const unsigned int dst_stride2,
const uint8_t * src, const unsigned int src_stride1, const unsigned int src_stride2,
unsigned int w, unsigned int h, const unsigned int shr)
{
const unsigned int n = dst_stride1 / 2;
unsigned int j;
// This is true for our current layouts
av_assert0(dst_stride1 == src_stride1);
// As we have the same stride1 for src & dest and src is wider than dest
// then if we loop on src we can always write contiguously to dest
// We make no effort to copy an exact width - round up to nearest src stripe
// as we will always have storage in dest for that
#if ARCH_ARM && HAVE_NEON
if (shr == 3 && src_stride1 == 128) {
for (j = 0; j + n < w; j += dst_stride1) {
uint8_t * d = dst + j * dst_stride2;
const uint8_t * s1 = src + j * 2 * src_stride2;
const uint8_t * s2 = s1 + src_stride1 * src_stride2;
ff_rpi_sand128b_stripe_to_8_10(d, s1, s2, h);
}
}
else
#endif
{
for (j = 0; j + n < w; j += dst_stride1) {
uint8_t * d = dst + j * dst_stride2;
const uint8_t * s1 = src + j * 2 * src_stride2;
const uint8_t * s2 = s1 + src_stride1 * src_stride2;
for (unsigned int i = 0; i != h; ++i, s1 += src_stride1, s2 += src_stride1, d += dst_stride1) {
cpy16_to_8(d, s1, n, shr);
cpy16_to_8(d + n, s2, n, shr);
}
}
}
// Fix up a trailing dest half stripe
if (j < w) {
uint8_t * d = dst + j * dst_stride2;
const uint8_t * s1 = src + j * 2 * src_stride2;
for (unsigned int i = 0; i != h; ++i, s1 += src_stride1, d += dst_stride1) {
cpy16_to_8(d, s1, n, shr);
}
}
}
int av_rpi_sand_to_planar_frame(AVFrame * const dst, const AVFrame * const src)
{
const int w = av_frame_cropped_width(src);
const int h = av_frame_cropped_height(src);
const int x = src->crop_left;
const int y = src->crop_top;
const unsigned int stride2_y = av_rpi_sand_frame_stride2_y(src);
const unsigned int stride2_c = av_rpi_sand_frame_stride2_c(src);
// We will crop as part of the conversion
dst->crop_top = 0;
dst->crop_left = 0;
dst->crop_bottom = 0;
dst->crop_right = 0;
switch (src->format){
case AV_PIX_FMT_SAND128:
case AV_PIX_FMT_RPI4_8:
switch (dst->format){
case AV_PIX_FMT_YUV420P:
av_rpi_sand_to_planar_y8(dst->data[0], dst->linesize[0],
src->data[0],
av_rpi_sand_frame_stride1(src), stride2_y,
x, y, w, h);
av_rpi_sand_to_planar_c8(dst->data[1], dst->linesize[1],
dst->data[2], dst->linesize[2],
src->data[1],
av_rpi_sand_frame_stride1(src), stride2_c,
x/2, y/2, w/2, h/2);
break;
case AV_PIX_FMT_NV12:
av_rpi_sand_to_planar_y8(dst->data[0], dst->linesize[0],
src->data[0],
av_rpi_sand_frame_stride1(src), stride2_y,
x, y, w, h);
av_rpi_sand_to_planar_y8(dst->data[1], dst->linesize[1],
src->data[1],
av_rpi_sand_frame_stride1(src), stride2_c,
x/2, y/2, w, h/2);
break;
default:
return -1;
}
break;
case AV_PIX_FMT_SAND64_10:
switch (dst->format){
case AV_PIX_FMT_YUV420P10:
av_rpi_sand_to_planar_y16(dst->data[0], dst->linesize[0],
src->data[0],
av_rpi_sand_frame_stride1(src), stride2_y,
x*2, y, w*2, h);
av_rpi_sand_to_planar_c16(dst->data[1], dst->linesize[1],
dst->data[2], dst->linesize[2],
src->data[1],
av_rpi_sand_frame_stride1(src), stride2_c,
x, y/2, w, h/2);
break;
default:
return -1;
}
break;
case AV_PIX_FMT_RPI4_10:
switch (dst->format){
case AV_PIX_FMT_YUV420P10:
av_rpi_sand30_to_planar_y16(dst->data[0], dst->linesize[0],
src->data[0],
av_rpi_sand_frame_stride1(src), stride2_y,
x, y, w, h);
av_rpi_sand30_to_planar_c16(dst->data[1], dst->linesize[1],
dst->data[2], dst->linesize[2],
src->data[1],
av_rpi_sand_frame_stride1(src), stride2_c,
x/2, y/2, w/2, h/2);
break;
case AV_PIX_FMT_NV12:
av_rpi_sand30_to_planar_y8(dst->data[0], dst->linesize[0],
src->data[0],
av_rpi_sand_frame_stride1(src), stride2_y,
x, y, w, h);
av_rpi_sand30_to_planar_y8(dst->data[1], dst->linesize[1],
src->data[1],
av_rpi_sand_frame_stride1(src), stride2_c,
x/2, y/2, w, h/2);
break;
default:
return -1;
}
break;
default:
return -1;
}
return av_frame_copy_props(dst, src);
}