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/chrome_object_extensions_utils.h"
14 #include "content/renderer/render_thread_impl.h"
15 #include "gin/arguments.h"
16 #include "gin/handle.h"
17 #include "gin/object_template_builder.h"
18 #include "skia/ext/benchmarking_canvas.h"
19 #include "third_party/WebKit/public/platform/WebArrayBuffer.h"
20 #include "third_party/WebKit/public/web/WebArrayBufferConverter.h"
21 #include "third_party/WebKit/public/web/WebFrame.h"
22 #include "third_party/WebKit/public/web/WebKit.h"
23 #include "third_party/skia/include/core/SkCanvas.h"
24 #include "third_party/skia/include/core/SkColorPriv.h"
25 #include "third_party/skia/include/core/SkGraphics.h"
26 #include "third_party/skia/include/core/SkStream.h"
27 #include "third_party/skia/src/utils/debugger/SkDebugCanvas.h"
28 #include "third_party/skia/src/utils/debugger/SkDrawCommand.h"
29 #include "ui/gfx/rect_conversions.h"
30 #include "ui/gfx/skia_util.h"
31 #include "v8/include/v8.h"
38 scoped_ptr
<base::Value
> ParsePictureArg(v8::Isolate
* isolate
,
39 v8::Handle
<v8::Value
> arg
) {
40 scoped_ptr
<content::V8ValueConverter
> converter(
41 content::V8ValueConverter::create());
42 return scoped_ptr
<base::Value
>(
43 converter
->FromV8Value(arg
, isolate
->GetCurrentContext()));
46 scoped_refptr
<cc::Picture
> ParsePictureStr(v8::Isolate
* isolate
,
47 v8::Handle
<v8::Value
> arg
) {
48 scoped_ptr
<base::Value
> picture_value
= ParsePictureArg(isolate
, arg
);
51 return cc::Picture::CreateFromSkpValue(picture_value
.get());
54 scoped_refptr
<cc::Picture
> ParsePictureHash(v8::Isolate
* isolate
,
55 v8::Handle
<v8::Value
> arg
) {
56 scoped_ptr
<base::Value
> picture_value
= ParsePictureArg(isolate
, arg
);
59 return cc::Picture::CreateFromValue(picture_value
.get());
64 gin::WrapperInfo
SkiaBenchmarking::kWrapperInfo
= {gin::kEmbedderNativeGin
};
67 void SkiaBenchmarking::Install(blink::WebFrame
* frame
) {
68 v8::Isolate
* isolate
= blink::mainThreadIsolate();
69 v8::HandleScope
handle_scope(isolate
);
70 v8::Handle
<v8::Context
> context
= frame
->mainWorldScriptContext();
71 if (context
.IsEmpty())
74 v8::Context::Scope
context_scope(context
);
76 gin::Handle
<SkiaBenchmarking
> controller
=
77 gin::CreateHandle(isolate
, new SkiaBenchmarking());
78 if (controller
.IsEmpty())
81 v8::Handle
<v8::Object
> chrome
= GetOrCreateChromeObject(isolate
,
83 chrome
->Set(gin::StringToV8(isolate
, "skiaBenchmarking"), controller
.ToV8());
87 void SkiaBenchmarking::Initialize() {
88 DCHECK(RenderThreadImpl::current());
89 // FIXME: remove this after Skia updates SkGraphics::Init() to be
90 // thread-safe and idempotent.
91 static bool skia_initialized
= false;
92 if (!skia_initialized
) {
93 LOG(WARNING
) << "Enabling unsafe Skia benchmarking extension.";
95 skia_initialized
= true;
99 SkiaBenchmarking::SkiaBenchmarking() {
103 SkiaBenchmarking::~SkiaBenchmarking() {}
105 gin::ObjectTemplateBuilder
SkiaBenchmarking::GetObjectTemplateBuilder(
106 v8::Isolate
* isolate
) {
107 return gin::Wrappable
<SkiaBenchmarking
>::GetObjectTemplateBuilder(isolate
)
108 .SetMethod("rasterize", &SkiaBenchmarking::Rasterize
)
109 .SetMethod("getOps", &SkiaBenchmarking::GetOps
)
110 .SetMethod("getOpTimings", &SkiaBenchmarking::GetOpTimings
)
111 .SetMethod("getInfo", &SkiaBenchmarking::GetInfo
);
114 void SkiaBenchmarking::Rasterize(gin::Arguments
* args
) {
115 v8::Isolate
* isolate
= args
->isolate();
116 if (args
->PeekNext().IsEmpty())
118 v8::Handle
<v8::Value
> picture_handle
;
119 args
->GetNext(&picture_handle
);
120 scoped_refptr
<cc::Picture
> picture
=
121 ParsePictureHash(isolate
, picture_handle
);
126 gfx::Rect
clip_rect(picture
->LayerRect());
128 bool overdraw
= false;
130 v8::Handle
<v8::Context
> context
= isolate
->GetCurrentContext();
131 if (!args
->PeekNext().IsEmpty()) {
132 v8::Handle
<v8::Value
> params
;
133 args
->GetNext(¶ms
);
134 scoped_ptr
<content::V8ValueConverter
> converter(
135 content::V8ValueConverter::create());
136 scoped_ptr
<base::Value
> params_value(
137 converter
->FromV8Value(params
, context
));
139 const base::DictionaryValue
* params_dict
= NULL
;
140 if (params_value
.get() && params_value
->GetAsDictionary(¶ms_dict
)) {
141 params_dict
->GetDouble("scale", &scale
);
142 params_dict
->GetInteger("stop", &stop_index
);
143 params_dict
->GetBoolean("overdraw", &overdraw
);
145 const base::Value
* clip_value
= NULL
;
146 if (params_dict
->Get("clip", &clip_value
))
147 cc::MathUtil::FromValue(clip_value
, &clip_rect
);
151 gfx::RectF
clip(clip_rect
);
152 clip
.Intersect(picture
->LayerRect());
154 gfx::Rect snapped_clip
= gfx::ToEnclosingRect(clip
);
157 if (!bitmap
.tryAllocN32Pixels(snapped_clip
.width(), snapped_clip
.height()))
159 bitmap
.eraseARGB(0, 0, 0, 0);
161 SkCanvas
canvas(bitmap
);
162 canvas
.translate(SkFloatToScalar(-clip
.x()), SkFloatToScalar(-clip
.y()));
163 canvas
.clipRect(gfx::RectToSkRect(snapped_clip
));
164 canvas
.scale(scale
, scale
);
165 canvas
.translate(picture
->LayerRect().x(), picture
->LayerRect().y());
167 // First, build a debug canvas for the given picture.
168 SkDebugCanvas
debug_canvas(picture
->LayerRect().width(),
169 picture
->LayerRect().height());
170 picture
->Replay(&debug_canvas
);
172 // Raster the requested command subset into the bitmap-backed canvas.
173 int last_index
= debug_canvas
.getSize() - 1;
174 if (last_index
>= 0) {
175 debug_canvas
.setOverdrawViz(overdraw
);
178 stop_index
< 0 ? last_index
: std::min(last_index
, stop_index
));
181 blink::WebArrayBuffer buffer
=
182 blink::WebArrayBuffer::create(bitmap
.getSize(), 1);
183 uint32
* packed_pixels
= reinterpret_cast<uint32
*>(bitmap
.getPixels());
184 uint8
* buffer_pixels
= reinterpret_cast<uint8
*>(buffer
.data());
185 // Swizzle from native Skia format to RGBA as we copy out.
186 for (size_t i
= 0; i
< bitmap
.getSize(); i
+= 4) {
187 uint32 c
= packed_pixels
[i
>> 2];
188 buffer_pixels
[i
] = SkGetPackedR32(c
);
189 buffer_pixels
[i
+ 1] = SkGetPackedG32(c
);
190 buffer_pixels
[i
+ 2] = SkGetPackedB32(c
);
191 buffer_pixels
[i
+ 3] = SkGetPackedA32(c
);
194 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
195 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
196 v8::Number::New(isolate
, snapped_clip
.width()));
197 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
198 v8::Number::New(isolate
, snapped_clip
.height()));
199 result
->Set(v8::String::NewFromUtf8(isolate
, "data"),
200 blink::WebArrayBufferConverter::toV8Value(
201 &buffer
, context
->Global(), isolate
));
203 args
->Return(result
);
206 void SkiaBenchmarking::GetOps(gin::Arguments
* args
) {
207 v8::Isolate
* isolate
= args
->isolate();
208 if (args
->PeekNext().IsEmpty())
210 v8::Handle
<v8::Value
> picture_handle
;
211 args
->GetNext(&picture_handle
);
212 scoped_refptr
<cc::Picture
> picture
=
213 ParsePictureHash(isolate
, picture_handle
);
217 gfx::Rect bounds
= picture
->LayerRect();
218 SkDebugCanvas
canvas(bounds
.width(), bounds
.height());
219 picture
->Replay(&canvas
);
221 v8::Handle
<v8::Array
> result
= v8::Array::New(isolate
, canvas
.getSize());
222 for (int i
= 0; i
< canvas
.getSize(); ++i
) {
223 DrawType cmd_type
= canvas
.getDrawCommandAt(i
)->getType();
224 v8::Handle
<v8::Object
> cmd
= v8::Object::New(isolate
);
225 cmd
->Set(v8::String::NewFromUtf8(isolate
, "cmd_type"),
226 v8::Integer::New(isolate
, cmd_type
));
227 cmd
->Set(v8::String::NewFromUtf8(isolate
, "cmd_string"),
228 v8::String::NewFromUtf8(
229 isolate
, SkDrawCommand::GetCommandString(cmd_type
)));
231 SkTDArray
<SkString
*>* info
= canvas
.getCommandInfo(i
);
234 v8::Local
<v8::Array
> v8_info
= v8::Array::New(isolate
, info
->count());
235 for (int j
= 0; j
< info
->count(); ++j
) {
236 const SkString
* info_str
= (*info
)[j
];
238 v8_info
->Set(j
, v8::String::NewFromUtf8(isolate
, info_str
->c_str()));
241 cmd
->Set(v8::String::NewFromUtf8(isolate
, "info"), v8_info
);
246 args
->Return(result
.As
<v8::Object
>());
249 void SkiaBenchmarking::GetOpTimings(gin::Arguments
* args
) {
250 v8::Isolate
* isolate
= args
->isolate();
251 if (args
->PeekNext().IsEmpty())
253 v8::Handle
<v8::Value
> picture_handle
;
254 args
->GetNext(&picture_handle
);
255 scoped_refptr
<cc::Picture
> picture
=
256 ParsePictureHash(isolate
, picture_handle
);
260 gfx::Rect bounds
= picture
->LayerRect();
262 // Measure the total time by drawing straight into a bitmap-backed canvas.
264 bitmap
.allocN32Pixels(bounds
.width(), bounds
.height());
265 SkCanvas
bitmap_canvas(bitmap
);
266 bitmap_canvas
.clear(SK_ColorTRANSPARENT
);
267 base::TimeTicks t0
= base::TimeTicks::HighResNow();
268 picture
->Replay(&bitmap_canvas
);
269 base::TimeDelta total_time
= base::TimeTicks::HighResNow() - t0
;
271 // Gather per-op timing info by drawing into a BenchmarkingCanvas.
272 skia::BenchmarkingCanvas
benchmarking_canvas(bounds
.width(), bounds
.height());
273 picture
->Replay(&benchmarking_canvas
);
275 v8::Local
<v8::Array
> op_times
=
276 v8::Array::New(isolate
, benchmarking_canvas
.CommandCount());
277 for (size_t i
= 0; i
< benchmarking_canvas
.CommandCount(); ++i
)
278 op_times
->Set(i
, v8::Number::New(isolate
, benchmarking_canvas
.GetTime(i
)));
280 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
281 result
->Set(v8::String::NewFromUtf8(isolate
, "total_time"),
282 v8::Number::New(isolate
, total_time
.InMillisecondsF()));
283 result
->Set(v8::String::NewFromUtf8(isolate
, "cmd_times"), op_times
);
285 args
->Return(result
);
288 void SkiaBenchmarking::GetInfo(gin::Arguments
* args
) {
289 v8::Isolate
* isolate
= args
->isolate();
290 if (args
->PeekNext().IsEmpty())
292 v8::Handle
<v8::Value
> picture_handle
;
293 args
->GetNext(&picture_handle
);
294 scoped_refptr
<cc::Picture
> picture
=
295 ParsePictureStr(isolate
, picture_handle
);
299 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
300 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
301 v8::Number::New(isolate
, picture
->LayerRect().width()));
302 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
303 v8::Number::New(isolate
, picture
->LayerRect().height()));
305 args
->Return(result
);
308 } // namespace content