Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/aom/aom_dsp/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 35 kB image not shown  

Quelle  convolve_hwy.h

  Sprache: C
 

/*
 * Copyright (c) 2026, Alliance for Open Media. All rights reserved.
 *
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
 * Media Patent License 1.0 was not distributed with this source code in the
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
 */


#ifndef AOM_AOM_DSP_CONVOLVE_HWY_H_
#define AOM_AOM_DSP_CONVOLVE_HWY_H_

#include <cassert>

#include "aom_dsp/arm/aom_filter.h"
#include "third_party/highway/hwy/highway.h"

HWY_BEFORE_NAMESPACE();

namespace {
namespace HWY_NAMESPACE {

namespace hn = hwy::HWY_NAMESPACE;

template <typename D>
HWY_ATTR HWY_INLINE hn::VFromD<D> LoadUnaligned4x4(D tag16, const uint8_t *buf,
                                                   ptrdiff_t stride) {
  hn::CappedTag<uint32_t, 4> tag32;
  uint32_t r0, r1, r2, r3;
  memcpy(&r0, buf, 4);
  memcpy(&r1, buf + stride, 4);
  memcpy(&r2, buf + 2 * stride, 4);
  memcpy(&r3, buf + 3 * stride, 4);
  auto v32 = hn::Zero(tag32);
  v32 = hn::InsertLane(v32, 0, r0);
  v32 = hn::InsertLane(v32, 1, r1);
  v32 = hn::InsertLane(v32, 2, r2);
  v32 = hn::InsertLane(v32, 3, r3);
  hn::Rebind<uint8_t, D> tag8;
  return hn::PromoteTo(tag16, hn::BitCast(tag8, v32));
}

template <typename D>
HWY_ATTR HWY_INLINE void StoreUnaligned4x4(D tag16, uint8_t *buf,
                                           ptrdiff_t stride,
                                           hn::VFromD<D> &vec) {
  (void)tag16;
  hn::Rebind<uint8_t, D> tag8;
  constexpr hn::Half<D> half_tag16;
  auto vec_demoted = hn::ReorderDemote2To(tag8, hn::LowerHalf(half_tag16, vec),
                                          hn::UpperHalf(half_tag16, vec));
  constexpr hn::Half<decltype(tag8)> half_tag;
  constexpr hn::Half<decltype(half_tag)> quarter_tag;
  auto vec1_2 = hn::LowerHalf(half_tag, vec_demoted);
  auto vec2_2 = hn::UpperHalf(half_tag, vec_demoted);
  auto vec1_4 = hn::LowerHalf(quarter_tag, vec1_2);
  auto vec2_4 = hn::UpperHalf(quarter_tag, vec1_2);
  auto vec3_4 = hn::LowerHalf(quarter_tag, vec2_2);
  auto vec4_4 = hn::UpperHalf(quarter_tag, vec2_2);
  hn::StoreU(vec1_4, quarter_tag, buf);
  hn::StoreU(vec2_4, quarter_tag, buf + stride);
  hn::StoreU(vec3_4, quarter_tag, buf + 2 * stride);
  hn::StoreU(vec4_4, quarter_tag, buf + 3 * stride);
}

template <typename D>
HWY_ATTR HWY_INLINE hn::VFromD<D> LoadUnaligned2x8(D tag16, const uint8_t *buf,
                                                   ptrdiff_t stride) {
  hn::Rebind<uint8_t, D> tag8;
  constexpr hn::Half<decltype(tag8)> half_tag8;
  auto first_half = hn::LoadU(half_tag8, buf);
  auto second_half = hn::LoadU(half_tag8, buf + stride);
  return hn::PromoteTo(tag16, hn::Combine(tag8, first_half, second_half));
}

template <typename D>
HWY_ATTR HWY_INLINE void StoreUnaligned2x8(D tag, uint8_t *buf,
                                           ptrdiff_t stride,
                                           hn::VFromD<D> &vec) {
  (void)tag;
  hn::Rebind<uint8_t, D> tag8;
  constexpr hn::Half<D> half_tag16;
  auto vec_demoted = hn::ReorderDemote2To(tag8, hn::LowerHalf(half_tag16, vec),
                                          hn::UpperHalf(half_tag16, vec));
  constexpr hn::Half<decltype(tag8)> half_tag8;
  auto vec1_2 = hn::UpperHalf(half_tag8, vec_demoted);
  auto vec2_2 = hn::LowerHalf(half_tag8, vec_demoted);
  hn::StoreU(vec1_2, half_tag8, buf);
  hn::StoreU(vec2_2, half_tag8, buf + stride);
}

template <typename D>
HWY_ATTR HWY_INLINE hn::VFromD<D> LoadUnaligned4x8(D scalable_tag,
                                                   const uint8_t *buf,
                                                   ptrdiff_t stride) {
  hn::Rebind<uint8_t, D> tag8;
  constexpr hn::Half<decltype(tag8)> half_tag8;
  constexpr hn::Half<decltype(half_tag8)> quarter_tag8;
  auto first_quarter = hn::LoadU(quarter_tag8, buf);
  auto second_quarter = hn::LoadU(quarter_tag8, buf + stride);
  auto third_quarter = hn::LoadU(quarter_tag8, buf + 2 * stride);
  auto fourth_quarter = hn::LoadU(quarter_tag8, buf + 3 * stride);
  return hn::PromoteTo(
      scalable_tag,
      hn::Combine(tag8, hn::Combine(half_tag8, first_quarter, second_quarter),
                  hn::Combine(half_tag8, third_quarter, fourth_quarter)));
}

template <typename D>
HWY_ATTR HWY_INLINE void StoreUnaligned4x8(D tag, uint8_t *buf,
                                           ptrdiff_t stride,
                                           hn::VFromD<D> &vec) {
  (void)tag;
  hn::Rebind<uint8_t, D> tag8;
  constexpr hn::Half<D> half_tag16;
  auto vec_demoted = hn::ReorderDemote2To(tag8, hn::LowerHalf(half_tag16, vec),
                                          hn::UpperHalf(half_tag16, vec));
  constexpr hn::Half<decltype(tag8)> half_tag8;
  constexpr hn::Half<decltype(half_tag8)> quarter_tag8;
  auto vec1_2 = hn::UpperHalf(half_tag8, vec_demoted);
  auto vec2_2 = hn::LowerHalf(half_tag8, vec_demoted);
  auto vec1_4 = hn::UpperHalf(quarter_tag8, vec1_2);
  auto vec2_4 = hn::LowerHalf(quarter_tag8, vec1_2);
  auto vec3_4 = hn::UpperHalf(quarter_tag8, vec2_2);
  auto vec4_4 = hn::LowerHalf(quarter_tag8, vec2_2);
  hn::StoreU(vec1_4, quarter_tag8, buf);
  hn::StoreU(vec2_4, quarter_tag8, buf + stride);
  hn::StoreU(vec3_4, quarter_tag8, buf + 2 * stride);
  hn::StoreU(vec4_4, quarter_tag8, buf + 3 * stride);
}

HWY_ATTR inline void ConvolveHoriz2Tap(const uint8_t *src, ptrdiff_t src_stride,
                                       uint8_t *dst, ptrdiff_t dst_stride,
                                       const int16_t *filter_x, int w, int h) {
  hn::ScalableTag<int16_t> mul_tag;
  hn::Rebind<uint8_t, decltype(mul_tag)> pixel_tag;
  auto filter_0 = hn::Set(mul_tag, filter_x[3]);
  auto filter_1 = hn::Set(mul_tag, filter_x[4]);
  auto vw = hn::Lanes(mul_tag);
  for (int i = 0; i < h; ++i) {
    for (int j = 0; j < w; j += vw) {
      auto src0 = hn::PromoteTo(mul_tag, hn::LoadU(pixel_tag, &src[j]));
      auto src1 = hn::PromoteTo(mul_tag, hn::LoadU(pixel_tag, &src[j + 1]));
      auto mulv = hn::RoundingShiftRight<FILTER_BITS>(src0 * filter_0 +
                                                      src1 * filter_1);
      auto mulv_demoted = hn::DemoteTo(pixel_tag, mulv);
      if (j + static_cast<int>(vw) > w) {
        hn::StoreN(mulv_demoted, pixel_tag, &dst[j], w - j);
      } else {
        hn::StoreU(mulv_demoted, pixel_tag, &dst[j]);
      }
    }
    src += src_stride;
    dst += dst_stride;
  }
}

template <typename D, typename DFilter>
HWY_ATTR HWY_INLINE hn::VFromD<D> Convolve4_8(
    D tag16, DFilter tag_filter, hn::VFromD<D> &s0, hn::VFromD<D> &s1,
    hn::VFromD<D> &s2, hn::VFromD<D> &s3, hn::VFromD<DFilter> &filter) {
  (void)tag_filter;
  auto mul0 = hn::Mul(s0, hn::Set(tag16, hn::ExtractLane(filter, 0)));
  auto mul1 = hn::Mul(s1, hn::Set(tag16, hn::ExtractLane(filter, 1)));
  auto mul2 = hn::Mul(s2, hn::Set(tag16, hn::ExtractLane(filter, 2)));
  auto mul3 = hn::Mul(s3, hn::Set(tag16, hn::ExtractLane(filter, 3)));

  auto res = mul0 + mul1 + mul2 + mul3;
  // Shift (FILTER_BITS - 1) because filter values were halved.
  return hn::RoundingShiftRight<FILTER_BITS - 1>(res);
}

HWY_ATTR inline void ConvolveHoriz4Tap(const uint8_t *src, ptrdiff_t src_stride,
                                       uint8_t *dst, ptrdiff_t dst_stride,
                                       const int16_t *filter_x, int w, int h) {
  hn::CappedTag<int16_t, 16> tag16;
  hn::CappedTag<int16_t, 4> filter_tag;
  auto f_vec = hn::LoadU(filter_tag, filter_x + 2);
  // All filter values are even, halve to reduce intermediate precision
  // requirements.
  f_vec = hn::ShiftRight<1>(f_vec);

  if (w == 4) {
    // Each iteration processes a 4x4 block
    do {
      auto src0 = LoadUnaligned4x4(tag16, src, src_stride);
      auto src1 = LoadUnaligned4x4(tag16, src + 1, src_stride);
      auto src2 = LoadUnaligned4x4(tag16, src + 2, src_stride);
      auto src3 = LoadUnaligned4x4(tag16, src + 3, src_stride);
      auto result =
          Convolve4_8(tag16, filter_tag, src0, src1, src2, src3, f_vec);
      StoreUnaligned4x4(tag16, dst, dst_stride, result);
      h -= 4;
      src += 4 * src_stride;
      dst += 4 * dst_stride;
    } while (h > 0);
  } else if (w == 8) {
    // Each iteration processes a 2x8 block
    do {
      auto src0 = LoadUnaligned2x8(tag16, src, src_stride);
      auto src1 = LoadUnaligned2x8(tag16, src + 1, src_stride);
      auto src2 = LoadUnaligned2x8(tag16, src + 2, src_stride);
      auto src3 = LoadUnaligned2x8(tag16, src + 3, src_stride);
      auto result =
          Convolve4_8(tag16, filter_tag, src0, src1, src2, src3, f_vec);
      StoreUnaligned2x8(tag16, dst, dst_stride, result);
      h -= 2;
      src += 2 * src_stride;
      dst += 2 * dst_stride;
    } while (h > 0);
  } else if (w == 16) {
    // One 1x16 block a time
    do {
      hn::Rebind<uint8_t, decltype(tag16)> tag8;
      auto src0 = hn::PromoteTo(tag16, hn::LoadU(tag8, src));
      auto src1 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 1));
      auto src2 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 2));
      auto src3 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 3));
      auto result =
          Convolve4_8(tag16, filter_tag, src0, src1, src2, src3, f_vec);
      hn::StoreU(hn::DemoteTo(tag8, result), tag8, dst);
      h--;
      src += src_stride;
      dst += dst_stride;
    } while (h > 0);
  } else {
    hn::ScalableTag<int16_t> mul_tag;
    hn::Rebind<uint8_t, decltype(mul_tag)> pixel_tag;
    auto vw = hn::Lanes(mul_tag);
    for (int i = 0; i < h; ++i) {
      for (int j = 0; j < w; j += vw) {
        auto src0 = hn::PromoteTo(mul_tag, hn::LoadU(pixel_tag, &src[j]));
        auto src1 = hn::PromoteTo(mul_tag, hn::LoadU(pixel_tag, &src[j + 1]));
        auto src2 = hn::PromoteTo(mul_tag, hn::LoadU(pixel_tag, &src[j + 2]));
        auto src3 = hn::PromoteTo(mul_tag, hn::LoadU(pixel_tag, &src[j + 3]));
        auto result =
            Convolve4_8(mul_tag, filter_tag, src0, src1, src2, src3, f_vec);
        auto result_demoted = hn::DemoteTo(pixel_tag, result);
        if (j + static_cast<int>(vw) > w) {
          hn::StoreN(result_demoted, pixel_tag, &dst[j], w - j);
        } else {
          hn::StoreU(result_demoted, pixel_tag, &dst[j]);
        }
      }
      src += src_stride;
      dst += dst_stride;
    }
  }
}

