OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... |
21 #include "vp9/decoder/vp9_thread.h" | 21 #include "vp9/decoder/vp9_thread.h" |
22 | 22 |
23 namespace { | 23 namespace { |
24 | 24 |
25 using std::string; | 25 using std::string; |
26 | 26 |
27 class VP9WorkerThreadTest : public ::testing::TestWithParam<bool> { | 27 class VP9WorkerThreadTest : public ::testing::TestWithParam<bool> { |
28 protected: | 28 protected: |
29 virtual ~VP9WorkerThreadTest() {} | 29 virtual ~VP9WorkerThreadTest() {} |
30 virtual void SetUp() { | 30 virtual void SetUp() { |
31 vp9_worker_init(&worker_); | 31 vp9_get_worker_interface()->init(&worker_); |
32 } | 32 } |
33 | 33 |
34 virtual void TearDown() { | 34 virtual void TearDown() { |
35 vp9_worker_end(&worker_); | 35 vp9_get_worker_interface()->end(&worker_); |
36 } | 36 } |
37 | 37 |
38 VP9Worker worker_; | 38 VP9Worker worker_; |
39 }; | 39 }; |
40 | 40 |
41 int ThreadHook(void* data, void* return_value) { | 41 int ThreadHook(void* data, void* return_value) { |
42 int* const hook_data = reinterpret_cast<int*>(data); | 42 int* const hook_data = reinterpret_cast<int*>(data); |
43 *hook_data = 5; | 43 *hook_data = 5; |
44 return *reinterpret_cast<int*>(return_value); | 44 return *reinterpret_cast<int*>(return_value); |
45 } | 45 } |
46 | 46 |
47 TEST_P(VP9WorkerThreadTest, HookSuccess) { | 47 TEST_P(VP9WorkerThreadTest, HookSuccess) { |
48 EXPECT_NE(vp9_worker_sync(&worker_), 0); // should be a no-op. | 48 // should be a no-op. |
| 49 EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0); |
49 | 50 |
50 for (int i = 0; i < 2; ++i) { | 51 for (int i = 0; i < 2; ++i) { |
51 EXPECT_NE(vp9_worker_reset(&worker_), 0); | 52 EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0); |
52 | 53 |
53 int hook_data = 0; | 54 int hook_data = 0; |
54 int return_value = 1; // return successfully from the hook | 55 int return_value = 1; // return successfully from the hook |
55 worker_.hook = ThreadHook; | 56 worker_.hook = ThreadHook; |
56 worker_.data1 = &hook_data; | 57 worker_.data1 = &hook_data; |
57 worker_.data2 = &return_value; | 58 worker_.data2 = &return_value; |
58 | 59 |
59 const bool synchronous = GetParam(); | 60 const bool synchronous = GetParam(); |
60 if (synchronous) { | 61 if (synchronous) { |
61 vp9_worker_execute(&worker_); | 62 vp9_get_worker_interface()->execute(&worker_); |
62 } else { | 63 } else { |
63 vp9_worker_launch(&worker_); | 64 vp9_get_worker_interface()->launch(&worker_); |
64 } | 65 } |
65 EXPECT_NE(vp9_worker_sync(&worker_), 0); | 66 EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0); |
66 EXPECT_FALSE(worker_.had_error); | 67 EXPECT_FALSE(worker_.had_error); |
67 EXPECT_EQ(5, hook_data); | 68 EXPECT_EQ(5, hook_data); |
68 | 69 |
69 EXPECT_NE(vp9_worker_sync(&worker_), 0); // should be a no-op. | 70 // should be a no-op. |
| 71 EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0); |
70 } | 72 } |
71 } | 73 } |
72 | 74 |
73 TEST_P(VP9WorkerThreadTest, HookFailure) { | 75 TEST_P(VP9WorkerThreadTest, HookFailure) { |
74 EXPECT_NE(vp9_worker_reset(&worker_), 0); | 76 EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0); |
75 | 77 |
76 int hook_data = 0; | 78 int hook_data = 0; |
77 int return_value = 0; // return failure from the hook | 79 int return_value = 0; // return failure from the hook |
78 worker_.hook = ThreadHook; | 80 worker_.hook = ThreadHook; |
79 worker_.data1 = &hook_data; | 81 worker_.data1 = &hook_data; |
80 worker_.data2 = &return_value; | 82 worker_.data2 = &return_value; |
81 | 83 |
82 const bool synchronous = GetParam(); | 84 const bool synchronous = GetParam(); |
83 if (synchronous) { | 85 if (synchronous) { |
84 vp9_worker_execute(&worker_); | 86 vp9_get_worker_interface()->execute(&worker_); |
85 } else { | 87 } else { |
86 vp9_worker_launch(&worker_); | 88 vp9_get_worker_interface()->launch(&worker_); |
87 } | 89 } |
88 EXPECT_FALSE(vp9_worker_sync(&worker_)); | 90 EXPECT_FALSE(vp9_get_worker_interface()->sync(&worker_)); |
89 EXPECT_EQ(1, worker_.had_error); | 91 EXPECT_EQ(1, worker_.had_error); |
90 | 92 |
91 // Ensure _reset() clears the error and _launch() can be called again. | 93 // Ensure _reset() clears the error and _launch() can be called again. |
92 return_value = 1; | 94 return_value = 1; |
93 EXPECT_NE(vp9_worker_reset(&worker_), 0); | 95 EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0); |
94 EXPECT_FALSE(worker_.had_error); | 96 EXPECT_FALSE(worker_.had_error); |
95 vp9_worker_launch(&worker_); | 97 vp9_get_worker_interface()->launch(&worker_); |
96 EXPECT_NE(vp9_worker_sync(&worker_), 0); | 98 EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0); |
97 EXPECT_FALSE(worker_.had_error); | 99 EXPECT_FALSE(worker_.had_error); |
98 } | 100 } |
99 | 101 |
| 102 TEST(VP9WorkerThreadTest, TestInterfaceAPI) { |
| 103 EXPECT_EQ(0, vp9_set_worker_interface(NULL)); |
| 104 EXPECT_TRUE(vp9_get_worker_interface() != NULL); |
| 105 for (int i = 0; i < 6; ++i) { |
| 106 VP9WorkerInterface winterface = *vp9_get_worker_interface(); |
| 107 switch (i) { |
| 108 default: |
| 109 case 0: winterface.init = NULL; break; |
| 110 case 1: winterface.reset = NULL; break; |
| 111 case 2: winterface.sync = NULL; break; |
| 112 case 3: winterface.launch = NULL; break; |
| 113 case 4: winterface.execute = NULL; break; |
| 114 case 5: winterface.end = NULL; break; |
| 115 } |
| 116 EXPECT_EQ(0, vp9_set_worker_interface(&winterface)); |
| 117 } |
| 118 } |
| 119 |
100 // ----------------------------------------------------------------------------- | 120 // ----------------------------------------------------------------------------- |
101 // Multi-threaded decode tests | 121 // Multi-threaded decode tests |
102 | 122 |
103 #if CONFIG_WEBM_IO | 123 #if CONFIG_WEBM_IO |
| 124 struct FileList { |
| 125 const char *name; |
| 126 const char *expected_md5; |
| 127 }; |
| 128 |
104 // Decodes |filename| with |num_threads|. Returns the md5 of the decoded frames. | 129 // Decodes |filename| with |num_threads|. Returns the md5 of the decoded frames. |
105 string DecodeFile(const string& filename, int num_threads) { | 130 string DecodeFile(const string& filename, int num_threads) { |
106 libvpx_test::WebMVideoSource video(filename); | 131 libvpx_test::WebMVideoSource video(filename); |
107 video.Init(); | 132 video.Init(); |
108 | 133 |
109 vpx_codec_dec_cfg_t cfg = {0}; | 134 vpx_codec_dec_cfg_t cfg = {0}; |
110 cfg.threads = num_threads; | 135 cfg.threads = num_threads; |
111 libvpx_test::VP9Decoder decoder(cfg, 0); | 136 libvpx_test::VP9Decoder decoder(cfg, 0); |
112 | 137 |
113 libvpx_test::MD5 md5; | 138 libvpx_test::MD5 md5; |
114 for (video.Begin(); video.cxdata(); video.Next()) { | 139 for (video.Begin(); video.cxdata(); video.Next()) { |
115 const vpx_codec_err_t res = | 140 const vpx_codec_err_t res = |
116 decoder.DecodeFrame(video.cxdata(), video.frame_size()); | 141 decoder.DecodeFrame(video.cxdata(), video.frame_size()); |
117 if (res != VPX_CODEC_OK) { | 142 if (res != VPX_CODEC_OK) { |
118 EXPECT_EQ(VPX_CODEC_OK, res) << decoder.DecodeError(); | 143 EXPECT_EQ(VPX_CODEC_OK, res) << decoder.DecodeError(); |
119 break; | 144 break; |
120 } | 145 } |
121 | 146 |
122 libvpx_test::DxDataIterator dec_iter = decoder.GetDxData(); | 147 libvpx_test::DxDataIterator dec_iter = decoder.GetDxData(); |
123 const vpx_image_t *img = NULL; | 148 const vpx_image_t *img = NULL; |
124 | 149 |
125 // Get decompressed data | 150 // Get decompressed data |
126 while ((img = dec_iter.Next())) { | 151 while ((img = dec_iter.Next())) { |
127 md5.Add(img); | 152 md5.Add(img); |
128 } | 153 } |
129 } | 154 } |
130 return string(md5.Get()); | 155 return string(md5.Get()); |
131 } | 156 } |
132 | 157 |
133 TEST(VP9DecodeMTTest, MTDecode) { | 158 void DecodeFiles(const FileList files[]) { |
134 // no tiles or frame parallel; this exercises loop filter threading. | 159 for (const FileList *iter = files; iter->name != NULL; ++iter) { |
135 EXPECT_STREQ("b35a1b707b28e82be025d960aba039bc", | 160 SCOPED_TRACE(iter->name); |
136 DecodeFile("vp90-2-03-size-226x226.webm", 2).c_str()); | 161 for (int t = 2; t <= 8; ++t) { |
| 162 EXPECT_EQ(iter->expected_md5, DecodeFile(iter->name, t)) |
| 163 << "threads = " << t; |
| 164 } |
| 165 } |
137 } | 166 } |
138 | 167 |
139 TEST(VP9DecodeMTTest, MTDecode2) { | 168 // Trivial serialized thread worker interface implementation. |
140 static const struct { | 169 // Note any worker that requires synchronization between other workers will |
141 const char *name; | 170 // hang. |
142 const char *expected_md5; | 171 namespace impl { |
143 } files[] = { | 172 |
| 173 void Init(VP9Worker *const worker) { memset(worker, 0, sizeof(*worker)); } |
| 174 int Reset(VP9Worker *const /*worker*/) { return 1; } |
| 175 int Sync(VP9Worker *const worker) { return !worker->had_error; } |
| 176 |
| 177 void Execute(VP9Worker *const worker) { |
| 178 worker->had_error |= worker->hook(worker->data1, worker->data2); |
| 179 } |
| 180 |
| 181 void Launch(VP9Worker *const worker) { Execute(worker); } |
| 182 void End(VP9Worker *const /*worker*/) {} |
| 183 |
| 184 } // namespace impl |
| 185 |
| 186 TEST(VP9WorkerThreadTest, TestSerialInterface) { |
| 187 static const VP9WorkerInterface serial_interface = { |
| 188 impl::Init, impl::Reset, impl::Sync, impl::Launch, impl::Execute, impl::End |
| 189 }; |
| 190 // TODO(jzern): Avoid using a file that will use the row-based thread |
| 191 // loopfilter, with the simple serialized implementation it will hang. This is |
| 192 // due to its expectation that rows will be run in parallel as they wait on |
| 193 // progress in the row above before proceeding. |
| 194 static const char expected_md5[] = "b35a1b707b28e82be025d960aba039bc"; |
| 195 static const char filename[] = "vp90-2-03-size-226x226.webm"; |
| 196 VP9WorkerInterface default_interface = *vp9_get_worker_interface(); |
| 197 |
| 198 EXPECT_NE(vp9_set_worker_interface(&serial_interface), 0); |
| 199 EXPECT_EQ(expected_md5, DecodeFile(filename, 2)); |
| 200 |
| 201 // Reset the interface. |
| 202 EXPECT_NE(vp9_set_worker_interface(&default_interface), 0); |
| 203 EXPECT_EQ(expected_md5, DecodeFile(filename, 2)); |
| 204 } |
| 205 |
| 206 TEST(VP9DecodeMultiThreadedTest, Decode) { |
| 207 // no tiles or frame parallel; this exercises loop filter threading. |
| 208 EXPECT_EQ("b35a1b707b28e82be025d960aba039bc", |
| 209 DecodeFile("vp90-2-03-size-226x226.webm", 2)); |
| 210 } |
| 211 |
| 212 TEST(VP9DecodeMultiThreadedTest, Decode2) { |
| 213 static const FileList files[] = { |
144 { "vp90-2-08-tile_1x2_frame_parallel.webm", | 214 { "vp90-2-08-tile_1x2_frame_parallel.webm", |
145 "68ede6abd66bae0a2edf2eb9232241b6" }, | 215 "68ede6abd66bae0a2edf2eb9232241b6" }, |
146 { "vp90-2-08-tile_1x4_frame_parallel.webm", | 216 { "vp90-2-08-tile_1x4_frame_parallel.webm", |
147 "368ebc6ebf3a5e478d85b2c3149b2848" }, | 217 "368ebc6ebf3a5e478d85b2c3149b2848" }, |
148 { "vp90-2-08-tile_1x8_frame_parallel.webm", | 218 { "vp90-2-08-tile_1x8_frame_parallel.webm", |
149 "17e439da2388aff3a0f69cb22579c6c1" }, | 219 "17e439da2388aff3a0f69cb22579c6c1" }, |
| 220 { NULL, NULL } |
150 }; | 221 }; |
151 | 222 |
152 for (int i = 0; i < static_cast<int>(sizeof(files) / sizeof(files[0])); ++i) { | 223 DecodeFiles(files); |
153 for (int t = 2; t <= 8; ++t) { | |
154 EXPECT_STREQ(files[i].expected_md5, DecodeFile(files[i].name, t).c_str()) | |
155 << "threads = " << t; | |
156 } | |
157 } | |
158 } | 224 } |
159 | 225 |
160 // Test tile quantity changes within one file. | 226 // Test tile quantity changes within one file. |
161 TEST(VP9DecodeMTTest, MTDecode3) { | 227 TEST(VP9DecodeMultiThreadedTest, Decode3) { |
162 static const struct { | 228 static const FileList files[] = { |
163 const char *name; | |
164 const char *expected_md5; | |
165 } files[] = { | |
166 { "vp90-2-14-resize-fp-tiles-1-16.webm", | 229 { "vp90-2-14-resize-fp-tiles-1-16.webm", |
167 "0cd5e632c326297e975f38949c31ea94" }, | 230 "0cd5e632c326297e975f38949c31ea94" }, |
168 { "vp90-2-14-resize-fp-tiles-1-2-4-8-16.webm", | 231 { "vp90-2-14-resize-fp-tiles-1-2-4-8-16.webm", |
169 "5c78a96a42e7f4a4f6b2edcdb791e44c" }, | 232 "5c78a96a42e7f4a4f6b2edcdb791e44c" }, |
170 { "vp90-2-14-resize-fp-tiles-1-2.webm", | 233 { "vp90-2-14-resize-fp-tiles-1-2.webm", |
171 "e030450ae85c3277be2a418769df98e2" }, | 234 "e030450ae85c3277be2a418769df98e2" }, |
172 { "vp90-2-14-resize-fp-tiles-1-4.webm", | 235 { "vp90-2-14-resize-fp-tiles-1-4.webm", |
173 "312eed4e2b64eb7a4e7f18916606a430" }, | 236 "312eed4e2b64eb7a4e7f18916606a430" }, |
174 { "vp90-2-14-resize-fp-tiles-16-1.webm", | 237 { "vp90-2-14-resize-fp-tiles-16-1.webm", |
175 "1755c16d8af16a9cb3fe7338d90abe52" }, | 238 "1755c16d8af16a9cb3fe7338d90abe52" }, |
(...skipping 24 matching lines...) Expand all Loading... |
200 { "vp90-2-14-resize-fp-tiles-4-8.webm", | 263 { "vp90-2-14-resize-fp-tiles-4-8.webm", |
201 "7f76d96036382f45121e3d5aa6f8ec52" }, | 264 "7f76d96036382f45121e3d5aa6f8ec52" }, |
202 { "vp90-2-14-resize-fp-tiles-8-16.webm", | 265 { "vp90-2-14-resize-fp-tiles-8-16.webm", |
203 "76a43fcdd7e658542913ea43216ec55d" }, | 266 "76a43fcdd7e658542913ea43216ec55d" }, |
204 { "vp90-2-14-resize-fp-tiles-8-1.webm", | 267 { "vp90-2-14-resize-fp-tiles-8-1.webm", |
205 "8e3fbe89486ca60a59299dea9da91378" }, | 268 "8e3fbe89486ca60a59299dea9da91378" }, |
206 { "vp90-2-14-resize-fp-tiles-8-2.webm", | 269 { "vp90-2-14-resize-fp-tiles-8-2.webm", |
207 "ae96f21f21b6370cc0125621b441fc52" }, | 270 "ae96f21f21b6370cc0125621b441fc52" }, |
208 { "vp90-2-14-resize-fp-tiles-8-4.webm", | 271 { "vp90-2-14-resize-fp-tiles-8-4.webm", |
209 "3eb4f24f10640d42218f7fd7b9fd30d4" }, | 272 "3eb4f24f10640d42218f7fd7b9fd30d4" }, |
| 273 { NULL, NULL } |
210 }; | 274 }; |
211 | 275 |
212 for (int i = 0; i < static_cast<int>(sizeof(files) / sizeof(files[0])); ++i) { | 276 DecodeFiles(files); |
213 for (int t = 2; t <= 8; ++t) { | |
214 EXPECT_STREQ(files[i].expected_md5, DecodeFile(files[i].name, t).c_str()) | |
215 << "threads = " << t; | |
216 } | |
217 } | |
218 } | 277 } |
219 #endif // CONFIG_WEBM_IO | 278 #endif // CONFIG_WEBM_IO |
220 | 279 |
221 INSTANTIATE_TEST_CASE_P(Synchronous, VP9WorkerThreadTest, ::testing::Bool()); | 280 INSTANTIATE_TEST_CASE_P(Synchronous, VP9WorkerThreadTest, ::testing::Bool()); |
222 | 281 |
223 } // namespace | 282 } // namespace |
OLD | NEW |