Implemented printToSkPicture without using WebViewBenchmarkSupport.
[chromium-blink-merge.git] / content / renderer / gpu / gpu_benchmarking_extension.cc
blob4cd00ee68425aa93a017f91f471fe2c519d783b6
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"
7 #include <string>
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();
46 if (pr != NULL) {
47 SkData* data = pr->refEncodedData();
48 if (data != NULL) {
49 *offset = bm.pixelRefOffset();
50 return data;
53 std::vector<unsigned char> vector;
54 if (gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &vector)) {
55 return SkData::NewWithCopy(&vector.front() , vector.size());
57 return NULL;
60 namespace {
62 class SkPictureSerializer {
63 public:
64 explicit SkPictureSerializer(const base::FilePath& dirpath)
65 : dirpath_(dirpath),
66 layer_id_(0) {
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();
82 if (!picture)
83 return;
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.
88 // CRBUG: 139640.
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);
97 private:
98 base::FilePath dirpath_;
99 int layer_id_;
102 class RenderingStatsEnumerator : public cc::RenderingStats::Enumerator {
103 public:
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()));
125 private:
126 v8::Handle<v8::Object> stats_object;
129 } // namespace
131 namespace content {
133 namespace {
135 class CallbackAndContext : public base::RefCounted<CallbackAndContext> {
136 public:
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() {
146 return 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_);
157 private:
158 friend class base::RefCounted<CallbackAndContext>;
160 virtual ~CallbackAndContext() {
161 callback_.Dispose();
162 context_.Dispose();
165 v8::Isolate* isolate_;
166 v8::Persistent<v8::Function> callback_;
167 v8::Persistent<v8::Context> context_;
168 DISALLOW_COPY_AND_ASSIGN(CallbackAndContext);
171 } // namespace
173 class GpuBenchmarkingWrapper : public v8::Extension {
174 public:
175 GpuBenchmarkingWrapper() :
176 v8::Extension(kGpuBenchmarkingExtensionName,
177 "if (typeof(chrome) == 'undefined') {"
178 " chrome = {};"
179 "};"
180 "if (typeof(chrome.gpuBenchmarking) == 'undefined') {"
181 " chrome.gpuBenchmarking = {};"
182 "};"
183 "chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers = function() {"
184 " native function SetNeedsDisplayOnAllLayers();"
185 " return SetNeedsDisplayOnAllLayers();"
186 "};"
187 "chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent = function() {"
188 " native function SetRasterizeOnlyVisibleContent();"
189 " return SetRasterizeOnlyVisibleContent();"
190 "};"
191 "chrome.gpuBenchmarking.renderingStats = function() {"
192 " native function GetRenderingStats();"
193 " return GetRenderingStats();"
194 "};"
195 "chrome.gpuBenchmarking.printToSkPicture = function(dirname) {"
196 " native function PrintToSkPicture();"
197 " return PrintToSkPicture(dirname);"
198 "};"
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);"
210 " } else {"
211 " return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
212 " Math.abs(pixels_to_scroll));"
213 " }"
214 "};"
215 "chrome.gpuBenchmarking.smoothScrollBySendsTouch = function() {"
216 " native function SmoothScrollSendsTouch();"
217 " return SmoothScrollSendsTouch();"
218 "};"
219 "chrome.gpuBenchmarking.beginWindowSnapshotPNG = function(callback) {"
220 " native function BeginWindowSnapshotPNG();"
221 " BeginWindowSnapshotPNG(callback);"
222 "};"
223 "chrome.gpuBenchmarking.clearImageCache = function() {"
224 " native function ClearImageCache();"
225 " ClearImageCache();"
226 "};") {}
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();
253 if (!web_frame)
254 return;
256 WebView* web_view = web_frame->view();
257 if (!web_view)
258 return;
260 RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
261 if (!render_view_impl)
262 return;
264 RenderWidgetCompositor* compositor = render_view_impl->compositor();
265 if (!compositor)
266 return;
268 compositor->SetNeedsDisplayOnAllLayers();
271 static void SetRasterizeOnlyVisibleContent(
272 const v8::FunctionCallbackInfo<v8::Value>& args) {
273 WebFrame* web_frame = WebFrame::frameForCurrentContext();
274 if (!web_frame)
275 return;
277 WebView* web_view = web_frame->view();
278 if (!web_view)
279 return;
281 RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
282 if (!render_view_impl)
283 return;
285 RenderWidgetCompositor* compositor = render_view_impl->compositor();
286 if (!compositor)
287 return;
289 compositor->SetRasterizeOnlyVisibleContent();
292 static void GetRenderingStats(
293 const v8::FunctionCallbackInfo<v8::Value>& args) {
295 WebFrame* web_frame = WebFrame::frameForCurrentContext();
296 if (!web_frame)
297 return;
299 WebView* web_view = web_frame->view();
300 if (!web_view)
301 return;
303 RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
304 if (!render_view_impl)
305 return;
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)
327 return;
329 v8::String::AsciiValue dirname(args[0]);
330 if (dirname.length() == 0)
331 return;
333 WebFrame* web_frame = WebFrame::frameForCurrentContext();
334 if (!web_frame)
335 return;
337 WebView* web_view = web_frame->view();
338 if (!web_view)
339 return;
341 RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
342 if (!render_view_impl)
343 return;
345 RenderWidgetCompositor* compositor = render_view_impl->compositor();
346 if (!compositor)
347 return;
349 const cc::Layer* root_layer = compositor->GetRootLayer();
350 if (!root_layer)
351 return;
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())));
361 return;
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);
374 if (frame) {
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);
385 #else
386 args.GetReturnValue().Set(false);
387 #endif
390 static void BeginSmoothScroll(
391 const v8::FunctionCallbackInfo<v8::Value>& args) {
392 WebFrame* web_frame = WebFrame::frameForCurrentContext();
393 if (!web_frame)
394 return;
396 WebView* web_view = web_frame->view();
397 if (!web_view)
398 return;
400 RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
401 if (!render_view_impl)
402 return;
404 // Account for the 2 optional arguments, mouse_event_x and mouse_event_y.
405 int arglen = args.Length();
406 if (arglen < 3 ||
407 !args[0]->IsBoolean() ||
408 !args[1]->IsFunction() ||
409 !args[2]->IsNumber()) {
410 args.GetReturnValue().Set(false);
411 return;
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(),
420 callback_local,
421 web_frame->mainWorldScriptContext());
423 int pixels_to_scroll = args[2]->IntegerValue();
425 int mouse_event_x = 0;
426 int mouse_event_y = 0;
428 if (arglen == 3) {
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;
432 } else {
433 if (arglen != 5 ||
434 !args[3]->IsNumber() ||
435 !args[4]->IsNumber()) {
436 args.GetReturnValue().Set(false);
437 return;
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,
446 // somehow.
447 render_view_impl->BeginSmoothScroll(
448 scroll_down,
449 base::Bind(&OnSmoothScrollCompleted,
450 callback_and_context),
451 pixels_to_scroll,
452 mouse_event_x,
453 mouse_event_y);
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);
465 if (frame) {
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()),
481 &base64_png);
483 result_object->Set(v8::String::New("data"),
484 v8::String::New(base64_png.c_str(), base64_png.size()));
486 result = result_object;
487 } else {
488 result = v8::Null();
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();
501 if (!web_frame)
502 return;
504 WebView* web_view = web_frame->view();
505 if (!web_view)
506 return;
508 RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
509 if (!render_view_impl)
510 return;
512 if (!args[0]->IsFunction())
513 return;
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(),
520 callback_local,
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