template <typename D, typename DFilter>
HWY_ATTR HWY_INLINE hn::VFromD<D> Convolve8_8(
    D tag16, DFilter tag_filter, hn::VFromD<D> &s0, hn::VFromD<D> &s1,
    hn::VFromD<D> &s2, hn::VFromD<D> &s3, hn::VFromD<D> &s4, hn::VFromD<D> &s5,
    hn::VFromD<D> &s6, hn::VFromD<D> &s7, hn::VFromD<DFilter> &filter) {
  (void)tag_filter;
  auto filter_0 = hn::ExtractLane(filter, 0);
  auto filter_1 = hn::ExtractLane(filter, 1);
  auto filter_2 = hn::ExtractLane(filter, 2);
  auto filter_3 = hn::ExtractLane(filter, 3);
  auto filter_4 = hn::ExtractLane(filter, 4);
  auto filter_5 = hn::ExtractLane(filter, 5);
  auto filter_6 = hn::ExtractLane(filter, 6);
  auto filter_7 = hn::ExtractLane(filter, 7);
  auto mul0 = hn::Mul(s0, hn::Set(tag16, filter_0));
  auto mul1 = hn::Mul(s1, hn::Set(tag16, filter_1));
  auto mul2 = hn::Mul(s2, hn::Set(tag16, filter_2));
  auto mul3 = hn::Mul(s3, hn::Set(tag16, filter_3));
  auto mul4 = hn::Mul(s4, hn::Set(tag16, filter_4));
  auto mul5 = hn::Mul(s5, hn::Set(tag16, filter_5));
  auto mul6 = hn::Mul(s6, hn::Set(tag16, filter_6));
  auto mul7 = hn::Mul(s7, hn::Set(tag16, filter_7));

  auto res = mul0 + mul1 + mul2 + mul3 + mul4 + mul5 + mul6 + mul7;
  // Shift (FILTER_BITS - 1) because filter values were halved.
  return hn::RoundingShiftRight<FILTER_BITS - 1>(res);
}

