Home | History | Annotate | Download | only in gpu
      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.
      4 
      5 #include "content/renderer/gpu/gpu_benchmarking_extension.h"
      6 
      7 #include <string>
      8 
      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/gpu/render_widget_compositor.h"
     18 #include "content/renderer/render_view_impl.h"
     19 #include "content/renderer/rendering_benchmark.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/WebKit/public/web/WebViewBenchmarkSupport.h"
     25 #include "third_party/skia/include/core/SkData.h"
     26 #include "third_party/skia/include/core/SkGraphics.h"
     27 #include "third_party/skia/include/core/SkPicture.h"
     28 #include "third_party/skia/include/core/SkPixelRef.h"
     29 #include "third_party/skia/include/core/SkStream.h"
     30 #include "ui/gfx/codec/png_codec.h"
     31 #include "v8/include/v8.h"
     32 #include "webkit/renderer/compositor_bindings/web_rendering_stats_impl.h"
     33 
     34 using WebKit::WebCanvas;
     35 using WebKit::WebFrame;
     36 using WebKit::WebImageCache;
     37 using WebKit::WebPrivatePtr;
     38 using WebKit::WebRenderingStatsImpl;
     39 using WebKit::WebSize;
     40 using WebKit::WebView;
     41 using WebKit::WebViewBenchmarkSupport;
     42 
     43 const char kGpuBenchmarkingExtensionName[] = "v8/GpuBenchmarking";
     44 
     45 static SkData* EncodeBitmapToData(size_t* offset, const SkBitmap& bm) {
     46     SkPixelRef* pr = bm.pixelRef();
     47     if (pr != NULL) {
     48         SkData* data = pr->refEncodedData();
     49         if (data != NULL) {
     50             *offset = bm.pixelRefOffset();
     51             return data;
     52         }
     53     }
     54     std::vector<unsigned char> vector;
     55     if (gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &vector)) {
     56         return SkData::NewWithCopy(&vector.front() , vector.size());
     57     }
     58     return NULL;
     59 }
     60 
     61 namespace {
     62 
     63 class SkPictureRecorder : public WebViewBenchmarkSupport::PaintClient {
     64  public:
     65   explicit SkPictureRecorder(const base::FilePath& dirpath)
     66       : dirpath_(dirpath),
     67         layer_id_(0) {
     68     // Let skia register known effect subclasses. This basically enables
     69     // reflection on those subclasses required for picture serialization.
     70     content::SkiaBenchmarkingExtension::InitSkGraphics();
     71   }
     72 
     73   virtual WebCanvas* willPaint(const WebSize& size) {
     74     return picture_.beginRecording(size.width, size.height);
     75   }
     76 
     77   virtual void didPaint(WebCanvas* canvas) {
     78     DCHECK(canvas == picture_.getRecordingCanvas());
     79     picture_.endRecording();
     80     // Serialize picture to file.
     81     // TODO(alokp): Note that for this to work Chrome needs to be launched with
     82     // --no-sandbox command-line flag. Get rid of this limitation.
     83     // CRBUG: 139640.
     84     std::string filename = "layer_" + base::IntToString(layer_id_++) + ".skp";
     85     std::string filepath = dirpath_.AppendASCII(filename).MaybeAsASCII();
     86     DCHECK(!filepath.empty());
     87     SkFILEWStream file(filepath.c_str());
     88     DCHECK(file.isValid());
     89     picture_.serialize(&file, &EncodeBitmapToData);
     90   }
     91 
     92  private:
     93   base::FilePath dirpath_;
     94   int layer_id_;
     95   SkPicture picture_;
     96 };
     97 
     98 class RenderingStatsEnumerator : public cc::RenderingStats::Enumerator {
     99  public:
    100   RenderingStatsEnumerator(v8::Handle<v8::Object> stats_object)
    101       : stats_object(stats_object) { }
    102 
    103   virtual void AddInt64(const char* name, int64 value) OVERRIDE {
    104     stats_object->Set(v8::String::New(name), v8::Number::New(value));
    105   }
    106 
    107   virtual void AddDouble(const char* name, double value) OVERRIDE {
    108     stats_object->Set(v8::String::New(name), v8::Number::New(value));
    109   }
    110 
    111   virtual void AddInt(const char* name, int value) OVERRIDE {
    112     stats_object->Set(v8::String::New(name), v8::Integer::New(value));
    113   }
    114 
    115   virtual void AddTimeDeltaInSecondsF(const char* name,
    116                                       const base::TimeDelta& value) OVERRIDE {
    117     stats_object->Set(v8::String::New(name),
    118                       v8::Number::New(value.InSecondsF()));
    119   }
    120 
    121  private:
    122   v8::Handle<v8::Object> stats_object;
    123 };
    124 
    125 }  // namespace
    126 
    127 namespace content {
    128 
    129 namespace {
    130 
    131 class CallbackAndContext : public base::RefCounted<CallbackAndContext> {
    132  public:
    133   CallbackAndContext(v8::Isolate* isolate,
    134                      v8::Handle<v8::Function> callback,
    135                      v8::Handle<v8::Context> context)
    136       : isolate_(isolate) {
    137     callback_.Reset(isolate_, callback);
    138     context_.Reset(isolate_, context);
    139   }
    140 
    141   v8::Isolate* isolate() {
    142     return isolate_;
    143   }
    144 
    145   v8::Handle<v8::Function> GetCallback() {
    146     return v8::Local<v8::Function>::New(isolate_, callback_);
    147   }
    148 
    149   v8::Handle<v8::Context> GetContext() {
    150     return v8::Local<v8::Context>::New(isolate_, context_);
    151   }
    152 
    153  private:
    154   friend class base::RefCounted<CallbackAndContext>;
    155 
    156   virtual ~CallbackAndContext() {
    157     callback_.Dispose();
    158     context_.Dispose();
    159   }
    160 
    161   v8::Isolate* isolate_;
    162   v8::Persistent<v8::Function> callback_;
    163   v8::Persistent<v8::Context> context_;
    164   DISALLOW_COPY_AND_ASSIGN(CallbackAndContext);
    165 };
    166 
    167 }  // namespace
    168 
    169 class GpuBenchmarkingWrapper : public v8::Extension {
    170  public:
    171   GpuBenchmarkingWrapper() :
    172       v8::Extension(kGpuBenchmarkingExtensionName,
    173           "if (typeof(chrome) == 'undefined') {"
    174           "  chrome = {};"
    175           "};"
    176           "if (typeof(chrome.gpuBenchmarking) == 'undefined') {"
    177           "  chrome.gpuBenchmarking = {};"
    178           "};"
    179           "chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers = function() {"
    180           "  native function SetNeedsDisplayOnAllLayers();"
    181           "  return SetNeedsDisplayOnAllLayers();"
    182           "};"
    183           "chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent = function() {"
    184           "  native function SetRasterizeOnlyVisibleContent();"
    185           "  return SetRasterizeOnlyVisibleContent();"
    186           "};"
    187           "chrome.gpuBenchmarking.renderingStats = function() {"
    188           "  native function GetRenderingStats();"
    189           "  return GetRenderingStats();"
    190           "};"
    191           "chrome.gpuBenchmarking.printToSkPicture = function(dirname) {"
    192           "  native function PrintToSkPicture();"
    193           "  return PrintToSkPicture(dirname);"
    194           "};"
    195           "chrome.gpuBenchmarking.smoothScrollBy = "
    196           "    function(pixels_to_scroll, opt_callback, opt_mouse_event_x,"
    197           "             opt_mouse_event_y) {"
    198           "  pixels_to_scroll = pixels_to_scroll || 0;"
    199           "  callback = opt_callback || function() { };"
    200           "  native function BeginSmoothScroll();"
    201           "  if (typeof opt_mouse_event_x !== 'undefined' &&"
    202           "      typeof opt_mouse_event_y !== 'undefined') {"
    203           "    return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
    204           "                             Math.abs(pixels_to_scroll),"
    205           "                             opt_mouse_event_x, opt_mouse_event_y);"
    206           "  } else {"
    207           "    return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
    208           "                             Math.abs(pixels_to_scroll));"
    209           "  }"
    210           "};"
    211           "chrome.gpuBenchmarking.smoothScrollBySendsTouch = function() {"
    212           "  native function SmoothScrollSendsTouch();"
    213           "  return SmoothScrollSendsTouch();"
    214           "};"
    215           "chrome.gpuBenchmarking.beginWindowSnapshotPNG = function(callback) {"
    216           "  native function BeginWindowSnapshotPNG();"
    217           "  BeginWindowSnapshotPNG(callback);"
    218           "};"
    219           "chrome.gpuBenchmarking.clearImageCache = function() {"
    220           "  native function ClearImageCache();"
    221           "  ClearImageCache();"
    222           "};") {}
    223 
    224   virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
    225       v8::Handle<v8::String> name) OVERRIDE {
    226     if (name->Equals(v8::String::New("SetNeedsDisplayOnAllLayers")))
    227       return v8::FunctionTemplate::New(SetNeedsDisplayOnAllLayers);
    228     if (name->Equals(v8::String::New("SetRasterizeOnlyVisibleContent")))
    229       return v8::FunctionTemplate::New(SetRasterizeOnlyVisibleContent);
    230     if (name->Equals(v8::String::New("GetRenderingStats")))
    231       return v8::FunctionTemplate::New(GetRenderingStats);
    232     if (name->Equals(v8::String::New("PrintToSkPicture")))
    233       return v8::FunctionTemplate::New(PrintToSkPicture);
    234     if (name->Equals(v8::String::New("BeginSmoothScroll")))
    235       return v8::FunctionTemplate::New(BeginSmoothScroll);
    236     if (name->Equals(v8::String::New("SmoothScrollSendsTouch")))
    237       return v8::FunctionTemplate::New(SmoothScrollSendsTouch);
    238     if (name->Equals(v8::String::New("BeginWindowSnapshotPNG")))
    239       return v8::FunctionTemplate::New(BeginWindowSnapshotPNG);
    240     if (name->Equals(v8::String::New("ClearImageCache")))
    241       return v8::FunctionTemplate::New(ClearImageCache);
    242 
    243     return v8::Handle<v8::FunctionTemplate>();
    244   }
    245 
    246   static void SetNeedsDisplayOnAllLayers(
    247       const v8::FunctionCallbackInfo<v8::Value>& args) {
    248     WebFrame* web_frame = WebFrame::frameForCurrentContext();
    249     if (!web_frame)
    250       return;
    251 
    252     WebView* web_view = web_frame->view();
    253     if (!web_view)
    254       return;
    255 
    256     RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
    257     if (!render_view_impl)
    258       return;
    259 
    260     RenderWidgetCompositor* compositor = render_view_impl->compositor();
    261     if (!compositor)
    262       return;
    263 
    264     compositor->SetNeedsDisplayOnAllLayers();
    265   }
    266 
    267   static void SetRasterizeOnlyVisibleContent(
    268       const v8::FunctionCallbackInfo<v8::Value>& args) {
    269     WebFrame* web_frame = WebFrame::frameForCurrentContext();
    270     if (!web_frame)
    271       return;
    272 
    273     WebView* web_view = web_frame->view();
    274     if (!web_view)
    275       return;
    276 
    277     RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
    278     if (!render_view_impl)
    279       return;
    280 
    281     RenderWidgetCompositor* compositor = render_view_impl->compositor();
    282     if (!compositor)
    283       return;
    284 
    285     compositor->SetRasterizeOnlyVisibleContent();
    286   }
    287 
    288   static void GetRenderingStats(
    289       const v8::FunctionCallbackInfo<v8::Value>& args) {
    290 
    291     WebFrame* web_frame = WebFrame::frameForCurrentContext();
    292     if (!web_frame)
    293       return;
    294 
    295     WebView* web_view = web_frame->view();
    296     if (!web_view)
    297       return;
    298 
    299     RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
    300     if (!render_view_impl)
    301       return;
    302 
    303     WebRenderingStatsImpl stats;
    304     render_view_impl->GetRenderingStats(stats);
    305 
    306     content::GpuRenderingStats gpu_stats;
    307     render_view_impl->GetGpuRenderingStats(&gpu_stats);
    308     BrowserRenderingStats browser_stats;
    309     render_view_impl->GetBrowserRenderingStats(&browser_stats);
    310     v8::Handle<v8::Object> stats_object = v8::Object::New();
    311 
    312     RenderingStatsEnumerator enumerator(stats_object);
    313     stats.rendering_stats.EnumerateFields(&enumerator);
    314     gpu_stats.EnumerateFields(&enumerator);
    315     browser_stats.EnumerateFields(&enumerator);
    316 
    317     args.GetReturnValue().Set(stats_object);
    318   }
    319 
    320   static void PrintToSkPicture(
    321       const v8::FunctionCallbackInfo<v8::Value>& args) {
    322     if (args.Length() != 1)
    323       return;
    324 
    325     v8::String::AsciiValue dirname(args[0]);
    326     if (dirname.length() == 0)
    327       return;
    328 
    329     WebFrame* web_frame = WebFrame::frameForCurrentContext();
    330     if (!web_frame)
    331       return;
    332 
    333     WebView* web_view = web_frame->view();
    334     if (!web_view)
    335       return;
    336 
    337     WebViewBenchmarkSupport* benchmark_support = web_view->benchmarkSupport();
    338     if (!benchmark_support)
    339       return;
    340 
    341     base::FilePath dirpath(
    342         base::FilePath::StringType(*dirname, *dirname + dirname.length()));
    343     if (!file_util::CreateDirectory(dirpath) ||
    344         !base::PathIsWritable(dirpath)) {
    345       std::string msg("Path is not writable: ");
    346       msg.append(dirpath.MaybeAsASCII());
    347       v8::ThrowException(v8::Exception::Error(
    348           v8::String::New(msg.c_str(), msg.length())));
    349       return;
    350     }
    351 
    352     SkPictureRecorder recorder(dirpath);
    353     benchmark_support->paint(&recorder,
    354                              WebViewBenchmarkSupport::PaintModeEverything);
    355   }
    356 
    357   static void OnSmoothScrollCompleted(
    358       CallbackAndContext* callback_and_context) {
    359     v8::HandleScope scope(callback_and_context->isolate());
    360     v8::Handle<v8::Context> context = callback_and_context->GetContext();
    361     v8::Context::Scope context_scope(context);
    362     WebFrame* frame = WebFrame::frameForContext(context);
    363     if (frame) {
    364       frame->callFunctionEvenIfScriptDisabled(
    365           callback_and_context->GetCallback(), v8::Object::New(), 0, NULL);
    366     }
    367   }
    368 
    369   static void SmoothScrollSendsTouch(
    370       const v8::FunctionCallbackInfo<v8::Value>& args) {
    371     // TODO(epenner): Should other platforms emulate touch events?
    372 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
    373     args.GetReturnValue().Set(true);
    374 #else
    375     args.GetReturnValue().Set(false);
    376 #endif
    377   }
    378 
    379   static void BeginSmoothScroll(
    380       const v8::FunctionCallbackInfo<v8::Value>& args) {
    381     WebFrame* web_frame = WebFrame::frameForCurrentContext();
    382     if (!web_frame)
    383       return;
    384 
    385     WebView* web_view = web_frame->view();
    386     if (!web_view)
    387       return;
    388 
    389     RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
    390     if (!render_view_impl)
    391       return;
    392 
    393     // Account for the 2 optional arguments, mouse_event_x and mouse_event_y.
    394     int arglen = args.Length();
    395     if (arglen < 3 ||
    396         !args[0]->IsBoolean() ||
    397         !args[1]->IsFunction() ||
    398         !args[2]->IsNumber()) {
    399       args.GetReturnValue().Set(false);
    400       return;
    401     }
    402 
    403     bool scroll_down = args[0]->BooleanValue();
    404     v8::Local<v8::Function> callback_local =
    405         v8::Local<v8::Function>::Cast(args[1]);
    406 
    407     scoped_refptr<CallbackAndContext> callback_and_context =
    408         new CallbackAndContext(args.GetIsolate(),
    409                                callback_local,
    410                                web_frame->mainWorldScriptContext());
    411 
    412     int pixels_to_scroll = args[2]->IntegerValue();
    413 
    414     int mouse_event_x = 0;
    415     int mouse_event_y = 0;
    416 
    417     if (arglen == 3) {
    418       WebKit::WebRect rect = render_view_impl->windowRect();
    419       mouse_event_x = rect.x + rect.width / 2;
    420       mouse_event_y = rect.y + rect.height / 2;
    421     } else {
    422       if (arglen != 5 ||
    423           !args[3]->IsNumber() ||
    424           !args[4]->IsNumber()) {
    425         args.GetReturnValue().Set(false);
    426         return;
    427       }
    428 
    429       mouse_event_x = args[3]->IntegerValue() * web_view->pageScaleFactor();
    430       mouse_event_y = args[4]->IntegerValue() * web_view->pageScaleFactor();
    431     }
    432 
    433     // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
    434     // progress, we will leak the callback and context. This needs to be fixed,
    435     // somehow.
    436     render_view_impl->BeginSmoothScroll(
    437         scroll_down,
    438         base::Bind(&OnSmoothScrollCompleted,
    439                    callback_and_context),
    440         pixels_to_scroll,
    441         mouse_event_x,
    442         mouse_event_y);
    443 
    444     args.GetReturnValue().Set(true);
    445   }
    446 
    447   static void OnSnapshotCompleted(CallbackAndContext* callback_and_context,
    448                                   const gfx::Size& size,
    449                                   const std::vector<unsigned char>& png) {
    450     v8::HandleScope scope(callback_and_context->isolate());
    451     v8::Handle<v8::Context> context = callback_and_context->GetContext();
    452     v8::Context::Scope context_scope(context);
    453     WebFrame* frame = WebFrame::frameForContext(context);
    454     if (frame) {
    455 
    456       v8::Handle<v8::Value> result;
    457 
    458       if(!size.IsEmpty()) {
    459         v8::Handle<v8::Object> result_object;
    460         result_object = v8::Object::New();
    461 
    462         result_object->Set(v8::String::New("width"),
    463                            v8::Number::New(size.width()));
    464         result_object->Set(v8::String::New("height"),
    465                            v8::Number::New(size.height()));
    466 
    467         std::string base64_png;
    468         base::Base64Encode(base::StringPiece(
    469             reinterpret_cast<const char*>(&*png.begin()), png.size()),
    470             &base64_png);
    471 
    472         result_object->Set(v8::String::New("data"),
    473             v8::String::New(base64_png.c_str(), base64_png.size()));
    474 
    475         result = result_object;
    476       } else {
    477         result = v8::Null();
    478       }
    479 
    480       v8::Handle<v8::Value> argv[] = { result };
    481 
    482       frame->callFunctionEvenIfScriptDisabled(
    483           callback_and_context->GetCallback(), v8::Object::New(), 1, argv);
    484     }
    485   }
    486 
    487   static void BeginWindowSnapshotPNG(
    488       const v8::FunctionCallbackInfo<v8::Value>& args) {
    489     WebFrame* web_frame = WebFrame::frameForCurrentContext();
    490     if (!web_frame)
    491       return;
    492 
    493     WebView* web_view = web_frame->view();
    494     if (!web_view)
    495       return;
    496 
    497     RenderViewImpl* render_view_impl = RenderViewImpl::FromWebView(web_view);
    498     if (!render_view_impl)
    499       return;
    500 
    501     if (!args[0]->IsFunction())
    502       return;
    503 
    504     v8::Local<v8::Function> callback_local =
    505         v8::Local<v8::Function>::Cast(args[0]);
    506 
    507     scoped_refptr<CallbackAndContext> callback_and_context =
    508         new CallbackAndContext(args.GetIsolate(),
    509                                callback_local,
    510                                web_frame->mainWorldScriptContext());
    511 
    512     render_view_impl->GetWindowSnapshot(
    513         base::Bind(&OnSnapshotCompleted, callback_and_context));
    514   }
    515 
    516   static void ClearImageCache(
    517       const v8::FunctionCallbackInfo<v8::Value>& args) {
    518     WebImageCache::clear();
    519   }
    520 };
    521 
    522 v8::Extension* GpuBenchmarkingExtension::Get() {
    523   return new GpuBenchmarkingWrapper();
    524 }
    525 
    526 }  // namespace content
    527