Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/aom/test/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 94 kB image not shown  

Quelle  av1_convolve_test.cc   Sprache: C

 
/*
 * Copyright (c) 2020, 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.
 */


#include <cstddef>
#include <cstdint>
#include <ostream>
#include <set>
#include <vector>
#include "config/av1_rtcd.h"
#include "config/aom_dsp_rtcd.h"
#include "aom_ports/aom_timer.h"
#include "gtest/gtest.h"
#include "test/acm_random.h"

namespace {

// TODO(any): Remove following INTERP_FILTERS_ALL define, so that 12-tap filter
// is tested once 12-tap filter SIMD is done.
#undef INTERP_FILTERS_ALL
#define INTERP_FILTERS_ALL 4

// All single reference convolve tests are parameterized on block size,
// bit-depth, and function to test.
//
// Note that parameterizing on these variables (and not other parameters) is
// a conscious decision - Jenkins needs some degree of parallelization to run
// the tests within the time limit, but if the number of parameters increases
// too much, the gtest framework does not handle it well (increased overhead per
// test, huge amount of output to stdout, etc.).
//
// Also note that the test suites must be named with the architecture, e.g.,
// C, C_X, AVX2_X, ... The test suite that runs on Jenkins sometimes runs tests
// that cannot deal with intrinsics (e.g., the Valgrind tests on 32-bit x86
// binaries) and will disable tests using a filter like
// --gtest_filter=-:SSE4_1.*. If the test suites are not named this way, the
// testing infrastructure will not selectively filter them properly.
class BlockSize {
 public:
  BlockSize(int w, int h) : width_(w), height_(h) {}

  int Width() const { return width_; }
  int Height() const { return height_; }

  bool operator<(const BlockSize &other) const {
    if (Width() == other.Width()) {
      return Height() < other.Height();
    }
    return Width() < other.Width();
  }

  bool operator==(const BlockSize &other) const {
    return Width() == other.Width() && Height() == other.Height();
  }

 private:
  int width_;
  int height_;
};

// Block size / bit depth / test function used to parameterize the tests.
template <typename T>
class TestParam {
 public:
  TestParam(const BlockSize &block, int bd, T test_func)
      : block_(block), bd_(bd), test_func_(test_func) {}

  const BlockSize &Block() const { return block_; }
  int BitDepth() const { return bd_; }
  T TestFunction() const { return test_func_; }

  bool operator==(const TestParam &other) const {
    return Block() == other.Block() && BitDepth() == other.BitDepth() &&
           TestFunction() == other.TestFunction();
  }

 private:
  BlockSize block_;
  int bd_;
  T test_func_;
};

template <typename T>
std::ostream &operator<<(std::ostream &os, const TestParam<T> &test_arg) ;{
  return os << "TestParam { width:" << test_arg.Block().Width()
            << " height:" << test_arg.Block().Height()
            << " bd:" << test_arg.BitDepth() << " }";
}

// Generate the list of all block widths / heights that need to be tested,
// includes chroma and luma sizes, for the given bit-depths. The test
// function is the same for all generated parameters.
template <typename T>
std::vector<TestParam<T>> GetTestParams(std::initializer_list<int> bit_depths,
                                        T test_func) {
  std::set<BlockSize> sizes;
  for (int b = BLOCK_4X4; b < BLOCK_SIZES_ALL; ++b) {
    const int w = block_size_wide[b];
    const int h = block_size_high[b];
    sizes.insert(BlockSize(w, h));
    // Add in smaller chroma sizes as well.
    if (w == 4 || h == 4) {
      sizes.insert(BlockSize(w / 2, h / 2));
    }
  }
  std::vector<TestParam<T>> result;
  for (const BlockSize &block : sizes) {
    for (int bd : bit_depths) {
      result.push_back(TestParam<T>(block, bd, test_func));
    }
  }
  return result;
}

template <typename T>
std::vector<TestParam<T>> GetLowbdTestParams(T test_func) {
  return GetTestParams({ 8 }, test_func);
}

template <typename T>
::testing::internal::ParamGenerator<TestParam<T>> BuildLowbdParams(
    T test_func) {
  return ::testing::ValuesIn(GetLowbdTestParams(test_func));
}

// Test the test-parameters generators work as expected.
class AV1ConvolveParametersTest : public ::testing::Test {};

TEST_F(AV1ConvolveParametersTest, GetLowbdTestParams) {
  auto v = GetLowbdTestParams(av1_convolve_x_sr_c);
  ASSERT_EQ(27U, v.size());
  for (const auto &p : v) {
    ASSERT_EQ(8, p.BitDepth());
    // Needed (instead of ASSERT_EQ(...) since gtest does not
    // have built in printing for arbitrary functions, which
    // causes a compilation error.
    bool same_fn = av1_convolve_x_sr_c == p.TestFunction();
    ASSERT_TRUE(same_fn);
  }
}

#if CONFIG_AV1_HIGHBITDEPTH
template <typename T>
std::vector<TestParam<T>> GetHighbdTestParams(T test_func) {
  return GetTestParams({ 10, 12 }, test_func);
}

template <typename T>
::testing::internal::ParamGenerator<TestParam<T>> BuildHighbdParams(
    T test_func) {
  return ::testing::ValuesIn(GetHighbdTestParams(test_func));
}

TEST_F(AV1ConvolveParametersTest, GetHighbdTestParams) {
  auto v = GetHighbdTestParams(av1_highbd_convolve_x_sr_c);
  ASSERT_EQ(54U, v.size());
  int num_10 = 0;
  int num_12 = 0;
  for (const auto &p : v) {
    ASSERT_TRUE(p.BitDepth() == 10 || p.BitDepth() == 12);
    bool same_fn = av1_highbd_convolve_x_sr_c == p.TestFunction();
    ASSERT_TRUE(same_fn);
    if (p.BitDepth() == 10) {
      ++num_10;
    } else {
      ++num_12;
    }
  }
  ASSERT_EQ(num_10, num_12);
}
#endif  // CONFIG_AV1_HIGHBITDEPTH

// AV1ConvolveTest is the base class that all convolve tests should derive from.
// It provides storage/methods for generating randomized buffers for both
// low bit-depth and high bit-depth, and setup/teardown methods for clearing
// system state. Implementors can get the bit-depth / block-size /
// test function by calling GetParam().
template <typename T>
class AV1ConvolveTest : public ::testing::TestWithParam<TestParam<T>> {
 public:
  ~AV1ConvolveTest() override = default;