DECLARE_ALIGNED(32static const uint8_t, filt_global[]) = {
  0,  1,  1,  2,  23,  3,  4,  4,  5,  5,  6,  6,  7,  7,  8,  0,  1,  1,
  2,  2,  3,  3,  44,  5,  5,  6,  6,  7,  7,  8,  2,  3,  3,  4,  4,  5,
  5,  6,  6,  7,  78,  8,  9,  9,  102,  3,  3,  4,  4,  5,  5,  6,  6,
  7,  7,  8,  8,  99,  104,  5,  5,  6,  6,  7,  7,  8,  8,  9,  9,  10,
  1011111245,  5,  6,  6,  7,  7,  8,  8,  9,  9,  10101111,
  126,  7,  7,  88,  9,  9,  1010111112121313146,  7,
  7,  8,  8,  9,  9101011111212131314
};

HWY_ATTR inline void ConvolveHoriz8Tap(const uint8_t *src, ptrdiff_t src_stride,
                                       uint8_t *dst, ptrdiff_t dst_stride,
                                       const int16_t *filter_x, int w, int h) {
  hn::CappedTag<int16_t, 16> tag16;
  hn::CappedTag<int16_t, 8> filter_tag;
  auto f_vec = hn::LoadU(filter_tag, filter_x);
  // All filter values are even, halve to reduce intermediate precision
  // requirements.
  f_vec = hn::ShiftRight<1>(f_vec);

  if (w == 4) {
    do {
      auto src0 = LoadUnaligned4x4(tag16, src, src_stride);
      auto src1 = LoadUnaligned4x4(tag16, src + 1, src_stride);
      auto src2 = LoadUnaligned4x4(tag16, src + 2, src_stride);
      auto src3 = LoadUnaligned4x4(tag16, src + 3, src_stride);
      auto src4 = LoadUnaligned4x4(tag16, src + 4, src_stride);
      auto src5 = LoadUnaligned4x4(tag16, src + 5, src_stride);
      auto src6 = LoadUnaligned4x4(tag16, src + 6, src_stride);
      auto src7 = LoadUnaligned4x4(tag16, src + 7, src_stride);
      auto result = Convolve8_8(tag16, filter_tag, src0, src1, src2, src3, src4,
                                src5, src6, src7, f_vec);
      StoreUnaligned4x4(tag16, dst, dst_stride, result);
      h -= 4;
      src += 4 * src_stride;
      dst += 4 * dst_stride;
    } while (h > 0);
  } else if (w == 8) {
    // Each iteration processes a 2x8 block
    do {
      auto src0 = LoadUnaligned2x8(tag16, src, src_stride);
      auto src1 = LoadUnaligned2x8(tag16, src + 1, src_stride);
      auto src2 = LoadUnaligned2x8(tag16, src + 2, src_stride);
      auto src3 = LoadUnaligned2x8(tag16, src + 3, src_stride);
      auto src4 = LoadUnaligned2x8(tag16, src + 4, src_stride);
      auto src5 = LoadUnaligned2x8(tag16, src + 5, src_stride);
      auto src6 = LoadUnaligned2x8(tag16, src + 6, src_stride);
      auto src7 = LoadUnaligned2x8(tag16, src + 7, src_stride);
      auto result = Convolve8_8(tag16, filter_tag, src0, src1, src2, src3, src4,
                                src5, src6, src7, f_vec);
      StoreUnaligned2x8(tag16, dst, dst_stride, result);
      h -= 2;
      src += 2 * src_stride;
      dst += 2 * dst_stride;
    } while (h > 0);
  } else if (w == 16) {
    // One 1x16 block a time
    do {
      hn::Rebind<uint8_t, decltype(tag16)> tag8;
      auto src0 = hn::PromoteTo(tag16, hn::LoadU(tag8, src));
      auto src1 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 1));
      auto src2 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 2));
      auto src3 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 3));
      auto src4 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 4));
      auto src5 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 5));
      auto src6 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 6));
      auto src7 = hn::PromoteTo(tag16, hn::LoadU(tag8, src + 7));
      auto result = Convolve8_8(tag16, filter_tag, src0, src1, src2, src3, src4,
                                src5, src6, src7, f_vec);
      hn::StoreU(hn::DemoteTo(tag8, result), tag8, dst);
      h--;
      src += src_stride;
      dst += dst_stride;
    } while (h > 0);
  } else {
    // This tag will have 32 lanes (for avx512) or 16 lanes (for avx2)
    hn::ScalableTag<int16_t> mul_tag;
    hn::Rebind<uint8_t, decltype(mul_tag)> pixel_tag;
    auto vw = hn::Lanes(mul_tag);
    for (int i = 0; i < h; ++i) {
      for (int j = 0; j < w; j += vw) {
        auto s0 = hn::LoadU(pixel_tag, &src[j]);
        auto s1 = hn::LoadU(pixel_tag, &src[j + 1]);
        auto s2 = hn::LoadU(pixel_tag, &src[j + 2]);
        auto s3 = hn::LoadU(pixel_tag, &src[j + 3]);
        auto s4 = hn::LoadU(pixel_tag, &src[j + 4]);
        auto s5 = hn::LoadU(pixel_tag, &src[j + 5]);
        auto s6 = hn::LoadU(pixel_tag, &src[j + 6]);
        auto s7 = hn::LoadU(pixel_tag, &src[j + 7]);
        auto src0 = hn::PromoteTo(mul_tag, s0);
        auto src1 = hn::PromoteTo(mul_tag, s1);
        auto src2 = hn::PromoteTo(mul_tag, s2);
        auto src3 = hn::PromoteTo(mul_tag, s3);
        auto src4 = hn::PromoteTo(mul_tag, s4);
        auto src5 = hn::PromoteTo(mul_tag, s5);
        auto src6 = hn::PromoteTo(mul_tag, s6);
        auto src7 = hn::PromoteTo(mul_tag, s7);
        auto result = Convolve8_8(mul_tag, filter_tag, src0, src1, src2, src3,
                                  src4, src5, src6, src7, f_vec);
        auto result_demoted = hn::DemoteTo(pixel_tag, result);
        if (j + static_cast<int>(vw) > w) {
          hn::StoreN(result_demoted, pixel_tag, &dst[j], w - j);
        } else {
          hn::StoreU(result_demoted, pixel_tag, &dst[j]);
        }
      }
      src += src_stride;
      dst += dst_stride;
    }
  }
}

