Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / gpu / skia_runner / skia_runner.cc
blob9fcf48e7c152934cfec6acaa02e0de5a28554cad
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <iostream>
7 #include "base/at_exit.h"
8 #include "base/command_line.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/files/file_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/time/time.h"
13 #include "gpu/skia_runner/in_process_graphics_system.h"
14 #include "gpu/skia_runner/sk_picture_rasterizer.h"
15 #include "third_party/WebKit/public/platform/WebData.h"
16 #include "third_party/WebKit/public/platform/WebImage.h"
17 #include "third_party/WebKit/public/platform/WebSize.h"
18 #include "third_party/skia/include/core/SkOSFile.h"
19 #include "third_party/skia/include/core/SkPicture.h"
20 #include "third_party/skia/include/core/SkStream.h"
21 #include "ui/gfx/codec/png_codec.h"
23 #if defined(OS_MACOSX)
24 #include "base/mac/scoped_nsautorelease_pool.h"
25 #endif
27 namespace {
29 bool WriteSkImagePNG(const SkImage* image, const base::FilePath& path) {
30 DCHECK(!path.empty());
32 if (!image) {
33 std::cout << "Unable to write empty bitmap for " << path.value() << ".\n";
34 return false;
37 std::string file_path = path.MaybeAsASCII();
38 SkFILEWStream stream(file_path.c_str());
39 if (!stream.isValid()) {
40 std::cout << "Unable to write to " << file_path.c_str() << ".\n";
41 return false;
44 SkImageInfo info = SkImageInfo::Make(image->width(), image->height(),
45 SkColorType::kBGRA_8888_SkColorType,
46 SkAlphaType::kPremul_SkAlphaType);
47 SkImageInfo::MakeN32Premul(image->width(), image->height());
49 const size_t rowBytes = image->width() * sizeof(SkPMColor);
50 std::vector<SkPMColor> pixels(image->width() * image->height());
52 if (image->readPixels(info, pixels.data(), rowBytes, 0, 0)) {
53 std::vector<unsigned char> png_data;
55 if (gfx::PNGCodec::Encode(
56 reinterpret_cast<const unsigned char*>(pixels.data()),
57 gfx::PNGCodec::FORMAT_BGRA,
58 gfx::Size(image->width(), image->height()),
59 static_cast<int>(rowBytes), false,
60 std::vector<gfx::PNGCodec::Comment>(), &png_data)) {
61 if (stream.write(png_data.data(), png_data.size())) {
62 return true;
67 return false;
70 bool onDecode(const void* buffer, size_t size, SkBitmap* bm) {
71 blink::WebData web_data(static_cast<const char*>(buffer), size);
72 blink::WebImage image = blink::WebImage::fromData(web_data, blink::WebSize());
73 if (!image.isNull()) {
74 *bm = image.getSkBitmap();
75 return true;
77 std::cout << "Error decoding image.\n";
78 return false;
81 skia::RefPtr<SkPicture> ReadPicture(const base::FilePath& path) {
82 skia::RefPtr<SkPicture> picture;
83 std::string file_path = path.MaybeAsASCII();
84 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(file_path.c_str()));
85 if (!stream.get()) {
86 std::cout << "Unable to read " << file_path.c_str() << ".\n";
87 return picture;
90 picture = skia::AdoptRef(SkPicture::CreateFromStream(stream.get(), onDecode));
91 if (!picture) {
92 std::cout << "Unable to load " << file_path.c_str()
93 << " as an SkPicture.\n";
95 return picture;
98 base::FilePath MakeDestinationPNGFilename(base::FilePath source_file,
99 base::FilePath destination_folder) {
100 base::FilePath filename = source_file.BaseName().RemoveExtension();
101 filename = filename.AddExtension(FILE_PATH_LITERAL("png"));
102 return destination_folder.AsEndingWithSeparator().Append(filename);
105 std::vector<std::pair<base::FilePath, base::FilePath>> GetSkpsToRasterize(
106 base::FilePath input_path,
107 base::FilePath output_path) {
108 std::vector<std::pair<base::FilePath, base::FilePath>> files;
110 if (base::DirectoryExists(input_path)) {
111 if (!output_path.empty() && !base::DirectoryExists(output_path))
112 return files;
114 base::FilePath::StringType extension = FILE_PATH_LITERAL(".skp");
115 base::FileEnumerator file_iter(input_path, false,
116 base::FileEnumerator::FILES);
117 while (!file_iter.Next().empty()) {
118 if (file_iter.GetInfo().GetName().MatchesExtension(extension)) {
119 base::FilePath skp_file = file_iter.GetInfo().GetName();
120 skp_file = input_path.AsEndingWithSeparator().Append(skp_file);
121 base::FilePath png_file;
122 if (!output_path.empty())
123 png_file = MakeDestinationPNGFilename(skp_file, output_path);
124 files.push_back(std::make_pair(skp_file, png_file));
127 } else {
128 // Single file passed. If the output file is a folder, make a name.
129 if (base::DirectoryExists(output_path))
130 output_path = MakeDestinationPNGFilename(input_path, output_path);
131 files.push_back(std::make_pair(input_path, output_path));
133 return files;
136 int GetSwitchInt(const base::CommandLine* command_line,
137 std::string switch_name,
138 int default_value) {
139 if (command_line->HasSwitch(switch_name)) {
140 std::string value = command_line->GetSwitchValueASCII(switch_name);
141 int result = default_value;
142 if (base::StringToInt(value, &result))
143 return result;
145 return default_value;
148 static const char kHelpMessage[] =
149 "This program renders a skia SKP to a PNG using GPU rasterization via the\n"
150 "command buffer.\n\n"
151 "following command line flags to control its behavior:\n"
152 "\n"
153 " --in-skp=skp[:DIRECTORY_PATH|FILE_PATH]\n"
154 " Input SKP file. If a directory is provided, all SKP files will be\n"
155 " converted.\n"
156 " --out-png=png[:DIRECTORY_PATH|:FILE_PATH]\n"
157 " Output PNG file. If a directory is provided, the SKP filename is "
158 "used.\n\n"
159 " --use-lcd-text\n"
160 " Turn on lcd text rendering.\n"
161 " --use-distance-field-text\n"
162 " Turn on distance field text rendering.\n"
163 " --msaa-sample-count=(0|2|4|8|16)\n"
164 " Turn on multi-sample anti-aliasing.\n"
165 " --use-gl=(desktop|osmesa|egl|swiftshader)\n"
166 " Specify Gl driver. --swiftshader-path required for swiftshader.\n"
167 " --repeat-raster-count=N\n"
168 " Specify the number of times to repeat rasterization for timing.\n";
170 } // namespace anonymous
172 int main(int argc, char** argv) {
173 base::AtExitManager exit_manager;
174 base::CommandLine::Init(argc, argv);
175 #if defined(OS_MACOSX)
176 base::mac::ScopedNSAutoreleasePool autorelease_pool;
177 #endif
178 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
179 base::FilePath input_path(command_line->GetSwitchValuePath("in-skp"));
180 base::FilePath output_path(command_line->GetSwitchValuePath("out-png"));
182 if (input_path.empty()) {
183 std::cout << kHelpMessage;
184 return 0;
187 if (output_path.empty())
188 std::cout << "--out-png path not specified. Timing rasterization only.\n";
190 std::vector<std::pair<base::FilePath, base::FilePath>> files =
191 GetSkpsToRasterize(input_path, output_path);
193 if (files.empty()) {
194 if (!base::DirectoryExists(output_path))
195 std::cout << "You must specify an existing directory using '--out-png'\n";
196 else
197 std::cout << "No skp files found at " << input_path.value() << "\n";
198 return 0;
201 skia_runner::InProcessGraphicsSystem graphics_system;
202 if (!graphics_system.IsSuccessfullyInitialized()) {
203 LOG(ERROR) << "Unable to initialize rasterizer.";
204 return 0;
207 skia_runner::SkPictureRasterizer picture_rasterizer(
208 graphics_system.GetGrContext(), graphics_system.GetMaxTextureSize());
210 // Set up command-line render options.
211 picture_rasterizer.set_use_lcd_text(command_line->HasSwitch("use-lcd-text"));
212 picture_rasterizer.set_use_distance_field_text(
213 command_line->HasSwitch("use-distance-field-text"));
215 int msaa = GetSwitchInt(command_line, "msaa-sample-count", 0);
216 if (msaa != 0 && msaa != 2 && msaa != 4 && msaa != 8 && msaa != 16) {
217 std::cout << "Error: msaa sample count must be 0, 2, 4, 8 or 16.\n";
218 return 0;
220 picture_rasterizer.set_msaa_sample_count(msaa);
222 int repeat_raster_count =
223 std::max(1, GetSwitchInt(command_line, "repeat-raster-count", 1));
225 // Disable the security precautions to ensure we correctly read the picture.
226 SkPicture::SetPictureIOSecurityPrecautionsEnabled_Dangerous(false);
228 for (auto file_pair : files) {
229 skia::RefPtr<SkPicture> picture = ReadPicture(file_pair.first);
230 if (!picture) {
231 std::cout << "Error reading: " << file_pair.first.value() << "\n";
232 continue;
235 // GPU Rasterize the picture and time it.
236 base::TimeTicks rasterize_start = base::TimeTicks::Now();
237 skia::RefPtr<SkImage> image;
238 for (int i = 0; i < repeat_raster_count; ++i) {
239 image = picture_rasterizer.Rasterize(picture.get());
240 graphics_system.GetGrContext()->flush();
242 base::TimeDelta rasterize_time = base::TimeTicks::Now() - rasterize_start;
243 double average_time = rasterize_time.InSecondsF() / repeat_raster_count;
244 std::cout << "Average rasterization time for " << file_pair.first.value()
245 << ": " << average_time << "s (" << 1.0 / average_time
246 << " fps)\n";
248 if (!file_pair.second.empty()) {
249 if (!WriteSkImagePNG(image.get(), file_pair.second))
250 std::cout << "Error writing: " << file_pair.second.value() << "\n";
251 else
252 std::cout << file_pair.second.value() << " successfully created.\n";