  void SetUp() override {
    rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed());
  }

  // Randomizes the 8-bit input buffer and returns a pointer to it. Note that
  // the pointer is safe to use with an 8-tap filter. The stride can range
  // from width to (width + kPadding). Also note that the pointer is to the
  // same memory location.
  static constexpr int kInputPadding = 12;

  // Get a pointer to a buffer with stride == width. Note that we must have
  // the test param passed in explicitly -- the gtest framework does not
  // support calling GetParam() within a templatized class.
  // Note that FirstRandomInput8 always returns the same pointer -- if two
  // inputs are needed, also use SecondRandomInput8.
  const uint8_t *FirstRandomInput8(const TestParam<T> ¶m) {
    // Note we can't call GetParam() directly -- gtest does not support
    // this for parameterized types.
    return RandomInput8(input8_1_, param);
  }

  const uint8_t *SecondRandomInput8(const TestParam<T> ¶m) {
    return RandomInput8(input8_2_, param);
  }

  // Some of the intrinsics perform writes in 32 byte chunks. Moreover, some
  // of the instrinsics assume that the stride is also a multiple of 32.
  // To satisfy these constraints and also remain simple, output buffer strides
  // are assumed MAX_SB_SIZE.
  static constexpr int kOutputStride = MAX_SB_SIZE;

  // Check that two 8-bit output buffers are identical.
  void AssertOutputBufferEq(const uint8_t *p1, const uint8_t *p2, int width,
                            int height) {
    ASSERT_TRUE(p1 != p2) << "Buffers must be at different memory locations";
    for (int j = 0; j < height; ++j) {
      if (memcmp(p1, p2, sizeof(*p1) * width) == 0) {
        p1 += kOutputStride;
        p2 += kOutputStride;
        continue;
      }
      for (int i = 0; i < width; ++i) {
        ASSERT_EQ(p1[i], p2[i])
            << width << "x" << height << " Pixel mismatch at (" << i << ", "
            << j << ")";
      }
    }
  }

  // Check that two 16-bit output buffers are identical.
  void AssertOutputBufferEq(const uint16_t *p1, const uint16_t *p2, int width,
                            int height) {
    ASSERT_TRUE(p1 != p2) << "Buffers must be in different memory locations";
    for (int j = 0; j < height; ++j) {
      if (memcmp(p1, p2, sizeof(*p1) * width) == 0) {
        p1 += kOutputStride;
        p2 += kOutputStride;
        continue;
      }
      for (int i = 0; i < width; ++i) {
        ASSERT_EQ(p1[i], p2[i])
            << width << "x" << height << " Pixel mismatch at (" << i << ", "
            << j << ")";
      }
    }
  }

#if CONFIG_AV1_HIGHBITDEPTH
  // Note that the randomized values are capped by bit-depth.
  const uint16_t *FirstRandomInput16(const TestParam<T> ¶m) {
    return RandomInput16(input16_1_, param);
  }

  const uint16_t *SecondRandomInput16(const TestParam<T> ¶m) {
    return RandomInput16(input16_2_, param);
  }
#endif

 private:
  const uint8_t *RandomInput8(uint8_t *p, const TestParam<T> ¶m) {
    EXPECT_EQ(8, param.BitDepth());
    EXPECT_GE(MAX_SB_SIZE, param.Block().Width());
    EXPECT_GE(MAX_SB_SIZE, param.Block().Height());
    const int padded_width = param.Block().Width() + kInputPadding;
    const int padded_height = param.Block().Height() + kInputPadding;
    Randomize(p, padded_width * padded_height);
    return p + (kInputPadding / 2) * padded_width + kInputPadding / 2;
  }

  void Randomize(uint8_t *p, int size) {
    for (int i = 0; i < size; ++i) {
      p[i] = rnd_.Rand8();
    }
  }

#if CONFIG_AV1_HIGHBITDEPTH
  const uint16_t *RandomInput16(uint16_t *p, const TestParam<T> ¶m) {
    // Check that this is only called with high bit-depths.
    EXPECT_TRUE(param.BitDepth() == 10 || param.BitDepth() == 12);
    EXPECT_GE(MAX_SB_SIZE, param.Block().Width());
    EXPECT_GE(MAX_SB_SIZE, param.Block().Height());
    const int padded_width = param.Block().Width() + kInputPadding;
    const int padded_height = param.Block().Height() + kInputPadding;
    Randomize(p, padded_width * padded_height, param.BitDepth());
    return p + (kInputPadding / 2) * padded_width + kInputPadding / 2;
  }

  void Randomize(uint16_t *p, int size, int bit_depth) {
    for (int i = 0; i < size; ++i) {
      p[i] = rnd_.Rand16() & ((1 << bit_depth) - 1);
    }
  }
#endif

  static constexpr int kInputStride = MAX_SB_SIZE + kInputPadding;

  libaom_test::ACMRandom rnd_;
  // Statically allocate all the memory that is needed for the tests. Note
  // that we cannot allocate output memory here. It must use DECLARE_ALIGNED,
  // which is a C99 feature and interacts badly with C++ member variables.
  uint8_t input8_1_[kInputStride * kInputStride];
  uint8_t input8_2_[kInputStride * kInputStride];
#if CONFIG_AV1_HIGHBITDEPTH
  uint16_t input16_1_[kInputStride * kInputStride];
  uint16_t input16_2_[kInputStride * kInputStride];
#endif
};

////////////////////////////////////////////////////////
// Single reference convolve-x functions (low bit-depth)
////////////////////////////////////////////////////////
typedef void (*convolve_x_func)(const uint8_t *src, int src_stride,
                                uint8_t *dst, int dst_stride, int w, int h,
                                const InterpFilterParams *filter_params_x,
                                const int subpel_x_qn,
                                ConvolveParams *conv_params);

class AV1ConvolveXTest : public AV1ConvolveTest<convolve_x_func> {
 public:
  void RunTest() {
    // Do not test the no-op filter.
    for (int sub_x = 1; sub_x < 16; ++sub_x) {
      for (int filter = EIGHTTAP_REGULAR; filter <= INTERP_FILTERS_ALL;
           ++filter) {
        InterpFilter f = static_cast<InterpFilter>(filter);
        TestConvolve(sub_x, f);
      }
    }
  }

