OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Provides a minimal wrapping of the Blink image decoders. Used to perform | 5 // Provides a minimal wrapping of the Blink image decoders. Used to perform |
6 // a non-threaded, memory-to-memory image decode using micro second accuracy | 6 // a non-threaded, memory-to-memory image decode using micro second accuracy |
7 // clocks to measure image decode time. Optionally applies color correction | 7 // clocks to measure image decode time. Optionally applies color correction |
8 // during image decoding on supported platforms (default off). Usage: | 8 // during image decoding on supported platforms (default off). Usage: |
scroggo_chromium
2017/05/15 15:59:04
Add comments about using --raster
cblume
2017/05/19 16:47:58
Done.
| |
9 // | 9 // |
10 // % ninja -C out/Release image_decode_bench && | 10 // % ninja -C out/Release image_decode_bench && |
11 // ./out/Release/image_decode_bench file [iterations] | 11 // ./out/Release/image_decode_bench file [iterations] |
12 // | 12 // |
13 // TODO(noel): Consider adding md5 checksum support to WTF. Use it to compute | 13 // TODO(noel): Consider adding md5 checksum support to WTF. Use it to compute |
14 // the decoded image frame md5 and output that value. | 14 // the decoded image frame md5 and output that value. |
15 // | 15 // |
16 // TODO(noel): Consider integrating this tool in Chrome telemetry for realz, | 16 // TODO(noel): Consider integrating this tool in Chrome telemetry for realz, |
17 // using the image corpii used to assess Blink image decode performance. Refer | 17 // using the image corpii used to assess Blink image decode performance. Refer |
18 // to http://crbug.com/398235#c103 and http://crbug.com/258324#c5 | 18 // to http://crbug.com/398235#c103 and http://crbug.com/258324#c5 |
19 | 19 |
20 #include <memory> | 20 #include <memory> |
21 #include <vector> | |
21 #include "base/command_line.h" | 22 #include "base/command_line.h" |
22 #include "platform/SharedBuffer.h" | 23 #include "platform/SharedBuffer.h" |
23 #include "platform/image-decoders/ImageDecoder.h" | 24 #include "platform/image-decoders/ImageDecoder.h" |
24 #include "platform/wtf/PassRefPtr.h" | 25 #include "platform/wtf/PassRefPtr.h" |
25 #include "platform/wtf/PtrUtil.h" | 26 #include "platform/wtf/PtrUtil.h" |
26 #include "public/platform/Platform.h" | 27 #include "public/platform/Platform.h" |
28 #include "third_party/skia/include/core/SkCanvas.h" | |
29 #include "third_party/skia/include/core/SkSurface.h" | |
27 #include "ui/gfx/test/icc_profiles.h" | 30 #include "ui/gfx/test/icc_profiles.h" |
28 | 31 |
29 #if defined(_WIN32) | 32 #if defined(_WIN32) |
30 #include <mmsystem.h> | 33 #include <mmsystem.h> |
31 #include <sys/stat.h> | 34 #include <sys/stat.h> |
32 #include <time.h> | 35 #include <time.h> |
33 #define stat(x, y) _stat(x, y) | 36 #define stat(x, y) _stat(x, y) |
34 typedef struct _stat sttype; | 37 typedef struct _stat sttype; |
35 #else | 38 #else |
36 #include <sys/stat.h> | 39 #include <sys/stat.h> |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
197 WrapArrayUnique(new unsigned char[file_size]); | 200 WrapArrayUnique(new unsigned char[file_size]); |
198 if (file_size != fread(buffer.get(), 1, file_size, fp)) { | 201 if (file_size != fread(buffer.get(), 1, file_size, fp)) { |
199 fprintf(stderr, "Error reading file %s\n", file_name); | 202 fprintf(stderr, "Error reading file %s\n", file_name); |
200 exit(2); | 203 exit(2); |
201 } | 204 } |
202 | 205 |
203 fclose(fp); | 206 fclose(fp); |
204 return SharedBuffer::Create(buffer.get(), file_size); | 207 return SharedBuffer::Create(buffer.get(), file_size); |
205 } | 208 } |
206 | 209 |
207 bool DecodeImageData(SharedBuffer* data, | 210 void Print1DResults(const std::vector<double>& timings) { |
scroggo_chromium
2017/05/15 15:59:04
I suppose you put this here to keep it near Print2
cblume
2017/05/19 16:47:58
Done.
| |
208 bool color_correction, | 211 for (double iteration_time : timings) { |
209 size_t packet_size) { | 212 printf("%f,", iteration_time); |
210 std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create( | 213 } |
211 data, true, ImageDecoder::kAlphaPremultiplied, | 214 printf("\n"); |
212 color_correction ? ColorBehavior::TransformToTargetForTesting() | 215 } |
213 : ColorBehavior::Ignore()); | |
214 if (!packet_size) { | |
215 bool all_data_received = true; | |
216 decoder->SetData(data, all_data_received); | |
217 | 216 |
218 int frame_count = decoder->FrameCount(); | 217 // The outer vector represents multiple iterations through the image. |
scroggo_chromium
2017/05/15 15:59:04
This comment appears several times. It makes me th
cblume
2017/05/19 16:47:58
Yeah. I had tried to come up with a typedef / usin
| |
219 for (int i = 0; i < frame_count; ++i) { | 218 // The inner vector represents individual frames in an animation. |
220 if (!decoder->FrameBufferAtIndex(i)) | 219 void Print2DResults(const std::vector<std::vector<double>>& timings) { |
221 return false; | 220 for (const std::vector<double>& iteration : timings) { |
221 for (double frame_time : iteration) { | |
222 printf("%f,", frame_time); | |
222 } | 223 } |
224 printf("\n"); | |
225 } | |
226 printf("\n"); | |
227 } | |
223 | 228 |
224 return !decoder->Failed(); | 229 void TimeDecode(ImageDecoder* decoder, SharedBuffer* data, size_t iterations) { |
230 bool all_data_received = true; | |
231 decoder->SetData(data, all_data_received); | |
232 | |
233 size_t frame_count = decoder->FrameCount(); | |
234 | |
235 // The outer vector represents multiple iterations through the image. | |
236 // The inner vector represents individual frames in an animation. | |
237 std::vector<std::vector<double>> timings( | |
238 iterations, std::vector<double>(frame_count, 0.0)); | |
239 | |
240 for (size_t iteration = 0; iteration < iterations; ++iteration) { | |
241 for (size_t frame_index = 0; frame_index < frame_count; ++frame_index) { | |
242 double start_time = GetCurrentTime(); | |
243 ImageFrame* frame = decoder->FrameBufferAtIndex(frame_index); | |
244 double elapsed_time = GetCurrentTime() - start_time; | |
245 if (!frame) { | |
scroggo_chromium
2017/05/15 15:59:04
As I've stated elsewhere, I don't think this can e
cblume
2017/05/19 16:47:58
Done.
| |
246 return; | |
247 } | |
248 timings[iteration][frame_index] = elapsed_time; | |
249 } | |
225 } | 250 } |
226 | 251 |
252 Print2DResults(timings); | |
253 } | |
254 | |
255 // This function mimics what actually happens in Chromium. | |
scroggo_chromium
2017/05/15 15:59:04
There are different paths that result in a decode,
cblume
2017/05/19 16:47:58
Done.
| |
256 void TimePacketedDecode(ImageDecoder* decoder, | |
257 SharedBuffer* data, | |
258 size_t packet_size, | |
259 size_t iterations) { | |
260 // Find total frame count. | |
261 // Doing this requires a decoder with full data (no packet size). | |
262 bool all_data_received = true; | |
scroggo_chromium
2017/05/15 15:59:04
This variable ends up being shadowed by a variable
cblume
2017/05/19 16:47:58
Right, I only have this bool for readability. It c
scroggo_chromium
2017/05/19 21:04:15
Ah, I prefer not having a named bool, but it makes
cblume
2017/05/19 21:47:38
I agree that I prefer the enum.
I'll leave it for
| |
263 decoder->SetData(data, all_data_received); | |
264 size_t total_frame_count = decoder->FrameCount(); | |
265 | |
266 // The outer vector represents multiple iterations through the image. | |
267 // The inner vector represents individual frames in an animation. | |
268 std::vector<std::vector<double>> timings( | |
269 iterations, std::vector<double>(total_frame_count, 0.0)); | |
270 | |
271 // Fill the buffer with only some data (|packet_size|), decode all the frames | |
272 // we can with that data, and then repeat -- filling with another packet. | |
227 RefPtr<SharedBuffer> packet_data = SharedBuffer::Create(); | 273 RefPtr<SharedBuffer> packet_data = SharedBuffer::Create(); |
228 size_t position = 0; | 274 size_t position = 0; |
229 size_t next_frame_to_decode = 0; | 275 size_t next_frame_to_decode = 0; |
230 while (true) { | 276 while (true) { |
231 const char* packet; | 277 const char* packet; |
232 size_t length = data->GetSomeData(packet, position); | 278 size_t length = data->GetSomeData(packet, position); |
233 | 279 |
234 length = std::min(length, packet_size); | 280 length = std::min(length, packet_size); |
235 packet_data->Append(packet, length); | 281 packet_data->Append(packet, length); |
236 position += length; | 282 position += length; |
237 | 283 |
238 bool all_data_received = position == data->size(); | 284 bool all_data_received = position == data->size(); |
285 | |
239 size_t frame_count = decoder->FrameCount(); | 286 size_t frame_count = decoder->FrameCount(); |
scroggo_chromium
2017/05/15 15:59:04
Since you're using the same decoder as above, I do
cblume
2017/05/19 16:47:58
Oh, you're right.
I want to create a new decoder h
| |
240 for (; next_frame_to_decode < frame_count; ++next_frame_to_decode) { | 287 for (size_t iteration = 0; iteration < iterations; ++iteration) { |
241 decoder->SetData(packet_data.Get(), all_data_received); | 288 for (; next_frame_to_decode < frame_count; ++next_frame_to_decode) { |
242 ImageFrame* frame = decoder->FrameBufferAtIndex(next_frame_to_decode); | 289 decoder->SetData(packet_data.Get(), all_data_received); |
243 if (frame->GetStatus() != ImageFrame::kFrameComplete) | 290 double start_time = GetCurrentTime(); |
244 break; | 291 ImageFrame* frame = decoder->FrameBufferAtIndex(next_frame_to_decode); |
245 decoder->SetData(PassRefPtr<SegmentReader>(nullptr), false); | 292 double elapsed_time = GetCurrentTime() - start_time; |
246 decoder->ClearCacheExceptFrame(next_frame_to_decode); | 293 if (frame->GetStatus() != ImageFrame::kFrameComplete) |
294 break; | |
295 timings[iteration][next_frame_to_decode] = elapsed_time; | |
296 decoder->SetData(PassRefPtr<SegmentReader>(nullptr), false); | |
297 decoder->ClearCacheExceptFrame(next_frame_to_decode); | |
298 } | |
247 } | 299 } |
248 | 300 |
249 if (all_data_received || decoder->Failed()) | 301 if (all_data_received || decoder->Failed()) |
250 break; | 302 return; |
251 } | 303 } |
252 | 304 |
253 return !decoder->Failed(); | 305 Print2DResults(timings); |
306 } | |
307 | |
308 void TimeRaster(ImageDecoder* decoder, SharedBuffer* data, size_t iterations) { | |
309 // Decode first frame | |
scroggo_chromium
2017/05/15 15:59:04
I'm guilty of this, too, but try to keep your comm
cblume
2017/05/19 16:47:58
Done.
| |
310 const bool all_data_received = true; | |
311 decoder->SetData(data, all_data_received); | |
312 ImageFrame* frame = decoder->FrameBufferAtIndex(0); | |
313 | |
314 // Create raster target | |
315 const auto size = decoder->Size(); | |
316 auto surface = SkSurface::MakeRasterN32Premul(size.Width(), size.Height()); | |
317 auto canvas = surface->getCanvas(); | |
318 | |
319 // Time raster iterations | |
320 std::vector<double> timings(iterations); | |
321 for (size_t iteration = 0; iteration < iterations; ++iteration) { | |
322 double start_time = GetCurrentTime(); | |
323 canvas->drawBitmap(frame->Bitmap(), 0, 0, nullptr); | |
324 double elapsed_time = GetCurrentTime() - start_time; | |
325 timings[iteration] = elapsed_time; | |
326 } | |
327 | |
328 Print1DResults(timings); | |
254 } | 329 } |
255 | 330 |
256 } // namespace | 331 } // namespace |
257 | 332 |
258 int Main(int argc, char* argv[]) { | 333 int Main(int argc, char* argv[]) { |
259 base::CommandLine::Init(argc, argv); | 334 base::CommandLine::Init(argc, argv); |
260 | |
261 // If the platform supports color correction, allow it to be controlled. | 335 // If the platform supports color correction, allow it to be controlled. |
262 | 336 |
263 bool apply_color_correction = false; | 337 bool apply_color_correction = false; |
264 | |
265 if (argc >= 2 && strcmp(argv[1], "--color-correct") == 0) { | 338 if (argc >= 2 && strcmp(argv[1], "--color-correct") == 0) { |
266 apply_color_correction = (--argc, ++argv, true); | 339 --argc; |
340 ++argv; | |
341 apply_color_correction = true; | |
267 gfx::ICCProfile profile = gfx::ICCProfileForTestingColorSpin(); | 342 gfx::ICCProfile profile = gfx::ICCProfileForTestingColorSpin(); |
268 ColorBehavior::SetGlobalTargetColorProfile(profile); | 343 ColorBehavior::SetGlobalTargetColorProfile(profile); |
269 } | 344 } |
270 | 345 |
346 bool time_raster = false; | |
347 if (argc >= 2 && strcmp(argv[1], "--raster") == 0) { | |
348 --argc; | |
349 ++argv; | |
350 time_raster = true; | |
351 } | |
352 | |
271 if (argc < 2) { | 353 if (argc < 2) { |
272 fprintf(stderr, | 354 fprintf(stderr, |
273 "Usage: %s [--color-correct] file [iterations] [packetSize]\n", | 355 "Usage: %s [--color-correct] [--raster] file [iterations] " |
356 "[packetSize]\n", | |
274 argv[0]); | 357 argv[0]); |
275 exit(1); | 358 exit(1); |
276 } | 359 } |
277 | 360 |
278 // Control decode bench iterations and packet size. | 361 // Control decode bench iterations and packet size. |
279 | 362 |
280 size_t iterations = 1; | 363 size_t iterations = 1; |
281 if (argc >= 3) { | 364 if (argc >= 3) { |
282 char* end = 0; | 365 char* end = 0; |
283 iterations = strtol(argv[2], &end, 10); | 366 iterations = strtol(argv[2], &end, 10); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
315 // segments into one, contiguous block of memory. | 398 // segments into one, contiguous block of memory. |
316 | 399 |
317 RefPtr<SharedBuffer> data = ReadFile(argv[1]); | 400 RefPtr<SharedBuffer> data = ReadFile(argv[1]); |
318 if (!data.Get() || !data->size()) { | 401 if (!data.Get() || !data->size()) { |
319 fprintf(stderr, "Error reading image data from [%s]\n", argv[1]); | 402 fprintf(stderr, "Error reading image data from [%s]\n", argv[1]); |
320 exit(2); | 403 exit(2); |
321 } | 404 } |
322 | 405 |
323 data->Data(); | 406 data->Data(); |
324 | 407 |
325 // Warm-up: throw out the first iteration for more consistent results. | |
scroggo_chromium
2017/05/15 15:59:04
Is this no longer necessary?
cblume
2017/05/19 16:47:58
If you only do 1 iteration then it'll pretty much
| |
326 | |
327 if (!DecodeImageData(data.Get(), apply_color_correction, packet_size)) { | |
328 fprintf(stderr, "Image decode failed [%s]\n", argv[1]); | |
329 exit(3); | |
330 } | |
331 | |
332 // Image decode bench for iterations. | 408 // Image decode bench for iterations. |
333 | 409 |
334 double total_time = 0.0; | 410 std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create( |
335 | 411 data, true, ImageDecoder::kAlphaPremultiplied, |
336 for (size_t i = 0; i < iterations; ++i) { | 412 apply_color_correction ? ColorBehavior::TransformToTargetForTesting() |
337 double start_time = GetCurrentTime(); | 413 : ColorBehavior::Ignore()); |
338 bool decoded = | 414 if (time_raster) { |
339 DecodeImageData(data.Get(), apply_color_correction, packet_size); | 415 blink::TimeRaster(decoder.get(), data.Get(), iterations); |
340 double elapsed_time = GetCurrentTime() - start_time; | 416 } else { |
341 total_time += elapsed_time; | 417 if (packet_size) { |
342 if (!decoded) { | 418 blink::TimePacketedDecode(decoder.get(), data.Get(), packet_size, |
343 fprintf(stderr, "Image decode failed [%s]\n", argv[1]); | 419 iterations); |
344 exit(3); | 420 } else { |
421 blink::TimeDecode(decoder.get(), data.Get(), iterations); | |
345 } | 422 } |
346 } | 423 } |
347 | 424 |
348 // Results to stdout. | |
349 | |
350 double average_time = total_time / static_cast<double>(iterations); | |
351 printf("%f %f\n", total_time, average_time); | |
scroggo_chromium
2017/05/15 15:59:04
Now that we do not print the average, is the expec
cblume
2017/05/19 16:47:58
I want to be able to visualize the data. It also l
scroggo_chromium
2017/05/19 21:04:15
I guess what I'm getting at is, what do you do to
cblume
2017/05/19 21:47:38
I'm printing out the data in a way that makes it c
| |
352 return 0; | 425 return 0; |
353 } | 426 } |
354 | 427 |
355 } // namespace blink | 428 } // namespace blink |
356 | 429 |
357 int main(int argc, char* argv[]) { | 430 int main(int argc, char* argv[]) { |
358 return blink::Main(argc, argv); | 431 return blink::Main(argc, argv); |
359 } | 432 } |
OLD | NEW |