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
3 // found in the LICENSE file.
5 #include "content/renderer/skia_benchmarking_extension.h"
7 #include "base/base64.h"
8 #include "base/time/time.h"
9 #include "base/values.h"
10 #include "cc/base/math_util.h"
11 #include "cc/resources/picture.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"
17 #include "skia/ext/benchmarking_canvas.h"
18 #include "third_party/WebKit/public/platform/WebArrayBuffer.h"
19 #include "third_party/WebKit/public/web/WebArrayBufferConverter.h"
20 #include "third_party/WebKit/public/web/WebFrame.h"
21 #include "third_party/WebKit/public/web/WebKit.h"
22 #include "third_party/skia/include/core/SkCanvas.h"
23 #include "third_party/skia/include/core/SkColorPriv.h"
24 #include "third_party/skia/include/core/SkGraphics.h"
25 #include "third_party/skia/include/core/SkStream.h"
26 #include "third_party/skia/src/utils/debugger/SkDebugCanvas.h"
27 #include "third_party/skia/src/utils/debugger/SkDrawCommand.h"
28 #include "ui/gfx/rect_conversions.h"
29 #include "ui/gfx/skia_util.h"
30 #include "v8/include/v8.h"
37 scoped_ptr
<base::Value
> ParsePictureArg(v8::Isolate
* isolate
,
38 v8::Handle
<v8::Value
> arg
) {
39 scoped_ptr
<content::V8ValueConverter
> converter(
40 content::V8ValueConverter::create());
41 return scoped_ptr
<base::Value
>(
42 converter
->FromV8Value(arg
, isolate
->GetCurrentContext()));
45 scoped_refptr
<cc::Picture
> ParsePictureStr(v8::Isolate
* isolate
,
46 v8::Handle
<v8::Value
> arg
) {
47 scoped_ptr
<base::Value
> picture_value
= ParsePictureArg(isolate
, arg
);
50 return cc::Picture::CreateFromSkpValue(picture_value
.get());
53 scoped_refptr
<cc::Picture
> ParsePictureHash(v8::Isolate
* isolate
,
54 v8::Handle
<v8::Value
> arg
) {
55 scoped_ptr
<base::Value
> picture_value
= ParsePictureArg(isolate
, arg
);
58 return cc::Picture::CreateFromValue(picture_value
.get());
63 gin::WrapperInfo
SkiaBenchmarking::kWrapperInfo
= {gin::kEmbedderNativeGin
};
66 void SkiaBenchmarking::Install(blink::WebFrame
* frame
) {
67 v8::Isolate
* isolate
= blink::mainThreadIsolate();
68 v8::HandleScope
handle_scope(isolate
);
69 v8::Handle
<v8::Context
> context
= frame
->mainWorldScriptContext();
70 if (context
.IsEmpty())
73 v8::Context::Scope
context_scope(context
);
75 gin::Handle
<SkiaBenchmarking
> controller
=
76 gin::CreateHandle(isolate
, new SkiaBenchmarking());
77 if (controller
.IsEmpty())
80 v8::Handle
<v8::Object
> global
= context
->Global();
81 v8::Handle
<v8::Object
> chrome
=
82 global
->Get(gin::StringToV8(isolate
, "chrome"))->ToObject();
83 if (chrome
.IsEmpty()) {
84 chrome
= v8::Object::New(isolate
);
85 global
->Set(gin::StringToV8(isolate
, "chrome"), chrome
);
87 chrome
->Set(gin::StringToV8(isolate
, "skiaBenchmarking"), controller
.ToV8());
91 void SkiaBenchmarking::Initialize() {
92 DCHECK(RenderThreadImpl::current());
93 // FIXME: remove this after Skia updates SkGraphics::Init() to be
94 // thread-safe and idempotent.
95 static bool skia_initialized
= false;
96 if (!skia_initialized
) {
97 LOG(WARNING
) << "Enabling unsafe Skia benchmarking extension.";
99 skia_initialized
= true;
103 SkiaBenchmarking::SkiaBenchmarking() {
107 SkiaBenchmarking::~SkiaBenchmarking() {}
109 gin::ObjectTemplateBuilder
SkiaBenchmarking::GetObjectTemplateBuilder(
110 v8::Isolate
* isolate
) {
111 return gin::Wrappable
<SkiaBenchmarking
>::GetObjectTemplateBuilder(isolate
)
112 .SetMethod("rasterize", &SkiaBenchmarking::Rasterize
)
113 .SetMethod("getOps", &SkiaBenchmarking::GetOps
)
114 .SetMethod("getOpTimings", &SkiaBenchmarking::GetOpTimings
)
115 .SetMethod("getInfo", &SkiaBenchmarking::GetInfo
);
118 void SkiaBenchmarking::Rasterize(gin::Arguments
* args
) {
119 v8::Isolate
* isolate
= args
->isolate();
120 if (args
->PeekNext().IsEmpty())
122 v8::Handle
<v8::Value
> picture_handle
;
123 args
->GetNext(&picture_handle
);
124 scoped_refptr
<cc::Picture
> picture
=
125 ParsePictureHash(isolate
, picture_handle
);
130 gfx::Rect
clip_rect(picture
->LayerRect());
132 bool overdraw
= false;
134 v8::Handle
<v8::Context
> context
= isolate
->GetCurrentContext();
135 if (!args
->PeekNext().IsEmpty()) {
136 v8::Handle
<v8::Value
> params
;
137 args
->GetNext(¶ms
);
138 scoped_ptr
<content::V8ValueConverter
> converter(
139 content::V8ValueConverter::create());
140 scoped_ptr
<base::Value
> params_value(
141 converter
->FromV8Value(params
, context
));
143 const base::DictionaryValue
* params_dict
= NULL
;
144 if (params_value
.get() && params_value
->GetAsDictionary(¶ms_dict
)) {
145 params_dict
->GetDouble("scale", &scale
);
146 params_dict
->GetInteger("stop", &stop_index
);
147 params_dict
->GetBoolean("overdraw", &overdraw
);
149 const base::Value
* clip_value
= NULL
;
150 if (params_dict
->Get("clip", &clip_value
))
151 cc::MathUtil::FromValue(clip_value
, &clip_rect
);
155 gfx::RectF
clip(clip_rect
);
156 clip
.Intersect(picture
->LayerRect());
158 gfx::Rect snapped_clip
= gfx::ToEnclosingRect(clip
);
161 if (!bitmap
.allocN32Pixels(snapped_clip
.width(), snapped_clip
.height()))
163 bitmap
.eraseARGB(0, 0, 0, 0);
165 SkCanvas
canvas(bitmap
);
166 canvas
.translate(SkFloatToScalar(-clip
.x()), SkFloatToScalar(-clip
.y()));
167 canvas
.clipRect(gfx::RectToSkRect(snapped_clip
));
168 canvas
.scale(scale
, scale
);
169 canvas
.translate(picture
->LayerRect().x(), picture
->LayerRect().y());
171 // First, build a debug canvas for the given picture.
172 SkDebugCanvas
debug_canvas(picture
->LayerRect().width(),
173 picture
->LayerRect().height());
174 picture
->Replay(&debug_canvas
);
176 // Raster the requested command subset into the bitmap-backed canvas.
177 int last_index
= debug_canvas
.getSize() - 1;
178 if (last_index
>= 0) {
179 debug_canvas
.setOverdrawViz(overdraw
);
182 stop_index
< 0 ? last_index
: std::min(last_index
, stop_index
));
185 blink::WebArrayBuffer buffer
=
186 blink::WebArrayBuffer::create(bitmap
.getSize(), 1);
187 uint32
* packed_pixels
= reinterpret_cast<uint32
*>(bitmap
.getPixels());
188 uint8
* buffer_pixels
= reinterpret_cast<uint8
*>(buffer
.data());
189 // Swizzle from native Skia format to RGBA as we copy out.
190 for (size_t i
= 0; i
< bitmap
.getSize(); i
+= 4) {
191 uint32 c
= packed_pixels
[i
>> 2];
192 buffer_pixels
[i
] = SkGetPackedR32(c
);
193 buffer_pixels
[i
+ 1] = SkGetPackedG32(c
);
194 buffer_pixels
[i
+ 2] = SkGetPackedB32(c
);
195 buffer_pixels
[i
+ 3] = SkGetPackedA32(c
);
198 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
199 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
200 v8::Number::New(isolate
, snapped_clip
.width()));
201 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
202 v8::Number::New(isolate
, snapped_clip
.height()));
203 result
->Set(v8::String::NewFromUtf8(isolate
, "data"),
204 blink::WebArrayBufferConverter::toV8Value(
205 &buffer
, context
->Global(), isolate
));
207 args
->Return(result
);
210 void SkiaBenchmarking::GetOps(gin::Arguments
* args
) {
211 v8::Isolate
* isolate
= args
->isolate();
212 if (args
->PeekNext().IsEmpty())
214 v8::Handle
<v8::Value
> picture_handle
;
215 args
->GetNext(&picture_handle
);
216 scoped_refptr
<cc::Picture
> picture
=
217 ParsePictureHash(isolate
, picture_handle
);
221 gfx::Rect bounds
= picture
->LayerRect();
222 SkDebugCanvas
canvas(bounds
.width(), bounds
.height());
223 picture
->Replay(&canvas
);
225 v8::Handle
<v8::Array
> result
= v8::Array::New(isolate
, canvas
.getSize());
226 for (int i
= 0; i
< canvas
.getSize(); ++i
) {
227 DrawType cmd_type
= canvas
.getDrawCommandAt(i
)->getType();
228 v8::Handle
<v8::Object
> cmd
= v8::Object::New(isolate
);
229 cmd
->Set(v8::String::NewFromUtf8(isolate
, "cmd_type"),
230 v8::Integer::New(isolate
, cmd_type
));
231 cmd
->Set(v8::String::NewFromUtf8(isolate
, "cmd_string"),
232 v8::String::NewFromUtf8(
233 isolate
, SkDrawCommand::GetCommandString(cmd_type
)));
235 SkTDArray
<SkString
*>* info
= canvas
.getCommandInfo(i
);
238 v8::Local
<v8::Array
> v8_info
= v8::Array::New(isolate
, info
->count());
239 for (int j
= 0; j
< info
->count(); ++j
) {
240 const SkString
* info_str
= (*info
)[j
];
242 v8_info
->Set(j
, v8::String::NewFromUtf8(isolate
, info_str
->c_str()));
245 cmd
->Set(v8::String::NewFromUtf8(isolate
, "info"), v8_info
);
250 args
->Return(result
.As
<v8::Object
>());
253 void SkiaBenchmarking::GetOpTimings(gin::Arguments
* args
) {
254 v8::Isolate
* isolate
= args
->isolate();
255 if (args
->PeekNext().IsEmpty())
257 v8::Handle
<v8::Value
> picture_handle
;
258 args
->GetNext(&picture_handle
);
259 scoped_refptr
<cc::Picture
> picture
=
260 ParsePictureHash(isolate
, picture_handle
);
264 gfx::Rect bounds
= picture
->LayerRect();
266 // Measure the total time by drawing straight into a bitmap-backed canvas.
268 bitmap
.allocN32Pixels(bounds
.width(), bounds
.height());
269 SkCanvas
bitmap_canvas(bitmap
);
270 bitmap_canvas
.clear(SK_ColorTRANSPARENT
);
271 base::TimeTicks t0
= base::TimeTicks::HighResNow();
272 picture
->Replay(&bitmap_canvas
);
273 base::TimeDelta total_time
= base::TimeTicks::HighResNow() - t0
;
275 // Gather per-op timing info by drawing into a BenchmarkingCanvas.
276 skia::BenchmarkingCanvas
benchmarking_canvas(bounds
.width(), bounds
.height());
277 picture
->Replay(&benchmarking_canvas
);
279 v8::Local
<v8::Array
> op_times
=
280 v8::Array::New(isolate
, benchmarking_canvas
.CommandCount());
281 for (size_t i
= 0; i
< benchmarking_canvas
.CommandCount(); ++i
)
282 op_times
->Set(i
, v8::Number::New(isolate
, benchmarking_canvas
.GetTime(i
)));
284 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
285 result
->Set(v8::String::NewFromUtf8(isolate
, "total_time"),
286 v8::Number::New(isolate
, total_time
.InMillisecondsF()));
287 result
->Set(v8::String::NewFromUtf8(isolate
, "cmd_times"), op_times
);
289 args
->Return(result
);
292 void SkiaBenchmarking::GetInfo(gin::Arguments
* args
) {
293 v8::Isolate
* isolate
= args
->isolate();
294 if (args
->PeekNext().IsEmpty())
296 v8::Handle
<v8::Value
> picture_handle
;
297 args
->GetNext(&picture_handle
);
298 scoped_refptr
<cc::Picture
> picture
=
299 ParsePictureStr(isolate
, picture_handle
);
303 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
304 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
305 v8::Number::New(isolate
, picture
->LayerRect().width()));
306 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
307 v8::Number::New(isolate
, picture
->LayerRect().height()));
309 args
->Return(result
);
312 } // namespace content