 public:
  void SpeedTest() {
    for (int filter = EIGHTTAP_REGULAR; filter <= INTERP_FILTERS_ALL;
         ++filter) {
      InterpFilter f = static_cast<InterpFilter>(filter);
      TestConvolveSpeed(f, 10000);
    }
  }

 private:
  void TestConvolve(const int sub_x, const InterpFilter filter) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();

    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(filter, width);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    const uint8_t *input = FirstRandomInput8(GetParam());
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    av1_convolve_x_sr_c(input, width, reference, kOutputStride, width, height,
                        filter_params_x, sub_x, &conv_params1);

    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    convolve_x_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    test_func(input, width, test, kOutputStride, width, height, filter_params_x,
              sub_x, &conv_params2);
    AssertOutputBufferEq(reference, test, width, height);
  }

 private:
  void TestConvolveSpeed(const InterpFilter filter, const int num_iters) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();

    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(filter, width);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    const uint8_t *input = FirstRandomInput8(GetParam());
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);

    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      av1_convolve_x_sr_c(input, width, reference, kOutputStride, width, height,
                          filter_params_x, 0, &conv_params1);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    convolve_x_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);

    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_x, 0, &conv_params2);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1,
           time2, time1 / time2);
  }
};

TEST_P(AV1ConvolveXTest, RunTest) { RunTest(); }

TEST_P(AV1ConvolveXTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveXTest,
                         BuildLowbdParams(av1_convolve_x_sr_c));

#if HAVE_SSE2
INSTANTIATE_TEST_SUITE_P(SSE2, AV1ConvolveXTest,
                         BuildLowbdParams(av1_convolve_x_sr_sse2));
#endif

#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(AVX2, AV1ConvolveXTest,
                         BuildLowbdParams(av1_convolve_x_sr_avx2));
#endif

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1ConvolveXTest,
                         BuildLowbdParams(av1_convolve_x_sr_neon));
#endif

#if HAVE_NEON_DOTPROD
INSTANTIATE_TEST_SUITE_P(NEON_DOTPROD, AV1ConvolveXTest,
                         BuildLowbdParams(av1_convolve_x_sr_neon_dotprod));
#endif

#if HAVE_NEON_I8MM
INSTANTIATE_TEST_SUITE_P(NEON_I8MM, AV1ConvolveXTest,
                         BuildLowbdParams(av1_convolve_x_sr_neon_i8mm));
#endif

////////////////////////////////////////////////////////////////
// Single reference convolve-x IntraBC functions (low bit-depth)
////////////////////////////////////////////////////////////////

class AV1ConvolveXIntraBCTest : public AV1ConvolveTest<convolve_x_func> {
 public:
  void RunTest() {
    // IntraBC functions only operate for subpel_x_qn = 8.
    constexpr int kSubX = 8;
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const InterpFilterParams *filter_params_x = &av1_intrabc_filter_params;
    const uint8_t *input = FirstRandomInput8(GetParam());

    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    // Use a stride different from width to avoid potential storing errors that
    // would go undetected. The input buffer is filled using a padding of 12, so
    // the stride can be anywhere between width and width + 12.
    av1_convolve_x_sr_intrabc_c(input, width + 2, reference, kOutputStride,
                                width, height, filter_params_x, kSubX,
                                &conv_params1);

    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    convolve_x_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    test_func(input, width + 2, test, kOutputStride, width, height,
              filter_params_x, kSubX, &conv_params2);

    AssertOutputBufferEq(reference, test, width, height);
  }

  void SpeedTest() {
    constexpr int kNumIters = 10000;
    const InterpFilter filter = static_cast<InterpFilter>(BILINEAR);
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const InterpFilterParams *filter_params_x = &av1_intrabc_filter_params;
    const uint8_t *input = FirstRandomInput8(GetParam());

    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      av1_convolve_x_sr_intrabc_c(input, width, reference, kOutputStride, width,
                                  height, filter_params_x, 0, &conv_params1);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    convolve_x_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_x, 0, &conv_params2);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1,
           time2, time1 / time2);
  }
};

TEST_P(AV1ConvolveXIntraBCTest, RunTest) { RunTest(); }

TEST_P(AV1ConvolveXIntraBCTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveXIntraBCTest,
                         BuildLowbdParams(av1_convolve_x_sr_intrabc_c));

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1ConvolveXIntraBCTest,
                         BuildLowbdParams(av1_convolve_x_sr_intrabc_neon));
#endif

#if CONFIG_AV1_HIGHBITDEPTH
/////////////////////////////////////////////////////////
// Single reference convolve-x functions (high bit-depth)
/////////////////////////////////////////////////////////
typedef void (*highbd_convolve_x_func)(
    const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w,
    int h, const InterpFilterParams *filter_params_x, const int subpel_x_qn,
    ConvolveParams *conv_params, int bd);

class AV1ConvolveXHighbdTest : public AV1ConvolveTest<highbd_convolve_x_func> {
 public:
  void RunTest() {
    // Do not test the no-op filter.
    for (int sub_x = 1; sub_x < 16; ++sub_x) {
      for (int filter = EIGHTTAP_REGULAR; filter <= INTERP_FILTERS_ALL;
           ++filter) {
        InterpFilter f = static_cast<InterpFilter>(filter);
        TestConvolve(sub_x, f);
      }
    }
  }

 public:
  void SpeedTest() {
    for (int filter = EIGHTTAP_REGULAR; filter <= INTERP_FILTERS_ALL;
         ++filter) {
      InterpFilter f = static_cast<InterpFilter>(filter);
      TestConvolveSpeed(f, 10000);
    }
  }

 private:
  void TestConvolve(const int sub_x, const InterpFilter filter) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(filter, width);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, bit_depth);
    const uint16_t *input = FirstRandomInput16(GetParam());
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    av1_highbd_convolve_x_sr_c(input, width, reference, kOutputStride, width,
                               height, filter_params_x, sub_x, &conv_params1,
                               bit_depth);

    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, bit_depth);
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    GetParam().TestFunction()(input, width, test, kOutputStride, width, height,
                              filter_params_x, sub_x, &conv_params2, bit_depth);
    AssertOutputBufferEq(reference, test, width, height);
  }

 private:
  void TestConvolveSpeed(const InterpFilter filter, const int num_iters) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(filter, width);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    const uint16_t *input = FirstRandomInput16(GetParam());
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);

    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      av1_highbd_convolve_x_sr_c(input, width, reference, kOutputStride, width,
                                 height, filter_params_x, 0, &conv_params1,
                                 bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    highbd_convolve_x_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);

    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_x, 0, &conv_params2, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1,
           time2, time1 / time2);
  }
};

