// Compute the scan of a register holding 32-bit integers. If the register holds // x0..x7 then the scan will hold x0, x0+x1, x0+x1+x2, ..., x0+x1+...+x7 // // For the AVX2 example below, let [...] represent a 128-bit block, and let a, // ..., h be 32-bit integers (assumed small enough to be able to add them // without overflow). // // Use -> as shorthand for summing, i.e. h->a = h + g + f + e + d + c + b + a. // // x = [h g f e][d c b a] // x01 = [g f e 0][c b a 0] // x02 = [g+h f+g e+f e][c+d b+c a+b a] // x03 = [e+f e 0 0][a+b a 0 0] // x04 = [e->h e->g e->f e][a->d a->c a->b a] // s = a->d // s01 = [a->d a->d a->d a->d] // s02 = [a->d a->d a->d a->d][0 0 0 0] // ret = [a->h a->g a->f a->e][a->d a->c a->b a] template <typename D>
HWY_ATTR HWY_INLINE hn::VFromD<D> Scan32(D int32_tag, hn::VFromD<D> x) { constauto x01 = hn::ShiftLeftBytes<4>(x); constauto x02 = hn::Add(x, x01); constauto x03 = hn::ShiftLeftBytes<8>(x02); constauto x04 = hn::Add(x02, x03); return ScanTraits<int32_tag.MaxBlocks()>::AddBlocks(int32_tag, x04);
}
// Compute two integral images from src. B sums elements; A sums their // squares. The images are offset by one pixel, so will have width and height // equal to width + 1, height + 1 and the first row and column will be zero. // // A+1 and B+1 should be aligned to 32 bytes. buf_stride should be a multiple // of 8. template <typename T, typename D>
HWY_ATTR HWY_INLINE void IntegralImages(D int32_tag, const T *HWY_RESTRICT src,
int src_stride, int width, int height,
int32_t *HWY_RESTRICT A,
int32_t *HWY_RESTRICT B,
int buf_stride) {
constexpr hn::Rebind<T, D> uint_tag;
constexpr hn::Repartition<int16_t, D> int16_tag; // Write out the zero top row
hwy::ZeroBytes(A, 4 * (width + 8));
hwy::ZeroBytes(B, 4 * (width + 8));
for (int i = 0; i < height; ++i) { // Zero the left column.
A[(i + 1) * buf_stride] = B[(i + 1) * buf_stride] = 0;
// ldiff is the difference H - D where H is the output sample immediately // to the left and D is the output sample above it. These are scalars, // replicated across the eight lanes. auto ldiff1 = hn::Zero(int32_tag); auto ldiff2 = hn::Zero(int32_tag);
for (int j = 0; j < width; j += hn::MaxLanes(int32_tag)) { const int ABj = 1 + j;
constauto above1 = hn::Load(int32_tag, B + ABj + i * buf_stride); constauto above2 = hn::Load(int32_tag, A + ABj + i * buf_stride);
// The final filter for self-guided restoration. Computes a weighted average // across A, B with "cross sums" (see CrossSum implementation above). template <typename DL>
HWY_ATTR HWY_INLINE void FinalFilter(
DL int32_tag, int32_t *HWY_RESTRICT dst, int dst_stride, const int32_t *HWY_RESTRICT A, const int32_t *HWY_RESTRICT B,
int buf_stride, constvoid *HWY_RESTRICT dgd8, int dgd_stride, int width,
int height, int highbd) {
constexpr hn::Repartition<uint8_t, hn::Half<DL>> uint8_half_tag;
constexpr hn::Repartition<int16_t, DL> int16_tag;
constexpr int nb = 5;
constexpr int kShift = SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS; constauto rounding = RoundForShift(int32_tag, kShift); const uint8_t *HWY_RESTRICT dgd_real =
highbd ? reinterpret_cast<const uint8_t *>(CONVERT_TO_SHORTPTR(dgd8))
: reinterpret_cast<const uint8_t *>(dgd8);
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; j += hn::MaxLanes(int32_tag)) { constauto a = CrossSum(int32_tag, A + i * buf_stride + j, buf_stride); constauto b = CrossSum(int32_tag, B + i * buf_stride + j, buf_stride);
auto v =
hn::Add(hn::WidenMulPairwiseAdd(int32_tag, hn::BitCast(int16_tag, a),
hn::BitCast(int16_tag, src)),
b); auto w = hn::ShiftRight<kShift>(hn::Add(v, rounding));
// Assumes that C, D are integral images for the original buffer which has been // extended to have a padding of SGRPROJ_BORDER_VERT/SGRPROJ_BORDER_HORZ pixels // on the sides. A, B, C, D point at logical position (0, 0). template <int Step, typename DL>
HWY_ATTR HWY_INLINE void CalcAB(DL int32_tag, int32_t *HWY_RESTRICT A,
int32_t *HWY_RESTRICT B, const int32_t *HWY_RESTRICT C, const int32_t *HWY_RESTRICT D, int width,
int height, int buf_stride, int bit_depth,
int sgr_params_idx, int radius_idx) {
constexpr hn::Repartition<int16_t, DL> int16_tag;
constexpr hn::Repartition<uint32_t, DL> uint32_tag; const sgr_params_type *HWY_RESTRICT const params =
&av1_sgr_params[sgr_params_idx]; const int r = params->r[radius_idx]; const int n = (2 * r + 1) * (2 * r + 1); constauto s = hn::Set(int32_tag, params->s[radius_idx]); // one_over_n[n-1] is 2^12/n, so easily fits in an int16 constauto one_over_n =
hn::BitCast(int16_tag, hn::Set(int32_tag, av1_one_by_x[n - 1]));
// Set up masks const int max_lanes = static_cast<int>(hn::MaxLanes(int32_tag));
HWY_ALIGN hn::Mask<decltype(int32_tag)> mask[max_lanes];
for (int idx = 0; idx < max_lanes; idx++) {
mask[idx] = hn::FirstN(int32_tag, idx);
}
for (int i = -1; i < height + 1; i += Step) {
for (int j = -1; j < width + 1; j += max_lanes) { const int32_t *HWY_RESTRICT Cij = C + i * buf_stride + j; const int32_t *HWY_RESTRICT Dij = D + i * buf_stride + j;
auto sum1 = BoxSumFromII(int32_tag, Dij, buf_stride, r); auto sum2 = BoxSumFromII(int32_tag, Cij, buf_stride, r);
// When width + 2 isn't a multiple of 8, sum1 and sum2 will contain // some uninitialised data in their upper words. We use a mask to // ensure that these bits are set to 0.
int idx = AOMMIN(max_lanes, width + 1 - j);
assert(idx >= 1);
// sum1 might have lanes greater than 2^15, so we can't use madd to do // multiplication involving sum1. However, a_complement and one_over_n // are both less than 256, so we can multiply them first. constauto a_comp_over_n = hn::WidenMulPairwiseAdd(
int32_tag, hn::BitCast(int16_tag, a_complement), one_over_n); constauto b_int = hn::Mul(a_comp_over_n, sum1); constauto b_res =
hn::ShiftRight<SGRPROJ_RECIP_BITS>(hn::Add(b_int, rnd_res));
hn::StoreU(b_res, int32_tag, B + i * buf_stride + j);
}
}
}
// The final filter for the self-guided restoration. Computes a // weighted average across A, B with "cross sums" (see cross_sum_... // implementations above). template <typename DL>
HWY_ATTR HWY_INLINE void FinalFilterFast(
DL int32_tag, int32_t *HWY_RESTRICT dst, int dst_stride, const int32_t *HWY_RESTRICT A, const int32_t *HWY_RESTRICT B,
int buf_stride, constvoid *HWY_RESTRICT dgd8, int dgd_stride, int width,
int height, int highbd) {
constexpr hn::Repartition<uint8_t, hn::Half<DL>> uint8_half_tag;
constexpr hn::Repartition<int16_t, DL> int16_tag;
constexpr int nb0 = 5;
constexpr int nb1 = 4;
constexpr int kShift0 = SGRPROJ_SGR_BITS + nb0 - SGRPROJ_RST_BITS;
constexpr int kShift1 = SGRPROJ_SGR_BITS + nb1 - SGRPROJ_RST_BITS;
for (int i = 0; i < height; ++i) {
if (!(i & 1)) { // even row
for (int j = 0; j < width; j += hn::MaxLanes(int32_tag)) { constauto a =
CrossSumFastEvenRow(int32_tag, A + i * buf_stride + j, buf_stride); constauto b =
CrossSumFastEvenRow(int32_tag, B + i * buf_stride + j, buf_stride);
auto v = hn::Add(
hn::WidenMulPairwiseAdd(int32_tag, hn::BitCast(int16_tag, a),
hn::BitCast(int16_tag, src)),
b); auto w = hn::ShiftRight<kShift0>(hn::Add(v, rounding0));
hn::StoreU(w, int32_tag, dst + i * dst_stride + j);
}
} else { // odd row
for (int j = 0; j < width; j += hn::MaxLanes(int32_tag)) { constauto a = CrossSumFastOddRow(int32_tag, A + i * buf_stride + j); constauto b = CrossSumFastOddRow(int32_tag, B + i * buf_stride + j);
auto v = hn::Add(
hn::WidenMulPairwiseAdd(int32_tag, hn::BitCast(int16_tag, a),
hn::BitCast(int16_tag, src)),
b); auto w = hn::ShiftRight<kShift1>(hn::Add(v, rounding1));
HWY_ATTR HWY_INLINE int SelfGuidedRestoration( const uint8_t *dgd8, int width, int height, int dgd_stride,
int32_t *HWY_RESTRICT flt0, int32_t *HWY_RESTRICT flt1, int flt_stride,
int sgr_params_idx, int bit_depth, int highbd) {
constexpr hn::ScalableTag<int32_t> int32_tag;
constexpr int kAlignment32Log2 = hwy::CeilLog2(hn::MaxLanes(int32_tag)); // The ALIGN_POWER_OF_TWO macro here ensures that column 1 of Atl, Btl, Ctl // and Dtl is vector aligned. const int buf_elts =
ALIGN_POWER_OF_TWO(RESTORATION_PROC_UNIT_PELS, kAlignment32Log2);
const int width_ext = width + 2 * SGRPROJ_BORDER_HORZ; const int height_ext = height + 2 * SGRPROJ_BORDER_VERT;
// Adjusting the stride of A and B here appears to avoid bad cache effects, // leading to a significant speed improvement. // We also align the stride to a multiple of the vector size for efficiency.
int buf_stride =
ALIGN_POWER_OF_TWO(width_ext + (2 << kAlignment32Log2), kAlignment32Log2);
// The "tl" pointers point at the top-left of the initialised data for the // array.
int32_t *Atl = buf + 0 * buf_elts + (1 << kAlignment32Log2) - 1;
int32_t *Btl = buf + 1 * buf_elts + (1 << kAlignment32Log2) - 1;
int32_t *Ctl = buf + 2 * buf_elts + (1 << kAlignment32Log2) - 1;
int32_t *Dtl = buf + 3 * buf_elts + (1 << kAlignment32Log2) - 1;
// The "0" pointers are (- SGRPROJ_BORDER_VERT, -SGRPROJ_BORDER_HORZ). Note // there's a zero row and column in A, B (integral images), so we move down // and right one for them. const int buf_diag_border =
SGRPROJ_BORDER_HORZ + buf_stride * SGRPROJ_BORDER_VERT;
// Generate integral images from the input. C will contain sums of squares; D // will contain just sums
if (highbd) {
IntegralImages(int32_tag, CONVERT_TO_SHORTPTR(dgd0), dgd_stride, width_ext,
height_ext, Ctl, Dtl, buf_stride);
} else {
IntegralImages(int32_tag, dgd0, dgd_stride, width_ext, height_ext, Ctl, Dtl,
buf_stride);
}
const sgr_params_type *const params = &av1_sgr_params[sgr_params_idx]; // Write to flt0 and flt1 // If params->r == 0 we skip the corresponding filter. We only allow one of // the radii to be 0, as having both equal to 0 would be equivalent to // skipping SGR entirely.
assert(!(params->r[0] == 0 && params->r[1] == 0));
assert(params->r[0] < AOMMIN(SGRPROJ_BORDER_VERT, SGRPROJ_BORDER_HORZ));
assert(params->r[1] < AOMMIN(SGRPROJ_BORDER_VERT, SGRPROJ_BORDER_HORZ));
if (params->r[0] > 0) {
CalcAB<2>(int32_tag, A, B, C, D, width, height, buf_stride, bit_depth,
sgr_params_idx, 0);
FinalFilterFast(int32_tag, flt0, flt_stride, A, B, buf_stride, dgd8,
dgd_stride, width, height, highbd);
}
HWY_ATTR HWY_INLINE int ApplySelfGuidedRestoration( const uint8_t *HWY_RESTRICT dat8, int width, int height, int stride,
int eps, const int *HWY_RESTRICT xqd, uint8_t *HWY_RESTRICT dst8,
int dst_stride, int32_t *HWY_RESTRICT tmpbuf, int bit_depth, int highbd) {
constexpr hn::CappedTag<int32_t, 16> int32_tag;
constexpr size_t kBatchSize = hn::MaxLanes(int32_tag) * 2;
int32_t *flt0 = tmpbuf;
int32_t *flt1 = flt0 + RESTORATION_UNITPELS_MAX;
assert(width * height <= RESTORATION_UNITPELS_MAX); #if HWY_TARGET == HWY_SSE4 const int ret = av1_selfguided_restoration_sse4_1(
dat8, width, height, stride, flt0, flt1, width, eps, bit_depth, highbd); #elif HWY_TARGET == HWY_AVX2 const int ret = av1_selfguided_restoration_avx2(
dat8, width, height, stride, flt0, flt1, width, eps, bit_depth, highbd); #elif HWY_TARGET <= HWY_AVX3 const int ret = av1_selfguided_restoration_avx512(
dat8, width, height, stride, flt0, flt1, width, eps, bit_depth, highbd); #else #error"HWY_TARGET is not supported." const int ret = -1; #endif
if (ret != 0) { return ret;
} const sgr_params_type *const params = &av1_sgr_params[eps];
int xq[2];
av1_decode_xq(xqd, xq, params);
auto xq0 = hn::Set(int32_tag, xq[0]); auto xq1 = hn::Set(int32_tag, xq[1]);
for (int i = 0; i < height; ++i) { // Calculate output in batches of pixels
for (int j = 0; j < width; j += kBatchSize) { const int k = i * width + j; const int m = i * dst_stride + j;
if (highbd) { // Pack into 16 bits and clamp to [0, 2^bit_depth) // Note that packing into 16 bits messes up the order of the bits, // so we use a permute function to correct this
constexpr hn::Repartition<uint16_t, decltype(int32_tag)> uint16_tag; constauto tmp = hn::OrderedDemote2To(uint16_tag, w_0, w_1); constauto max = hn::Set(uint16_tag, (1 << bit_depth) - 1); constauto res = hn::Min(tmp, max);
hn::StoreU(res, uint16_tag, CONVERT_TO_SHORTPTR(dst8 + m));
} else { // Pack into 8 bits and clamp to [0, 256) // Note that each pack messes up the order of the bits, // so we use a permute function to correct this
constexpr hn::Repartition<int16_t, decltype(int32_tag)> int16_tag;
constexpr hn::Repartition<uint8_t, hn::Half<decltype(int32_tag)>>
uint8_tag; constauto tmp = hn::OrderedDemote2To(int16_tag, w_0, w_1); constauto res = hn::DemoteTo(uint8_tag, tmp);
hn::StoreU(res, uint8_tag, dst8 + m);
}
}
} return0;
}
} // namespace HWY_NAMESPACE
} // namespace
HWY_AFTER_NAMESPACE();
#define MAKE_SELFGUIDED_RESTORATION(suffix) \
extern "C" int av1_selfguided_restoration_##suffix( \ const uint8_t *dgd8, int width, int height, int dgd_stride, \
int32_t *flt0, int32_t *flt1, int flt_stride, int sgr_params_idx, \
int bit_depth, int highbd); \
HWY_ATTR HWY_NOINLINE int av1_selfguided_restoration_##suffix( \ const uint8_t *dgd8, int width, int height, int dgd_stride, \
int32_t *flt0, int32_t *flt1, int flt_stride, int sgr_params_idx, \
int bit_depth, int highbd) { \ return HWY_NAMESPACE::SelfGuidedRestoration( \
dgd8, width, height, dgd_stride, flt0, flt1, flt_stride, \
sgr_params_idx, bit_depth, highbd); \
} \
extern "C" int av1_apply_selfguided_restoration_##suffix( \ const uint8_t *dat8, int width, int height, int stride, int eps, \ const int *xqd, uint8_t *dst8, int dst_stride, int32_t *tmpbuf, \
int bit_depth, int highbd); \
HWY_ATTR int av1_apply_selfguided_restoration_##suffix( \ const uint8_t *dat8, int width, int height, int stride, int eps, \ const int *xqd, uint8_t *dst8, int dst_stride, int32_t *tmpbuf, \
int bit_depth, int highbd) { \ return HWY_NAMESPACE::ApplySelfGuidedRestoration( \
dat8, width, height, stride, eps, xqd, dst8, dst_stride, tmpbuf, \
bit_depth, highbd); \
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.