Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 #include "content/renderer/skia_benchmarking_extension.h" | 5 #include "content/renderer/skia_benchmarking_extension.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/time/time.h" | 8 #include "base/time/time.h" |
| 9 #include "base/values.h" | 9 #include "base/values.h" |
| 10 #include "cc/base/math_util.h" | 10 #include "cc/base/math_util.h" |
| 11 #include "cc/resources/picture.h" | 11 #include "cc/resources/picture.h" |
| 12 #include "content/public/renderer/v8_value_converter.h" | 12 #include "content/public/renderer/v8_value_converter.h" |
| 13 #include "content/renderer/render_thread_impl.h" | |
| 14 #include "gin/arguments.h" | |
| 15 #include "gin/handle.h" | |
| 16 #include "gin/object_template_builder.h" | |
| 13 #include "skia/ext/benchmarking_canvas.h" | 17 #include "skia/ext/benchmarking_canvas.h" |
| 14 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" | 18 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" |
| 15 #include "third_party/WebKit/public/web/WebFrame.h" | 19 #include "third_party/WebKit/public/web/WebFrame.h" |
| 20 #include "third_party/WebKit/public/web/WebKit.h" | |
| 16 #include "third_party/skia/include/core/SkBitmapDevice.h" | 21 #include "third_party/skia/include/core/SkBitmapDevice.h" |
| 17 #include "third_party/skia/include/core/SkCanvas.h" | 22 #include "third_party/skia/include/core/SkCanvas.h" |
| 18 #include "third_party/skia/include/core/SkColorPriv.h" | 23 #include "third_party/skia/include/core/SkColorPriv.h" |
| 19 #include "third_party/skia/include/core/SkGraphics.h" | 24 #include "third_party/skia/include/core/SkGraphics.h" |
| 20 #include "third_party/skia/include/core/SkStream.h" | 25 #include "third_party/skia/include/core/SkStream.h" |
| 21 #include "third_party/skia/src/utils/debugger/SkDebugCanvas.h" | 26 #include "third_party/skia/src/utils/debugger/SkDebugCanvas.h" |
| 22 #include "third_party/skia/src/utils/debugger/SkDrawCommand.h" | 27 #include "third_party/skia/src/utils/debugger/SkDrawCommand.h" |
| 23 #include "ui/gfx/rect_conversions.h" | 28 #include "ui/gfx/rect_conversions.h" |
| 24 #include "ui/gfx/skia_util.h" | 29 #include "ui/gfx/skia_util.h" |
| 25 #include "v8/include/v8.h" | 30 #include "v8/include/v8.h" |
| 26 | 31 |
| 27 using blink::WebFrame; | 32 |
| 33 namespace content { | |
| 28 | 34 |
| 29 namespace { | 35 namespace { |
| 30 | 36 |
| 31 const char kSkiaBenchmarkingExtensionName[] = "v8/SkiaBenchmarking"; | 37 scoped_ptr<base::Value> ParsePictureArg(v8::Isolate* isolate, |
| 32 | 38 v8::Handle<v8::Value> arg) { |
| 33 static scoped_ptr<base::Value> ParsePictureArg(v8::Isolate* isolate, | |
| 34 v8::Handle<v8::Value> arg) { | |
| 35 scoped_ptr<content::V8ValueConverter> converter( | 39 scoped_ptr<content::V8ValueConverter> converter( |
| 36 content::V8ValueConverter::create()); | 40 content::V8ValueConverter::create()); |
| 37 return scoped_ptr<base::Value>( | 41 return scoped_ptr<base::Value>( |
| 38 converter->FromV8Value(arg, isolate->GetCurrentContext())); | 42 converter->FromV8Value(arg, isolate->GetCurrentContext())); |
| 39 } | 43 } |
| 40 | 44 |
| 41 static scoped_refptr<cc::Picture> ParsePictureStr(v8::Isolate* isolate, | 45 scoped_refptr<cc::Picture> ParsePictureStr(v8::Isolate* isolate, |
| 42 v8::Handle<v8::Value> arg) { | 46 v8::Handle<v8::Value> arg) { |
| 43 scoped_ptr<base::Value> picture_value = ParsePictureArg(isolate, arg); | 47 scoped_ptr<base::Value> picture_value = ParsePictureArg(isolate, arg); |
| 44 if (!picture_value) | 48 if (!picture_value) |
| 45 return NULL; | 49 return NULL; |
| 46 return cc::Picture::CreateFromSkpValue(picture_value.get()); | 50 return cc::Picture::CreateFromSkpValue(picture_value.get()); |
| 47 } | 51 } |
| 48 | 52 |
| 49 static scoped_refptr<cc::Picture> ParsePictureHash(v8::Isolate* isolate, | 53 scoped_refptr<cc::Picture> ParsePictureHash(v8::Isolate* isolate, |
| 50 v8::Handle<v8::Value> arg) { | 54 v8::Handle<v8::Value> arg) { |
| 51 scoped_ptr<base::Value> picture_value = ParsePictureArg(isolate, arg); | 55 scoped_ptr<base::Value> picture_value = ParsePictureArg(isolate, arg); |
| 52 if (!picture_value) | 56 if (!picture_value) |
| 53 return NULL; | 57 return NULL; |
| 54 return cc::Picture::CreateFromValue(picture_value.get()); | 58 return cc::Picture::CreateFromValue(picture_value.get()); |
| 55 } | 59 } |
| 56 | 60 |
| 57 class SkiaBenchmarkingWrapper : public v8::Extension { | 61 } // namespace |
| 58 public: | 62 |
| 59 SkiaBenchmarkingWrapper() : | 63 gin::WrapperInfo SkiaBenchmarking::kWrapperInfo = {gin::kEmbedderNativeGin}; |
| 60 v8::Extension(kSkiaBenchmarkingExtensionName, | 64 |
| 61 "if (typeof(chrome) == 'undefined') {" | 65 // static |
| 62 " chrome = {};" | 66 void SkiaBenchmarking::Install(blink::WebFrame* frame) { |
| 63 "};" | 67 v8::Isolate* isolate = blink::mainThreadIsolate(); |
| 64 "if (typeof(chrome.skiaBenchmarking) == 'undefined') {" | 68 v8::HandleScope handle_scope(isolate); |
| 65 " chrome.skiaBenchmarking = {};" | 69 v8::Handle<v8::Context> context = frame->mainWorldScriptContext(); |
| 66 "};" | 70 if (context.IsEmpty()) |
| 67 "chrome.skiaBenchmarking.rasterize = function(picture, params) {" | 71 return; |
| 68 " /* " | 72 |
|
f(malita)
2014/01/21 14:43:40
Can we preserve the API docs in some form? Even if
| |
| 69 " Rasterizes a Picture JSON-encoded by cc::Picture::AsValue()." | 73 v8::Context::Scope context_scope(context); |
| 70 " @param {Object} picture A json-encoded cc::Picture." | 74 |
| 71 " @param {" | 75 gin::Handle<SkiaBenchmarking> controller = |
| 72 " 'scale': {Number}," | 76 gin::CreateHandle(isolate, new SkiaBenchmarking()); |
| 73 " 'stop': {Number}," | 77 v8::Handle<v8::Object> global = context->Global(); |
| 74 " 'overdraw': {Boolean}," | 78 v8::Handle<v8::Object> chrome = |
| 75 " 'clip': [Number, Number, Number, Number]" | 79 global->Get(gin::StringToV8(isolate, "chrome"))->ToObject(); |
| 76 " } (optional) Rasterization parameters." | 80 if (chrome.IsEmpty()) { |
| 77 " @returns {" | 81 chrome = v8::Object::New(isolate); |
| 78 " 'width': {Number}," | 82 global->Set(gin::StringToV8(isolate, "chrome"), chrome); |
| 79 " 'height': {Number}," | 83 } |
| 80 " 'data': {ArrayBuffer}" | 84 chrome->Set(gin::StringToV8(isolate, "skiaBenchmarking"), controller.ToV8()); |
| 81 " }" | 85 } |
| 82 " @returns undefined if the arguments are invalid or the picture" | 86 |
| 83 " version is not supported." | 87 // static |
| 84 " */" | 88 void SkiaBenchmarking::Initialize() { |
| 85 " native function Rasterize();" | 89 DCHECK(RenderThreadImpl::current()); |
| 86 " return Rasterize(picture, params);" | 90 // FIXME: remove this after Skia updates SkGraphics::Init() to be |
| 87 "};" | 91 // thread-safe and idempotent. |
| 88 "chrome.skiaBenchmarking.getOps = function(picture) {" | 92 static bool skia_initialized = false; |
| 89 " /* " | 93 if (!skia_initialized) { |
| 90 " Extracts the Skia draw commands from a JSON-encoded cc::Picture" | 94 LOG(WARNING) << "Enabling unsafe Skia benchmarking extension."; |
| 91 " @param {Object} picture A json-encoded cc::Picture." | 95 SkGraphics::Init(); |
| 92 " @returns [{ 'cmd': {String}, 'info': [String, ...] }, ...]" | 96 skia_initialized = true; |
| 93 " @returns undefined if the arguments are invalid or the picture" | 97 } |
| 94 " version is not supported." | 98 } |
| 95 " */" | 99 |
| 96 " native function GetOps();" | 100 SkiaBenchmarking::SkiaBenchmarking() { |
| 97 " return GetOps(picture);" | 101 Initialize(); |
| 98 "};" | 102 } |
| 99 "chrome.skiaBenchmarking.getOpTimings = function(picture) {" | 103 |
| 100 " /* " | 104 SkiaBenchmarking::~SkiaBenchmarking() {} |
| 101 " Returns timing information for the given picture." | 105 |
| 102 " @param {Object} picture A json-encoded cc::Picture." | 106 gin::ObjectTemplateBuilder SkiaBenchmarking::GetObjectTemplateBuilder( |
| 103 " @returns { 'total_time': {Number}, 'cmd_times': [Number, ...] }" | 107 v8::Isolate* isolate) { |
| 104 " @returns undefined if the arguments are invalid or the picture" | 108 return gin::Wrappable<SkiaBenchmarking>::GetObjectTemplateBuilder(isolate) |
| 105 " version is not supported." | 109 .SetMethod("rasterize", &SkiaBenchmarking::Rasterize) |
| 106 " */" | 110 .SetMethod("getOps", &SkiaBenchmarking::GetOps) |
| 107 " native function GetOpTimings();" | 111 .SetMethod("getOpTiminsg", &SkiaBenchmarking::GetOpTimings) |
|
f(malita)
2014/01/21 14:43:40
'getOpTimings'
| |
| 108 " return GetOpTimings(picture);" | 112 .SetMethod("getInfo", &SkiaBenchmarking::GetInfo); |
| 109 "};" | 113 } |
| 110 "chrome.skiaBenchmarking.getInfo = function(picture) {" | 114 |
| 111 " /* " | 115 void SkiaBenchmarking::Rasterize(gin::Arguments* args) { |
| 112 " Returns meta information for the given picture." | 116 v8::Isolate* isolate = args->isolate(); |
| 113 " @param {Object} picture A base64 encoded SKP." | 117 if (args->PeekNext().IsEmpty()) |
| 114 " @returns { 'width': {Number}, 'height': {Number} }" | 118 return; |
| 115 " @returns undefined if the picture version is not supported." | 119 v8::Handle<v8::Value> picture_handle; |
| 116 " */" | 120 args->GetNext(&picture_handle); |
| 117 " native function GetInfo();" | 121 scoped_refptr<cc::Picture> picture = |
| 118 " return GetInfo(picture);" | 122 ParsePictureHash(isolate, picture_handle); |
| 119 "};" | 123 if (!picture.get()) |
| 120 ) { | 124 return; |
| 121 content::SkiaBenchmarkingExtension::InitSkGraphics(); | 125 |
| 122 } | 126 double scale = 1.0; |
| 123 | 127 gfx::Rect clip_rect(picture->LayerRect()); |
| 124 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate( | 128 int stop_index = -1; |
| 125 v8::Isolate* isolate, | 129 bool overdraw = false; |
| 126 v8::Handle<v8::String> name) OVERRIDE { | 130 |
| 127 if (name->Equals(v8::String::NewFromUtf8(isolate, "Rasterize"))) | 131 if (!args->PeekNext().IsEmpty()) { |
| 128 return v8::FunctionTemplate::New(isolate, Rasterize); | 132 v8::Handle<v8::Value> params; |
| 129 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetOps"))) | 133 args->GetNext(¶ms); |
| 130 return v8::FunctionTemplate::New(isolate, GetOps); | 134 scoped_ptr<content::V8ValueConverter> converter( |
| 131 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetOpTimings"))) | 135 content::V8ValueConverter::create()); |
| 132 return v8::FunctionTemplate::New(isolate, GetOpTimings); | 136 scoped_ptr<base::Value> params_value( |
| 133 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetInfo"))) | 137 converter->FromV8Value(params, isolate->GetCurrentContext())); |
| 134 return v8::FunctionTemplate::New(isolate, GetInfo); | 138 |
| 135 | 139 const base::DictionaryValue* params_dict = NULL; |
| 136 return v8::Handle<v8::FunctionTemplate>(); | 140 if (params_value.get() && params_value->GetAsDictionary(¶ms_dict)) { |
| 137 } | 141 params_dict->GetDouble("scale", &scale); |
| 138 | 142 params_dict->GetInteger("stop", &stop_index); |
| 139 static void Rasterize(const v8::FunctionCallbackInfo<v8::Value>& args) { | 143 params_dict->GetBoolean("overdraw", &overdraw); |
| 140 if (args.Length() < 1) | 144 |
| 141 return; | 145 const base::Value* clip_value = NULL; |
| 142 | 146 if (params_dict->Get("clip", &clip_value)) |
| 143 v8::Isolate* isolate = args.GetIsolate(); | 147 cc::MathUtil::FromValue(clip_value, &clip_rect); |
| 144 scoped_refptr<cc::Picture> picture = ParsePictureHash(isolate, args[0]); | |
| 145 if (!picture.get()) | |
| 146 return; | |
| 147 | |
| 148 double scale = 1.0; | |
| 149 gfx::Rect clip_rect(picture->LayerRect()); | |
| 150 int stop_index = -1; | |
| 151 bool overdraw = false; | |
| 152 | |
| 153 if (args.Length() > 1) { | |
| 154 scoped_ptr<content::V8ValueConverter> converter( | |
| 155 content::V8ValueConverter::create()); | |
| 156 scoped_ptr<base::Value> params_value( | |
| 157 converter->FromV8Value(args[1], isolate->GetCurrentContext())); | |
| 158 | |
| 159 const base::DictionaryValue* params_dict = NULL; | |
| 160 if (params_value.get() && params_value->GetAsDictionary(¶ms_dict)) { | |
| 161 params_dict->GetDouble("scale", &scale); | |
| 162 params_dict->GetInteger("stop", &stop_index); | |
| 163 params_dict->GetBoolean("overdraw", &overdraw); | |
| 164 | |
| 165 const base::Value* clip_value = NULL; | |
| 166 if (params_dict->Get("clip", &clip_value)) | |
| 167 cc::MathUtil::FromValue(clip_value, &clip_rect); | |
| 168 } | |
| 169 } | 148 } |
| 170 | 149 } |
| 171 gfx::RectF clip(clip_rect); | 150 |
| 172 clip.Intersect(picture->LayerRect()); | 151 gfx::RectF clip(clip_rect); |
| 173 clip.Scale(scale); | 152 clip.Intersect(picture->LayerRect()); |
| 174 gfx::Rect snapped_clip = gfx::ToEnclosingRect(clip); | 153 clip.Scale(scale); |
| 175 | 154 gfx::Rect snapped_clip = gfx::ToEnclosingRect(clip); |
| 176 const int kMaxBitmapSize = 4096; | 155 |
| 177 if (snapped_clip.width() > kMaxBitmapSize | 156 const int kMaxBitmapSize = 4096; |
| 178 || snapped_clip.height() > kMaxBitmapSize) | 157 if (snapped_clip.width() > kMaxBitmapSize || |
| 179 return; | 158 snapped_clip.height() > kMaxBitmapSize) |
| 180 | 159 return; |
| 181 SkBitmap bitmap; | 160 |
| 182 bitmap.setConfig(SkBitmap::kARGB_8888_Config, snapped_clip.width(), | 161 SkBitmap bitmap; |
| 183 snapped_clip.height()); | 162 bitmap.setConfig( |
| 184 if (!bitmap.allocPixels()) | 163 SkBitmap::kARGB_8888_Config, snapped_clip.width(), snapped_clip.height()); |
| 185 return; | 164 if (!bitmap.allocPixels()) |
| 186 bitmap.eraseARGB(0, 0, 0, 0); | 165 return; |
| 187 | 166 bitmap.eraseARGB(0, 0, 0, 0); |
| 188 SkCanvas canvas(bitmap); | 167 |
| 189 canvas.translate(SkFloatToScalar(-clip.x()), | 168 SkCanvas canvas(bitmap); |
| 190 SkFloatToScalar(-clip.y())); | 169 canvas.translate(SkFloatToScalar(-clip.x()), SkFloatToScalar(-clip.y())); |
| 191 canvas.clipRect(gfx::RectToSkRect(snapped_clip)); | 170 canvas.clipRect(gfx::RectToSkRect(snapped_clip)); |
| 192 canvas.scale(scale, scale); | 171 canvas.scale(scale, scale); |
| 193 canvas.translate(picture->LayerRect().x(), | 172 canvas.translate(picture->LayerRect().x(), picture->LayerRect().y()); |
| 194 picture->LayerRect().y()); | 173 |
| 195 | 174 // First, build a debug canvas for the given picture. |
| 196 // First, build a debug canvas for the given picture. | 175 SkDebugCanvas debug_canvas(picture->LayerRect().width(), |
| 197 SkDebugCanvas debug_canvas(picture->LayerRect().width(), | 176 picture->LayerRect().height()); |
| 198 picture->LayerRect().height()); | 177 picture->Replay(&debug_canvas); |
| 199 picture->Replay(&debug_canvas); | 178 |
| 200 | 179 // Raster the requested command subset into the bitmap-backed canvas. |
| 201 // Raster the requested command subset into the bitmap-backed canvas. | 180 int last_index = debug_canvas.getSize() - 1; |
| 202 int last_index = debug_canvas.getSize() - 1; | 181 if (last_index >= 0) { |
| 203 if (last_index >= 0) { | 182 debug_canvas.setOverdrawViz(overdraw); |
| 204 debug_canvas.setOverdrawViz(overdraw); | 183 debug_canvas.drawTo( |
| 205 debug_canvas.drawTo(&canvas, stop_index < 0 | 184 &canvas, |
| 206 ? last_index | 185 stop_index < 0 ? last_index : std::min(last_index, stop_index)); |
| 207 : std::min(last_index, stop_index)); | 186 } |
| 187 | |
| 188 blink::WebArrayBuffer buffer = | |
| 189 blink::WebArrayBuffer::create(bitmap.getSize(), 1); | |
| 190 uint32* packed_pixels = reinterpret_cast<uint32*>(bitmap.getPixels()); | |
| 191 uint8* buffer_pixels = reinterpret_cast<uint8*>(buffer.data()); | |
| 192 // Swizzle from native Skia format to RGBA as we copy out. | |
| 193 for (size_t i = 0; i < bitmap.getSize(); i += 4) { | |
| 194 uint32 c = packed_pixels[i >> 2]; | |
| 195 buffer_pixels[i] = SkGetPackedR32(c); | |
| 196 buffer_pixels[i + 1] = SkGetPackedG32(c); | |
| 197 buffer_pixels[i + 2] = SkGetPackedB32(c); | |
| 198 buffer_pixels[i + 3] = SkGetPackedA32(c); | |
| 199 } | |
| 200 | |
| 201 v8::Handle<v8::Object> result = v8::Object::New(isolate); | |
| 202 result->Set(v8::String::NewFromUtf8(isolate, "width"), | |
| 203 v8::Number::New(isolate, snapped_clip.width())); | |
| 204 result->Set(v8::String::NewFromUtf8(isolate, "height"), | |
| 205 v8::Number::New(isolate, snapped_clip.height())); | |
| 206 result->Set(v8::String::NewFromUtf8(isolate, "data"), buffer.toV8Value()); | |
| 207 | |
| 208 args->Return(result); | |
| 209 } | |
| 210 | |
| 211 void SkiaBenchmarking::GetOps(gin::Arguments* args) { | |
| 212 v8::Isolate* isolate = args->isolate(); | |
| 213 if (args->PeekNext().IsEmpty()) | |
| 214 return; | |
| 215 v8::Handle<v8::Value> picture_handle; | |
| 216 args->GetNext(&picture_handle); | |
| 217 scoped_refptr<cc::Picture> picture = | |
| 218 ParsePictureHash(isolate, picture_handle); | |
| 219 if (!picture.get()) | |
| 220 return; | |
| 221 | |
| 222 gfx::Rect bounds = picture->LayerRect(); | |
| 223 SkDebugCanvas canvas(bounds.width(), bounds.height()); | |
| 224 picture->Replay(&canvas); | |
| 225 | |
| 226 v8::Handle<v8::Array> result = v8::Array::New(isolate, canvas.getSize()); | |
| 227 for (int i = 0; i < canvas.getSize(); ++i) { | |
| 228 DrawType cmd_type = canvas.getDrawCommandAt(i)->getType(); | |
| 229 v8::Handle<v8::Object> cmd = v8::Object::New(isolate); | |
| 230 cmd->Set(v8::String::NewFromUtf8(isolate, "cmd_type"), | |
| 231 v8::Integer::New(isolate, cmd_type)); | |
| 232 cmd->Set(v8::String::NewFromUtf8(isolate, "cmd_string"), | |
| 233 v8::String::NewFromUtf8( | |
| 234 isolate, SkDrawCommand::GetCommandString(cmd_type))); | |
| 235 | |
| 236 SkTDArray<SkString*>* info = canvas.getCommandInfo(i); | |
| 237 DCHECK(info); | |
| 238 | |
| 239 v8::Local<v8::Array> v8_info = v8::Array::New(isolate, info->count()); | |
| 240 for (int j = 0; j < info->count(); ++j) { | |
| 241 const SkString* info_str = (*info)[j]; | |
| 242 DCHECK(info_str); | |
| 243 v8_info->Set(j, v8::String::NewFromUtf8(isolate, info_str->c_str())); | |
| 208 } | 244 } |
| 209 | 245 |
| 210 blink::WebArrayBuffer buffer = | 246 cmd->Set(v8::String::NewFromUtf8(isolate, "info"), v8_info); |
| 211 blink::WebArrayBuffer::create(bitmap.getSize(), 1); | 247 |
| 212 uint32* packed_pixels = reinterpret_cast<uint32*>(bitmap.getPixels()); | 248 result->Set(i, cmd); |
| 213 uint8* buffer_pixels = reinterpret_cast<uint8*>(buffer.data()); | 249 } |
| 214 // Swizzle from native Skia format to RGBA as we copy out. | 250 |
| 215 for (size_t i = 0; i < bitmap.getSize(); i += 4) { | 251 args->Return(result.As<v8::Object>()); |
| 216 uint32 c = packed_pixels[i >> 2]; | 252 } |
| 217 buffer_pixels[i] = SkGetPackedR32(c); | 253 |
| 218 buffer_pixels[i + 1] = SkGetPackedG32(c); | 254 void SkiaBenchmarking::GetOpTimings(gin::Arguments* args) { |
| 219 buffer_pixels[i + 2] = SkGetPackedB32(c); | 255 v8::Isolate* isolate = args->isolate(); |
| 220 buffer_pixels[i + 3] = SkGetPackedA32(c); | 256 if (args->PeekNext().IsEmpty()) |
| 221 } | 257 return; |
| 222 | 258 v8::Handle<v8::Value> picture_handle; |
| 223 v8::Handle<v8::Object> result = v8::Object::New(isolate); | 259 args->GetNext(&picture_handle); |
| 224 result->Set(v8::String::NewFromUtf8(isolate, "width"), | 260 scoped_refptr<cc::Picture> picture = |
| 225 v8::Number::New(isolate, snapped_clip.width())); | 261 ParsePictureHash(isolate, picture_handle); |
| 226 result->Set(v8::String::NewFromUtf8(isolate, "height"), | 262 if (!picture.get()) |
| 227 v8::Number::New(isolate, snapped_clip.height())); | 263 return; |
| 228 result->Set(v8::String::NewFromUtf8(isolate, "data"), buffer.toV8Value()); | 264 |
| 229 | 265 gfx::Rect bounds = picture->LayerRect(); |
| 230 args.GetReturnValue().Set(result); | 266 |
| 231 } | 267 // Measure the total time by drawing straight into a bitmap-backed canvas. |
| 232 | 268 skia::RefPtr<SkBaseDevice> device = skia::AdoptRef(SkNEW_ARGS( |
| 233 static void GetOps(const v8::FunctionCallbackInfo<v8::Value>& args) { | 269 SkBitmapDevice, |
| 234 if (args.Length() != 1) | 270 (SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height()))); |
| 235 return; | 271 SkCanvas bitmap_canvas(device.get()); |
| 236 | 272 bitmap_canvas.clear(SK_ColorTRANSPARENT); |
| 237 v8::Isolate* isolate = args.GetIsolate(); | 273 base::TimeTicks t0 = base::TimeTicks::HighResNow(); |
| 238 scoped_refptr<cc::Picture> picture = ParsePictureHash(isolate, args[0]); | 274 picture->Replay(&bitmap_canvas); |
| 239 if (!picture.get()) | 275 base::TimeDelta total_time = base::TimeTicks::HighResNow() - t0; |
| 240 return; | 276 |
| 241 | 277 // Gather per-op timing info by drawing into a BenchmarkingCanvas. |
| 242 gfx::Rect bounds = picture->LayerRect(); | 278 skia::BenchmarkingCanvas benchmarking_canvas(bounds.width(), bounds.height()); |
| 243 SkDebugCanvas canvas(bounds.width(), bounds.height()); | 279 picture->Replay(&benchmarking_canvas); |
| 244 picture->Replay(&canvas); | 280 |
| 245 | 281 v8::Local<v8::Array> op_times = |
| 246 v8::Local<v8::Array> result = v8::Array::New(isolate, canvas.getSize()); | 282 v8::Array::New(isolate, benchmarking_canvas.CommandCount()); |
| 247 for (int i = 0; i < canvas.getSize(); ++i) { | 283 for (size_t i = 0; i < benchmarking_canvas.CommandCount(); ++i) |
| 248 DrawType cmd_type = canvas.getDrawCommandAt(i)->getType(); | 284 op_times->Set(i, v8::Number::New(isolate, benchmarking_canvas.GetTime(i))); |
| 249 v8::Handle<v8::Object> cmd = v8::Object::New(isolate); | 285 |
| 250 cmd->Set(v8::String::NewFromUtf8(isolate, "cmd_type"), | 286 v8::Handle<v8::Object> result = v8::Object::New(isolate); |
| 251 v8::Integer::New(isolate, cmd_type)); | 287 result->Set(v8::String::NewFromUtf8(isolate, "total_time"), |
| 252 cmd->Set(v8::String::NewFromUtf8(isolate, "cmd_string"), | 288 v8::Number::New(isolate, total_time.InMillisecondsF())); |
| 253 v8::String::NewFromUtf8( | 289 result->Set(v8::String::NewFromUtf8(isolate, "cmd_times"), op_times); |
| 254 isolate, SkDrawCommand::GetCommandString(cmd_type))); | 290 |
| 255 | 291 args->Return(result); |
| 256 SkTDArray<SkString*>* info = canvas.getCommandInfo(i); | 292 } |
| 257 DCHECK(info); | 293 |
| 258 | 294 void SkiaBenchmarking::GetInfo(gin::Arguments* args) { |
| 259 v8::Local<v8::Array> v8_info = v8::Array::New(isolate, info->count()); | 295 v8::Isolate* isolate = args->isolate(); |
| 260 for (int j = 0; j < info->count(); ++j) { | 296 if (args->PeekNext().IsEmpty()) |
| 261 const SkString* info_str = (*info)[j]; | 297 return; |
| 262 DCHECK(info_str); | 298 v8::Handle<v8::Value> picture_handle; |
| 263 v8_info->Set(j, v8::String::NewFromUtf8(isolate, info_str->c_str())); | 299 args->GetNext(&picture_handle); |
| 264 } | 300 scoped_refptr<cc::Picture> picture = |
| 265 | 301 ParsePictureStr(isolate, picture_handle); |
| 266 cmd->Set(v8::String::NewFromUtf8(isolate, "info"), v8_info); | 302 if (!picture.get()) |
| 267 | 303 return; |
| 268 result->Set(i, cmd); | 304 |
| 269 } | 305 v8::Handle<v8::Object> result = v8::Object::New(isolate); |
| 270 | 306 result->Set(v8::String::NewFromUtf8(isolate, "width"), |
| 271 args.GetReturnValue().Set(result); | 307 v8::Number::New(isolate, picture->LayerRect().width())); |
| 272 } | 308 result->Set(v8::String::NewFromUtf8(isolate, "height"), |
| 273 | 309 v8::Number::New(isolate, picture->LayerRect().height())); |
| 274 static void GetOpTimings(const v8::FunctionCallbackInfo<v8::Value>& args) { | 310 |
| 275 if (args.Length() != 1) | 311 args->Return(result); |
| 276 return; | |
| 277 | |
| 278 v8::Isolate* isolate = args.GetIsolate(); | |
| 279 scoped_refptr<cc::Picture> picture = ParsePictureHash(isolate, args[0]); | |
| 280 if (!picture.get()) | |
| 281 return; | |
| 282 | |
| 283 gfx::Rect bounds = picture->LayerRect(); | |
| 284 | |
| 285 // Measure the total time by drawing straight into a bitmap-backed canvas. | |
| 286 skia::RefPtr<SkBaseDevice> device = skia::AdoptRef( | |
| 287 SkNEW_ARGS(SkBitmapDevice, | |
| 288 (SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height()))); | |
| 289 SkCanvas bitmap_canvas(device.get()); | |
| 290 bitmap_canvas.clear(SK_ColorTRANSPARENT); | |
| 291 base::TimeTicks t0 = base::TimeTicks::HighResNow(); | |
| 292 picture->Replay(&bitmap_canvas); | |
| 293 base::TimeDelta total_time = base::TimeTicks::HighResNow() - t0; | |
| 294 | |
| 295 // Gather per-op timing info by drawing into a BenchmarkingCanvas. | |
| 296 skia::BenchmarkingCanvas benchmarking_canvas(bounds.width(), | |
| 297 bounds.height()); | |
| 298 picture->Replay(&benchmarking_canvas); | |
| 299 | |
| 300 v8::Local<v8::Array> op_times = | |
| 301 v8::Array::New(isolate, benchmarking_canvas.CommandCount()); | |
| 302 for (size_t i = 0; i < benchmarking_canvas.CommandCount(); ++i) | |
| 303 op_times->Set(i, v8::Number::New(isolate, | |
| 304 benchmarking_canvas.GetTime(i))); | |
| 305 | |
| 306 v8::Handle<v8::Object> result = v8::Object::New(isolate); | |
| 307 result->Set(v8::String::NewFromUtf8(isolate, "total_time"), | |
| 308 v8::Number::New(isolate, total_time.InMillisecondsF())); | |
| 309 result->Set(v8::String::NewFromUtf8(isolate, "cmd_times"), op_times); | |
| 310 | |
| 311 args.GetReturnValue().Set(result); | |
| 312 } | |
| 313 | |
| 314 static void GetInfo(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 315 if (args.Length() != 1) | |
| 316 return; | |
| 317 | |
| 318 v8::Isolate* isolate = args.GetIsolate(); | |
| 319 scoped_refptr<cc::Picture> picture = ParsePictureStr(isolate, args[0]); | |
| 320 if (!picture.get()) | |
| 321 return; | |
| 322 | |
| 323 v8::Handle<v8::Object> result = v8::Object::New(isolate); | |
| 324 result->Set(v8::String::NewFromUtf8(isolate, "width"), | |
| 325 v8::Number::New(isolate, picture->LayerRect().width())); | |
| 326 result->Set(v8::String::NewFromUtf8(isolate, "height"), | |
| 327 v8::Number::New(isolate, picture->LayerRect().height())); | |
| 328 | |
| 329 args.GetReturnValue().Set(result); | |
| 330 } | |
| 331 }; | |
| 332 | |
| 333 } // namespace | |
| 334 | |
| 335 namespace content { | |
| 336 | |
| 337 v8::Extension* SkiaBenchmarkingExtension::Get() { | |
| 338 return new SkiaBenchmarkingWrapper(); | |
| 339 } | |
| 340 | |
| 341 void SkiaBenchmarkingExtension::InitSkGraphics() { | |
| 342 // Always call on the main render thread. | |
| 343 // Does not need to be thread-safe, as long as the above holds. | |
| 344 // FIXME: remove this after Skia updates SkGraphics::Init() to be | |
| 345 // thread-safe and idempotent. | |
| 346 static bool skia_initialized = false; | |
| 347 if (!skia_initialized) { | |
| 348 SkGraphics::Init(); | |
| 349 skia_initialized = true; | |
| 350 } | |
| 351 } | 312 } |
| 352 | 313 |
| 353 } // namespace content | 314 } // namespace content |
| OLD | NEW |