TEST_P(AV1ConvolveXHighbdTest, RunTest) { RunTest(); }

TEST_P(AV1ConvolveXHighbdTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveXHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_x_sr_c));

#if HAVE_SSSE3
INSTANTIATE_TEST_SUITE_P(SSSE3, AV1ConvolveXHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_x_sr_ssse3));
#endif

#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(AVX2, AV1ConvolveXHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_x_sr_avx2));
#endif

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1ConvolveXHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_x_sr_neon));
#endif

#if HAVE_SVE2
INSTANTIATE_TEST_SUITE_P(SVE2, AV1ConvolveXHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_x_sr_sve2));
#endif

/////////////////////////////////////////////////////////////////
// Single reference convolve-x IntraBC functions (high bit-depth)
/////////////////////////////////////////////////////////////////

class AV1ConvolveXHighbdIntraBCTest
    : public AV1ConvolveTest<highbd_convolve_x_func> {
 public:
  void RunTest() {
    // IntraBC functions only operate for subpel_x_qn = 8.
    constexpr int kSubX = 8;
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_x = &av1_intrabc_filter_params;
    const uint16_t *input = FirstRandomInput16(GetParam());

    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, bit_depth);
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    // Use a stride different from width to avoid potential storing errors that
    // would go undetected. The input buffer is filled using a padding of 12, so
    // the stride can be anywhere between width and width + 12.
    av1_highbd_convolve_x_sr_intrabc_c(
        input, width + 2, reference, kOutputStride, width, height,
        filter_params_x, kSubX, &conv_params1, bit_depth);

    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, bit_depth);
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    GetParam().TestFunction()(input, width + 2, test, kOutputStride, width,
                              height, filter_params_x, kSubX, &conv_params2,
                              bit_depth);

    AssertOutputBufferEq(reference, test, width, height);
  }

  void SpeedTest() {
    constexpr int kNumIters = 10000;
    const InterpFilter filter = static_cast<InterpFilter>(BILINEAR);
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_x = &av1_intrabc_filter_params;
    const uint16_t *input = FirstRandomInput16(GetParam());

    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      av1_highbd_convolve_x_sr_intrabc_c(input, width, reference, kOutputStride,
                                         width, height, filter_params_x, 0,
                                         &conv_params1, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    highbd_convolve_x_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_x, 0, &conv_params2, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1,
           time2, time1 / time2);
  }
};

TEST_P(AV1ConvolveXHighbdIntraBCTest, RunTest) { RunTest(); }

TEST_P(AV1ConvolveXHighbdIntraBCTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveXHighbdIntraBCTest,
                         BuildHighbdParams(av1_highbd_convolve_x_sr_intrabc_c));

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(
    NEON, AV1ConvolveXHighbdIntraBCTest,
    BuildHighbdParams(av1_highbd_convolve_x_sr_intrabc_neon));
#endif

#endif  // CONFIG_AV1_HIGHBITDEPTH

////////////////////////////////////////////////////////
// Single reference convolve-y functions (low bit-depth)
////////////////////////////////////////////////////////
typedef void (*convolve_y_func)(const uint8_t *src, int src_stride,
                                uint8_t *dst, int dst_stride, int w, int h,
                                const InterpFilterParams *filter_params_y,
                                const int subpel_y_qn);

class AV1ConvolveYTest : public AV1ConvolveTest<convolve_y_func> {
 public:
  void RunTest() {
    // Do not test the no-op filter.
    for (int sub_y = 1; sub_y < 16; ++sub_y) {
      for (int filter = EIGHTTAP_REGULAR; filter <= INTERP_FILTERS_ALL;
           ++filter) {
        InterpFilter f = static_cast<InterpFilter>(filter);
        TestConvolve(sub_y, f);
      }
    }
  }

 public:
  void SpeedTest() {
    for (int filter = EIGHTTAP_REGULAR; filter <= INTERP_FILTERS_ALL;
         ++filter) {
      InterpFilter f = static_cast<InterpFilter>(filter);
      TestConvolveSpeed(f, 10000);
    }
  }

 private:
  void TestConvolve(const int sub_y, const InterpFilter filter) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();

    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(filter, height);
    const uint8_t *input = FirstRandomInput8(GetParam());
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    av1_convolve_y_sr_c(input, width, reference, kOutputStride, width, height,
                        filter_params_y, sub_y);
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    GetParam().TestFunction()(input, width, test, kOutputStride, width, height,
                              filter_params_y, sub_y);
    AssertOutputBufferEq(reference, test, width, height);
  }

 private:
  void TestConvolveSpeed(const InterpFilter filter, const int num_iters) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();

    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(filter, height);
    const uint8_t *input = FirstRandomInput8(GetParam());
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);

    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      av1_convolve_y_sr_c(input, width, reference, kOutputStride, width, height,
                          filter_params_y, 0);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);

    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      GetParam().TestFunction()(input, width, test, kOutputStride, width,
                                height, filter_params_y, 0);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1,
           time2, time1 / time2);
  }
};

TEST_P(AV1ConvolveYTest, RunTest) { RunTest(); }

TEST_P(AV1ConvolveYTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveYTest,
                         BuildLowbdParams(av1_convolve_y_sr_c));

#if HAVE_SSE2
INSTANTIATE_TEST_SUITE_P(SSE2, AV1ConvolveYTest,
                         BuildLowbdParams(av1_convolve_y_sr_sse2));
#endif

#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(AVX2, AV1ConvolveYTest,
                         BuildLowbdParams(av1_convolve_y_sr_avx2));
#endif

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1ConvolveYTest,
                         BuildLowbdParams(av1_convolve_y_sr_neon));
#endif

#if HAVE_NEON_DOTPROD
INSTANTIATE_TEST_SUITE_P(NEON_DOTPROD, AV1ConvolveYTest,
                         BuildLowbdParams(av1_convolve_y_sr_neon_dotprod));
#endif

