Chromium Code Reviews| 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 |