HWY_ATTR inline void ConvolveVert2Tap(const uint8_t *src, ptrdiff_t src_stride,
                                      uint8_t *dst, ptrdiff_t dst_stride,
                                      const int16_t *filter_y, int w, int h) {
  hn::CappedTag<int16_t, 16> tag16;
  hn::Rebind<uint8_t, decltype(tag16)> pixel_tag;

  auto f0 = hn::Set(tag16, filter_y[3]);
  auto f1 = hn::Set(tag16, filter_y[4]);
  auto round_offset = hn::Set(tag16, 1 << (FILTER_BITS - 1));

  if (w == 4) {
    for (int y = 0; y < h; y += 4) {
      auto s0 = LoadUnaligned4x4(tag16, src + y * src_stride, src_stride);
      auto s1 = LoadUnaligned4x4(tag16, src + (y + 1) * src_stride, src_stride);
      auto res = hn::ShiftRight<FILTER_BITS>(s0 * f0 + s1 * f1 + round_offset);
      StoreUnaligned4x4(tag16, dst + y * dst_stride, dst_stride, res);
    }
  } else if (w == 8) {
    for (int y = 0; y < h; y += 4) {
      auto s0 = LoadUnaligned2x8(tag16, src + y * src_stride, src_stride);
      auto s1 = LoadUnaligned2x8(tag16, src + (y + 1) * src_stride, src_stride);
      auto s2 = LoadUnaligned2x8(tag16, src + (y + 2) * src_stride, src_stride);
      auto s3 = LoadUnaligned2x8(tag16, src + (y + 3) * src_stride, src_stride);
      auto res0 = hn::ShiftRight<FILTER_BITS>(s0 * f0 + s1 * f1 + round_offset);
      auto res1 = hn::ShiftRight<FILTER_BITS>(s2 * f0 + s3 * f1 + round_offset);
      StoreUnaligned2x8(tag16, dst + y * dst_stride, dst_stride, res0);
      StoreUnaligned2x8(tag16, dst + (y + 2) * dst_stride, dst_stride, res1);
    }
  } else if (w == 16) {
    constexpr hn::Half<decltype(tag16)> half_tag16;
    auto s0 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src));
    for (int y = 0; y < h; y += 4) {
      auto s1 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 1) * src_stride));
      auto s2 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 2) * src_stride));
      auto s3 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 3) * src_stride));
      auto s4 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 4) * src_stride));

      auto res0 = hn::ShiftRight<FILTER_BITS>(s0 * f0 + s1 * f1 + round_offset);
      auto res1 = hn::ShiftRight<FILTER_BITS>(s1 * f0 + s2 * f1 + round_offset);
      auto res2 = hn::ShiftRight<FILTER_BITS>(s2 * f0 + s3 * f1 + round_offset);
      auto res3 = hn::ShiftRight<FILTER_BITS>(s3 * f0 + s4 * f1 + round_offset);

      hn::StoreU(
          hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res0),
                               hn::UpperHalf(half_tag16, res0)),
          pixel_tag, dst + y * dst_stride);
      hn::StoreU(
          hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res1),
                               hn::UpperHalf(half_tag16, res1)),
          pixel_tag, dst + (y + 1) * dst_stride);
      hn::StoreU(
          hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res2),
                               hn::UpperHalf(half_tag16, res2)),
          pixel_tag, dst + (y + 2) * dst_stride);
      hn::StoreU(
          hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res3),
                               hn::UpperHalf(half_tag16, res3)),
          pixel_tag, dst + (y + 3) * dst_stride);

      s0 = s4;
    }
  } else {
    hn::ScalableTag<int16_t> mul_tag;
    hn::Rebind<uint8_t, decltype(mul_tag)> p_tag;
    auto f0_s = hn::Set(mul_tag, filter_y[3]);
    auto f1_s = hn::Set(mul_tag, filter_y[4]);
    auto round_offset_s = hn::Set(mul_tag, 1 << (FILTER_BITS - 1));
    auto vw = hn::Lanes(mul_tag);
    for (int x = 0; x < w; x += vw) {
      auto s0 = hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x));
      for (int y = 0; y < h; ++y) {
        auto s1 = hn::PromoteTo(
            mul_tag, hn::LoadU(p_tag, src + x + (y + 1) * src_stride));
        auto res =
            hn::ShiftRight<FILTER_BITS>(s0 * f0_s + s1 * f1_s + round_offset_s);
        auto res_demoted = hn::DemoteTo(p_tag, res);
        if (x + static_cast<int>(vw) > w) {
          hn::StoreN(res_demoted, p_tag, dst + x + y * dst_stride, w - x);
        } else {
          hn::StoreU(res_demoted, p_tag, dst + x + y * dst_stride);
        }
        s0 = s1;
      }
    }
  }
}