#if HAVE_NEON_I8MM
INSTANTIATE_TEST_SUITE_P(NEON_I8MM, AV1ConvolveYTest,
                         BuildLowbdParams(av1_convolve_y_sr_neon_i8mm));
#endif

////////////////////////////////////////////////////////////////
// Single reference convolve-y IntraBC functions (low bit-depth)
////////////////////////////////////////////////////////////////

class AV1ConvolveYIntraBCTest : public AV1ConvolveTest<convolve_y_func> {
 public:
  void RunTest() {
    // IntraBC functions only operate for subpel_y_qn = 8.
    constexpr int kSubY = 8;
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const InterpFilterParams *filter_params_y = &av1_intrabc_filter_params;
    const uint8_t *input = FirstRandomInput8(GetParam());

    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    // Use a stride different from width to avoid potential storing errors that
    // would go undetected. The input buffer is filled using a padding of 12, so
    // the stride can be anywhere between width and width + 12.
    av1_convolve_y_sr_intrabc_c(input, width + 2, reference, kOutputStride,
                                width, height, filter_params_y, kSubY);

    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    GetParam().TestFunction()(input, width + 2, test, kOutputStride, width,
                              height, filter_params_y, kSubY);

    AssertOutputBufferEq(reference, test, width, height);
  }

  void SpeedTest() {
    constexpr int kNumIters = 10000;
    const InterpFilter filter = static_cast<InterpFilter>(BILINEAR);
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();

    const InterpFilterParams *filter_params_y = &av1_intrabc_filter_params;
    const uint8_t *input = FirstRandomInput8(GetParam());
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);

    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      av1_convolve_y_sr_intrabc_c(input, width, reference, kOutputStride, width,
                                  height, filter_params_y, 0);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    convolve_y_func test_func = GetParam().TestFunction();
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_y, 0);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1,
           time2, time1 / time2);
  }
};

TEST_P(AV1ConvolveYIntraBCTest, RunTest) { RunTest(); }

TEST_P(AV1ConvolveYIntraBCTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveYIntraBCTest,
                         BuildLowbdParams(av1_convolve_y_sr_intrabc_c));

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1ConvolveYIntraBCTest,
                         BuildLowbdParams(av1_convolve_y_sr_intrabc_neon));
#endif

#if CONFIG_AV1_HIGHBITDEPTH
/////////////////////////////////////////////////////////
// Single reference convolve-y functions (high bit-depth)
/////////////////////////////////////////////////////////
typedef void (*highbd_convolve_y_func)(
    const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w,
    int h, const InterpFilterParams *filter_params_y, const int subpel_y_qn,
    int bd);

class AV1ConvolveYHighbdTest : public AV1ConvolveTest<highbd_convolve_y_func> {
 public:
  void RunTest() {
    // Do not test the no-op filter.
    for (int sub_y = 1; sub_y < 16; ++sub_y) {
      for (int filter = EIGHTTAP_REGULAR; filter <= INTERP_FILTERS_ALL;
           ++filter) {
        InterpFilter f = static_cast<InterpFilter>(filter);
        TestConvolve(sub_y, f);
      }
    }
  }

 public:
  void SpeedTest() {
    for (int filter = EIGHTTAP_REGULAR; filter <= INTERP_FILTERS_ALL;
         ++filter) {
      InterpFilter f = static_cast<InterpFilter>(filter);
      TestConvolveSpeed(f, 10000);
    }
  }

 private:
  void TestConvolve(const int sub_y, const InterpFilter filter) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(filter, height);
    const uint16_t *input = FirstRandomInput16(GetParam());
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    av1_highbd_convolve_y_sr_c(input, width, reference, kOutputStride, width,
                               height, filter_params_y, sub_y, bit_depth);
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    GetParam().TestFunction()(input, width, test, kOutputStride, width, height,
                              filter_params_y, sub_y, bit_depth);
    AssertOutputBufferEq(reference, test, width, height);
  }

 private:
  void TestConvolveSpeed(const InterpFilter filter, const int num_iters) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(filter, width);
    const uint16_t *input = FirstRandomInput16(GetParam());
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);

    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      av1_highbd_convolve_y_sr_c(input, width, reference, kOutputStride, width,
                                 height, filter_params_y, 0, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    highbd_convolve_y_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);

    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_y, 0, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1,
           time2, time1 / time2);
  }
};

TEST_P(AV1ConvolveYHighbdTest, RunTest) { RunTest(); }

TEST_P(AV1ConvolveYHighbdTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveYHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_y_sr_c));

#if HAVE_SSSE3
INSTANTIATE_TEST_SUITE_P(SSSE3, AV1ConvolveYHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_y_sr_ssse3));
#endif

#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(AVX2, AV1ConvolveYHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_y_sr_avx2));
#endif

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1ConvolveYHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_y_sr_neon));
#endif

#if HAVE_SVE2
INSTANTIATE_TEST_SUITE_P(SVE2, AV1ConvolveYHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_y_sr_sve2));
#endif

/////////////////////////////////////////////////////////////////
// Single reference convolve-y IntraBC functions (high bit-depth)
/////////////////////////////////////////////////////////////////

class AV1ConvolveYHighbdIntraBCTest
    : public AV1ConvolveTest<highbd_convolve_y_func> {
 public:
  void RunTest() {
    // IntraBC functions only operate for subpel_y_qn = 8.
    constexpr int kSubY = 8;
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_y = &av1_intrabc_filter_params;
    const uint16_t *input = FirstRandomInput16(GetParam());

    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    // Use a stride different from width to avoid potential storing errors that
    // would go undetected. The input buffer is filled using a padding of 12, so
    // the stride can be anywhere between width and width + 12.
    av1_highbd_convolve_y_sr_intrabc_c(input, width + 2, reference,
                                       kOutputStride, width, height,
                                       filter_params_y, kSubY, bit_depth);

    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    GetParam().TestFunction()(input, width + 2, test, kOutputStride, width,
                              height, filter_params_y, kSubY, bit_depth);

    AssertOutputBufferEq(reference, test, width, height);
  }

  void SpeedTest() {
    constexpr int kNumIters = 10000;
    const InterpFilter filter = static_cast<InterpFilter>(BILINEAR);
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(filter, width);
    const uint16_t *input = FirstRandomInput16(GetParam());

    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      av1_highbd_convolve_y_sr_intrabc_c(input, width, reference, kOutputStride,
                                         width, height, filter_params_y, 0,
                                         bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    highbd_convolve_y_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_y, 0, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1,
           time2, time1 / time2);
  }
};

