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/web/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"
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
> chrome
= GetOrCreateChromeObject(isolate
,
82 chrome
->Set(gin::StringToV8(isolate
, "skiaBenchmarking"), controller
.ToV8());
86 void SkiaBenchmarking::Initialize() {
87 DCHECK(RenderThreadImpl::current());
88 // FIXME: remove this after Skia updates SkGraphics::Init() to be
89 // thread-safe and idempotent.
90 static bool skia_initialized
= false;
91 if (!skia_initialized
) {
92 LOG(WARNING
) << "Enabling unsafe Skia benchmarking extension.";
94 skia_initialized
= true;
98 SkiaBenchmarking::SkiaBenchmarking() {
102 SkiaBenchmarking::~SkiaBenchmarking() {}
104 gin::ObjectTemplateBuilder
SkiaBenchmarking::GetObjectTemplateBuilder(
105 v8::Isolate
* isolate
) {
106 return gin::Wrappable
<SkiaBenchmarking
>::GetObjectTemplateBuilder(isolate
)
107 .SetMethod("rasterize", &SkiaBenchmarking::Rasterize
)
108 .SetMethod("getOps", &SkiaBenchmarking::GetOps
)
109 .SetMethod("getOpTimings", &SkiaBenchmarking::GetOpTimings
)
110 .SetMethod("getInfo", &SkiaBenchmarking::GetInfo
);
113 void SkiaBenchmarking::Rasterize(gin::Arguments
* args
) {
114 v8::Isolate
* isolate
= args
->isolate();
115 if (args
->PeekNext().IsEmpty())
117 v8::Handle
<v8::Value
> picture_handle
;
118 args
->GetNext(&picture_handle
);
119 scoped_refptr
<cc::Picture
> picture
=
120 ParsePictureHash(isolate
, picture_handle
);
125 gfx::Rect
clip_rect(picture
->LayerRect());
127 bool overdraw
= false;
129 v8::Handle
<v8::Context
> context
= isolate
->GetCurrentContext();
130 if (!args
->PeekNext().IsEmpty()) {
131 v8::Handle
<v8::Value
> params
;
132 args
->GetNext(¶ms
);
133 scoped_ptr
<content::V8ValueConverter
> converter(
134 content::V8ValueConverter::create());
135 scoped_ptr
<base::Value
> params_value(
136 converter
->FromV8Value(params
, context
));
138 const base::DictionaryValue
* params_dict
= NULL
;
139 if (params_value
.get() && params_value
->GetAsDictionary(¶ms_dict
)) {
140 params_dict
->GetDouble("scale", &scale
);
141 params_dict
->GetInteger("stop", &stop_index
);
142 params_dict
->GetBoolean("overdraw", &overdraw
);
144 const base::Value
* clip_value
= NULL
;
145 if (params_dict
->Get("clip", &clip_value
))
146 cc::MathUtil::FromValue(clip_value
, &clip_rect
);
150 gfx::RectF
clip(clip_rect
);
151 clip
.Intersect(picture
->LayerRect());
153 gfx::Rect snapped_clip
= gfx::ToEnclosingRect(clip
);
156 if (!bitmap
.tryAllocN32Pixels(snapped_clip
.width(), snapped_clip
.height()))
158 bitmap
.eraseARGB(0, 0, 0, 0);
160 SkCanvas
canvas(bitmap
);
161 canvas
.translate(SkFloatToScalar(-clip
.x()), SkFloatToScalar(-clip
.y()));
162 canvas
.clipRect(gfx::RectToSkRect(snapped_clip
));
163 canvas
.scale(scale
, scale
);
164 canvas
.translate(picture
->LayerRect().x(), picture
->LayerRect().y());
166 // First, build a debug canvas for the given picture.
167 SkDebugCanvas
debug_canvas(picture
->LayerRect().width(),
168 picture
->LayerRect().height());
169 picture
->Replay(&debug_canvas
);
171 // Raster the requested command subset into the bitmap-backed canvas.
172 int last_index
= debug_canvas
.getSize() - 1;
173 if (last_index
>= 0) {
174 debug_canvas
.setOverdrawViz(overdraw
);
177 stop_index
< 0 ? last_index
: std::min(last_index
, stop_index
));
180 blink::WebArrayBuffer buffer
=
181 blink::WebArrayBuffer::create(bitmap
.getSize(), 1);
182 uint32
* packed_pixels
= reinterpret_cast<uint32
*>(bitmap
.getPixels());
183 uint8
* buffer_pixels
= reinterpret_cast<uint8
*>(buffer
.data());
184 // Swizzle from native Skia format to RGBA as we copy out.
185 for (size_t i
= 0; i
< bitmap
.getSize(); i
+= 4) {
186 uint32 c
= packed_pixels
[i
>> 2];
187 buffer_pixels
[i
] = SkGetPackedR32(c
);
188 buffer_pixels
[i
+ 1] = SkGetPackedG32(c
);
189 buffer_pixels
[i
+ 2] = SkGetPackedB32(c
);
190 buffer_pixels
[i
+ 3] = SkGetPackedA32(c
);
193 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
194 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
195 v8::Number::New(isolate
, snapped_clip
.width()));
196 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
197 v8::Number::New(isolate
, snapped_clip
.height()));
198 result
->Set(v8::String::NewFromUtf8(isolate
, "data"),
199 blink::WebArrayBufferConverter::toV8Value(
200 &buffer
, context
->Global(), isolate
));
202 args
->Return(result
);
205 void SkiaBenchmarking::GetOps(gin::Arguments
* args
) {
206 v8::Isolate
* isolate
= args
->isolate();
207 if (args
->PeekNext().IsEmpty())
209 v8::Handle
<v8::Value
> picture_handle
;
210 args
->GetNext(&picture_handle
);
211 scoped_refptr
<cc::Picture
> picture
=
212 ParsePictureHash(isolate
, picture_handle
);
216 gfx::Rect bounds
= picture
->LayerRect();
217 SkDebugCanvas
canvas(bounds
.width(), bounds
.height());
218 picture
->Replay(&canvas
);
220 v8::Handle
<v8::Array
> result
= v8::Array::New(isolate
, canvas
.getSize());
221 for (int i
= 0; i
< canvas
.getSize(); ++i
) {
222 DrawType cmd_type
= canvas
.getDrawCommandAt(i
)->getType();
223 v8::Handle
<v8::Object
> cmd
= v8::Object::New(isolate
);
224 cmd
->Set(v8::String::NewFromUtf8(isolate
, "cmd_type"),
225 v8::Integer::New(isolate
, cmd_type
));
226 cmd
->Set(v8::String::NewFromUtf8(isolate
, "cmd_string"),
227 v8::String::NewFromUtf8(
228 isolate
, SkDrawCommand::GetCommandString(cmd_type
)));
230 const SkTDArray
<SkString
*>* info
= canvas
.getCommandInfo(i
);
233 v8::Local
<v8::Array
> v8_info
= v8::Array::New(isolate
, info
->count());
234 for (int j
= 0; j
< info
->count(); ++j
) {
235 const SkString
* info_str
= (*info
)[j
];
237 v8_info
->Set(j
, v8::String::NewFromUtf8(isolate
, info_str
->c_str()));
240 cmd
->Set(v8::String::NewFromUtf8(isolate
, "info"), v8_info
);
245 args
->Return(result
.As
<v8::Object
>());
248 void SkiaBenchmarking::GetOpTimings(gin::Arguments
* args
) {
249 v8::Isolate
* isolate
= args
->isolate();
250 if (args
->PeekNext().IsEmpty())
252 v8::Handle
<v8::Value
> picture_handle
;
253 args
->GetNext(&picture_handle
);
254 scoped_refptr
<cc::Picture
> picture
=
255 ParsePictureHash(isolate
, picture_handle
);
259 gfx::Rect bounds
= picture
->LayerRect();
261 // Measure the total time by drawing straight into a bitmap-backed canvas.
263 bitmap
.allocN32Pixels(bounds
.width(), bounds
.height());
264 SkCanvas
bitmap_canvas(bitmap
);
265 bitmap_canvas
.clear(SK_ColorTRANSPARENT
);
266 base::TimeTicks t0
= base::TimeTicks::HighResNow();
267 picture
->Replay(&bitmap_canvas
);
268 base::TimeDelta total_time
= base::TimeTicks::HighResNow() - t0
;
270 // Gather per-op timing info by drawing into a BenchmarkingCanvas.
271 skia::BenchmarkingCanvas
benchmarking_canvas(bounds
.width(), bounds
.height());
272 picture
->Replay(&benchmarking_canvas
);
274 v8::Local
<v8::Array
> op_times
=
275 v8::Array::New(isolate
, benchmarking_canvas
.CommandCount());
276 for (size_t i
= 0; i
< benchmarking_canvas
.CommandCount(); ++i
)
277 op_times
->Set(i
, v8::Number::New(isolate
, benchmarking_canvas
.GetTime(i
)));
279 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
280 result
->Set(v8::String::NewFromUtf8(isolate
, "total_time"),
281 v8::Number::New(isolate
, total_time
.InMillisecondsF()));
282 result
->Set(v8::String::NewFromUtf8(isolate
, "cmd_times"), op_times
);
284 args
->Return(result
);
287 void SkiaBenchmarking::GetInfo(gin::Arguments
* args
) {
288 v8::Isolate
* isolate
= args
->isolate();
289 if (args
->PeekNext().IsEmpty())
291 v8::Handle
<v8::Value
> picture_handle
;
292 args
->GetNext(&picture_handle
);
293 scoped_refptr
<cc::Picture
> picture
=
294 ParsePictureStr(isolate
, picture_handle
);
298 v8::Handle
<v8::Object
> result
= v8::Object::New(isolate
);
299 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
300 v8::Number::New(isolate
, picture
->LayerRect().width()));
301 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
302 v8::Number::New(isolate
, picture
->LayerRect().height()));
304 args
->Return(result
);
307 } // namespace content