1619 lines · 73.8 KB · C++
RawDownload
/* *  Copyright (c) 2012 The WebM project authors. All Rights Reserved. * *  Use of this source code is governed by a BSD-style license *  that can be found in the LICENSE file in the root of the source *  tree. An additional intellectual property rights grant can be found *  in the file PATENTS.  All contributing project authors may *  be found in the AUTHORS file in the root of the source tree. */ #include <cstdlib>#include <new> #include "third_party/googletest/src/include/gtest/gtest.h" #include "./vpx_config.h"#include "./vpx_dsp_rtcd.h"#include "test/acm_random.h"#include "test/clear_system_state.h"#include "test/register_state_check.h"#include "vpx/vpx_codec.h"#include "vpx/vpx_integer.h"#include "vpx_dsp/variance.h"#include "vpx_mem/vpx_mem.h"#include "vpx_ports/mem.h"#include "vpx_ports/vpx_timer.h" namespace { typedef unsigned int (*Get4x4SseFunc)(const uint8_t *a, int a_stride,                                      const uint8_t *b, int b_stride);typedef unsigned int (*SumOfSquaresFunction)(const int16_t *src); using libvpx_test::ACMRandom; // Truncate high bit depth results by downshifting (with rounding) by:// 2 * (bit_depth - 8) for sse// (bit_depth - 8) for sestatic void RoundHighBitDepth(int bit_depth, int64_t *se, uint64_t *sse) {  switch (bit_depth) {    case VPX_BITS_12:      *sse = (*sse + 128) >> 8;      *se = (*se + 8) >> 4;      break;    case VPX_BITS_10:      *sse = (*sse + 8) >> 4;      *se = (*se + 2) >> 2;      break;    case VPX_BITS_8:    default: break;  }} static unsigned int mb_ss_ref(const int16_t *src) {  unsigned int res = 0;  for (int i = 0; i < 256; ++i) {    res += src[i] * src[i];  }  return res;} /* Note: *  Our codebase calculates the "diff" value in the variance algorithm by *  (src - ref). */static uint32_t variance_ref(const uint8_t *src, const uint8_t *ref, int l2w,                             int l2h, int src_stride, int ref_stride,                             uint32_t *sse_ptr, bool use_high_bit_depth_,                             vpx_bit_depth_t bit_depth) {  int64_t se = 0;  uint64_t sse = 0;  const int w = 1 << l2w;  const int h = 1 << l2h;  for (int y = 0; y < h; y++) {    for (int x = 0; x < w; x++) {      int diff;      if (!use_high_bit_depth_) {        diff = src[y * src_stride + x] - ref[y * ref_stride + x];        se += diff;        sse += diff * diff;#if CONFIG_VP9_HIGHBITDEPTH      } else {        diff = CONVERT_TO_SHORTPTR(src)[y * src_stride + x] -               CONVERT_TO_SHORTPTR(ref)[y * ref_stride + x];        se += diff;        sse += diff * diff;#endif  // CONFIG_VP9_HIGHBITDEPTH      }    }  }  RoundHighBitDepth(bit_depth, &se, &sse);  *sse_ptr = static_cast<uint32_t>(sse);  return static_cast<uint32_t>(      sse - ((static_cast<int64_t>(se) * se) >> (l2w + l2h)));} /* The subpel reference functions differ from the codec version in one aspect: * they calculate the bilinear factors directly instead of using a lookup table * and therefore upshift xoff and yoff by 1. Only every other calculated value * is used so the codec version shrinks the table to save space and maintain * compatibility with vp8. */static uint32_t subpel_variance_ref(const uint8_t *ref, const uint8_t *src,                                    int l2w, int l2h, int xoff, int yoff,                                    uint32_t *sse_ptr, bool use_high_bit_depth_,                                    vpx_bit_depth_t bit_depth) {  int64_t se = 0;  uint64_t sse = 0;  const int w = 1 << l2w;  const int h = 1 << l2h;   xoff <<= 1;  yoff <<= 1;   for (int y = 0; y < h; y++) {    for (int x = 0; x < w; x++) {      // Bilinear interpolation at a 16th pel step.      if (!use_high_bit_depth_) {        const int a1 = ref[(w + 1) * (y + 0) + x + 0];        const int a2 = ref[(w + 1) * (y + 0) + x + 1];        const int b1 = ref[(w + 1) * (y + 1) + x + 0];        const int b2 = ref[(w + 1) * (y + 1) + x + 1];        const int a = a1 + (((a2 - a1) * xoff + 8) >> 4);        const int b = b1 + (((b2 - b1) * xoff + 8) >> 4);        const int r = a + (((b - a) * yoff + 8) >> 4);        const int diff = r - src[w * y + x];        se += diff;        sse += diff * diff;#if CONFIG_VP9_HIGHBITDEPTH      } else {        uint16_t *ref16 = CONVERT_TO_SHORTPTR(ref);        uint16_t *src16 = CONVERT_TO_SHORTPTR(src);        const int a1 = ref16[(w + 1) * (y + 0) + x + 0];        const int a2 = ref16[(w + 1) * (y + 0) + x + 1];        const int b1 = ref16[(w + 1) * (y + 1) + x + 0];        const int b2 = ref16[(w + 1) * (y + 1) + x + 1];        const int a = a1 + (((a2 - a1) * xoff + 8) >> 4);        const int b = b1 + (((b2 - b1) * xoff + 8) >> 4);        const int r = a + (((b - a) * yoff + 8) >> 4);        const int diff = r - src16[w * y + x];        se += diff;        sse += diff * diff;#endif  // CONFIG_VP9_HIGHBITDEPTH      }    }  }  RoundHighBitDepth(bit_depth, &se, &sse);  *sse_ptr = static_cast<uint32_t>(sse);  return static_cast<uint32_t>(      sse - ((static_cast<int64_t>(se) * se) >> (l2w + l2h)));} static uint32_t subpel_avg_variance_ref(const uint8_t *ref, const uint8_t *src,                                        const uint8_t *second_pred, int l2w,                                        int l2h, int xoff, int yoff,                                        uint32_t *sse_ptr,                                        bool use_high_bit_depth,                                        vpx_bit_depth_t bit_depth) {  int64_t se = 0;  uint64_t sse = 0;  const int w = 1 << l2w;  const int h = 1 << l2h;   xoff <<= 1;  yoff <<= 1;   for (int y = 0; y < h; y++) {    for (int x = 0; x < w; x++) {      // bilinear interpolation at a 16th pel step      if (!use_high_bit_depth) {        const int a1 = ref[(w + 1) * (y + 0) + x + 0];        const int a2 = ref[(w + 1) * (y + 0) + x + 1];        const int b1 = ref[(w + 1) * (y + 1) + x + 0];        const int b2 = ref[(w + 1) * (y + 1) + x + 1];        const int a = a1 + (((a2 - a1) * xoff + 8) >> 4);        const int b = b1 + (((b2 - b1) * xoff + 8) >> 4);        const int r = a + (((b - a) * yoff + 8) >> 4);        const int diff =            ((r + second_pred[w * y + x] + 1) >> 1) - src[w * y + x];        se += diff;        sse += diff * diff;#if CONFIG_VP9_HIGHBITDEPTH      } else {        const uint16_t *ref16 = CONVERT_TO_SHORTPTR(ref);        const uint16_t *src16 = CONVERT_TO_SHORTPTR(src);        const uint16_t *sec16 = CONVERT_TO_SHORTPTR(second_pred);        const int a1 = ref16[(w + 1) * (y + 0) + x + 0];        const int a2 = ref16[(w + 1) * (y + 0) + x + 1];        const int b1 = ref16[(w + 1) * (y + 1) + x + 0];        const int b2 = ref16[(w + 1) * (y + 1) + x + 1];        const int a = a1 + (((a2 - a1) * xoff + 8) >> 4);        const int b = b1 + (((b2 - b1) * xoff + 8) >> 4);        const int r = a + (((b - a) * yoff + 8) >> 4);        const int diff = ((r + sec16[w * y + x] + 1) >> 1) - src16[w * y + x];        se += diff;        sse += diff * diff;#endif  // CONFIG_VP9_HIGHBITDEPTH      }    }  }  RoundHighBitDepth(bit_depth, &se, &sse);  *sse_ptr = static_cast<uint32_t>(sse);  return static_cast<uint32_t>(      sse - ((static_cast<int64_t>(se) * se) >> (l2w + l2h)));} //////////////////////////////////////////////////////////////////////////////// class SumOfSquaresTest : public ::testing::TestWithParam<SumOfSquaresFunction> { public:  SumOfSquaresTest() : func_(GetParam()) {}   virtual ~SumOfSquaresTest() { libvpx_test::ClearSystemState(); }  protected:  void ConstTest();  void RefTest();   SumOfSquaresFunction func_;  ACMRandom rnd_;}; void SumOfSquaresTest::ConstTest() {  int16_t mem[256];  unsigned int res;  for (int v = 0; v < 256; ++v) {    for (int i = 0; i < 256; ++i) {      mem[i] = v;    }    ASM_REGISTER_STATE_CHECK(res = func_(mem));    EXPECT_EQ(256u * (v * v), res);  }} void SumOfSquaresTest::RefTest() {  int16_t mem[256];  for (int i = 0; i < 100; ++i) {    for (int j = 0; j < 256; ++j) {      mem[j] = rnd_.Rand8() - rnd_.Rand8();    }     const unsigned int expected = mb_ss_ref(mem);    unsigned int res;    ASM_REGISTER_STATE_CHECK(res = func_(mem));    EXPECT_EQ(expected, res);  }} ////////////////////////////////////////////////////////////////////////////////// Encapsulating struct to store the function to test along with// some testing context.// Can be used for MSE, SSE, Variance, etc. template <typename Func>struct TestParams {  TestParams(int log2w = 0, int log2h = 0, Func function = nullptr,             int bit_depth_value = 0)      : log2width(log2w), log2height(log2h), func(function) {    use_high_bit_depth = (bit_depth_value > 0);    if (use_high_bit_depth) {      bit_depth = static_cast<vpx_bit_depth_t>(bit_depth_value);    } else {      bit_depth = VPX_BITS_8;    }    width = 1 << log2width;    height = 1 << log2height;    block_size = width * height;    mask = (1u << bit_depth) - 1;  }   int log2width, log2height;  int width, height;  int block_size;  Func func;  vpx_bit_depth_t bit_depth;  bool use_high_bit_depth;  uint32_t mask;}; template <typename Func>std::ostream &operator<<(std::ostream &os, const TestParams<Func> &p) {  return os << "log2width/height:" << p.log2width << "/" << p.log2height            << " function:" << reinterpret_cast<const void *>(p.func)            << " bit-depth:" << p.bit_depth;} // Main class for testing a function typetemplate <typename FunctionType>class MainTestClass    : public ::testing::TestWithParam<TestParams<FunctionType> > { public:  virtual void SetUp() {    params_ = this->GetParam();     rnd_.Reset(ACMRandom::DeterministicSeed());    const size_t unit =        use_high_bit_depth() ? sizeof(uint16_t) : sizeof(uint8_t);    src_ = reinterpret_cast<uint8_t *>(vpx_memalign(16, block_size() * unit));    ref_ = new uint8_t[block_size() * unit];    ASSERT_NE(src_, nullptr);    ASSERT_NE(ref_, nullptr);#if CONFIG_VP9_HIGHBITDEPTH    if (use_high_bit_depth()) {      // TODO(skal): remove!      src_ = CONVERT_TO_BYTEPTR(src_);      ref_ = CONVERT_TO_BYTEPTR(ref_);    }#endif  }   virtual void TearDown() {#if CONFIG_VP9_HIGHBITDEPTH    if (use_high_bit_depth()) {      // TODO(skal): remove!      src_ = reinterpret_cast<uint8_t *>(CONVERT_TO_SHORTPTR(src_));      ref_ = reinterpret_cast<uint8_t *>(CONVERT_TO_SHORTPTR(ref_));    }#endif     vpx_free(src_);    delete[] ref_;    src_ = nullptr;    ref_ = nullptr;    libvpx_test::ClearSystemState();  }  protected:  // We could sub-class MainTestClass into dedicated class for Variance  // and MSE/SSE, but it involves a lot of 'this->xxx' dereferencing  // to access top class fields xxx. That's cumbersome, so for now we'll just  // implement the testing methods here:   // Variance tests  void ZeroTest();  void RefTest();  void RefStrideTest();  void OneQuarterTest();  void SpeedTest();   // MSE/SSE tests  void RefTestMse();  void RefTestSse();  void MaxTestMse();  void MaxTestSse();  protected:  ACMRandom rnd_;  uint8_t *src_;  uint8_t *ref_;  TestParams<FunctionType> params_;   // some relay helpers  bool use_high_bit_depth() const { return params_.use_high_bit_depth; }  int byte_shift() const { return params_.bit_depth - 8; }  int block_size() const { return params_.block_size; }  int width() const { return params_.width; }  int height() const { return params_.height; }  uint32_t mask() const { return params_.mask; }}; ////////////////////////////////////////////////////////////////////////////////// Tests related to variance. template <typename VarianceFunctionType>void MainTestClass<VarianceFunctionType>::ZeroTest() {  for (int i = 0; i <= 255; ++i) {    if (!use_high_bit_depth()) {      memset(src_, i, block_size());    } else {      uint16_t *const src16 = CONVERT_TO_SHORTPTR(src_);      for (int k = 0; k < block_size(); ++k) src16[k] = i << byte_shift();    }    for (int j = 0; j <= 255; ++j) {      if (!use_high_bit_depth()) {        memset(ref_, j, block_size());      } else {        uint16_t *const ref16 = CONVERT_TO_SHORTPTR(ref_);        for (int k = 0; k < block_size(); ++k) ref16[k] = j << byte_shift();      }      unsigned int sse, var;      ASM_REGISTER_STATE_CHECK(          var = params_.func(src_, width(), ref_, width(), &sse));      EXPECT_EQ(0u, var) << "src values: " << i << " ref values: " << j;    }  }} template <typename VarianceFunctionType>void MainTestClass<VarianceFunctionType>::RefTest() {  for (int i = 0; i < 10; ++i) {    for (int j = 0; j < block_size(); j++) {      if (!use_high_bit_depth()) {        src_[j] = rnd_.Rand8();        ref_[j] = rnd_.Rand8();#if CONFIG_VP9_HIGHBITDEPTH      } else {        CONVERT_TO_SHORTPTR(src_)[j] = rnd_.Rand16() & mask();        CONVERT_TO_SHORTPTR(ref_)[j] = rnd_.Rand16() & mask();#endif  // CONFIG_VP9_HIGHBITDEPTH      }    }    unsigned int sse1, sse2, var1, var2;    const int stride = width();    ASM_REGISTER_STATE_CHECK(        var1 = params_.func(src_, stride, ref_, stride, &sse1));    var2 =        variance_ref(src_, ref_, params_.log2width, params_.log2height, stride,                     stride, &sse2, use_high_bit_depth(), params_.bit_depth);    EXPECT_EQ(sse1, sse2) << "Error at test index: " << i;    EXPECT_EQ(var1, var2) << "Error at test index: " << i;  }} template <typename VarianceFunctionType>void MainTestClass<VarianceFunctionType>::RefStrideTest() {  for (int i = 0; i < 10; ++i) {    const int ref_stride = (i & 1) * width();    const int src_stride = ((i >> 1) & 1) * width();    for (int j = 0; j < block_size(); j++) {      const int ref_ind = (j / width()) * ref_stride + j % width();      const int src_ind = (j / width()) * src_stride + j % width();      if (!use_high_bit_depth()) {        src_[src_ind] = rnd_.Rand8();        ref_[ref_ind] = rnd_.Rand8();#if CONFIG_VP9_HIGHBITDEPTH      } else {        CONVERT_TO_SHORTPTR(src_)[src_ind] = rnd_.Rand16() & mask();        CONVERT_TO_SHORTPTR(ref_)[ref_ind] = rnd_.Rand16() & mask();#endif  // CONFIG_VP9_HIGHBITDEPTH      }    }    unsigned int sse1, sse2;    unsigned int var1, var2;     ASM_REGISTER_STATE_CHECK(        var1 = params_.func(src_, src_stride, ref_, ref_stride, &sse1));    var2 = variance_ref(src_, ref_, params_.log2width, params_.log2height,                        src_stride, ref_stride, &sse2, use_high_bit_depth(),                        params_.bit_depth);    EXPECT_EQ(sse1, sse2) << "Error at test index: " << i;    EXPECT_EQ(var1, var2) << "Error at test index: " << i;  }} template <typename VarianceFunctionType>void MainTestClass<VarianceFunctionType>::OneQuarterTest() {  const int half = block_size() / 2;  if (!use_high_bit_depth()) {    memset(src_, 255, block_size());    memset(ref_, 255, half);    memset(ref_ + half, 0, half);#if CONFIG_VP9_HIGHBITDEPTH  } else {    vpx_memset16(CONVERT_TO_SHORTPTR(src_), 255 << byte_shift(), block_size());    vpx_memset16(CONVERT_TO_SHORTPTR(ref_), 255 << byte_shift(), half);    vpx_memset16(CONVERT_TO_SHORTPTR(ref_) + half, 0, half);#endif  // CONFIG_VP9_HIGHBITDEPTH  }  unsigned int sse, var, expected;  ASM_REGISTER_STATE_CHECK(      var = params_.func(src_, width(), ref_, width(), &sse));  expected = block_size() * 255 * 255 / 4;  EXPECT_EQ(expected, var);} template <typename VarianceFunctionType>void MainTestClass<VarianceFunctionType>::SpeedTest() {  const int half = block_size() / 2;  if (!use_high_bit_depth()) {    memset(src_, 255, block_size());    memset(ref_, 255, half);    memset(ref_ + half, 0, half);#if CONFIG_VP9_HIGHBITDEPTH  } else {    vpx_memset16(CONVERT_TO_SHORTPTR(src_), 255 << byte_shift(), block_size());    vpx_memset16(CONVERT_TO_SHORTPTR(ref_), 255 << byte_shift(), half);    vpx_memset16(CONVERT_TO_SHORTPTR(ref_) + half, 0, half);#endif  // CONFIG_VP9_HIGHBITDEPTH  }  unsigned int sse;   vpx_usec_timer timer;  vpx_usec_timer_start(&timer);  for (int i = 0; i < (1 << 30) / block_size(); ++i) {    const uint32_t variance = params_.func(src_, width(), ref_, width(), &sse);    // Ignore return value.    (void)variance;  }  vpx_usec_timer_mark(&timer);  const int elapsed_time = static_cast<int>(vpx_usec_timer_elapsed(&timer));  printf("Variance %dx%d time: %5d ms\n", width(), height(),         elapsed_time / 1000);} ////////////////////////////////////////////////////////////////////////////////// Tests related to MSE / SSE. template <typename FunctionType>void MainTestClass<FunctionType>::RefTestMse() {  for (int i = 0; i < 10; ++i) {

Lines 1–500 of 1619.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t