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 clip_rect
.Intersect(picture
->LayerRect());
163 gfx::Rect snapped_clip
= gfx::ScaleToEnclosingRect(clip_rect
, scale
);
166 if (!bitmap
.tryAllocN32Pixels(snapped_clip
.width(), snapped_clip
.height()))
168 bitmap
.eraseARGB(0, 0, 0, 0);
170 SkCanvas
canvas(bitmap
);
171 canvas
.translate(SkIntToScalar(-clip_rect
.x()),
172 SkIntToScalar(-clip_rect
.y()));
173 canvas
.clipRect(gfx::RectToSkRect(snapped_clip
));
174 canvas
.scale(scale
, scale
);
175 canvas
.translate(picture
->LayerRect().x(), picture
->LayerRect().y());
177 skia::BenchmarkingCanvas
benchmarking_canvas(
179 overdraw
? skia::BenchmarkingCanvas::kOverdrawVisualization_Flag
: 0);
180 size_t playback_count
=
181 (stop_index
< 0) ? std::numeric_limits
<size_t>::max() : stop_index
;
182 PicturePlaybackController
controller(benchmarking_canvas
, playback_count
);
183 picture
->Replay(&benchmarking_canvas
, &controller
);
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::Local
<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::Local
<v8::Value
> picture_handle
;
215 args
->GetNext(&picture_handle
);
216 scoped_refptr
<cc::Picture
> picture
=
217 ParsePictureHash(isolate
, picture_handle
);
221 SkCanvas
canvas(picture
->LayerRect().width(), picture
->LayerRect().height());
222 skia::BenchmarkingCanvas
benchmarking_canvas(&canvas
);
223 picture
->Replay(&benchmarking_canvas
);
225 v8::Local
<v8::Context
> context
= isolate
->GetCurrentContext();
226 scoped_ptr
<content::V8ValueConverter
> converter(
227 content::V8ValueConverter::create());
229 args
->Return(converter
->ToV8Value(&benchmarking_canvas
.Commands(), context
));
232 void SkiaBenchmarking::GetOpTimings(gin::Arguments
* args
) {
233 v8::Isolate
* isolate
= args
->isolate();
234 if (args
->PeekNext().IsEmpty())
236 v8::Local
<v8::Value
> picture_handle
;
237 args
->GetNext(&picture_handle
);
238 scoped_refptr
<cc::Picture
> picture
=
239 ParsePictureHash(isolate
, picture_handle
);
243 gfx::Rect bounds
= picture
->LayerRect();
245 // Measure the total time by drawing straight into a bitmap-backed canvas.
247 bitmap
.allocN32Pixels(bounds
.width(), bounds
.height());
248 SkCanvas
bitmap_canvas(bitmap
);
249 bitmap_canvas
.clear(SK_ColorTRANSPARENT
);
250 base::TimeTicks t0
= base::TimeTicks::Now();
251 picture
->Replay(&bitmap_canvas
);
252 base::TimeDelta total_time
= base::TimeTicks::Now() - t0
;
254 // Gather per-op timing info by drawing into a BenchmarkingCanvas.
255 SkCanvas
canvas(bitmap
);
256 canvas
.clear(SK_ColorTRANSPARENT
);
257 skia::BenchmarkingCanvas
benchmarking_canvas(&canvas
);
258 picture
->Replay(&benchmarking_canvas
);
260 v8::Local
<v8::Array
> op_times
=
261 v8::Array::New(isolate
, benchmarking_canvas
.CommandCount());
262 for (size_t i
= 0; i
< benchmarking_canvas
.CommandCount(); ++i
) {
263 op_times
->Set(i
, v8::Number::New(isolate
, benchmarking_canvas
.GetTime(i
)));
266 v8::Local
<v8::Object
> result
= v8::Object::New(isolate
);
267 result
->Set(v8::String::NewFromUtf8(isolate
, "total_time"),
268 v8::Number::New(isolate
, total_time
.InMillisecondsF()));
269 result
->Set(v8::String::NewFromUtf8(isolate
, "cmd_times"), op_times
);
271 args
->Return(result
);
274 void SkiaBenchmarking::GetInfo(gin::Arguments
* args
) {
275 v8::Isolate
* isolate
= args
->isolate();
276 if (args
->PeekNext().IsEmpty())
278 v8::Local
<v8::Value
> picture_handle
;
279 args
->GetNext(&picture_handle
);
280 scoped_refptr
<cc::Picture
> picture
=
281 ParsePictureStr(isolate
, picture_handle
);
285 v8::Local
<v8::Object
> result
= v8::Object::New(isolate
);
286 result
->Set(v8::String::NewFromUtf8(isolate
, "width"),
287 v8::Number::New(isolate
, picture
->LayerRect().width()));
288 result
->Set(v8::String::NewFromUtf8(isolate
, "height"),
289 v8::Number::New(isolate
, picture
->LayerRect().height()));
291 args
->Return(result
);
294 } // namespace content