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 "content/common/browser_rendering_stats.h"
15 #include "content/common/gpu/gpu_rendering_stats.h"
16 #include "content/public/renderer/render_thread.h"
17 #include "content/renderer/all_rendering_benchmarks.h"
18 #include "content/renderer/gpu/render_widget_compositor.h"
19 #include "content/renderer/render_view_impl.h"
20 #include "content/renderer/rendering_benchmark.h"
21 #include "content/renderer/skia_benchmarking_extension.h"
22 #include "third_party/WebKit/public/web/WebFrame.h"
23 #include "third_party/WebKit/public/web/WebImageCache.h"
24 #include "third_party/WebKit/public/web/WebView.h"
25 #include "third_party/WebKit/public/web/WebViewBenchmarkSupport.h"
26 #include "third_party/skia/include/core/SkData.h"
27 #include "third_party/skia/include/core/SkGraphics.h"
28 #include "third_party/skia/include/core/SkPicture.h"
29 #include "third_party/skia/include/core/SkPixelRef.h"
30 #include "third_party/skia/include/core/SkStream.h"
31 #include "ui/gfx/codec/png_codec.h"
32 #include "v8/include/v8.h"
33 #include "webkit/renderer/compositor_bindings/web_rendering_stats_impl.h"
35 using WebKit::WebCanvas
;
36 using WebKit::WebFrame
;
37 using WebKit::WebImageCache
;
38 using WebKit::WebPrivatePtr
;
39 using WebKit::WebRenderingStatsImpl
;
40 using WebKit::WebSize
;
41 using WebKit::WebView
;
42 using WebKit::WebViewBenchmarkSupport
;
44 const char kGpuBenchmarkingExtensionName
[] = "v8/GpuBenchmarking";
46 static SkData
* EncodeBitmapToData(size_t* offset
, const SkBitmap
& bm
) {
47 SkPixelRef
* pr
= bm
.pixelRef();
49 SkData
* data
= pr
->refEncodedData();
51 *offset
= bm
.pixelRefOffset();
55 std::vector
<unsigned char> vector
;
56 if (gfx::PNGCodec::EncodeBGRASkBitmap(bm
, true, &vector
)) {
57 return SkData::NewWithCopy(&vector
.front() , vector
.size());
64 class SkPictureRecorder
: public WebViewBenchmarkSupport::PaintClient
{
66 explicit SkPictureRecorder(const base::FilePath
& dirpath
)
69 // Let skia register known effect subclasses. This basically enables
70 // reflection on those subclasses required for picture serialization.
71 content::SkiaBenchmarkingExtension::InitSkGraphics();
74 virtual WebCanvas
* willPaint(const WebSize
& size
) {
75 return picture_
.beginRecording(size
.width
, size
.height
);
78 virtual void didPaint(WebCanvas
* canvas
) {
79 DCHECK(canvas
== picture_
.getRecordingCanvas());
80 picture_
.endRecording();
81 // Serialize picture to file.
82 // TODO(alokp): Note that for this to work Chrome needs to be launched with
83 // --no-sandbox command-line flag. Get rid of this limitation.
85 std::string filename
= "layer_" + base::IntToString(layer_id_
++) + ".skp";
86 std::string filepath
= dirpath_
.AppendASCII(filename
).MaybeAsASCII();
87 DCHECK(!filepath
.empty());
88 SkFILEWStream
file(filepath
.c_str());
89 DCHECK(file
.isValid());
90 picture_
.serialize(&file
, &EncodeBitmapToData
);
94 base::FilePath dirpath_
;
99 class RenderingStatsEnumerator
: public cc::RenderingStats::Enumerator
{
101 RenderingStatsEnumerator(v8::Handle
<v8::Object
> stats_object
)
102 : stats_object(stats_object
) { }
104 virtual void AddInt64(const char* name
, int64 value
) OVERRIDE
{
105 stats_object
->Set(v8::String::New(name
), v8::Number::New(value
));
108 virtual void AddDouble(const char* name
, double value
) OVERRIDE
{
109 stats_object
->Set(v8::String::New(name
), v8::Number::New(value
));
112 virtual void AddInt(const char* name
, int value
) OVERRIDE
{
113 stats_object
->Set(v8::String::New(name
), v8::Integer::New(value
));
116 virtual void AddTimeDeltaInSecondsF(const char* name
,
117 const base::TimeDelta
& value
) OVERRIDE
{
118 stats_object
->Set(v8::String::New(name
),
119 v8::Number::New(value
.InSecondsF()));
123 v8::Handle
<v8::Object
> stats_object
;
132 class CallbackAndContext
: public base::RefCounted
<CallbackAndContext
> {
134 CallbackAndContext(v8::Isolate
* isolate
,
135 v8::Handle
<v8::Function
> callback
,
136 v8::Handle
<v8::Context
> context
)
137 : isolate_(isolate
) {
138 callback_
.Reset(isolate_
, callback
);
139 context_
.Reset(isolate_
, context
);
142 v8::Isolate
* isolate() {
146 v8::Handle
<v8::Function
> GetCallback() {
147 return v8::Local
<v8::Function
>::New(isolate_
, callback_
);
150 v8::Handle
<v8::Context
> GetContext() {
151 return v8::Local
<v8::Context
>::New(isolate_
, context_
);
155 friend class base::RefCounted
<CallbackAndContext
>;
157 virtual ~CallbackAndContext() {
162 v8::Isolate
* isolate_
;
163 v8::Persistent
<v8::Function
> callback_
;
164 v8::Persistent
<v8::Context
> context_
;
165 DISALLOW_COPY_AND_ASSIGN(CallbackAndContext
);
170 class GpuBenchmarkingWrapper
: public v8::Extension
{
172 GpuBenchmarkingWrapper() :
173 v8::Extension(kGpuBenchmarkingExtensionName
,
174 "if (typeof(chrome) == 'undefined') {"
177 "if (typeof(chrome.gpuBenchmarking) == 'undefined') {"
178 " chrome.gpuBenchmarking = {};"
180 "chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers = function() {"
181 " native function SetNeedsDisplayOnAllLayers();"
182 " return SetNeedsDisplayOnAllLayers();"
184 "chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent = function() {"
185 " native function SetRasterizeOnlyVisibleContent();"
186 " return SetRasterizeOnlyVisibleContent();"
188 "chrome.gpuBenchmarking.renderingStats = function() {"
189 " native function GetRenderingStats();"
190 " return GetRenderingStats();"
192 "chrome.gpuBenchmarking.printToSkPicture = function(dirname) {"
193 " native function PrintToSkPicture();"
194 " return PrintToSkPicture(dirname);"
196 "chrome.gpuBenchmarking.smoothScrollBy = "
197 " function(pixels_to_scroll, opt_callback, opt_mouse_event_x,"
198 " opt_mouse_event_y) {"
199 " pixels_to_scroll = pixels_to_scroll || 0;"
200 " callback = opt_callback || function() { };"
201 " native function BeginSmoothScroll();"
202 " if (typeof opt_mouse_event_x !== 'undefined' &&"
203 " typeof opt_mouse_event_y !== 'undefined') {"
204 " return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
205 " Math.abs(pixels_to_scroll),"
206 " opt_mouse_event_x, opt_mouse_event_y);"
208 " return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
209 " Math.abs(pixels_to_scroll));"
212 "chrome.gpuBenchmarking.runRenderingBenchmarks = function(filter) {"
213 " native function RunRenderingBenchmarks();"
214 " return RunRenderingBenchmarks(filter);"
216 "chrome.gpuBenchmarking.beginWindowSnapshotPNG = function(callback) {"
217 " native function BeginWindowSnapshotPNG();"
218 " BeginWindowSnapshotPNG(callback);"
220 "chrome.gpuBenchmarking.clearImageCache = function() {"
221 " native function ClearImageCache();"
222 " ClearImageCache();"
225 virtual v8::Handle
<v8::FunctionTemplate
> GetNativeFunction(
226 v8::Handle
<v8::String
> name
) OVERRIDE
{
227 if (name
->Equals(v8::String::New("SetNeedsDisplayOnAllLayers")))
228 return v8::FunctionTemplate::New(SetNeedsDisplayOnAllLayers
);
229 if (name
->Equals(v8::String::New("SetRasterizeOnlyVisibleContent")))
230 return v8::FunctionTemplate::New(SetRasterizeOnlyVisibleContent
);
231 if (name
->Equals(v8::String::New("GetRenderingStats")))
232 return v8::FunctionTemplate::New(GetRenderingStats
);
233 if (name
->Equals(v8::String::New("PrintToSkPicture")))
234 return v8::FunctionTemplate::New(PrintToSkPicture
);
235 if (name
->Equals(v8::String::New("BeginSmoothScroll")))
236 return v8::FunctionTemplate::New(BeginSmoothScroll
);
237 if (name
->Equals(v8::String::New("RunRenderingBenchmarks")))
238 return v8::FunctionTemplate::New(RunRenderingBenchmarks
);
239 if (name
->Equals(v8::String::New("BeginWindowSnapshotPNG")))
240 return v8::FunctionTemplate::New(BeginWindowSnapshotPNG
);
241 if (name
->Equals(v8::String::New("ClearImageCache")))
242 return v8::FunctionTemplate::New(ClearImageCache
);
244 return v8::Handle
<v8::FunctionTemplate
>();
247 static void SetNeedsDisplayOnAllLayers(
248 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
249 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
253 WebView
* web_view
= web_frame
->view();
257 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
258 if (!render_view_impl
)
261 RenderWidgetCompositor
* compositor
= render_view_impl
->compositor();
265 compositor
->SetNeedsDisplayOnAllLayers();
268 static void SetRasterizeOnlyVisibleContent(
269 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
270 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
274 WebView
* web_view
= web_frame
->view();
278 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
279 if (!render_view_impl
)
282 RenderWidgetCompositor
* compositor
= render_view_impl
->compositor();
286 compositor
->SetRasterizeOnlyVisibleContent();
289 static void GetRenderingStats(
290 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
292 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
296 WebView
* web_view
= web_frame
->view();
300 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
301 if (!render_view_impl
)
304 WebRenderingStatsImpl stats
;
305 render_view_impl
->GetRenderingStats(stats
);
307 content::GpuRenderingStats gpu_stats
;
308 render_view_impl
->GetGpuRenderingStats(&gpu_stats
);
309 BrowserRenderingStats browser_stats
;
310 render_view_impl
->GetBrowserRenderingStats(&browser_stats
);
311 v8::Handle
<v8::Object
> stats_object
= v8::Object::New();
313 RenderingStatsEnumerator
enumerator(stats_object
);
314 stats
.rendering_stats
.EnumerateFields(&enumerator
);
315 gpu_stats
.EnumerateFields(&enumerator
);
316 browser_stats
.EnumerateFields(&enumerator
);
318 args
.GetReturnValue().Set(stats_object
);
321 static void PrintToSkPicture(
322 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
323 if (args
.Length() != 1)
326 v8::String::AsciiValue
dirname(args
[0]);
327 if (dirname
.length() == 0)
330 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
334 WebView
* web_view
= web_frame
->view();
338 WebViewBenchmarkSupport
* benchmark_support
= web_view
->benchmarkSupport();
339 if (!benchmark_support
)
342 base::FilePath
dirpath(
343 base::FilePath::StringType(*dirname
, *dirname
+ dirname
.length()));
344 if (!file_util::CreateDirectory(dirpath
) ||
345 !file_util::PathIsWritable(dirpath
)) {
346 std::string
msg("Path is not writable: ");
347 msg
.append(dirpath
.MaybeAsASCII());
348 v8::ThrowException(v8::Exception::Error(
349 v8::String::New(msg
.c_str(), msg
.length())));
353 SkPictureRecorder
recorder(dirpath
);
354 benchmark_support
->paint(&recorder
,
355 WebViewBenchmarkSupport::PaintModeEverything
);
358 static void OnSmoothScrollCompleted(
359 CallbackAndContext
* callback_and_context
) {
360 v8::HandleScope
scope(callback_and_context
->isolate());
361 v8::Handle
<v8::Context
> context
= callback_and_context
->GetContext();
362 v8::Context::Scope
context_scope(context
);
363 WebFrame
* frame
= WebFrame::frameForContext(context
);
365 frame
->callFunctionEvenIfScriptDisabled(
366 callback_and_context
->GetCallback(), v8::Object::New(), 0, NULL
);
370 static void BeginSmoothScroll(
371 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
372 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
376 WebView
* web_view
= web_frame
->view();
380 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
381 if (!render_view_impl
)
384 // Account for the 2 optional arguments, mouse_event_x and mouse_event_y.
385 int arglen
= args
.Length();
387 !args
[0]->IsBoolean() ||
388 !args
[1]->IsFunction() ||
389 !args
[2]->IsNumber()) {
390 args
.GetReturnValue().Set(false);
394 bool scroll_down
= args
[0]->BooleanValue();
395 v8::Local
<v8::Function
> callback_local
=
396 v8::Local
<v8::Function
>::Cast(args
[1]);
398 scoped_refptr
<CallbackAndContext
> callback_and_context
=
399 new CallbackAndContext(args
.GetIsolate(),
401 web_frame
->mainWorldScriptContext());
403 int pixels_to_scroll
= args
[2]->IntegerValue();
405 int mouse_event_x
= 0;
406 int mouse_event_y
= 0;
409 WebKit::WebRect rect
= render_view_impl
->windowRect();
410 mouse_event_x
= rect
.x
+ rect
.width
/ 2;
411 mouse_event_y
= rect
.y
+ rect
.height
/ 2;
414 !args
[3]->IsNumber() ||
415 !args
[4]->IsNumber()) {
416 args
.GetReturnValue().Set(false);
420 mouse_event_x
= args
[3]->IntegerValue() * web_view
->pageScaleFactor();
421 mouse_event_y
= args
[4]->IntegerValue() * web_view
->pageScaleFactor();
424 // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
425 // progress, we will leak the callback and context. This needs to be fixed,
427 render_view_impl
->BeginSmoothScroll(
429 base::Bind(&OnSmoothScrollCompleted
,
430 callback_and_context
),
435 args
.GetReturnValue().Set(true);
438 static void RunRenderingBenchmarks(
439 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
440 // For our name filter, the argument can be undefined or null to run
441 // all benchmarks, or a string for filtering by name.
442 if (!args
.Length() ||
443 (!args
[0]->IsString() &&
444 !(args
[0]->IsNull() || args
[0]->IsUndefined()))) {
448 std::string name_filter
;
449 if (args
[0]->IsNull() || args
[0]->IsUndefined()) {
453 args
[0]->ToString()->WriteUtf8(filter
, sizeof(filter
)-1);
454 name_filter
= std::string(filter
);
457 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
461 WebView
* web_view
= web_frame
->view();
465 WebViewBenchmarkSupport
* support
= web_view
->benchmarkSupport();
469 ScopedVector
<RenderingBenchmark
> benchmarks
= AllRenderingBenchmarks();
471 v8::Handle
<v8::Array
> results
= v8::Array::New(0);
472 ScopedVector
<RenderingBenchmark
>::const_iterator it
;
473 for (it
= benchmarks
.begin(); it
!= benchmarks
.end(); it
++) {
474 RenderingBenchmark
* benchmark
= *it
;
475 const std::string
& name
= benchmark
->name();
476 if (name_filter
!= "" &&
477 std::string::npos
== name
.find(name_filter
)) {
480 benchmark
->SetUp(support
);
481 double result
= benchmark
->Run(support
);
482 benchmark
->TearDown(support
);
484 v8::Handle
<v8::Object
> result_object
= v8::Object::New();
485 result_object
->Set(v8::String::New("benchmark", 9),
486 v8::String::New(name
.c_str(), -1));
487 result_object
->Set(v8::String::New("result", 6), v8::Number::New(result
));
488 results
->Set(results
->Length(), result_object
);
491 args
.GetReturnValue().Set(results
);
494 static void OnSnapshotCompleted(CallbackAndContext
* callback_and_context
,
495 const gfx::Size
& size
,
496 const std::vector
<unsigned char>& png
) {
497 v8::HandleScope
scope(callback_and_context
->isolate());
498 v8::Handle
<v8::Context
> context
= callback_and_context
->GetContext();
499 v8::Context::Scope
context_scope(context
);
500 WebFrame
* frame
= WebFrame::frameForContext(context
);
503 v8::Handle
<v8::Value
> result
;
505 if(!size
.IsEmpty()) {
506 v8::Handle
<v8::Object
> result_object
;
507 result_object
= v8::Object::New();
509 result_object
->Set(v8::String::New("width"),
510 v8::Number::New(size
.width()));
511 result_object
->Set(v8::String::New("height"),
512 v8::Number::New(size
.height()));
514 std::string base64_png
;
515 base::Base64Encode(base::StringPiece(
516 reinterpret_cast<const char*>(&*png
.begin()), png
.size()),
519 result_object
->Set(v8::String::New("data"),
520 v8::String::New(base64_png
.c_str(), base64_png
.size()));
522 result
= result_object
;
527 v8::Handle
<v8::Value
> argv
[] = { result
};
529 frame
->callFunctionEvenIfScriptDisabled(
530 callback_and_context
->GetCallback(), v8::Object::New(), 1, argv
);
534 static void BeginWindowSnapshotPNG(
535 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
536 WebFrame
* web_frame
= WebFrame::frameForCurrentContext();
540 WebView
* web_view
= web_frame
->view();
544 RenderViewImpl
* render_view_impl
= RenderViewImpl::FromWebView(web_view
);
545 if (!render_view_impl
)
548 if (!args
[0]->IsFunction())
551 v8::Local
<v8::Function
> callback_local
=
552 v8::Local
<v8::Function
>::Cast(args
[0]);
554 scoped_refptr
<CallbackAndContext
> callback_and_context
=
555 new CallbackAndContext(args
.GetIsolate(),
557 web_frame
->mainWorldScriptContext());
559 render_view_impl
->GetWindowSnapshot(
560 base::Bind(&OnSnapshotCompleted
, callback_and_context
));
563 static void ClearImageCache(
564 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
565 WebImageCache::clear();
569 v8::Extension
* GpuBenchmarkingExtension::Get() {
570 return new GpuBenchmarkingWrapper();
573 } // namespace content