vp9[loongarch]: Optimize fdct4x4/8x8_lsx
[platform/upstream/libvpx.git] / test / vp9_ethread_test.cc
1 /*
2  *  Copyright (c) 2014 The WebM project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10
11 #include <string>
12 #include <vector>
13 #include "third_party/googletest/src/include/gtest/gtest.h"
14 #include "test/codec_factory.h"
15 #include "test/encode_test_driver.h"
16 #include "test/md5_helper.h"
17 #include "test/util.h"
18 #include "test/y4m_video_source.h"
19 #include "vp9/encoder/vp9_firstpass.h"
20
21 namespace {
22 // FIRSTPASS_STATS struct:
23 // {
24 //   25 double members;
25 //   1 int64_t member;
26 // }
27 // Whenever FIRSTPASS_STATS struct is modified, the following constants need to
28 // be revisited.
29 const int kDbl = 25;
30 const int kInt = 1;
31 const size_t kFirstPassStatsSz = kDbl * sizeof(double) + kInt * sizeof(int64_t);
32
33 class VPxFirstPassEncoderThreadTest
34     : public ::libvpx_test::EncoderTest,
35       public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
36  protected:
37   VPxFirstPassEncoderThreadTest()
38       : EncoderTest(GET_PARAM(0)), encoder_initialized_(false), tiles_(0),
39         encoding_mode_(GET_PARAM(1)), set_cpu_used_(GET_PARAM(2)) {
40     init_flags_ = VPX_CODEC_USE_PSNR;
41
42     row_mt_mode_ = 1;
43     first_pass_only_ = true;
44     firstpass_stats_.buf = nullptr;
45     firstpass_stats_.sz = 0;
46   }
47   virtual ~VPxFirstPassEncoderThreadTest() { free(firstpass_stats_.buf); }
48
49   virtual void SetUp() {
50     InitializeConfig();
51     SetMode(encoding_mode_);
52
53     cfg_.rc_end_usage = VPX_VBR;
54     cfg_.rc_2pass_vbr_minsection_pct = 5;
55     cfg_.rc_2pass_vbr_maxsection_pct = 2000;
56     cfg_.rc_max_quantizer = 56;
57     cfg_.rc_min_quantizer = 0;
58   }
59
60   virtual void BeginPassHook(unsigned int /*pass*/) {
61     encoder_initialized_ = false;
62     abort_ = false;
63   }
64
65   virtual void EndPassHook() {
66     // For first pass stats test, only run first pass encoder.
67     if (first_pass_only_ && cfg_.g_pass == VPX_RC_FIRST_PASS)
68       abort_ |= first_pass_only_;
69   }
70
71   virtual void PreEncodeFrameHook(::libvpx_test::VideoSource * /*video*/,
72                                   ::libvpx_test::Encoder *encoder) {
73     if (!encoder_initialized_) {
74       // Encode in 2-pass mode.
75       encoder->Control(VP9E_SET_TILE_COLUMNS, tiles_);
76       encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
77       encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 1);
78       encoder->Control(VP8E_SET_ARNR_MAXFRAMES, 7);
79       encoder->Control(VP8E_SET_ARNR_STRENGTH, 5);
80       encoder->Control(VP8E_SET_ARNR_TYPE, 3);
81       encoder->Control(VP9E_SET_FRAME_PARALLEL_DECODING, 0);
82
83       if (encoding_mode_ == ::libvpx_test::kTwoPassGood)
84         encoder->Control(VP9E_SET_ROW_MT, row_mt_mode_);
85
86       encoder_initialized_ = true;
87     }
88   }
89
90   virtual void StatsPktHook(const vpx_codec_cx_pkt_t *pkt) {
91     const uint8_t *const pkt_buf =
92         reinterpret_cast<uint8_t *>(pkt->data.twopass_stats.buf);
93     const size_t pkt_size = pkt->data.twopass_stats.sz;
94
95     // First pass stats size equals sizeof(FIRSTPASS_STATS)
96     EXPECT_EQ(pkt_size, kFirstPassStatsSz)
97         << "Error: First pass stats size doesn't equal kFirstPassStatsSz";
98
99     firstpass_stats_.buf =
100         realloc(firstpass_stats_.buf, firstpass_stats_.sz + pkt_size);
101     ASSERT_NE(firstpass_stats_.buf, nullptr);
102     memcpy((uint8_t *)firstpass_stats_.buf + firstpass_stats_.sz, pkt_buf,
103            pkt_size);
104     firstpass_stats_.sz += pkt_size;
105   }
106
107   bool encoder_initialized_;
108   int tiles_;
109   ::libvpx_test::TestMode encoding_mode_;
110   int set_cpu_used_;
111   int row_mt_mode_;
112   bool first_pass_only_;
113   vpx_fixed_buf_t firstpass_stats_;
114 };
115
116 static void compare_fp_stats(vpx_fixed_buf_t *fp_stats, double factor) {
117   // fp_stats consists of 2 set of first pass encoding stats. These 2 set of
118   // stats are compared to check if the stats match or at least are very close.
119   FIRSTPASS_STATS *stats1 = reinterpret_cast<FIRSTPASS_STATS *>(fp_stats->buf);
120   int nframes_ = (int)(fp_stats->sz / sizeof(FIRSTPASS_STATS));
121   FIRSTPASS_STATS *stats2 = stats1 + nframes_ / 2;
122   int i, j;
123
124   // The total stats are also output and included in the first pass stats. Here
125   // ignore that in the comparison.
126   for (i = 0; i < (nframes_ / 2 - 1); ++i) {
127     const double *frame_stats1 = reinterpret_cast<double *>(stats1);
128     const double *frame_stats2 = reinterpret_cast<double *>(stats2);
129
130     for (j = 0; j < kDbl; ++j) {
131       ASSERT_LE(fabs(*frame_stats1 - *frame_stats2),
132                 fabs(*frame_stats1) / factor)
133           << "First failure @ frame #" << i << " stat #" << j << " ("
134           << *frame_stats1 << " vs. " << *frame_stats2 << ")";
135       frame_stats1++;
136       frame_stats2++;
137     }
138
139     stats1++;
140     stats2++;
141   }
142
143   // Reset firstpass_stats_ to 0.
144   memset((uint8_t *)fp_stats->buf, 0, fp_stats->sz);
145   fp_stats->sz = 0;
146 }
147
148 static void compare_fp_stats_md5(vpx_fixed_buf_t *fp_stats) {
149   // fp_stats consists of 2 set of first pass encoding stats. These 2 set of
150   // stats are compared to check if the stats match.
151   uint8_t *stats1 = reinterpret_cast<uint8_t *>(fp_stats->buf);
152   uint8_t *stats2 = stats1 + fp_stats->sz / 2;
153   ::libvpx_test::MD5 md5_row_mt_0, md5_row_mt_1;
154
155   md5_row_mt_0.Add(stats1, fp_stats->sz / 2);
156   const char *md5_row_mt_0_str = md5_row_mt_0.Get();
157
158   md5_row_mt_1.Add(stats2, fp_stats->sz / 2);
159   const char *md5_row_mt_1_str = md5_row_mt_1.Get();
160
161   // Check md5 match.
162   ASSERT_STREQ(md5_row_mt_0_str, md5_row_mt_1_str)
163       << "MD5 checksums don't match";
164
165   // Reset firstpass_stats_ to 0.
166   memset((uint8_t *)fp_stats->buf, 0, fp_stats->sz);
167   fp_stats->sz = 0;
168 }
169
170 TEST_P(VPxFirstPassEncoderThreadTest, FirstPassStatsTest) {
171   ::libvpx_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 60);
172
173   first_pass_only_ = true;
174   cfg_.rc_target_bitrate = 1000;
175
176   // Test row_mt_mode: 0 vs 1 at single thread case(threads = 1, tiles_ = 0)
177   tiles_ = 0;
178   cfg_.g_threads = 1;
179
180   row_mt_mode_ = 0;
181   init_flags_ = VPX_CODEC_USE_PSNR;
182   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
183
184   row_mt_mode_ = 1;
185   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
186
187   // Compare to check if using or not using row-mt generates close stats.
188   ASSERT_NO_FATAL_FAILURE(compare_fp_stats(&firstpass_stats_, 1000.0));
189
190   // Test single thread vs multiple threads
191   row_mt_mode_ = 1;
192   tiles_ = 0;
193
194   cfg_.g_threads = 1;
195   init_flags_ = VPX_CODEC_USE_PSNR;
196   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
197
198   cfg_.g_threads = 4;
199   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
200
201   // Compare to check if single-thread and multi-thread stats are close enough.
202   ASSERT_NO_FATAL_FAILURE(compare_fp_stats(&firstpass_stats_, 1000.0));
203
204   // Bit exact test in row_mt mode.
205   // When row_mt_mode_=1 and using >1 threads, the encoder generates bit exact
206   // result.
207   row_mt_mode_ = 1;
208   tiles_ = 2;
209
210   cfg_.g_threads = 2;
211   init_flags_ = VPX_CODEC_USE_PSNR;
212   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
213
214   cfg_.g_threads = 8;
215   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
216
217   // Compare to check if stats match with row-mt=0/1.
218   compare_fp_stats_md5(&firstpass_stats_);
219 }
220
221 class VPxEncoderThreadTest
222     : public ::libvpx_test::EncoderTest,
223       public ::libvpx_test::CodecTestWith4Params<libvpx_test::TestMode, int,
224                                                  int, int> {
225  protected:
226   VPxEncoderThreadTest()
227       : EncoderTest(GET_PARAM(0)), encoder_initialized_(false),
228         tiles_(GET_PARAM(3)), threads_(GET_PARAM(4)),
229         encoding_mode_(GET_PARAM(1)), set_cpu_used_(GET_PARAM(2)) {
230     init_flags_ = VPX_CODEC_USE_PSNR;
231     md5_.clear();
232     row_mt_mode_ = 1;
233     psnr_ = 0.0;
234     nframes_ = 0;
235   }
236   virtual ~VPxEncoderThreadTest() {}
237
238   virtual void SetUp() {
239     InitializeConfig();
240     SetMode(encoding_mode_);
241
242     if (encoding_mode_ != ::libvpx_test::kRealTime) {
243       cfg_.rc_end_usage = VPX_VBR;
244       cfg_.rc_2pass_vbr_minsection_pct = 5;
245       cfg_.rc_2pass_vbr_maxsection_pct = 2000;
246     } else {
247       cfg_.g_lag_in_frames = 0;
248       cfg_.rc_end_usage = VPX_CBR;
249       cfg_.g_error_resilient = 1;
250     }
251     cfg_.rc_max_quantizer = 56;
252     cfg_.rc_min_quantizer = 0;
253   }
254
255   virtual void BeginPassHook(unsigned int /*pass*/) {
256     encoder_initialized_ = false;
257     psnr_ = 0.0;
258     nframes_ = 0;
259   }
260
261   virtual void PreEncodeFrameHook(::libvpx_test::VideoSource * /*video*/,
262                                   ::libvpx_test::Encoder *encoder) {
263     if (!encoder_initialized_) {
264       // Encode 4 column tiles.
265       encoder->Control(VP9E_SET_TILE_COLUMNS, tiles_);
266       encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
267       if (encoding_mode_ != ::libvpx_test::kRealTime) {
268         encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 1);
269         encoder->Control(VP8E_SET_ARNR_MAXFRAMES, 7);
270         encoder->Control(VP8E_SET_ARNR_STRENGTH, 5);
271         encoder->Control(VP8E_SET_ARNR_TYPE, 3);
272         encoder->Control(VP9E_SET_FRAME_PARALLEL_DECODING, 0);
273       } else {
274         encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 0);
275         encoder->Control(VP9E_SET_AQ_MODE, 3);
276       }
277       encoder->Control(VP9E_SET_ROW_MT, row_mt_mode_);
278
279       encoder_initialized_ = true;
280     }
281   }
282
283   virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
284     psnr_ += pkt->data.psnr.psnr[0];
285     nframes_++;
286   }
287
288   virtual void DecompressedFrameHook(const vpx_image_t &img,
289                                      vpx_codec_pts_t /*pts*/) {
290     ::libvpx_test::MD5 md5_res;
291     md5_res.Add(&img);
292     md5_.push_back(md5_res.Get());
293   }
294
295   virtual bool HandleDecodeResult(const vpx_codec_err_t res,
296                                   const libvpx_test::VideoSource & /*video*/,
297                                   libvpx_test::Decoder * /*decoder*/) {
298     if (res != VPX_CODEC_OK) {
299       EXPECT_EQ(VPX_CODEC_OK, res);
300       return false;
301     }
302
303     return true;
304   }
305
306   double GetAveragePsnr() const { return nframes_ ? (psnr_ / nframes_) : 0.0; }
307
308   bool encoder_initialized_;
309   int tiles_;
310   int threads_;
311   ::libvpx_test::TestMode encoding_mode_;
312   int set_cpu_used_;
313   int row_mt_mode_;
314   double psnr_;
315   unsigned int nframes_;
316   std::vector<std::string> md5_;
317 };
318
319 TEST_P(VPxEncoderThreadTest, EncoderResultTest) {
320   ::libvpx_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 15, 20);
321   cfg_.rc_target_bitrate = 1000;
322
323   // Part 1: Bit exact test for row_mt_mode_ = 0.
324   // This part keeps original unit tests done before row-mt code is checked in.
325   row_mt_mode_ = 0;
326
327   // Encode using single thread.
328   cfg_.g_threads = 1;
329   init_flags_ = VPX_CODEC_USE_PSNR;
330   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
331   const std::vector<std::string> single_thr_md5 = md5_;
332   md5_.clear();
333
334   // Encode using multiple threads.
335   cfg_.g_threads = threads_;
336   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
337   const std::vector<std::string> multi_thr_md5 = md5_;
338   md5_.clear();
339
340   // Compare to check if two vectors are equal.
341   ASSERT_EQ(single_thr_md5, multi_thr_md5);
342
343   // Part 2: row_mt_mode_ = 0 vs row_mt_mode_ = 1 single thread bit exact test.
344   row_mt_mode_ = 1;
345
346   // Encode using single thread
347   cfg_.g_threads = 1;
348   init_flags_ = VPX_CODEC_USE_PSNR;
349   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
350   std::vector<std::string> row_mt_single_thr_md5 = md5_;
351   md5_.clear();
352
353   ASSERT_EQ(single_thr_md5, row_mt_single_thr_md5);
354
355   // Part 3: Bit exact test with row-mt on
356   // When row_mt_mode_=1 and using >1 threads, the encoder generates bit exact
357   // result.
358   row_mt_mode_ = 1;
359   row_mt_single_thr_md5.clear();
360
361   // Encode using 2 threads.
362   cfg_.g_threads = 2;
363   init_flags_ = VPX_CODEC_USE_PSNR;
364   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
365   row_mt_single_thr_md5 = md5_;
366   md5_.clear();
367
368   // Encode using multiple threads.
369   cfg_.g_threads = threads_;
370   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
371   const std::vector<std::string> row_mt_multi_thr_md5 = md5_;
372   md5_.clear();
373
374   // Compare to check if two vectors are equal.
375   ASSERT_EQ(row_mt_single_thr_md5, row_mt_multi_thr_md5);
376
377   // Part 4: PSNR test with bit_match_mode_ = 0
378   row_mt_mode_ = 1;
379
380   // Encode using single thread.
381   cfg_.g_threads = 1;
382   init_flags_ = VPX_CODEC_USE_PSNR;
383   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
384   const double single_thr_psnr = GetAveragePsnr();
385
386   // Encode using multiple threads.
387   cfg_.g_threads = threads_;
388   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
389   const double multi_thr_psnr = GetAveragePsnr();
390
391   EXPECT_NEAR(single_thr_psnr, multi_thr_psnr, 0.2);
392 }
393
394 INSTANTIATE_TEST_SUITE_P(
395     VP9, VPxFirstPassEncoderThreadTest,
396     ::testing::Combine(
397         ::testing::Values(
398             static_cast<const libvpx_test::CodecFactory *>(&libvpx_test::kVP9)),
399         ::testing::Values(::libvpx_test::kTwoPassGood),
400         ::testing::Range(0, 4)));  // cpu_used
401
402 // Split this into two instantiations so that we can distinguish
403 // between very slow runs ( ie cpu_speed 0 ) vs ones that can be
404 // run nightly by adding Large to the title.
405 INSTANTIATE_TEST_SUITE_P(
406     VP9, VPxEncoderThreadTest,
407     ::testing::Combine(
408         ::testing::Values(
409             static_cast<const libvpx_test::CodecFactory *>(&libvpx_test::kVP9)),
410         ::testing::Values(::libvpx_test::kTwoPassGood,
411                           ::libvpx_test::kOnePassGood,
412                           ::libvpx_test::kRealTime),
413         ::testing::Range(3, 10),   // cpu_used
414         ::testing::Range(0, 3),    // tile_columns
415         ::testing::Range(2, 5)));  // threads
416
417 INSTANTIATE_TEST_SUITE_P(
418     VP9Large, VPxEncoderThreadTest,
419     ::testing::Combine(
420         ::testing::Values(
421             static_cast<const libvpx_test::CodecFactory *>(&libvpx_test::kVP9)),
422         ::testing::Values(::libvpx_test::kTwoPassGood,
423                           ::libvpx_test::kOnePassGood,
424                           ::libvpx_test::kRealTime),
425         ::testing::Range(0, 3),    // cpu_used
426         ::testing::Range(0, 3),    // tile_columns
427         ::testing::Range(2, 5)));  // threads
428
429 }  // namespace