TEST_P(AV1ConvolveYHighbdIntraBCTest, RunTest) { RunTest(); }

TEST_P(AV1ConvolveYHighbdIntraBCTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveYHighbdIntraBCTest,
                         BuildHighbdParams(av1_highbd_convolve_y_sr_intrabc_c));

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(
    NEON, AV1ConvolveYHighbdIntraBCTest,
    BuildHighbdParams(av1_highbd_convolve_y_sr_intrabc_neon));
#endif

#endif  // CONFIG_AV1_HIGHBITDEPTH

//////////////////////////////////////////////////////////////
// Single reference convolve-copy functions (low bit-depth)
//////////////////////////////////////////////////////////////
typedef void (*convolve_copy_func)(const uint8_t *src, ptrdiff_t src_stride,
                                   uint8_t *dst, ptrdiff_t dst_stride, int w,
                                   int h);

class AV1ConvolveCopyTest : public AV1ConvolveTest<convolve_copy_func> {
 public:
  void RunTest() {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const uint8_t *input = FirstRandomInput8(GetParam());
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    aom_convolve_copy_c(input, width, reference, kOutputStride, width, height);
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    GetParam().TestFunction()(input, width, test, kOutputStride, width, height);
    AssertOutputBufferEq(reference, test, width, height);
  }
};

// Note that even though these are AOM convolve functions, we are using the
// newer AV1 test framework.
TEST_P(AV1ConvolveCopyTest, RunTest) { RunTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveCopyTest,
                         BuildLowbdParams(aom_convolve_copy_c));

#if HAVE_SSE2
INSTANTIATE_TEST_SUITE_P(SSE2, AV1ConvolveCopyTest,
                         BuildLowbdParams(aom_convolve_copy_sse2));
#endif

#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(AVX2, AV1ConvolveCopyTest,
                         BuildLowbdParams(aom_convolve_copy_avx2));
#endif

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1ConvolveCopyTest,
                         BuildLowbdParams(aom_convolve_copy_neon));
#endif

#if CONFIG_AV1_HIGHBITDEPTH
///////////////////////////////////////////////////////////////
// Single reference convolve-copy functions (high bit-depth)
///////////////////////////////////////////////////////////////
typedef void (*highbd_convolve_copy_func)(const uint16_t *src,
                                          ptrdiff_t src_stride, uint16_t *dst,
                                          ptrdiff_t dst_stride, int w, int h);

class AV1ConvolveCopyHighbdTest
    : public AV1ConvolveTest<highbd_convolve_copy_func> {
 public:
  void RunTest() {
    const BlockSize &block = GetParam().Block();
    const int width = block.Width();
    const int height = block.Height();
    const uint16_t *input = FirstRandomInput16(GetParam());
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    aom_highbd_convolve_copy_c(input, width, reference, kOutputStride, width,
                               height);
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    GetParam().TestFunction()(input, width, test, kOutputStride, width, height);
    AssertOutputBufferEq(reference, test, width, height);
  }
};

TEST_P(AV1ConvolveCopyHighbdTest, RunTest) { RunTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1ConvolveCopyHighbdTest,
                         BuildHighbdParams(aom_highbd_convolve_copy_c));

#if HAVE_SSE2
INSTANTIATE_TEST_SUITE_P(SSE2, AV1ConvolveCopyHighbdTest,
                         BuildHighbdParams(aom_highbd_convolve_copy_sse2));
#endif

#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(AVX2, AV1ConvolveCopyHighbdTest,
                         BuildHighbdParams(aom_highbd_convolve_copy_avx2));
#endif

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1ConvolveCopyHighbdTest,
                         BuildHighbdParams(aom_highbd_convolve_copy_neon));
#endif

#endif  // CONFIG_AV1_HIGHBITDEPTH

/////////////////////////////////////////////////////////
// Single reference convolve-2D functions (low bit-depth)
/////////////////////////////////////////////////////////
typedef void (*convolve_2d_func)(const uint8_t *src, int src_stride,
                                 uint8_t *dst, int dst_stride, int w, int h,
                                 const InterpFilterParams *filter_params_x,
                                 const InterpFilterParams *filter_params_y,
                                 const int subpel_x_qn, const int subpel_y_qn,
                                 ConvolveParams *conv_params);

class AV1Convolve2DTest : public AV1ConvolveTest<convolve_2d_func> {
 public:
  void RunTest() {
    // Do not test the no-op filter.
    for (int sub_x = 1; sub_x < 16; ++sub_x) {
      for (int sub_y = 1; sub_y < 16; ++sub_y) {
        for (int h_f = EIGHTTAP_REGULAR; h_f <= INTERP_FILTERS_ALL; ++h_f) {
          for (int v_f = EIGHTTAP_REGULAR; v_f <= INTERP_FILTERS_ALL; ++v_f) {
            if (((h_f == MULTITAP_SHARP2) && (v_f < MULTITAP_SHARP2)) ||
                ((h_f < MULTITAP_SHARP2) && (v_f == MULTITAP_SHARP2)))
              continue;
            TestConvolve(static_cast<InterpFilter>(h_f),
                         static_cast<InterpFilter>(v_f), sub_x, sub_y);
          }
        }
      }
    }
  }

 public:
  void SpeedTest() {
    for (int h_f = EIGHTTAP_REGULAR; h_f <= INTERP_FILTERS_ALL; ++h_f) {
      for (int v_f = EIGHTTAP_REGULAR; v_f <= INTERP_FILTERS_ALL; ++v_f) {
        if (((h_f == MULTITAP_SHARP2) && (v_f < MULTITAP_SHARP2)) ||
            ((h_f < MULTITAP_SHARP2) && (v_f == MULTITAP_SHARP2)))
          continue;
        TestConvolveSpeed(static_cast<InterpFilter>(h_f),
                          static_cast<InterpFilter>(v_f), 10000);
      }
    }
  }