HWY_ATTR inline void ConvolveVert4Tap(const uint8_t *src, ptrdiff_t src_stride,
                                      uint8_t *dst, ptrdiff_t dst_stride,
                                      const int16_t *filter_y, int w, int h) {
  hn::CappedTag<int16_t, 16> tag16;
  hn::Rebind<uint8_t, decltype(tag16)> pixel_tag;

  auto f0 = hn::Set(tag16, filter_y[2] >> 1);
  auto f1 = hn::Set(tag16, filter_y[3] >> 1);
  auto f2 = hn::Set(tag16, filter_y[4] >> 1);
  auto f3 = hn::Set(tag16, filter_y[5] >> 1);
  auto round_offset = hn::Set(tag16, 1 << (FILTER_BITS - 2));

  if (w == 4) {
    for (int y = 0; y < h; y += 4) {
      auto s0 = LoadUnaligned4x4(tag16, src + y * src_stride, src_stride);
      auto s1 = LoadUnaligned4x4(tag16, src + (y + 1) * src_stride, src_stride);
      auto s2 = LoadUnaligned4x4(tag16, src + (y + 2) * src_stride, src_stride);
      auto s3 = LoadUnaligned4x4(tag16, src + (y + 3) * src_stride, src_stride);
      auto res = hn::ShiftRight<FILTER_BITS - 1>(s0 * f0 + s1 * f1 + s2 * f2 +
                                                 s3 * f3 + round_offset);
      StoreUnaligned4x4(tag16, dst + y * dst_stride, dst_stride, res);
    }
  } else if (w == 8) {
    auto s0 = LoadUnaligned2x8(tag16, src + 0 * src_stride, src_stride);
    auto s1 = LoadUnaligned2x8(tag16, src + 1 * src_stride, src_stride);
    for (int y = 0; y < h; y += 4) {
      auto s2 = LoadUnaligned2x8(tag16, src + (y + 2) * src_stride, src_stride);
      auto s3 = LoadUnaligned2x8(tag16, src + (y + 3) * src_stride, src_stride);
      auto s4 = LoadUnaligned2x8(tag16, src + (y + 4) * src_stride, src_stride);
      auto s5 = LoadUnaligned2x8(tag16, src + (y + 5) * src_stride, src_stride);
      auto res0 = hn::ShiftRight<FILTER_BITS - 1>(s0 * f0 + s1 * f1 + s2 * f2 +
                                                  s3 * f3 + round_offset);
      auto res1 = hn::ShiftRight<FILTER_BITS - 1>(s2 * f0 + s3 * f1 + s4 * f2 +
                                                  s5 * f3 + round_offset);
      StoreUnaligned2x8(tag16, dst + y * dst_stride, dst_stride, res0);
      StoreUnaligned2x8(tag16, dst + (y + 2) * dst_stride, dst_stride, res1);
      s0 = s4;
      s1 = s5;
    }
  } else if (w == 16) {
    constexpr hn::Half<decltype(tag16)> half_tag16;
    auto s0 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 0 * src_stride));
    auto s1 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 1 * src_stride));
    auto s2 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 2 * src_stride));
    for (int y = 0; y < h; y += 4) {
      auto s3 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 3) * src_stride));
      auto s4 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 4) * src_stride));
      auto s5 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 5) * src_stride));
      auto s6 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 6) * src_stride));

      auto res0 = hn::ShiftRight<FILTER_BITS - 1>(s0 * f0 + s1 * f1 + s2 * f2 +
                                                  s3 * f3 + round_offset);
      auto res1 = hn::ShiftRight<FILTER_BITS - 1>(s1 * f0 + s2 * f1 + s3 * f2 +
                                                  s4 * f3 + round_offset);
      auto res2 = hn::ShiftRight<FILTER_BITS - 1>(s2 * f0 + s3 * f1 + s4 * f2 +
                                                  s5 * f3 + round_offset);
      auto res3 = hn::ShiftRight<FILTER_BITS - 1>(s3 * f0 + s4 * f1 + s5 * f2 +
                                                  s6 * f3 + round_offset);

      hn::StoreU(
          hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res0),
                               hn::UpperHalf(half_tag16, res0)),
          pixel_tag, dst + y * dst_stride);
      hn::StoreU(
          hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res1),
                               hn::UpperHalf(half_tag16, res1)),
          pixel_tag, dst + (y + 1) * dst_stride);
      hn::StoreU(
          hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res2),
                               hn::UpperHalf(half_tag16, res2)),
          pixel_tag, dst + (y + 2) * dst_stride);
      hn::StoreU(
          hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res3),
                               hn::UpperHalf(half_tag16, res3)),
          pixel_tag, dst + (y + 3) * dst_stride);

      s0 = s4;
      s1 = s5;
      s2 = s6;
    }
  } else {
    hn::ScalableTag<int16_t> mul_tag;
    hn::Rebind<uint8_t, decltype(mul_tag)> p_tag;
    auto f0_s = hn::Set(mul_tag, filter_y[2] >> 1);
    auto f1_s = hn::Set(mul_tag, filter_y[3] >> 1);
    auto f2_s = hn::Set(mul_tag, filter_y[4] >> 1);
    auto f3_s = hn::Set(mul_tag, filter_y[5] >> 1);
    auto round_offset_s = hn::Set(mul_tag, 1 << (FILTER_BITS - 2));
    auto vw = hn::Lanes(mul_tag);
    for (int x = 0; x < w; x += vw) {
      auto s0 = hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x));
      auto s1 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 1 * src_stride));
      auto s2 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 2 * src_stride));
      for (int y = 0; y < h; ++y) {
        auto s3 = hn::PromoteTo(
            mul_tag, hn::LoadU(p_tag, src + x + (y + 3) * src_stride));
        auto res = hn::ShiftRight<FILTER_BITS - 1>(
            s0 * f0_s + s1 * f1_s + s2 * f2_s + s3 * f3_s + round_offset_s);
        auto res_demoted = hn::DemoteTo(p_tag, res);
        if (x + static_cast<int>(vw) > w) {
          hn::StoreN(res_demoted, p_tag, dst + x + y * dst_stride, w - x);
        } else {
          hn::StoreU(res_demoted, p_tag, dst + x + y * dst_stride);
        }
        s0 = s1;
        s1 = s2;
        s2 = s3;
      }
    }
  }
}

