blob: 455e5bf46d176496210ac1feb73caba5bfcaa1a1 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Provides a minimal wrapping of the Blink image decoders. Used to perform
// a non-threaded, memory-to-memory image decode using micro second accuracy
// clocks to measure image decode time. Basic usage:
//
// % ninja -C out/Release image_decode_bench &&
// ./out/Release/image_decode_bench file [iterations]
//
// TODO(noel): Consider adding md5 checksum support to WTF. Use it to compute
// the decoded image frame md5 and output that value.
//
// TODO(noel): Consider integrating this tool in Chrome telemetry for realz,
// using the image corpora used to assess Blink image decode performance. See
// http://crbug.com/398235#c103 and http://crbug.com/258324#c5
#include <fstream>
#include "base/command_line.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_loop.h"
#include "mojo/edk/embedder/embedder.h"
#include "platform/SharedBuffer.h"
#include "platform/image-decoders/ImageDecoder.h"
#include "platform/wtf/Time.h"
#include "platform/wtf/Vector.h"
#include "public/platform/Platform.h"
namespace blink {
namespace {
scoped_refptr<SharedBuffer> ReadFile(const char* name) {
std::ifstream file(name, std::ios::in | std::ios::binary);
if (!file) {
fprintf(stderr, "Cannot open file %s\n", name);
exit(2);
}
file.seekg(0, std::ios::end);
size_t file_size = file.tellg();
file.seekg(0, std::ios::beg);
if (!file || !file_size)
return SharedBuffer::Create();
Vector<char> buffer(file_size);
if (!file.read(buffer.data(), file_size)) {
fprintf(stderr, "Error reading file %s\n", name);
exit(2);
}
return SharedBuffer::AdoptVector(buffer);
}
struct ImageMeta {
char* name;
int width;
int height;
int frames;
// Cumulative time in seconds to decode all frames.
double time;
};
void DecodeFailure(ImageMeta* image) {
fprintf(stderr, "Failed to decode image %s\n", image->name);
exit(3);
}
void DecodeImageData(SharedBuffer* data, size_t packet_size, ImageMeta* image) {
std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
data, true, ImageDecoder::kAlphaPremultiplied, ColorBehavior::Ignore());
if (!packet_size) {
auto start = CurrentTimeTicks();
bool all_data_received = true;
decoder->SetData(data, all_data_received);
size_t frame_count = decoder->FrameCount();
for (size_t index = 0; index < frame_count; ++index) {
if (!decoder->DecodeFrameBufferAtIndex(index))
DecodeFailure(image);
}
image->time += (CurrentTimeTicks() - start).InSecondsF();
image->width = decoder->Size().Width();
image->height = decoder->Size().Height();
image->frames = frame_count;
if (!frame_count || decoder->Failed())
DecodeFailure(image);
return;
}
scoped_refptr<SharedBuffer> packet_data = SharedBuffer::Create();
size_t frame_count = 0;
size_t position = 0;
size_t index = 0;
while (true) {
const char* packet;
size_t length = data->GetSomeData(packet, position);
length = std::min(length, packet_size);
packet_data->Append(packet, length);
position += length;
auto start = CurrentTimeTicks();
bool all_data_received = (position >= data->size());
decoder->SetData(packet_data.get(), all_data_received);
frame_count = decoder->FrameCount();
for (; index < frame_count; ++index) {
ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(index);
if (frame->GetStatus() != ImageFrame::kFrameComplete)
DecodeFailure(image);
}
image->time += (CurrentTimeTicks() - start).InSecondsF();
if (all_data_received || decoder->Failed())
break;
}
image->width = decoder->Size().Width();
image->height = decoder->Size().Height();
image->frames = frame_count;
if (!frame_count || decoder->Failed())
DecodeFailure(image);
}
} // namespace
int ImageDecodeBenchMain(int argc, char* argv[]) {
base::CommandLine::Init(argc, argv);
const char* program = argv[0];
if (argc < 2) {
fprintf(stderr, "Usage: %s file [iterations] [packetSize]\n", program);
exit(1);
}
// Control bench decode iterations and packet size.
size_t decode_iterations = 1;
if (argc >= 3) {
char* end = nullptr;
decode_iterations = strtol(argv[2], &end, 10);
if (*end != '\0' || !decode_iterations) {
fprintf(stderr,
"Second argument should be number of iterations. "
"The default is 1. You supplied %s\n",
argv[2]);
exit(1);
}
}
size_t packet_size = 0;
if (argc >= 4) {
char* end = nullptr;
packet_size = strtol(argv[3], &end, 10);
if (*end != '\0') {
fprintf(stderr,
"Third argument should be packet size. Default is "
"0, meaning to decode the entire image in one packet. You "
"supplied %s\n",
argv[3]);
exit(1);
}
}
// Create a web platform. blink::Platform can't be used directly because it
// has a protected constructor.
class WebPlatform : public Platform {};
Platform::Initialize(new WebPlatform());
// Read entire file content to data, and consolidate the SharedBuffer data
// segments into one, contiguous block of memory.
scoped_refptr<SharedBuffer> data = ReadFile(argv[1]);
if (!data.get() || !data->size()) {
fprintf(stderr, "Error reading image %s\n", argv[1]);
exit(2);
}
data->Data();
// Warm-up: throw out the first iteration for more consistent results.
ImageMeta image;
image.name = argv[1];
DecodeImageData(data.get(), packet_size, &image);
// Image decode bench for decode_iterations.
double total_time = 0.0;
for (size_t i = 0; i < decode_iterations; ++i) {
image.time = 0.0;
DecodeImageData(data.get(), packet_size, &image);
total_time += image.time;
}
// Results to stdout.
double average_time = total_time / decode_iterations;
printf("%f %f\n", total_time, average_time);
return 0;
}
} // namespace blink
int main(int argc, char* argv[]) {
base::MessageLoop message_loop;
mojo::edk::Init();
return blink::ImageDecodeBenchMain(argc, argv);
}