 private:
  void TestConvolve(const InterpFilter h_f, const InterpFilter v_f,
                    const int sub_x, const int sub_y) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(h_f, width);
    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(v_f, height);
    const uint8_t *input = FirstRandomInput8(GetParam());
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    av1_convolve_2d_sr_c(input, width, reference, kOutputStride, width, height,
                         filter_params_x, filter_params_y, sub_x, sub_y,
                         &conv_params1);
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    GetParam().TestFunction()(input, width, test, kOutputStride, width, height,
                              filter_params_x, filter_params_y, sub_x, sub_y,
                              &conv_params2);
    AssertOutputBufferEq(reference, test, width, height);
  }

 private:
  void TestConvolveSpeed(const InterpFilter h_f, const InterpFilter v_f,
                         int num_iters) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(h_f, width);
    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(v_f, height);
    const uint8_t *input = FirstRandomInput8(GetParam());
    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      av1_convolve_2d_sr_c(input, width, reference, kOutputStride, width,
                           height, filter_params_x, filter_params_y, 0, 0,
                           &conv_params1);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      GetParam().TestFunction()(input, width, test, kOutputStride, width,
                                height, filter_params_x, filter_params_y, 0, 0,
                                &conv_params2);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    printf("%d - %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", h_f, v_f, width, height,
           time1, time2, time1 / time2);
  }
};

TEST_P(AV1Convolve2DTest, RunTest) { RunTest(); }

TEST_P(AV1Convolve2DTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1Convolve2DTest,
                         BuildLowbdParams(av1_convolve_2d_sr_c));

#if HAVE_SSE2
INSTANTIATE_TEST_SUITE_P(SSE2, AV1Convolve2DTest,
                         BuildLowbdParams(av1_convolve_2d_sr_sse2));
#endif

#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(AVX2, AV1Convolve2DTest,
                         BuildLowbdParams(av1_convolve_2d_sr_avx2));
#endif

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1Convolve2DTest,
                         BuildLowbdParams(av1_convolve_2d_sr_neon));
#endif

#if HAVE_NEON_DOTPROD
INSTANTIATE_TEST_SUITE_P(NEON_DOTPROD, AV1Convolve2DTest,
                         BuildLowbdParams(av1_convolve_2d_sr_neon_dotprod));
#endif

#if HAVE_NEON_I8MM
INSTANTIATE_TEST_SUITE_P(NEON_I8MM, AV1Convolve2DTest,
                         BuildLowbdParams(av1_convolve_2d_sr_neon_i8mm));
#endif

#if HAVE_SVE2
INSTANTIATE_TEST_SUITE_P(SVE2, AV1Convolve2DTest,
                         BuildLowbdParams(av1_convolve_2d_sr_sve2));
#endif

/////////////////////////////////////////////////////////////////
// Single reference convolve-2D IntraBC functions (low bit-depth)
/////////////////////////////////////////////////////////////////

class AV1Convolve2DIntraBCTest : public AV1ConvolveTest<convolve_2d_func> {
 public:
  void RunTest() {
    // IntraBC functions only operate for subpel_x_qn = 8 and subpel_y_qn = 8.
    constexpr int kSubX = 8;
    constexpr int kSubY = 8;
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const InterpFilterParams *filter_params_x = &av1_intrabc_filter_params;
    const InterpFilterParams *filter_params_y = &av1_intrabc_filter_params;
    const uint8_t *input = FirstRandomInput8(GetParam());

    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    // Use a stride different from width to avoid potential storing errors that
    // would go undetected. The input buffer is filled using a padding of 12, so
    // the stride can be anywhere between width and width + 12.
    av1_convolve_2d_sr_intrabc_c(input, width + 2, reference, kOutputStride,
                                 width, height, filter_params_x,
                                 filter_params_y, kSubX, kSubY, &conv_params1);

    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    GetParam().TestFunction()(input, width + 2, test, kOutputStride, width,
                              height, filter_params_x, filter_params_y, kSubX,
                              kSubY, &conv_params2);

    AssertOutputBufferEq(reference, test, width, height);
  }

  void SpeedTest() {
    constexpr int kNumIters = 10000;
    const InterpFilter h_f = static_cast<InterpFilter>(BILINEAR);
    const InterpFilter v_f = static_cast<InterpFilter>(BILINEAR);
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const InterpFilterParams *filter_params_x = &av1_intrabc_filter_params;
    const InterpFilterParams *filter_params_y = &av1_intrabc_filter_params;
    const uint8_t *input = FirstRandomInput8(GetParam());

    DECLARE_ALIGNED(32, uint8_t, reference[MAX_SB_SQUARE]);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      av1_convolve_2d_sr_intrabc_c(input, width, reference, kOutputStride,
                                   width, height, filter_params_x,
                                   filter_params_y, 8, 8, &conv_params1);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    convolve_2d_func test_func = GetParam().TestFunction();
    DECLARE_ALIGNED(32, uint8_t, test[MAX_SB_SQUARE]);
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_x, filter_params_y, 8, 8, &conv_params2);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    printf("%d - %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", h_f, v_f, width, height,
           time1, time2, time1 / time2);
  }
};

TEST_P(AV1Convolve2DIntraBCTest, RunTest) { RunTest(); }

TEST_P(AV1Convolve2DIntraBCTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1Convolve2DIntraBCTest,
                         BuildLowbdParams(av1_convolve_2d_sr_intrabc_c));

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1Convolve2DIntraBCTest,
                         BuildLowbdParams(av1_convolve_2d_sr_intrabc_neon));
#endif

#if CONFIG_AV1_HIGHBITDEPTH
//////////////////////////////////////////////////////////
// Single reference convolve-2d functions (high bit-depth)
//////////////////////////////////////////////////////////

typedef void (*highbd_convolve_2d_func)(
    const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w,
    int h, const InterpFilterParams *filter_params_x,
    const InterpFilterParams *filter_params_y, const int subpel_x_qn,
    const int subpel_y_qn, ConvolveParams *conv_params, int bd);

