1 // Copyright (c) 2012 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/gpu/gpu_benchmarking_extension.h"
9 #include "base/base64.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/memory/scoped_vector.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "cc/layers/layer.h"
15 #include "content/common/browser_rendering_stats.h"
16 #include "content/common/gpu/gpu_rendering_stats.h"
17 #include "content/public/renderer/render_thread.h"
18 #include "content/renderer/gpu/render_widget_compositor.h"
19 #include "content/renderer/render_view_impl.h"
20 #include "content/renderer/skia_benchmarking_extension.h"
21 #include "third_party/WebKit/public/web/WebFrame.h"
22 #include "third_party/WebKit/public/web/WebImageCache.h"
23 #include "third_party/WebKit/public/web/WebView.h"
24 #include "third_party/skia/include/core/SkData.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/SkPixelRef.h"
28 #include "third_party/skia/include/core/SkStream.h"
29 #include "ui/gfx/codec/png_codec.h"
30 #include "v8/include/v8.h"
31 #include "webkit/renderer/compositor_bindings/web_rendering_stats_impl.h"
33 using WebKit::WebCanvas
;
34 using WebKit::WebFrame
;
35 using WebKit::WebImageCache
;
36 using WebKit::WebPrivatePtr
;
37 using WebKit::WebRenderingStatsImpl
;
38 using WebKit::WebSize
;
39 using WebKit::WebView
;
40 using WebKit::WebViewBenchmarkSupport
;
42 const char kGpuBenchmarkingExtensionName
[] = "v8/GpuBenchmarking";
44 static SkData
* EncodeBitmapToData(size_t* offset
, const SkBitmap
& bm
) {
45 SkPixelRef
* pr
= bm
.pixelRef();
47 SkData
* data
= pr
->refEncodedData();
49 *offset
= bm
.pixelRefOffset();
53 std::vector
<unsigned char> vector
;
54 if (gfx::PNGCodec::EncodeBGRASkBitmap(bm
, false, &vector
)) {
55 return SkData::NewWithCopy(&vector
.front() , vector
.size());
62 class SkPictureSerializer
{
64 explicit SkPictureSerializer(const base::FilePath
& dirpath
)
67 // Let skia register known effect subclasses. This basically enables
68 // reflection on those subclasses required for picture serialization.
69 content::SkiaBenchmarkingExtension::InitSkGraphics();
72 // Recursively serializes the layer tree.
73 // Each layer in the tree is serialized into a separate skp file
74 // in the given directory.
75 void Serialize(const cc::Layer
* layer
) {
76 const cc::LayerList
& children
= layer
->children();
77 for (size_t i
= 0; i
< children
.size(); ++i
) {
78 Serialize(children
[i
].get());
81 skia::RefPtr
<SkPicture
> picture
= layer
->GetPicture();
85 // Serialize picture to file.
86 // TODO(alokp): Note that for this to work Chrome needs to be launched with
87 // --no-sandbox command-line flag. Get rid of this limitation.
89 std::string filename
= "layer_" + base::IntToString(layer_id_
++) + ".skp";
90 std::string filepath
= dirpath_
.AppendASCII(filename
).MaybeAsASCII();
91 DCHECK(!filepath
.empty());
92 SkFILEWStream
file(filepath
.c_str());
93 DCHECK(file
.isValid());
94 picture
->serialize(&file
, &EncodeBitmapToData
);
98 base::FilePath dirpath_
;
102 class RenderingStatsEnumerator
: public cc::RenderingStats::Enumerator
{
104 RenderingStatsEnumerator(v8::Handle
<v8::Object
> stats_object
)
105 : stats_object(stats_object
) { }
107 virtual void AddInt64(const char* name
, int64 value
) OVERRIDE
{
108 stats_object
->Set(v8::String::New(name
), v8::Number::New(value
));
111 virtual void AddDouble(const char* name
, double value
) OVERRIDE
{
112 stats_object
->Set(v8::String::New(name
), v8::Number::New(value
));
115 virtual void AddInt(const char* name
, int value
) OVERRIDE
{
116 stats_object
->Set(v8::String::New(name
), v8::Integer::New(value
));
119 virtual void AddTimeDeltaInSecondsF(const char* name
,
120 const base::TimeDelta
& value
) OVERRIDE
{
121 stats_object
->Set(v8::String::New(name
),
122 v8::Number::New(value
.InSecondsF()));
126 v8::Handle
<v8::Object
> stats_object
;
135 class CallbackAndContext
: public base::RefCounted
<CallbackAndContext
> {
137 CallbackAndContext(v8::Isolate
* isolate
,
138 v8::Handle
<v8::Function
> callback
,
139 v8::Handle
<v8::Context
> context
)
140 : isolate_(isolate
) {
141 callback_
.Reset(isolate_
, callback
);
142 context_
.Reset(isolate_
, context
);
145 v8::Isolate
* isolate() {
149 v8::Handle
<v8::Function
> GetCallback() {
150 return v8::Local
<v8::Function
>::New(isolate_
, callback_
);
153 v8::Handle
<v8::Context
> GetContext() {
154 return v8::Local
<v8::Context
>::New(isolate_
, context_
);
158 friend class base::RefCounted
<CallbackAndContext
>;
160 virtual ~CallbackAndContext() {
165 v8::Isolate
* isolate_
;
166 v8::Persistent
<v8::Function
> callback_
;
167 v8::Persistent
<v8::Context
> context_
;
168 DISALLOW_COPY_AND_ASSIGN(CallbackAndContext
);
173 class GpuBenchmarkingWrapper
: public v8::Extension
{
175 GpuBenchmarkingWrapper() :
176 v8::Extension(kGpuBenchmarkingExtensionName
,
177 "if (typeof(chrome) == 'undefined') {"
180 "if (typeof(chrome.gpuBenchmarking) == 'undefined') {"
181 " chrome.gpuBenchmarking = {};"
183 "chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers = function() {"
184 " native function SetNeedsDisplayOnAllLayers();"
185 " return SetNeedsDisplayOnAllLayers();"
187 "chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent = function() {"
188 " native function SetRasterizeOnlyVisibleContent();"
189 " return SetRasterizeOnlyVisibleContent();"
191 "chrome.gpuBenchmarking.renderingStats = function() {"
192 " native function GetRenderingStats();"
193 " return GetRenderingStats();"
195 "chrome.gpuBenchmarking.printToSkPicture = function(dirname) {"
196 " native function PrintToSkPicture();"
197 " return PrintToSkPicture(dirname);"
199 "chrome.gpuBenchmarking.smoothScrollBy = "
200 " function(pixels_to_scroll, opt_callback, opt_mouse_event_x,"
201 " opt_mouse_event_y) {"
202 " pixels_to_scroll = pixels_to_scroll || 0;"
203 " callback = opt_callback || function() { };"
204 " native function BeginSmoothScroll();"
205 " if (typeof opt_mouse_event_x !== 'undefined' &&"
206 " typeof opt_mouse_event_y !== 'undefined') {"
207 " return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
208 " Math.abs(pixels_to_scroll),"
209 " opt_mouse_event_x, opt_mouse_event_y);"
211 " return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
212 " Math.abs(pixels_to_scroll));"
215 "chrome.gpuBenchmarking.smoothScrollBySendsTouch = function() {"
216 " native function SmoothScrollSendsTouch();"
217 " return SmoothScrollSendsTouch();"
219 "chrome.gpuBenchmarking.beginWindowSnapshotPNG = function(callback) {"
220 " native function BeginWindowSnapshotPNG();"
221 " BeginWindowSnapshotPNG(callback);"
223 "chrome.gpuBenchmarking.clearImageCache = function() {"
224 " native function ClearImageCache();"
225 " ClearImageCache();"
228 virtual v8::Handle
<v8::FunctionTemplate
> GetNativeFunction(
229 v8::Handle
<v8::String
> name
) OVERRIDE
{
230 if (name
->Equals(v8::String::New("SetNeedsDisplayOnAllLayers")))
231 return v8::FunctionTemplate::New(SetNeedsDisplayOnAllLayers
);
232 if (name
->Equals(v8::String::New("SetRasterizeOnlyVisibleContent")))
233 return v8::FunctionTemplate::New(SetRasterizeOnlyVisibleContent
);
234 if (name
->Equals(v8::String::New("GetRenderingStats")))
235 return v8::FunctionTemplate::New(GetRenderingStats
);
236 if (name
->Equals(v8::String::New("PrintToSkPicture")))
237 return v8::FunctionTemplate::New(PrintToSkPicture
);
238 if (name
->Equals(v8::String::New("BeginSmoothScroll")))
239 return v8::FunctionTemplate::New(BeginSmoothScroll
);
240 if (name
->Equals(v8::String::New("SmoothScrollSendsTouch")))
241 return v8::FunctionTemplate::New(SmoothScrollSendsTouch
);
242 if (name
->Equals(v8::String::New("BeginWindowSnapshotPNG")))
243 return v8::FunctionTemplate::New(BeginWindowSnapshotPNG
);
244 if (name
->Equals(v8::String::New("ClearImageCache")))
245 return v8::FunctionTemplate::New(ClearImageCache
);
247 return v8::Handle
<v8::FunctionTemplate
>();
250 static void SetNeedsDisplayOnAllLayers(
251 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
252 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
256 WebView
* web_view
= web_frame
->view();
260 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
261 if (!render_view_impl
)
264 RenderWidgetCompositor
* compositor
= render_view_impl
->compositor();
268 compositor
->SetNeedsDisplayOnAllLayers();
271 static void SetRasterizeOnlyVisibleContent(
272 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
273 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
277 WebView
* web_view
= web_frame
->view();
281 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
282 if (!render_view_impl
)
285 RenderWidgetCompositor
* compositor
= render_view_impl
->compositor();
289 compositor
->SetRasterizeOnlyVisibleContent();
292 static void GetRenderingStats(
293 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
295 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
299 WebView
* web_view
= web_frame
->view();
303 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
304 if (!render_view_impl
)
307 WebRenderingStatsImpl stats
;
308 render_view_impl
->GetRenderingStats(stats
);
310 content::GpuRenderingStats gpu_stats
;
311 render_view_impl
->GetGpuRenderingStats(&gpu_stats
);
312 BrowserRenderingStats browser_stats
;
313 render_view_impl
->GetBrowserRenderingStats(&browser_stats
);
314 v8::Handle
<v8::Object
> stats_object
= v8::Object::New();
316 RenderingStatsEnumerator
enumerator(stats_object
);
317 stats
.rendering_stats
.EnumerateFields(&enumerator
);
318 gpu_stats
.EnumerateFields(&enumerator
);
319 browser_stats
.EnumerateFields(&enumerator
);
321 args
.GetReturnValue().Set(stats_object
);
324 static void PrintToSkPicture(
325 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
326 if (args
.Length() != 1)
329 v8::String::AsciiValue
dirname(args
[0]);
330 if (dirname
.length() == 0)
333 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
337 WebView
* web_view
= web_frame
->view();
341 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
342 if (!render_view_impl
)
345 RenderWidgetCompositor
* compositor
= render_view_impl
->compositor();
349 const cc::Layer
* root_layer
= compositor
->GetRootLayer();
353 base::FilePath
dirpath(
354 base::FilePath::StringType(*dirname
, *dirname
+ dirname
.length()));
355 if (!file_util::CreateDirectory(dirpath
) ||
356 !base::PathIsWritable(dirpath
)) {
357 std::string
msg("Path is not writable: ");
358 msg
.append(dirpath
.MaybeAsASCII());
359 v8::ThrowException(v8::Exception::Error(
360 v8::String::New(msg
.c_str(), msg
.length())));
364 SkPictureSerializer
serializer(dirpath
);
365 serializer
.Serialize(root_layer
);
368 static void OnSmoothScrollCompleted(
369 CallbackAndContext
* callback_and_context
) {
370 v8::HandleScope
scope(callback_and_context
->isolate());
371 v8::Handle
<v8::Context
> context
= callback_and_context
->GetContext();
372 v8::Context::Scope
context_scope(context
);
373 WebFrame
* frame
= WebFrame::frameForContext(context
);
375 frame
->callFunctionEvenIfScriptDisabled(
376 callback_and_context
->GetCallback(), v8::Object::New(), 0, NULL
);
380 static void SmoothScrollSendsTouch(
381 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
382 // TODO(epenner): Should other platforms emulate touch events?
383 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
384 args
.GetReturnValue().Set(true);
386 args
.GetReturnValue().Set(false);
390 static void BeginSmoothScroll(
391 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
392 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
396 WebView
* web_view
= web_frame
->view();
400 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
401 if (!render_view_impl
)
404 // Account for the 2 optional arguments, mouse_event_x and mouse_event_y.
405 int arglen
= args
.Length();
407 !args
[0]->IsBoolean() ||
408 !args
[1]->IsFunction() ||
409 !args
[2]->IsNumber()) {
410 args
.GetReturnValue().Set(false);
414 bool scroll_down
= args
[0]->BooleanValue();
415 v8::Local
<v8::Function
> callback_local
=
416 v8::Local
<v8::Function
>::Cast(args
[1]);
418 scoped_refptr
<CallbackAndContext
> callback_and_context
=
419 new CallbackAndContext(args
.GetIsolate(),
421 web_frame
->mainWorldScriptContext());
423 int pixels_to_scroll
= args
[2]->IntegerValue();
425 int mouse_event_x
= 0;
426 int mouse_event_y
= 0;
429 WebKit::WebRect rect
= render_view_impl
->windowRect();
430 mouse_event_x
= rect
.x
+ rect
.width
/ 2;
431 mouse_event_y
= rect
.y
+ rect
.height
/ 2;
434 !args
[3]->IsNumber() ||
435 !args
[4]->IsNumber()) {
436 args
.GetReturnValue().Set(false);
440 mouse_event_x
= args
[3]->IntegerValue() * web_view
->pageScaleFactor();
441 mouse_event_y
= args
[4]->IntegerValue() * web_view
->pageScaleFactor();
444 // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
445 // progress, we will leak the callback and context. This needs to be fixed,
447 render_view_impl
->BeginSmoothScroll(
449 base::Bind(&OnSmoothScrollCompleted
,
450 callback_and_context
),
455 args
.GetReturnValue().Set(true);
458 static void OnSnapshotCompleted(CallbackAndContext
* callback_and_context
,
459 const gfx::Size
& size
,
460 const std::vector
<unsigned char>& png
) {
461 v8::HandleScope
scope(callback_and_context
->isolate());
462 v8::Handle
<v8::Context
> context
= callback_and_context
->GetContext();
463 v8::Context::Scope
context_scope(context
);
464 WebFrame
* frame
= WebFrame::frameForContext(context
);
467 v8::Handle
<v8::Value
> result
;
469 if(!size
.IsEmpty()) {
470 v8::Handle
<v8::Object
> result_object
;
471 result_object
= v8::Object::New();
473 result_object
->Set(v8::String::New("width"),
474 v8::Number::New(size
.width()));
475 result_object
->Set(v8::String::New("height"),
476 v8::Number::New(size
.height()));
478 std::string base64_png
;
479 base::Base64Encode(base::StringPiece(
480 reinterpret_cast<const char*>(&*png
.begin()), png
.size()),
483 result_object
->Set(v8::String::New("data"),
484 v8::String::New(base64_png
.c_str(), base64_png
.size()));
486 result
= result_object
;
491 v8::Handle
<v8::Value
> argv
[] = { result
};
493 frame
->callFunctionEvenIfScriptDisabled(
494 callback_and_context
->GetCallback(), v8::Object::New(), 1, argv
);
498 static void BeginWindowSnapshotPNG(
499 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
500 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
504 WebView
* web_view
= web_frame
->view();
508 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
509 if (!render_view_impl
)
512 if (!args
[0]->IsFunction())
515 v8::Local
<v8::Function
> callback_local
=
516 v8::Local
<v8::Function
>::Cast(args
[0]);
518 scoped_refptr
<CallbackAndContext
> callback_and_context
=
519 new CallbackAndContext(args
.GetIsolate(),
521 web_frame
->mainWorldScriptContext());
523 render_view_impl
->GetWindowSnapshot(
524 base::Bind(&OnSnapshotCompleted
, callback_and_context
));
527 static void ClearImageCache(
528 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
529 WebImageCache::clear();
533 v8::Extension
* GpuBenchmarkingExtension::Get() {
534 return new GpuBenchmarkingWrapper();
537 } // namespace content