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/playback/picture.h"
12 #include "content/public/child/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/SkPicture.h"
27 #include "third_party/skia/include/core/SkStream.h"
28 #include "ui/gfx/geometry/rect_conversions.h"
29 #include "ui/gfx/skia_util.h"
30 #include "v8/include/v8.h"
36 scoped_ptr
<base::Value
> ParsePictureArg(v8::Isolate
* isolate
,
37 v8::Local
<v8::Value
> arg
) {
38 scoped_ptr
<content::V8ValueConverter
> converter(
39 content::V8ValueConverter::create());
40 return scoped_ptr
<base::Value
>(
41 converter
->FromV8Value(arg
, isolate
->GetCurrentContext()));
44 scoped_refptr
<cc::Picture
> ParsePictureStr(v8::Isolate
* isolate
,
45 v8::Local
<v8::Value
> arg
) {
46 scoped_ptr
<base::Value
> picture_value
= ParsePictureArg(isolate
, arg
);
49 return cc::Picture::CreateFromSkpValue(picture_value
.get());
52 scoped_refptr
<cc::Picture
> ParsePictureHash(v8::Isolate
* isolate
,
53 v8::Local
<v8::Value
> arg
) {
54 scoped_ptr
<base::Value
> picture_value
= ParsePictureArg(isolate
, arg
);
57 return cc::Picture::CreateFromValue(picture_value
.get());
60 class PicturePlaybackController
: public SkPicture::AbortCallback
{
62 PicturePlaybackController(const skia::BenchmarkingCanvas
& canvas
,
64 : canvas_(canvas
), playback_count_(count
) {}
66 bool abort() override
{ return canvas_
.CommandCount() > playback_count_
; }
69 const skia::BenchmarkingCanvas
& canvas_
;
70 size_t playback_count_
;
75 gin::WrapperInfo
SkiaBenchmarking::kWrapperInfo
= {gin::kEmbedderNativeGin
};
78 void SkiaBenchmarking::Install(blink::WebFrame
* frame
) {
79 v8::Isolate
* isolate
= blink::mainThreadIsolate();
80 v8::HandleScope
handle_scope(isolate
);
81 v8::Local
<v8::Context
> context
= frame
->mainWorldScriptContext();
82 if (context
.IsEmpty())
85 v8::Context::Scope
context_scope(context
);
87 gin::Handle
<SkiaBenchmarking
> controller
=
88 gin::CreateHandle(isolate
, new SkiaBenchmarking());
89 if (controller
.IsEmpty())
92 v8::Local
<v8::Object
> chrome
= GetOrCreateChromeObject(isolate
,
94 chrome
->Set(gin::StringToV8(isolate
, "skiaBenchmarking"), controller
.ToV8());
98 void SkiaBenchmarking::Initialize() {
99 DCHECK(RenderThreadImpl::current());
100 // FIXME: remove this after Skia updates SkGraphics::Init() to be
101 // thread-safe and idempotent.
102 static bool skia_initialized
= false;
103 if (!skia_initialized
) {
104 LOG(WARNING
) << "Enabling unsafe Skia benchmarking extension.";
106 skia_initialized
= true;
110 SkiaBenchmarking::SkiaBenchmarking() {
114 SkiaBenchmarking::~SkiaBenchmarking() {}
116 gin::ObjectTemplateBuilder
SkiaBenchmarking::GetObjectTemplateBuilder(
117 v8::Isolate
* isolate
) {
118 return gin::Wrappable
<SkiaBenchmarking
>::GetObjectTemplateBuilder(isolate
)
119 .SetMethod("rasterize", &SkiaBenchmarking::Rasterize
)
120 .SetMethod("getOps", &SkiaBenchmarking::GetOps
)
121 .SetMethod("getOpTimings", &SkiaBenchmarking::GetOpTimings
)
122 .SetMethod("getInfo", &SkiaBenchmarking::GetInfo
);
125 void SkiaBenchmarking::Rasterize(gin::Arguments
* args
) {
126 v8::Isolate
* isolate
= args
->isolate();
127 if (args
->PeekNext().IsEmpty())
129 v8::Local
<v8::Value
> picture_handle
;
130 args
->GetNext(&picture_handle
);
131 scoped_refptr
<cc::Picture
> picture
=
132 ParsePictureHash(isolate
, picture_handle
);
137 gfx::Rect
clip_rect(picture
->LayerRect());
139 bool overdraw
= false;
141 v8::Local
<v8::Context
> context
= isolate
->GetCurrentContext();
142 if (!args
->PeekNext().IsEmpty()) {
143 v8::Local
<v8::Value
> params
;
144 args
->GetNext(¶ms
);
145 scoped_ptr
<content::V8ValueConverter
> converter(
146 content::V8ValueConverter::create());
147 scoped_ptr
<base::Value
> params_value(
148 converter
->FromV8Value(params
, context
));
150 const base::DictionaryValue
* params_dict
= NULL
;
151 if (params_value
.get() && params_value
->GetAsDictionary(¶ms_dict
)) {
152 params_dict
->GetDouble("scale", &scale
);
153 params_dict
->GetInteger("stop", &stop_index
);
154 params_dict
->GetBoolean("overdraw", &overdraw
);
156 const base::Value
* clip_value
= NULL
;
157 if (params_dict
->Get("clip", &clip_value
))
158 cc::MathUtil::FromValue(clip_value
, &clip_rect
);
162 gfx::RectF
clip(clip_rect
);
163 clip
.Intersect(picture
->LayerRect());
165 gfx::Rect snapped_clip
= gfx::ToEnclosingRect(clip
);
168 if (!bitmap
.tryAllocN32Pixels(snapped_clip
.width(), snapped_clip
.height()))
170 bitmap
.eraseARGB(0, 0, 0, 0);
172 SkCanvas
canvas(bitmap
);
173 canvas
.translate(SkFloatToScalar(-clip
.x()), SkFloatToScalar(-clip
.y()));
174 canvas
.clipRect(gfx::RectToSkRect(snapped_clip
));
175 canvas
.scale(scale
, scale
);
176 canvas
.translate(picture
->LayerRect().x(), picture
->LayerRect().y());
178 skia::BenchmarkingCanvas
benchmarking_canvas(
180 overdraw
? skia::BenchmarkingCanvas::kOverdrawVisualization_Flag
: 0);
181 size_t playback_count
=
182 (stop_index
< 0) ? std::numeric_limits
<size_t>::max() : stop_index
;
183 PicturePlaybackController
controller(benchmarking_canvas
, playback_count
);
184 picture
->Replay(&benchmarking_canvas
, &controller
);
186 blink::WebArrayBuffer buffer
=
187 blink::WebArrayBuffer::create(bitmap
.getSize(), 1);
188 uint32
* packed_pixels
= reinterpret_cast<uint32
*>(bitmap
.getPixels());
189 uint8
* buffer_pixels
= reinterpret_cast<uint8
*>(buffer
.data());
190 // Swizzle from native Skia format to RGBA as we copy out.
191 for (size_t i
= 0; i
< bitmap
.getSize(); i
+= 4) {
192 uint32 c
= packed_pixels
[i
>> 2];
193 buffer_pixels
[i
] = SkGetPackedR32(c
);
194 buffer_pixels
[i
+ 1] = SkGetPackedG32(c
);
195 buffer_pixels
[i
+ 2] = SkGetPackedB32(c
);
196 buffer_pixels
[i
+ 3] = SkGetPackedA32(c
);
199 v8::Local
<v8::Object
> result
= v8::Object::New(isolate
);
200 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
201 v8::Number::New(isolate
, snapped_clip
.width()));
202 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
203 v8::Number::New(isolate
, snapped_clip
.height()));
204 result
->Set(v8::String::NewFromUtf8(isolate
, "data"),
205 blink::WebArrayBufferConverter::toV8Value(
206 &buffer
, context
->Global(), isolate
));
208 args
->Return(result
);
211 void SkiaBenchmarking::GetOps(gin::Arguments
* args
) {
212 v8::Isolate
* isolate
= args
->isolate();
213 if (args
->PeekNext().IsEmpty())
215 v8::Local
<v8::Value
> picture_handle
;
216 args
->GetNext(&picture_handle
);
217 scoped_refptr
<cc::Picture
> picture
=
218 ParsePictureHash(isolate
, picture_handle
);
222 SkCanvas
canvas(picture
->LayerRect().width(), picture
->LayerRect().height());
223 skia::BenchmarkingCanvas
benchmarking_canvas(&canvas
);
224 picture
->Replay(&benchmarking_canvas
);
226 v8::Local
<v8::Context
> context
= isolate
->GetCurrentContext();
227 scoped_ptr
<content::V8ValueConverter
> converter(
228 content::V8ValueConverter::create());
230 args
->Return(converter
->ToV8Value(&benchmarking_canvas
.Commands(), context
));
233 void SkiaBenchmarking::GetOpTimings(gin::Arguments
* args
) {
234 v8::Isolate
* isolate
= args
->isolate();
235 if (args
->PeekNext().IsEmpty())
237 v8::Local
<v8::Value
> picture_handle
;
238 args
->GetNext(&picture_handle
);
239 scoped_refptr
<cc::Picture
> picture
=
240 ParsePictureHash(isolate
, picture_handle
);
244 gfx::Rect bounds
= picture
->LayerRect();
246 // Measure the total time by drawing straight into a bitmap-backed canvas.
248 bitmap
.allocN32Pixels(bounds
.width(), bounds
.height());
249 SkCanvas
bitmap_canvas(bitmap
);
250 bitmap_canvas
.clear(SK_ColorTRANSPARENT
);
251 base::TimeTicks t0
= base::TimeTicks::Now();
252 picture
->Replay(&bitmap_canvas
);
253 base::TimeDelta total_time
= base::TimeTicks::Now() - t0
;
255 // Gather per-op timing info by drawing into a BenchmarkingCanvas.
256 SkCanvas
canvas(bitmap
);
257 canvas
.clear(SK_ColorTRANSPARENT
);
258 skia::BenchmarkingCanvas
benchmarking_canvas(&canvas
);
259 picture
->Replay(&benchmarking_canvas
);
261 v8::Local
<v8::Array
> op_times
=
262 v8::Array::New(isolate
, benchmarking_canvas
.CommandCount());
263 for (size_t i
= 0; i
< benchmarking_canvas
.CommandCount(); ++i
) {
264 op_times
->Set(i
, v8::Number::New(isolate
, benchmarking_canvas
.GetTime(i
)));
267 v8::Local
<v8::Object
> result
= v8::Object::New(isolate
);
268 result
->Set(v8::String::NewFromUtf8(isolate
, "total_time"),
269 v8::Number::New(isolate
, total_time
.InMillisecondsF()));
270 result
->Set(v8::String::NewFromUtf8(isolate
, "cmd_times"), op_times
);
272 args
->Return(result
);
275 void SkiaBenchmarking::GetInfo(gin::Arguments
* args
) {
276 v8::Isolate
* isolate
= args
->isolate();
277 if (args
->PeekNext().IsEmpty())
279 v8::Local
<v8::Value
> picture_handle
;
280 args
->GetNext(&picture_handle
);
281 scoped_refptr
<cc::Picture
> picture
=
282 ParsePictureStr(isolate
, picture_handle
);
286 v8::Local
<v8::Object
> result
= v8::Object::New(isolate
);
287 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
288 v8::Number::New(isolate
, picture
->LayerRect().width()));
289 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
290 v8::Number::New(isolate
, picture
->LayerRect().height()));
292 args
->Return(result
);
295 } // namespace content