HWY_ATTR inline void ConvolveVert8Tap(const uint8_t *src, ptrdiff_t src_stride,
                                      uint8_t *dst, ptrdiff_t dst_stride,
                                      const int16_t *filter_y, int w, int h) {
  hn::CappedTag<int16_t, 16> tag16;
  hn::Rebind<uint8_t, decltype(tag16)> pixel_tag;

  auto f0 = hn::Set(tag16, filter_y[0] >> 1);
  auto f1 = hn::Set(tag16, filter_y[1] >> 1);
  auto f2 = hn::Set(tag16, filter_y[2] >> 1);
  auto f3 = hn::Set(tag16, filter_y[3] >> 1);
  auto f4 = hn::Set(tag16, filter_y[4] >> 1);
  auto f5 = hn::Set(tag16, filter_y[5] >> 1);
  auto f6 = hn::Set(tag16, filter_y[6] >> 1);
  auto f7 = hn::Set(tag16, filter_y[7] >> 1);
  auto round_offset = hn::Set(tag16, 1 << (FILTER_BITS - 2));

  if (w == 4) {
    for (int y = 0; y < h; y += 4) {
      auto s0 = LoadUnaligned4x4(tag16, src + (y + 0) * src_stride, src_stride);
      auto s1 = LoadUnaligned4x4(tag16, src + (y + 1) * src_stride, src_stride);
      auto s2 = LoadUnaligned4x4(tag16, src + (y + 2) * src_stride, src_stride);
      auto s3 = LoadUnaligned4x4(tag16, src + (y + 3) * src_stride, src_stride);
      auto s4 = LoadUnaligned4x4(tag16, src + (y + 4) * src_stride, src_stride);
      auto s5 = LoadUnaligned4x4(tag16, src + (y + 5) * src_stride, src_stride);
      auto s6 = LoadUnaligned4x4(tag16, src + (y + 6) * src_stride, src_stride);
      auto s7 = LoadUnaligned4x4(tag16, src + (y + 7) * src_stride, src_stride);
      auto res = hn::ShiftRight<FILTER_BITS - 1>(
          s0 * f0 + s1 * f1 + s2 * f2 + s3 * f3 + s4 * f4 + s5 * f5 + s6 * f6 +
          s7 * f7 + round_offset);
      StoreUnaligned4x4(tag16, dst + y * dst_stride, dst_stride, res);
    }
  } else if (w == 8) {
    auto s0 = LoadUnaligned2x8(tag16, src + 0 * src_stride, src_stride);
    auto s1 = LoadUnaligned2x8(tag16, src + 1 * src_stride, src_stride);
    auto s2 = LoadUnaligned2x8(tag16, src + 2 * src_stride, src_stride);
    auto s3 = LoadUnaligned2x8(tag16, src + 3 * src_stride, src_stride);
    auto s4 = LoadUnaligned2x8(tag16, src + 4 * src_stride, src_stride);
    auto s5 = LoadUnaligned2x8(tag16, src + 5 * src_stride, src_stride);
    for (int y = 0; y < h; y += 2) {
      auto s6 = LoadUnaligned2x8(tag16, src + (y + 6) * src_stride, src_stride);
      auto s7 = LoadUnaligned2x8(tag16, src + (y + 7) * src_stride, src_stride);
      auto res = hn::ShiftRight<FILTER_BITS - 1>(
          s0 * f0 + s1 * f1 + s2 * f2 + s3 * f3 + s4 * f4 + s5 * f5 + s6 * f6 +
          s7 * f7 + round_offset);
      StoreUnaligned2x8(tag16, dst + y * dst_stride, dst_stride, res);
      s0 = s2;
      s1 = s3;
      s2 = s4;
      s3 = s5;
      s4 = s6;
      s5 = s7;
    }
  } else if (w == 16) {
    constexpr hn::Half<decltype(tag16)> half_tag16;
    auto s0 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 0 * src_stride));
    auto s1 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 1 * src_stride));
    auto s2 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 2 * src_stride));
    auto s3 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 3 * src_stride));
    auto s4 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 4 * src_stride));
    auto s5 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 5 * src_stride));
    auto s6 = hn::PromoteTo(tag16, hn::LoadU(pixel_tag, src + 6 * src_stride));
    for (int y = 0; y < h; ++y) {
      auto s7 = hn::PromoteTo(tag16,
                              hn::LoadU(pixel_tag, src + (y + 7) * src_stride));
      auto res = hn::ShiftRight<FILTER_BITS - 1>(
          s0 * f0 + s1 * f1 + s2 * f2 + s3 * f3 + s4 * f4 + s5 * f5 + s6 * f6 +
          s7 * f7 + round_offset);
      hn::StoreU(hn::ReorderDemote2To(pixel_tag, hn::LowerHalf(half_tag16, res),
                                      hn::UpperHalf(half_tag16, res)),
                 pixel_tag, dst + y * dst_stride);
      s0 = s1;
      s1 = s2;
      s2 = s3;
      s3 = s4;
      s4 = s5;
      s5 = s6;
      s6 = s7;
    }
  } else {
    hn::ScalableTag<int16_t> mul_tag;
    hn::Rebind<uint8_t, decltype(mul_tag)> p_tag;
    auto f0_s = hn::Set(mul_tag, filter_y[0] >> 1);
    auto f1_s = hn::Set(mul_tag, filter_y[1] >> 1);
    auto f2_s = hn::Set(mul_tag, filter_y[2] >> 1);
    auto f3_s = hn::Set(mul_tag, filter_y[3] >> 1);
    auto f4_s = hn::Set(mul_tag, filter_y[4] >> 1);
    auto f5_s = hn::Set(mul_tag, filter_y[5] >> 1);
    auto f6_s = hn::Set(mul_tag, filter_y[6] >> 1);
    auto f7_s = hn::Set(mul_tag, filter_y[7] >> 1);
    auto round_offset_s = hn::Set(mul_tag, 1 << (FILTER_BITS - 2));
    auto vw = hn::Lanes(mul_tag);
    for (int x = 0; x < w; x += vw) {
      auto s0 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 0 * src_stride));
      auto s1 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 1 * src_stride));
      auto s2 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 2 * src_stride));
      auto s3 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 3 * src_stride));
      auto s4 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 4 * src_stride));
      auto s5 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 5 * src_stride));
      auto s6 =
          hn::PromoteTo(mul_tag, hn::LoadU(p_tag, src + x + 6 * src_stride));
      for (int y = 0; y < h; ++y) {
        auto s7 = hn::PromoteTo(
            mul_tag, hn::LoadU(p_tag, src + x + (y + 7) * src_stride));
        auto sum = s0 * f0_s + s1 * f1_s + s2 * f2_s + s3 * f3_s + s4 * f4_s +
                   s5 * f5_s + s6 * f6_s + s7 * f7_s;
        auto res = hn::ShiftRight<FILTER_BITS - 1>(sum + round_offset_s);
        auto res_demoted = hn::DemoteTo(p_tag, res);
        if (x + static_cast<int>(vw) > w) {
          hn::StoreN(res_demoted, p_tag, dst + x + y * dst_stride, w - x);
        } else {
          hn::StoreU(res_demoted, p_tag, dst + x + y * dst_stride);
        }
        s0 = s1;
        s1 = s2;
        s2 = s3;
        s3 = s4;
        s4 = s5;
        s5 = s6;
        s6 = s7;
      }
    }
  }
}

