From cb632c310910db456fbc3083fcad1fce0e1a2197 Mon Sep 17 00:00:00 2001
From: "piman@chromium.org"
 <piman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Thu, 5 Aug 2010 02:54:42 +0000
Subject: [PATCH] If SHM pixmaps support is available, for example, Intel
 drivers now support that
 (http://cgit.freedesktop.org/xorg/driver/xf86-video-intel/commit/?id=4b7142baa0b3bf6f38843d06aadc579d8624cefc),
 use SHM pixmaps support to accelerate windowless plugin painting. Modify
 WindowlessPaint to directly use Xlib interfaces for SHM pixmaps support,
 similarly to the way how backing_store_x handles different SHM support levels
 provided by X server.

BUG=50912
TEST=Open the page "http://disney.go.com/official-sites/demi-lovato/albums" using Chromium browser,
compare the CPU usage of browser and X server before and after the change, and confirm CPU usage
is reduced with this change (for example, on an Atom N450 Netbook with MeeGo 1.0 and Chromium browser
6.0.417.0 there's >30% CPU usage reduction, especially X server CPU usage is reduced by half).

Review URL: http://codereview.chromium.org/3052039
Patch from Yuqiang Xian <yuqiang.xian@intel.com>.

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@55020 0039d316-1c4b-4281-b951-d872f2087c98
---
 chrome/plugin/webplugin_proxy.cc              |  49 ++++++++
 chrome/plugin/webplugin_proxy.h               |   7 ++
 webkit/glue/plugins/webplugin_delegate_impl.h |  11 ++
 .../plugins/webplugin_delegate_impl_gtk.cc    | 109 +++++++++++++-----
 4 files changed, 148 insertions(+), 28 deletions(-)

diff --git a/chrome/plugin/webplugin_proxy.cc b/chrome/plugin/webplugin_proxy.cc
index 66f65fc074557..5441dc8494e5a 100644
--- a/chrome/plugin/webplugin_proxy.cc
+++ b/chrome/plugin/webplugin_proxy.cc
@@ -32,6 +32,10 @@
 #include "third_party/WebKit/WebKit/chromium/public/WebBindings.h"
 #include "webkit/glue/plugins/webplugin_delegate_impl.h"
 
+#if defined(USE_X11)
+#include "app/x11_util_internal.h"
+#endif
+
 using WebKit::WebBindings;
 using webkit_glue::WebPluginResourceClient;
 
@@ -58,11 +62,36 @@ WebPluginProxy::WebPluginProxy(
       transparent_(false),
       host_render_view_routing_id_(host_render_view_routing_id),
       ALLOW_THIS_IN_INITIALIZER_LIST(runnable_method_factory_(this)) {
+#if defined(USE_X11)
+      windowless_shm_pixmap_ = None;
+      use_shm_pixmap_ = false;
+
+      // If the X server supports SHM pixmaps
+      // and the color depth and masks match,
+      // then consider using SHM pixmaps for windowless plugin painting.
+      Display* display = x11_util::GetXDisplay();
+      if (x11_util::QuerySharedMemorySupport(display) ==
+              x11_util::SHARED_MEMORY_PIXMAP &&
+          x11_util::BitsPerPixelForPixmapDepth(
+              display, DefaultDepth(display, 0)) == 32) {
+        Visual* vis = DefaultVisual(display, 0);
+
+        if (vis->red_mask == 0xff0000 &&
+            vis->green_mask == 0xff00 &&
+            vis->blue_mask == 0xff)
+          use_shm_pixmap_ = true;
+      }
+#endif
 }
 
 WebPluginProxy::~WebPluginProxy() {
   if (cp_browsing_context_)
     GetContextMap().erase(cp_browsing_context_);
+
+#if defined(USE_X11)
+  if (windowless_shm_pixmap_ != None)
+    XFreePixmap(x11_util::GetXDisplay(), windowless_shm_pixmap_);
+#endif
 }
 
 bool WebPluginProxy::Send(IPC::Message* msg) {
@@ -567,6 +596,26 @@ void WebPluginProxy::SetWindowlessBuffer(
   } else {
     background_canvas_.reset();
   }
+
+  // If SHM pixmaps support is available, create a SHM pixmap and
+  // pass it to the delegate for windowless plugin painting.
+  if (delegate_->IsWindowless() && use_shm_pixmap_ && windowless_dib_.get()) {
+    Display* display = x11_util::GetXDisplay();
+    XID root_window = x11_util::GetX11RootWindow();
+    XShmSegmentInfo shminfo = {0};
+
+    if (windowless_shm_pixmap_ != None)
+      XFreePixmap(display, windowless_shm_pixmap_);
+
+    shminfo.shmseg = windowless_dib_->MapToX(display);
+    // Create a shared memory pixmap based on the image buffer.
+    windowless_shm_pixmap_ = XShmCreatePixmap(display, root_window,
+                                              NULL, &shminfo,
+                                              width, height,
+                                              DefaultDepth(display, 0));
+
+    delegate_->SetWindowlessShmPixmap(windowless_shm_pixmap_);
+  }
 }
 
 #endif
diff --git a/chrome/plugin/webplugin_proxy.h b/chrome/plugin/webplugin_proxy.h
index 1fb72c498f908..b39026171b9db 100644
--- a/chrome/plugin/webplugin_proxy.h
+++ b/chrome/plugin/webplugin_proxy.h
@@ -8,6 +8,9 @@
 
 #include <string>
 
+#if defined(USE_X11)
+#include "app/x11_util.h"
+#endif
 #include "app/surface/transport_dib.h"
 #include "base/hash_tables.h"
 #include "base/ref_counted.h"
@@ -185,6 +188,10 @@ class WebPluginProxy : public webkit_glue::WebPlugin {
 #if defined(USE_X11)
   scoped_ptr<TransportDIB> windowless_dib_;
   scoped_ptr<TransportDIB> background_dib_;
+  // If we can use SHM pixmaps for windowless plugin painting or not.
+  bool use_shm_pixmap_;
+  // The SHM pixmap for windowless plugin painting.
+  XID windowless_shm_pixmap_;
 #endif
 
 #endif
diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h
index ebf5d3e069598..1c7ccb2ba5327 100644
--- a/webkit/glue/plugins/webplugin_delegate_impl.h
+++ b/webkit/glue/plugins/webplugin_delegate_impl.h
@@ -27,6 +27,8 @@
 #endif
 
 #if defined(USE_X11)
+#include "app/x11_util.h"
+
 typedef struct _GdkDrawable GdkPixmap;
 #endif
 
@@ -181,6 +183,12 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate {
   void set_windowed_handle(gfx::PluginWindowHandle handle);
 #endif
 
+#if defined(USE_X11)
+  void SetWindowlessShmPixmap(XID shm_pixmap) {
+    windowless_shm_pixmap_ = shm_pixmap;
+  }
+#endif
+
  private:
   friend class DeleteTask<WebPluginDelegateImpl>;
   friend class webkit_glue::WebPluginDelegate;
@@ -292,6 +300,9 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate {
 #endif // OS_WIN
 
 #if defined(USE_X11)
+  // The SHM pixmap for a windowless plugin.
+  XID windowless_shm_pixmap_;
+
   // The pixmap we're drawing into, for a windowless plugin.
   GdkPixmap* pixmap_;
   double first_event_time_;
diff --git a/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
index 18b150474926c..d4a98a849e633 100644
--- a/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
+++ b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
@@ -44,6 +44,7 @@ WebPluginDelegateImpl::WebPluginDelegateImpl(
       windowless_(false),
       plugin_(NULL),
       instance_(instance),
+      windowless_shm_pixmap_(None),
       pixmap_(NULL),
       first_event_time_(-1.0),
       plug_(NULL),
@@ -399,47 +400,99 @@ void WebPluginDelegateImpl::WindowlessPaint(cairo_t* context,
                         pixmap_draw_rect.right(),
                         pixmap_draw_rect.bottom());
 
-  EnsurePixmapAtLeastSize(pixmap_rect.width(), pixmap_rect.height());
-
-  // Copy the current image into the pixmap, so the plugin can draw over
-  // this background.
-  cairo_t* cairo = gdk_cairo_create(pixmap_);
-  BlitContextToContext(cairo, pixmap_draw_rect, context, draw_rect.origin());
-  cairo_destroy(cairo);
-
   // Construct the paint message, targeting the pixmap.
   NPEvent np_event = {0};
   XGraphicsExposeEvent &event = np_event.xgraphicsexpose;
   event.type = GraphicsExpose;
-  event.display = GDK_DISPLAY();
-  event.drawable = GDK_PIXMAP_XID(pixmap_);
   event.x = pixmap_draw_rect.x();
   event.y = pixmap_draw_rect.y();
   event.width = pixmap_draw_rect.width();
   event.height = pixmap_draw_rect.height();
+  event.display = GDK_DISPLAY();
+
+  if (windowless_shm_pixmap_ != None) {
+    Pixmap pixmap = None;
+    GC xgc = NULL;
+    Display* display = event.display;
+    gfx::Rect plugin_draw_rect = draw_rect;
+
+    // Make plugin_draw_rect relative to the plugin window.
+    plugin_draw_rect.Offset(-window_rect_.x(), -window_rect_.y());
+
+    // In case the drawing area does not start with the plugin window origin,
+    // we can not let the plugin directly draw over the shared memory pixmap.
+    if (plugin_draw_rect.x() != pixmap_draw_rect.x() ||
+        plugin_draw_rect.y() != pixmap_draw_rect.y()) {
+      pixmap = XCreatePixmap(display, windowless_shm_pixmap_,
+                             std::max(1, pixmap_rect.width()),
+                             std::max(1, pixmap_rect.height()),
+                             DefaultDepth(display, 0));
+      xgc = XCreateGC(display, windowless_shm_pixmap_, 0, NULL);
+      // Copy the current image into the pixmap, so the plugin can draw over it.
+      XCopyArea(display, windowless_shm_pixmap_, pixmap, xgc,
+                plugin_draw_rect.x(), plugin_draw_rect.y(),
+                pixmap_draw_rect.width(), pixmap_draw_rect.height(),
+                pixmap_draw_rect.x(), pixmap_draw_rect.y());
+
+      event.drawable = pixmap;
+    } else {
+      event.drawable = windowless_shm_pixmap_;
+    }
+
+    // Tell the plugin to paint into the pixmap.
+    static StatsRate plugin_paint("Plugin.Paint");
+    StatsScope<StatsRate> scope(plugin_paint);
+    NPError err = instance()->NPP_HandleEvent(&np_event);
+    DCHECK_EQ(err, NPERR_NO_ERROR);
+
+    if (pixmap != None) {
+      // Copy the rendered image pixmap back into the shm pixmap
+      // and thus the drawing buffer.
+      XCopyArea(display, pixmap, windowless_shm_pixmap_, xgc,
+                pixmap_draw_rect.x(), pixmap_draw_rect.y(),
+                pixmap_draw_rect.width(), pixmap_draw_rect.height(),
+                plugin_draw_rect.x(), plugin_draw_rect.y());
+      XSync(display, FALSE);
+      if (xgc)
+        XFreeGC(display, xgc);
+      XFreePixmap(display, pixmap);
+    } else {
+      XSync(display, FALSE);
+    }
+  } else {
+    EnsurePixmapAtLeastSize(pixmap_rect.width(), pixmap_rect.height());
 
-  // Tell the plugin to paint into the pixmap.
-  static StatsRate plugin_paint("Plugin.Paint");
-  StatsScope<StatsRate> scope(plugin_paint);
-  NPError err = instance()->NPP_HandleEvent(&np_event);
-  DCHECK_EQ(err, NPERR_NO_ERROR);
+    // Copy the current image into the pixmap, so the plugin can draw over
+    // this background.
+    cairo_t* cairo = gdk_cairo_create(pixmap_);
+    BlitContextToContext(cairo, pixmap_draw_rect, context, draw_rect.origin());
+    cairo_destroy(cairo);
 
-  cairo_save(context);
-  // Now copy the rendered image pixmap back into the drawing buffer.
-  gdk_cairo_set_source_pixmap(context, pixmap_, -offset_x, -offset_y);
-  cairo_rectangle(context, draw_rect.x(), draw_rect.y(),
-                  draw_rect.width(), draw_rect.height());
-  cairo_clip(context);
-  cairo_paint(context);
+    event.drawable = GDK_PIXMAP_XID(pixmap_);
+
+    // Tell the plugin to paint into the pixmap.
+    static StatsRate plugin_paint("Plugin.Paint");
+    StatsScope<StatsRate> scope(plugin_paint);
+    NPError err = instance()->NPP_HandleEvent(&np_event);
+    DCHECK_EQ(err, NPERR_NO_ERROR);
+
+    cairo_save(context);
+    // Now copy the rendered image pixmap back into the drawing buffer.
+    gdk_cairo_set_source_pixmap(context, pixmap_, -offset_x, -offset_y);
+    cairo_rectangle(context, draw_rect.x(), draw_rect.y(),
+                    draw_rect.width(), draw_rect.height());
+    cairo_clip(context);
+    cairo_paint(context);
 
 #ifdef DEBUG_RECTANGLES
-  // Draw some debugging rectangles.
-  // Pixmap rect = blue.
-  DrawDebugRectangle(context, pixmap_rect, 0, 0, 1);
-  // Drawing rect = red.
-  DrawDebugRectangle(context, draw_rect, 1, 0, 0);
+    // Draw some debugging rectangles.
+    // Pixmap rect = blue.
+    DrawDebugRectangle(context, pixmap_rect, 0, 0, 1);
+    // Drawing rect = red.
+    DrawDebugRectangle(context, draw_rect, 1, 0, 0);
 #endif
-  cairo_restore(context);
+    cairo_restore(context);
+  }
 }
 
 void WebPluginDelegateImpl::WindowlessSetWindow() {
-- 
GitLab