class AV1Convolve2DHighbdTest
    : public AV1ConvolveTest<highbd_convolve_2d_func> {
 public:
  void RunTest() {
    // Do not test the no-op filter.
    for (int sub_x = 1; sub_x < 16; ++sub_x) {
      for (int sub_y = 1; sub_y < 16; ++sub_y) {
        for (int h_f = EIGHTTAP_REGULAR; h_f <= INTERP_FILTERS_ALL; ++h_f) {
          for (int v_f = EIGHTTAP_REGULAR; v_f <= INTERP_FILTERS_ALL; ++v_f) {
            if (((h_f == MULTITAP_SHARP2) && (v_f < MULTITAP_SHARP2)) ||
                ((h_f < MULTITAP_SHARP2) && (v_f == MULTITAP_SHARP2)))
              continue;
            TestConvolve(static_cast<InterpFilter>(h_f),
                         static_cast<InterpFilter>(v_f), sub_x, sub_y);
          }
        }
      }
    }
  }

 public:
  void SpeedTest() {
    for (int h_f = EIGHTTAP_REGULAR; h_f <= INTERP_FILTERS_ALL; ++h_f) {
      for (int v_f = EIGHTTAP_REGULAR; v_f <= INTERP_FILTERS_ALL; ++v_f) {
        if (((h_f == MULTITAP_SHARP2) && (v_f < MULTITAP_SHARP2)) ||
            ((h_f < MULTITAP_SHARP2) && (v_f == MULTITAP_SHARP2)))
          continue;
        TestConvolveSpeed(static_cast<InterpFilter>(h_f),
                          static_cast<InterpFilter>(v_f), 10000);
      }
    }
  }

 private:
  void TestConvolve(const InterpFilter h_f, const InterpFilter v_f,
                    const int sub_x, const int sub_y) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(h_f, width);
    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(v_f, height);
    const uint16_t *input = FirstRandomInput16(GetParam());
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, bit_depth);
    av1_highbd_convolve_2d_sr_c(input, width, reference, kOutputStride, width,
                                height, filter_params_x, filter_params_y, sub_x,
                                sub_y, &conv_params1, bit_depth);
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, bit_depth);
    GetParam().TestFunction()(input, width, test, kOutputStride, width, height,
                              filter_params_x, filter_params_y, sub_x, sub_y,
                              &conv_params2, bit_depth);
    AssertOutputBufferEq(reference, test, width, height);
  }

  void TestConvolveSpeed(const InterpFilter h_f, const InterpFilter v_f,
                         int num_iters) {
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(h_f, width);
    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(v_f, height);
    const uint16_t *input = FirstRandomInput16(GetParam());
    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      av1_highbd_convolve_2d_sr_c(input, width, reference, kOutputStride, width,
                                  height, filter_params_x, filter_params_y, 0,
                                  0, &conv_params1, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    aom_usec_timer_start(&timer);
    for (int i = 0; i < num_iters; ++i) {
      GetParam().TestFunction()(input, width, test, kOutputStride, width,
                                height, filter_params_x, filter_params_y, 0, 0,
                                &conv_params2, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));
    printf("%d - %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", h_f, v_f, width, height,
           time1, time2, time1 / time2);
  }
};

TEST_P(AV1Convolve2DHighbdTest, RunTest) { RunTest(); }

TEST_P(AV1Convolve2DHighbdTest, DISABLED_SpeedTest) { SpeedTest(); }

INSTANTIATE_TEST_SUITE_P(C, AV1Convolve2DHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_2d_sr_c));

#if HAVE_SSSE3
INSTANTIATE_TEST_SUITE_P(SSSE3, AV1Convolve2DHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_2d_sr_ssse3));
#endif

#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(AVX2, AV1Convolve2DHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_2d_sr_avx2));
#endif

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, AV1Convolve2DHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_2d_sr_neon));
#endif

#if HAVE_SVE2
INSTANTIATE_TEST_SUITE_P(SVE2, AV1Convolve2DHighbdTest,
                         BuildHighbdParams(av1_highbd_convolve_2d_sr_sve2));
#endif

//////////////////////////////////////////////////////////////////
// Single reference convolve-2d IntraBC functions (high bit-depth)
//////////////////////////////////////////////////////////////////

class AV1Convolve2DHighbdIntraBCTest
    : public AV1ConvolveTest<highbd_convolve_2d_func> {
 public:
  void RunTest() {
    // IntraBC functions only operate for subpel_x_qn = 8 and subpel_y_qn = 8.
    constexpr int kSubX = 8;
    constexpr int kSubY = 8;
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_x = &av1_intrabc_filter_params;
    const InterpFilterParams *filter_params_y = &av1_intrabc_filter_params;
    const uint16_t *input = FirstRandomInput16(GetParam());

    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, bit_depth);
    // Use a stride different from width to avoid potential storing errors that
    // would go undetected. The input buffer is filled using a padding of 12, so
    // the stride can be anywhere between width and width + 12.
    av1_highbd_convolve_2d_sr_intrabc_c(input, width + 2, reference,
                                        kOutputStride, width, height,
                                        filter_params_x, filter_params_y, kSubX,
                                        kSubY, &conv_params1, bit_depth);

    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, bit_depth);
    GetParam().TestFunction()(input, width + 2, test, kOutputStride, width,
                              height, filter_params_x, filter_params_y, kSubX,
                              kSubY, &conv_params2, bit_depth);

    AssertOutputBufferEq(reference, test, width, height);
  }

  void SpeedTest() {
    constexpr int kNumIters = 10000;
    const InterpFilter h_f = static_cast<InterpFilter>(BILINEAR);
    const InterpFilter v_f = static_cast<InterpFilter>(BILINEAR);
    const int width = GetParam().Block().Width();
    const int height = GetParam().Block().Height();
    const int bit_depth = GetParam().BitDepth();
    const InterpFilterParams *filter_params_x =
        av1_get_interp_filter_params_with_block_size(h_f, width);
    const InterpFilterParams *filter_params_y =
        av1_get_interp_filter_params_with_block_size(v_f, height);
    const uint16_t *input = FirstRandomInput16(GetParam());

    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
    ConvolveParams conv_params1 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    aom_usec_timer timer;
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      av1_highbd_convolve_2d_sr_intrabc_c(
          input, width, reference, kOutputStride, width, height,
          filter_params_x, filter_params_y, 0, 0, &conv_params1, bit_depth);
    }
    aom_usec_timer_mark(&timer);
    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));

    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
    highbd_convolve_2d_func test_func = GetParam().TestFunction();
    ConvolveParams conv_params2 =
        get_conv_params_no_round(0, 0, nullptr, 0, 0, 8);
    aom_usec_timer_start(&timer);
    for (int i = 0; i < kNumIters; ++i) {
      test_func(input, width, test, kOutputStride, width, height,
                filter_params_x, filter_params_y, 0, 0, &conv_params2,
                bit_depth);
    }
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=82 G=86

¤ Dauer der Verarbeitung: 0.17 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

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.