HWY_MAYBE_UNUSED void Convolve8Vert(const uint8_t *src, ptrdiff_t src_stride,
                                    uint8_t *dst, ptrdiff_t dst_stride,
                                    const int16_t *filter_x, int x_step_q4,
                                    const int16_t *filter_y, int y_step_q4,
                                    int w, int h) {
  assert((intptr_t)dst % 4 == 0);
  assert(dst_stride % 4 == 0);

  (void)x_step_q4;
  (void)filter_x;
  (void)y_step_q4;

  src -= src_stride * ((SUBPEL_TAPS / 2) - 1);
  int filter_taps = get_filter_taps_convolve8(filter_y);
  if (filter_taps == 2) {
    ConvolveVert2Tap(src + src_stride * 3, src_stride, dst, dst_stride,
                     filter_y, w, h);
  } else if (filter_taps == 4) {
    ConvolveVert4Tap(src + src_stride * 2, src_stride, dst, dst_stride,
                     filter_y, w, h);
  } else {
    // filter_taps = 8
    ConvolveVert8Tap(src, src_stride, dst, dst_stride, filter_y, w, h);
  }
}

HWY_MAYBE_UNUSED void Convolve8Horiz(const uint8_t *src, ptrdiff_t src_stride,
                                     uint8_t *dst, ptrdiff_t dst_stride,
                                     const int16_t *filter_x, int x_step_q4,
                                     const int16_t *filter_y, int y_step_q4,
                                     int w, int h) {
  assert((intptr_t)dst % 4 == 0);
  assert(dst_stride % 4 == 0);

  (void)x_step_q4;
  (void)filter_y;
  (void)y_step_q4;

  src -= ((SUBPEL_TAPS / 2) - 1);
  int filter_taps = get_filter_taps_convolve8(filter_x);
  if (filter_taps == 2) {
    ConvolveHoriz2Tap(src + 3, src_stride, dst, dst_stride, filter_x, w, h);
  } else if (filter_taps == 4) {
    ConvolveHoriz4Tap(src + 2, src_stride, dst, dst_stride, filter_x, w, h);
  } else {
    // filter_taps = 8
    ConvolveHoriz8Tap(src, src_stride, dst, dst_stride, filter_x, w, h);
  }
}

}  // namespace HWY_NAMESPACE
}  // namespace

#define CONVOLVE8HORIZ(suffix)                                                \
  extern "C" void aom_convolve8_horiz_##suffix(                               \
      const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst,                 \
      ptrdiff_t dst_stride, const int16_t *filter_x, int x_step_q4,           \
      const int16_t *filter_y, int y_step_q4, int w, int h);                  \
  HWY_ATTR void aom_convolve8_horiz_##suffix(                                 \
      const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst,                 \
      ptrdiff_t dst_stride, const int16_t *filter_x, int x_step_q4,           \
      const int16_t *filter_y, int y_step_q4, int w, int h) {                 \
    HWY_NAMESPACE::Convolve8Horiz(src, src_stride, dst, dst_stride, filter_x, \
                                  x_step_q4, filter_y, y_step_q4, w, h);      \
  }

HWY_AFTER_NAMESPACE();

#endif  // AOM_AOM_DSP_CONVOLVE_HWY_H_

Messung V0.5 in Prozent
C=98 H=87 G=92

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-08-24) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

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.