summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortschmelcher@chromium.org <tschmelcher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-25 22:13:52 +0000
committertschmelcher@chromium.org <tschmelcher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-25 22:13:52 +0000
commit9cf755682fcff0aa79e64c6f4fcb7ced80547d67 (patch)
tree227975248d1e781ac949dcde1a2291c11919d838
parentb8fede4e01b906a65ca9eb816e2cab0694810270 (diff)
downloadchromium_src-9cf755682fcff0aa79e64c6f4fcb7ced80547d67.zip
chromium_src-9cf755682fcff0aa79e64c6f4fcb7ced80547d67.tar.gz
chromium_src-9cf755682fcff0aa79e64c6f4fcb7ced80547d67.tar.bz2
Avoid double-buffering windowless plugins and instead use buffer flipping when feasible. This can significantly reduce the CPU usage of pages that host windowless plugins (down by ~15% absolute CPU usage on my old MacBookPro, or about a 40% relative decrease).
Also simplify the OS X code for managing TransportDIBs by creating them in uncached mode and transfering the FileDescriptors using dup+auto-close. TEST=(OS X 10.5) Google voice and video chat (O3D), YouTube, www.dkmsoftware.com/Yubotu.htm, and trailers.apple.com, incl. with violent resizing, and analyzed perf with Shark; (OS X 10.5, OS X 10.6, Linux, Windows) http://www.communitymx.com/content/source/E5141/wmodeopaque.htm, http://www.communitymx.com/content/source/E5141/wmodetrans.htm, http://www.communitymx.com/content/source/E5141/wmodenone.htm, hand-crafted perf test using Flash with wmode=opaque, analyzed perf with Activity Monitor / top / Process Explorer, verified in the debugger that buffer flips do not cause NPP_SetWindow calls BUG=none Review URL: http://codereview.chromium.org/7171024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93970 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/common/plugin_messages.h20
-rw-r--r--content/plugin/webplugin_delegate_stub.cc10
-rw-r--r--content/plugin/webplugin_proxy.cc340
-rw-r--r--content/plugin/webplugin_proxy.h80
-rw-r--r--content/renderer/webplugin_delegate_proxy.cc337
-rw-r--r--content/renderer/webplugin_delegate_proxy.h80
-rw-r--r--webkit/plugins/npapi/webplugin_delegate_impl.h1
-rw-r--r--webkit/plugins/npapi/webplugin_delegate_impl_mac.mm11
8 files changed, 516 insertions, 363 deletions
diff --git a/content/common/plugin_messages.h b/content/common/plugin_messages.h
index 0b01cc8..4909bc2 100644
--- a/content/common/plugin_messages.h
+++ b/content/common/plugin_messages.h
@@ -52,16 +52,10 @@ IPC_STRUCT_BEGIN(PluginMsg_UpdateGeometry_Param)
IPC_STRUCT_MEMBER(gfx::Rect, window_rect)
IPC_STRUCT_MEMBER(gfx::Rect, clip_rect)
IPC_STRUCT_MEMBER(bool, transparent)
- IPC_STRUCT_MEMBER(TransportDIB::Handle, windowless_buffer)
+ IPC_STRUCT_MEMBER(TransportDIB::Handle, windowless_buffer0)
+ IPC_STRUCT_MEMBER(TransportDIB::Handle, windowless_buffer1)
+ IPC_STRUCT_MEMBER(int, windowless_buffer_index)
IPC_STRUCT_MEMBER(TransportDIB::Handle, background_buffer)
-
-#if defined(OS_MACOSX)
- // This field contains a key that the plug-in process is expected to return
- // to the renderer in its ACK message, unless the value is -1, in which case
- // no ACK message is required. Other than the special -1 value, the values
- // used in ack_key are opaque to the plug-in process.
- IPC_STRUCT_MEMBER(int, ack_key)
-#endif
IPC_STRUCT_END()
//-----------------------------------------------------------------------------
@@ -323,8 +317,9 @@ IPC_MESSAGE_ROUTED1(PluginHostMsg_URLRequest,
IPC_MESSAGE_ROUTED1(PluginHostMsg_CancelResource,
int /* id */)
-IPC_MESSAGE_ROUTED1(PluginHostMsg_InvalidateRect,
- gfx::Rect /* rect */)
+IPC_MESSAGE_ROUTED2(PluginHostMsg_InvalidateRect,
+ gfx::Rect /* rect */,
+ bool /* allow_buffer_flipping */)
IPC_SYNC_MESSAGE_ROUTED1_1(PluginHostMsg_GetWindowScriptNPObject,
int /* route id */,
@@ -364,9 +359,6 @@ IPC_SYNC_MESSAGE_CONTROL1_0(PluginHostMsg_SetException,
IPC_MESSAGE_CONTROL0(PluginHostMsg_PluginShuttingDown)
#if defined(OS_MACOSX)
-IPC_MESSAGE_ROUTED1(PluginHostMsg_UpdateGeometry_ACK,
- int /* ack_key */)
-
IPC_MESSAGE_ROUTED1(PluginHostMsg_FocusChanged,
bool /* focused */)
diff --git a/content/plugin/webplugin_delegate_stub.cc b/content/plugin/webplugin_delegate_stub.cc
index 00987b2..87e8fda 100644
--- a/content/plugin/webplugin_delegate_stub.cc
+++ b/content/plugin/webplugin_delegate_stub.cc
@@ -266,13 +266,9 @@ void WebPluginDelegateStub::OnUpdateGeometry(
const PluginMsg_UpdateGeometry_Param& param) {
webplugin_->UpdateGeometry(
param.window_rect, param.clip_rect,
- param.windowless_buffer, param.background_buffer,
- param.transparent
-#if defined(OS_MACOSX)
- ,
- param.ack_key
-#endif
- );
+ param.windowless_buffer0, param.windowless_buffer1,
+ param.windowless_buffer_index, param.background_buffer,
+ param.transparent);
}
void WebPluginDelegateStub::OnGetPluginScriptableObject(int* route_id) {
diff --git a/content/plugin/webplugin_proxy.cc b/content/plugin/webplugin_proxy.cc
index 84d6d0d..a54f73a 100644
--- a/content/plugin/webplugin_proxy.cc
+++ b/content/plugin/webplugin_proxy.cc
@@ -60,33 +60,37 @@ WebPluginProxy::WebPluginProxy(
containing_window_(containing_window),
page_url_(page_url),
transparent_(false),
+ windowless_buffer_index_(0),
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 = ui::GetXDisplay();
- if (ui::QuerySharedMemorySupport(display) == ui::SHARED_MEMORY_PIXMAP &&
- ui::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;
- }
+ windowless_shm_pixmaps_[0] = None;
+ windowless_shm_pixmaps_[1] = 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 = ui::GetXDisplay();
+ if (ui::QuerySharedMemorySupport(display) == ui::SHARED_MEMORY_PIXMAP &&
+ ui::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 defined(USE_X11)
- if (windowless_shm_pixmap_ != None)
- XFreePixmap(ui::GetXDisplay(), windowless_shm_pixmap_);
+ if (windowless_shm_pixmaps_[0] != None)
+ XFreePixmap(ui::GetXDisplay(), windowless_shm_pixmaps_[0]);
+ if (windowless_shm_pixmaps_[1] != None)
+ XFreePixmap(ui::GetXDisplay(), windowless_shm_pixmaps_[1]);
#endif
#if defined(OS_MACOSX)
@@ -159,7 +163,7 @@ void WebPluginProxy::InvalidateRect(const gfx::Rect& rect) {
#if defined(OS_MACOSX)
// If this is a Core Animation plugin, all we need to do is inform the
// delegate.
- if (!windowless_context_.get()) {
+ if (!windowless_context()) {
delegate_->PluginDidInvalidate();
return;
}
@@ -328,10 +332,10 @@ void WebPluginProxy::HandleURLRequest(const char* url,
void WebPluginProxy::Paint(const gfx::Rect& rect) {
#if defined(OS_MACOSX)
- if (!windowless_context_.get())
+ if (!windowless_context())
return;
#else
- if (!windowless_canvas_.get())
+ if (!windowless_canvas())
return;
#endif
@@ -340,13 +344,17 @@ void WebPluginProxy::Paint(const gfx::Rect& rect) {
gfx::Rect offset_rect = rect;
offset_rect.Offset(delegate_->GetRect().origin());
#if defined(OS_MACOSX)
- CGContextSaveGState(windowless_context_);
- // It is possible for windowless_context_ to change during plugin painting
+ CGContextSaveGState(windowless_context());
+ // It is possible for windowless_contexts_ to change during plugin painting
// (since the plugin can make a synchronous call during paint event handling),
- // in which case we don't want to try to restore it later. Not an owning ref
+ // in which case we don't want to try to restore later. Not an owning ref
// since owning the ref without owning the shared backing memory doesn't make
// sense, so this should only be used for pointer comparisons.
- CGContextRef saved_context_weak = windowless_context_.get();
+ CGContextRef saved_context_weak = windowless_context();
+ // We also save the buffer index for the comparison because if we flip buffers
+ // but haven't reallocated them then we do need to restore the context because
+ // it is going to continue to be used.
+ int saved_index = windowless_buffer_index_;
if (background_context_.get()) {
base::mac::ScopedCFTypeRef<CGImageRef> image(
@@ -356,69 +364,66 @@ void WebPluginProxy::Paint(const gfx::Rect& rect) {
source_rect.origin.y = CGImageGetHeight(image) - rect.y() - rect.height();
base::mac::ScopedCFTypeRef<CGImageRef> sub_image(
CGImageCreateWithImageInRect(image, source_rect));
- CGContextDrawImage(windowless_context_, rect.ToCGRect(), sub_image);
+ CGContextDrawImage(windowless_context(), rect.ToCGRect(), sub_image);
} else if (transparent_) {
- CGContextClearRect(windowless_context_, rect.ToCGRect());
+ CGContextClearRect(windowless_context(), rect.ToCGRect());
}
- CGContextClipToRect(windowless_context_, rect.ToCGRect());
+ CGContextClipToRect(windowless_context(), rect.ToCGRect());
// TODO(caryclark): This is a temporary workaround to allow the Darwin / Skia
// port to share code with the Darwin / CG port. All ports will eventually use
// the common code below.
- delegate_->CGPaint(windowless_context_, rect);
- if (windowless_context_.get() == saved_context_weak)
- CGContextRestoreGState(windowless_context_);
+ delegate_->CGPaint(windowless_context(), rect);
+ if (windowless_contexts_[saved_index].get() == saved_context_weak)
+ CGContextRestoreGState(windowless_contexts_[saved_index]);
#else
- windowless_canvas_->save();
+ windowless_canvas()->save();
// The given clip rect is relative to the plugin coordinate system.
SkRect sk_rect = { SkIntToScalar(rect.x()),
SkIntToScalar(rect.y()),
SkIntToScalar(rect.right()),
SkIntToScalar(rect.bottom()) };
- windowless_canvas_->clipRect(sk_rect);
+ windowless_canvas()->clipRect(sk_rect);
// Setup the background.
if (background_canvas_.get()) {
// When a background canvas is given, we're in transparent mode. This means
// the plugin wants to have the image of the page in the canvas it's drawing
- // into (which is windowless_canvas_) so it can do blending. So we copy the
- // background bitmap into the windowless_canvas_.
+ // into (which is windowless_canvases_) so it can do blending. So we copy
+ // the background bitmap into the windowless canvas.
const SkBitmap& background_bitmap =
skia::GetTopDevice(*background_canvas_)->accessBitmap(false);
- windowless_canvas_->drawBitmap(background_bitmap, 0, 0);
+ windowless_canvas()->drawBitmap(background_bitmap, 0, 0);
} else {
// In non-transparent mode, the plugin doesn't care what's underneath, so we
// can just give it black.
SkPaint black_fill_paint;
black_fill_paint.setARGB(0xFF, 0x00, 0x00, 0x00);
- windowless_canvas_->drawPaint(black_fill_paint);
+ windowless_canvas()->drawPaint(black_fill_paint);
}
- // Bring the windowless_canvas_ into the window coordinate system, which is
+ // Bring the windowless canvas into the window coordinate system, which is
// how the plugin expects to draw (since the windowless API was originally
// designed just for scribbling over the web page).
- windowless_canvas_->translate(SkIntToScalar(-delegate_->GetRect().x()),
- SkIntToScalar(-delegate_->GetRect().y()));
+ windowless_canvas()->translate(SkIntToScalar(-delegate_->GetRect().x()),
+ SkIntToScalar(-delegate_->GetRect().y()));
// Before we send the invalidate, paint so that renderer uses the updated
// bitmap.
- delegate_->Paint(windowless_canvas_.get(), offset_rect);
+ delegate_->Paint(windowless_canvas(), offset_rect);
- windowless_canvas_->restore();
+ windowless_canvas()->restore();
#endif
}
void WebPluginProxy::UpdateGeometry(
const gfx::Rect& window_rect,
const gfx::Rect& clip_rect,
- const TransportDIB::Handle& windowless_buffer,
+ const TransportDIB::Handle& windowless_buffer0,
+ const TransportDIB::Handle& windowless_buffer1,
+ int windowless_buffer_index,
const TransportDIB::Handle& background_buffer,
- bool transparent
-#if defined(OS_MACOSX)
- ,
- int ack_key
-#endif
- ) {
+ bool transparent) {
gfx::Rect old = delegate_->GetRect();
gfx::Rect old_clip_rect = delegate_->GetClipRect();
transparent_ = transparent;
@@ -426,14 +431,23 @@ void WebPluginProxy::UpdateGeometry(
// Update the buffers before doing anything that could call into plugin code,
// so that we don't process buffer changes out of order if plugins make
// synchronous calls that lead to nested UpdateGeometry calls.
- if (TransportDIB::is_valid_handle(windowless_buffer)) {
- // The plugin's rect changed, so now we have a new buffer to draw into.
- SetWindowlessBuffer(windowless_buffer, background_buffer, window_rect);
+ if (TransportDIB::is_valid_handle(windowless_buffer0)) {
+ // The plugin's rect changed, so now we have new buffers to draw into.
+ SetWindowlessBuffers(windowless_buffer0,
+ windowless_buffer1,
+ background_buffer,
+ window_rect);
}
+ DCHECK(0 <= windowless_buffer_index && windowless_buffer_index <= 1);
+ windowless_buffer_index_ = windowless_buffer_index;
+#if defined(USE_X11)
+ delegate_->SetWindowlessShmPixmap(windowless_shm_pixmap());
+#endif
+
#if defined(OS_MACOSX)
- delegate_->UpdateGeometryAndContext(window_rect, clip_rect,
- windowless_context_);
+ delegate_->UpdateGeometryAndContext(
+ window_rect, clip_rect, windowless_context());
#else
delegate_->UpdateGeometry(window_rect, clip_rect);
#endif
@@ -444,45 +458,58 @@ void WebPluginProxy::UpdateGeometry(
!damaged_rect_.IsEmpty()) {
InvalidateRect(damaged_rect_);
}
-
-#if defined(OS_MACOSX)
- // The renderer is expecting an ACK message if ack_key is not -1.
- if (ack_key != -1) {
- Send(new PluginHostMsg_UpdateGeometry_ACK(route_id_, ack_key));
- }
-#endif
}
#if defined(OS_WIN)
-void WebPluginProxy::SetWindowlessBuffer(
- const TransportDIB::Handle& windowless_buffer,
- const TransportDIB::Handle& background_buffer,
- const gfx::Rect& window_rect) {
+
+void WebPluginProxy::CreateCanvasFromHandle(
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Rect& window_rect,
+ scoped_ptr<skia::PlatformCanvas>* canvas_out) {
// Create a canvas that will reference the shared bits. We have to handle
// errors here since we're mapping a large amount of memory that may not fit
// in our address space, or go wrong in some other way.
- windowless_canvas_.reset(new skia::PlatformCanvas);
- if (!windowless_canvas_->initialize(
+ scoped_ptr<skia::PlatformCanvas> canvas(new skia::PlatformCanvas);
+ if (!canvas->initialize(
window_rect.width(),
window_rect.height(),
true,
- chrome::GetSectionFromProcess(windowless_buffer,
+ chrome::GetSectionFromProcess(dib_handle,
channel_->renderer_handle(), false))) {
- windowless_canvas_.reset();
+ canvas.reset();
+ }
+ canvas_out->reset(canvas.release());
+}
+
+void WebPluginProxy::SetWindowlessBuffers(
+ const TransportDIB::Handle& windowless_buffer0,
+ const TransportDIB::Handle& windowless_buffer1,
+ const TransportDIB::Handle& background_buffer,
+ const gfx::Rect& window_rect) {
+ CreateCanvasFromHandle(windowless_buffer0,
+ window_rect,
+ &windowless_canvases_[0]);
+ if (!windowless_canvases_[0].get()) {
+ windowless_canvases_[1].reset();
+ background_canvas_.reset();
+ return;
+ }
+ CreateCanvasFromHandle(windowless_buffer1,
+ window_rect,
+ &windowless_canvases_[1]);
+ if (!windowless_canvases_[1].get()) {
+ windowless_canvases_[0].reset();
background_canvas_.reset();
return;
}
if (background_buffer) {
- background_canvas_.reset(new skia::PlatformCanvas);
- if (!background_canvas_->initialize(
- window_rect.width(),
- window_rect.height(),
- true,
- chrome::GetSectionFromProcess(background_buffer,
- channel_->renderer_handle(), false))) {
- windowless_canvas_.reset();
- background_canvas_.reset();
+ CreateCanvasFromHandle(background_buffer,
+ window_rect,
+ &background_canvas_);
+ if (!background_canvas_.get()) {
+ windowless_canvases_[0].reset();
+ windowless_canvases_[1].reset();
return;
}
}
@@ -490,79 +517,116 @@ void WebPluginProxy::SetWindowlessBuffer(
#elif defined(OS_MACOSX)
-void WebPluginProxy::SetWindowlessBuffer(
- const TransportDIB::Handle& windowless_buffer,
- const TransportDIB::Handle& background_buffer,
- const gfx::Rect& window_rect) {
+void WebPluginProxy::CreateDIBAndCGContextFromHandle(
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Rect& window_rect,
+ scoped_ptr<TransportDIB>* dib_out,
+ base::mac::ScopedCFTypeRef<CGContextRef>* cg_context_out) {
// Convert the shared memory handle to a handle that works in our process,
// and then use that to create a CGContextRef.
- windowless_dib_.reset(TransportDIB::Map(windowless_buffer));
- background_dib_.reset(TransportDIB::Map(background_buffer));
- windowless_context_.reset(CGBitmapContextCreate(
- windowless_dib_->memory(),
- window_rect.width(),
- window_rect.height(),
- 8, 4 * window_rect.width(),
- base::mac::GetSystemColorSpace(),
- kCGImageAlphaPremultipliedFirst |
- kCGBitmapByteOrder32Host));
- CGContextTranslateCTM(windowless_context_, 0, window_rect.height());
- CGContextScaleCTM(windowless_context_, 1, -1);
- if (background_dib_.get()) {
- background_context_.reset(CGBitmapContextCreate(
- background_dib_->memory(),
+ TransportDIB* dib = TransportDIB::Map(dib_handle);
+ CGContextRef cg_context = NULL;
+ if (dib) {
+ cg_context = CGBitmapContextCreate(
+ dib->memory(),
window_rect.width(),
window_rect.height(),
- 8, 4 * window_rect.width(),
+ 8,
+ 4 * window_rect.width(),
base::mac::GetSystemColorSpace(),
- kCGImageAlphaPremultipliedFirst |
- kCGBitmapByteOrder32Host));
- CGContextTranslateCTM(background_context_, 0, window_rect.height());
- CGContextScaleCTM(background_context_, 1, -1);
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
+ CGContextTranslateCTM(cg_context, 0, window_rect.height());
+ CGContextScaleCTM(cg_context, 1, -1);
}
+ dib_out->reset(dib);
+ cg_context_out->reset(cg_context);
}
-#elif defined(USE_X11)
-
-void WebPluginProxy::SetWindowlessBuffer(
- const TransportDIB::Handle& windowless_buffer,
+void WebPluginProxy::SetWindowlessBuffers(
+ const TransportDIB::Handle& windowless_buffer0,
+ const TransportDIB::Handle& windowless_buffer1,
const TransportDIB::Handle& background_buffer,
const gfx::Rect& window_rect) {
- int width = window_rect.width();
- int height = window_rect.height();
- windowless_dib_.reset(TransportDIB::Map(windowless_buffer));
- if (windowless_dib_.get()) {
- windowless_canvas_.reset(windowless_dib_->GetPlatformCanvas(width, height));
- } else {
- // This can happen if the renderer has already destroyed the TransportDIB
- // by the time we receive the handle, e.g. in case of multiple resizes.
- windowless_canvas_.reset();
- }
- background_dib_.reset(TransportDIB::Map(background_buffer));
- if (background_dib_.get()) {
- background_canvas_.reset(background_dib_->GetPlatformCanvas(width, height));
- } else {
- background_canvas_.reset();
+ CreateDIBAndCGContextFromHandle(windowless_buffer0,
+ window_rect,
+ &windowless_dibs_[0],
+ &windowless_contexts_[0]);
+ CreateDIBAndCGContextFromHandle(windowless_buffer1,
+ window_rect,
+ &windowless_dibs_[1],
+ &windowless_contexts_[1]);
+ CreateDIBAndCGContextFromHandle(background_buffer,
+ window_rect,
+ &background_dib_,
+ &background_context_);
+}
+
+#elif defined(USE_X11)
+
+void WebPluginProxy::CreateDIBAndCanvasFromHandle(
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Rect& window_rect,
+ scoped_ptr<TransportDIB>* dib_out,
+ scoped_ptr<skia::PlatformCanvas>* canvas_out) {
+ TransportDIB* dib = TransportDIB::Map(dib_handle);
+ skia::PlatformCanvas* canvas = NULL;
+ // dib may be NULL if the renderer has already destroyed the TransportDIB by
+ // the time we receive the handle, e.g. in case of multiple resizes.
+ if (dib) {
+ canvas = dib->GetPlatformCanvas(window_rect.width(), window_rect.height());
}
+ dib_out->reset(dib);
+ canvas_out->reset(canvas);
+}
- // 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()) {
+void WebPluginProxy::CreateShmPixmapFromDIB(
+ TransportDIB* dib,
+ const gfx::Rect& window_rect,
+ XID* pixmap_out) {
+ if (dib) {
Display* display = ui::GetXDisplay();
XID root_window = ui::GetX11RootWindow();
XShmSegmentInfo shminfo = {0};
- if (windowless_shm_pixmap_ != None)
- XFreePixmap(display, windowless_shm_pixmap_);
+ if (*pixmap_out != None)
+ XFreePixmap(display, *pixmap_out);
- shminfo.shmseg = windowless_dib_->MapToX(display);
+ shminfo.shmseg = 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));
+ *pixmap_out = XShmCreatePixmap(display, root_window,
+ NULL, &shminfo,
+ window_rect.width(), window_rect.height(),
+ DefaultDepth(display, 0));
+ }
+}
- delegate_->SetWindowlessShmPixmap(windowless_shm_pixmap_);
+void WebPluginProxy::SetWindowlessBuffers(
+ const TransportDIB::Handle& windowless_buffer0,
+ const TransportDIB::Handle& windowless_buffer1,
+ const TransportDIB::Handle& background_buffer,
+ const gfx::Rect& window_rect) {
+ CreateDIBAndCanvasFromHandle(windowless_buffer0,
+ window_rect,
+ &windowless_dibs_[0],
+ &windowless_canvases_[0]);
+ CreateDIBAndCanvasFromHandle(windowless_buffer1,
+ window_rect,
+ &windowless_dibs_[1],
+ &windowless_canvases_[1]);
+ CreateDIBAndCanvasFromHandle(background_buffer,
+ window_rect,
+ &background_dib_,
+ &background_canvas_);
+
+ // If SHM pixmaps support is available, create SHM pixmaps to pass to the
+ // delegate for windowless plugin painting.
+ if (delegate_->IsWindowless() && use_shm_pixmap_) {
+ CreateShmPixmapFromDIB(windowless_dibs_[0].get(),
+ window_rect,
+ &windowless_shm_pixmaps_[0]);
+ CreateShmPixmapFromDIB(windowless_dibs_[1].get(),
+ window_rect,
+ &windowless_shm_pixmaps_[1]);
}
}
@@ -644,7 +708,15 @@ void WebPluginProxy::OnPaint(const gfx::Rect& damaged_rect) {
content::GetContentClient()->SetActiveURL(page_url_);
Paint(damaged_rect);
- Send(new PluginHostMsg_InvalidateRect(route_id_, damaged_rect));
+ bool allow_buffer_flipping;
+#if defined(OS_MACOSX)
+ allow_buffer_flipping = delegate_->AllowBufferFlipping();
+#else
+ allow_buffer_flipping = true;
+#endif
+ Send(new PluginHostMsg_InvalidateRect(route_id_,
+ damaged_rect,
+ allow_buffer_flipping));
}
bool WebPluginProxy::IsOffTheRecord() {
diff --git a/content/plugin/webplugin_proxy.h b/content/plugin/webplugin_proxy.h
index a06d810..fee1ee0 100644
--- a/content/plugin/webplugin_proxy.h
+++ b/content/plugin/webplugin_proxy.h
@@ -110,14 +110,11 @@ class WebPluginProxy : public webkit::npapi::WebPlugin {
bool notify_redirects);
void UpdateGeometry(const gfx::Rect& window_rect,
const gfx::Rect& clip_rect,
- const TransportDIB::Handle& windowless_buffer,
+ const TransportDIB::Handle& windowless_buffer0,
+ const TransportDIB::Handle& windowless_buffer1,
+ int windowless_buffer_index,
const TransportDIB::Handle& background_buffer,
- bool transparent
-#if defined(OS_MACOSX)
- ,
- int ack_key
-#endif
- );
+ bool transparent);
virtual void CancelDocumentLoad();
virtual void InitiateHTTPRangeRequest(
const char* url, const char* range_info, int range_request_id);
@@ -173,10 +170,51 @@ class WebPluginProxy : public webkit::npapi::WebPlugin {
// Handler for sending over the paint event to the plugin.
void OnPaint(const gfx::Rect& damaged_rect);
- // Updates the shared memory section where windowless plugins paint.
- void SetWindowlessBuffer(const TransportDIB::Handle& windowless_buffer,
- const TransportDIB::Handle& background_buffer,
- const gfx::Rect& window_rect);
+#if defined(OS_WIN)
+ void CreateCanvasFromHandle(const TransportDIB::Handle& dib_handle,
+ const gfx::Rect& window_rect,
+ scoped_ptr<skia::PlatformCanvas>* canvas_out);
+#elif defined(OS_MACOSX)
+ static void CreateDIBAndCGContextFromHandle(
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Rect& window_rect,
+ scoped_ptr<TransportDIB>* dib_out,
+ base::mac::ScopedCFTypeRef<CGContextRef>* cg_context_out);
+#elif defined(USE_X11)
+ static void CreateDIBAndCanvasFromHandle(
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Rect& window_rect,
+ scoped_ptr<TransportDIB>* dib_out,
+ scoped_ptr<skia::PlatformCanvas>* canvas_out);
+
+ static void CreateShmPixmapFromDIB(
+ TransportDIB* dib,
+ const gfx::Rect& window_rect,
+ XID* pixmap_out);
+#endif
+
+ // Updates the shared memory sections where windowless plugins paint.
+ void SetWindowlessBuffers(const TransportDIB::Handle& windowless_buffer0,
+ const TransportDIB::Handle& windowless_buffer1,
+ const TransportDIB::Handle& background_buffer,
+ const gfx::Rect& window_rect);
+
+#if defined(OS_MACOSX)
+ CGContextRef windowless_context() const {
+ return windowless_contexts_[windowless_buffer_index_].get();
+ }
+#else
+ skia::PlatformCanvas* windowless_canvas() const {
+ return windowless_canvases_[windowless_buffer_index_].get();
+ }
+
+#if defined(USE_X11)
+ XID windowless_shm_pixmap() const {
+ return windowless_shm_pixmaps_[windowless_buffer_index_];
+ }
+#endif
+
+#endif
typedef base::hash_map<int, webkit::npapi::WebPluginResourceClient*>
ResourceClientMap;
@@ -193,26 +231,30 @@ class WebPluginProxy : public webkit::npapi::WebPlugin {
// The url of the main frame hosting the plugin.
GURL page_url_;
- // Variables used for desynchronized windowless plugin painting. See note in
- // webplugin_delegate_proxy.h for how this works.
+ // Variables used for desynchronized windowless plugin painting. See note in
+ // webplugin_delegate_proxy.h for how this works. The two sets of windowless_*
+ // fields are for the front-buffer and back-buffer of a buffer flipping system
+ // and windowless_buffer_index_ identifies which set we are using as the
+ // back-buffer at any given time.
bool transparent_;
+ int windowless_buffer_index_;
#if defined(OS_MACOSX)
- scoped_ptr<TransportDIB> windowless_dib_;
+ scoped_ptr<TransportDIB> windowless_dibs_[2];
scoped_ptr<TransportDIB> background_dib_;
- base::mac::ScopedCFTypeRef<CGContextRef> windowless_context_;
+ base::mac::ScopedCFTypeRef<CGContextRef> windowless_contexts_[2];
base::mac::ScopedCFTypeRef<CGContextRef> background_context_;
scoped_ptr<WebPluginAcceleratedSurfaceProxy> accelerated_surface_;
#else
- scoped_ptr<skia::PlatformCanvas> windowless_canvas_;
+ scoped_ptr<skia::PlatformCanvas> windowless_canvases_[2];
scoped_ptr<skia::PlatformCanvas> background_canvas_;
#if defined(USE_X11)
- scoped_ptr<TransportDIB> windowless_dib_;
+ scoped_ptr<TransportDIB> windowless_dibs_[2];
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_;
+ // The SHM pixmaps for windowless plugin painting.
+ XID windowless_shm_pixmaps_[2];
#endif
#endif
diff --git a/content/renderer/webplugin_delegate_proxy.cc b/content/renderer/webplugin_delegate_proxy.cc
index 1e9b1fd..d17b6bd 100644
--- a/content/renderer/webplugin_delegate_proxy.cc
+++ b/content/renderer/webplugin_delegate_proxy.cc
@@ -161,15 +161,6 @@ class ResourceClientProxy : public webkit::npapi::WebPluginResourceClient {
bool multibyte_response_expected_;
};
-#if defined(OS_MACOSX)
-static void ReleaseTransportDIB(TransportDIB* dib) {
- if (dib) {
- IPC::Message* message = new ViewHostMsg_FreeTransportDIB(dib->id());
- RenderThread::current()->Send(message);
- }
-}
-#endif
-
WebPluginDelegateProxy::WebPluginDelegateProxy(
const std::string& mime_type,
const base::WeakPtr<RenderView>& render_view)
@@ -183,24 +174,11 @@ WebPluginDelegateProxy::WebPluginDelegateProxy(
sad_plugin_(NULL),
invalidate_pending_(false),
transparent_(false),
+ front_buffer_index_(0),
page_url_(render_view_->webview()->mainFrame()->document().url()) {
}
WebPluginDelegateProxy::~WebPluginDelegateProxy() {
-#if defined(OS_MACOSX)
- // Ask the browser to release old TransportDIB objects for which no
- // PluginHostMsg_UpdateGeometry_ACK was ever received from the plugin
- // process.
- for (OldTransportDIBMap::iterator iterator = old_transport_dibs_.begin();
- iterator != old_transport_dibs_.end();
- ++iterator) {
- ReleaseTransportDIB(iterator->second.get());
- }
-
- // Ask the browser to release the "live" TransportDIB object.
- ReleaseTransportDIB(transport_store_.get());
- DCHECK(!background_store_.get());
-#endif
if (npobject_)
WebBindings::releaseObject(npobject_);
}
@@ -469,8 +447,6 @@ bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) {
OnStartIme);
IPC_MESSAGE_HANDLER(PluginHostMsg_BindFakePluginWindowHandle,
OnBindFakePluginWindowHandle);
- IPC_MESSAGE_HANDLER(PluginHostMsg_UpdateGeometry_ACK,
- OnUpdateGeometry_ACK)
// Used only on 10.6 and later.
IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedSurfaceSetIOSurface,
OnAcceleratedSurfaceSetIOSurface)
@@ -511,6 +487,69 @@ void WebPluginDelegateProxy::OnChannelError() {
#endif
}
+static void CopyTransportDIBHandleForMessage(
+ const TransportDIB::Handle& handle_in,
+ TransportDIB::Handle* handle_out) {
+#if defined(OS_MACOSX)
+ // On Mac, TransportDIB::Handle is typedef'ed to FileDescriptor, and
+ // FileDescriptor message fields needs to remain valid until the message is
+ // sent or else the sendmsg() call will fail.
+ if ((handle_out->fd = HANDLE_EINTR(dup(handle_in.fd))) < 0) {
+ PLOG(ERROR) << "dup()";
+ return;
+ }
+ handle_out->auto_close = true;
+#else
+ // Don't need to do anything special for other platforms.
+ *handle_out = handle_in;
+#endif
+}
+
+void WebPluginDelegateProxy::SendUpdateGeometry(
+ bool bitmaps_changed) {
+ PluginMsg_UpdateGeometry_Param param;
+ param.window_rect = plugin_rect_;
+ param.clip_rect = clip_rect_;
+ param.windowless_buffer0 = TransportDIB::DefaultHandleValue();
+ param.windowless_buffer1 = TransportDIB::DefaultHandleValue();
+ param.windowless_buffer_index = back_buffer_index();
+ param.background_buffer = TransportDIB::DefaultHandleValue();
+ param.transparent = transparent_;
+
+#if defined(OS_POSIX)
+ // If we're using POSIX mmap'd TransportDIBs, sending the handle across
+ // IPC establishes a new mapping rather than just sending a window ID,
+ // so only do so if we've actually changed the shared memory bitmaps.
+ if (bitmaps_changed)
+#endif
+ {
+ if (transport_stores_[0].dib.get())
+ CopyTransportDIBHandleForMessage(transport_stores_[0].dib->handle(),
+ &param.windowless_buffer0);
+
+ if (transport_stores_[1].dib.get())
+ CopyTransportDIBHandleForMessage(transport_stores_[1].dib->handle(),
+ &param.windowless_buffer1);
+
+ if (background_store_.dib.get())
+ CopyTransportDIBHandleForMessage(background_store_.dib->handle(),
+ &param.background_buffer);
+ }
+
+ IPC::Message* msg;
+#if defined(OS_WIN)
+ if (UseSynchronousGeometryUpdates()) {
+ msg = new PluginMsg_UpdateGeometrySync(instance_id_, param);
+ } else // NOLINT
+#endif
+ {
+ msg = new PluginMsg_UpdateGeometry(instance_id_, param);
+ msg->set_unblock(true);
+ }
+
+ Send(msg);
+}
+
void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect,
const gfx::Rect& clip_rect) {
// window_rect becomes either a window in native windowing system
@@ -528,15 +567,10 @@ void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect,
bool bitmaps_changed = false;
- PluginMsg_UpdateGeometry_Param param;
-#if defined(OS_MACOSX)
- param.ack_key = -1;
-#endif
-
if (uses_shared_bitmaps_) {
- if (!backing_store_canvas_.get() ||
- (window_rect.width() != backing_store_canvas_->getDevice()->width() ||
- window_rect.height() != backing_store_canvas_->getDevice()->height()))
+ if (!front_buffer_canvas() ||
+ (window_rect.width() != front_buffer_canvas()->getDevice()->width() ||
+ window_rect.height() != front_buffer_canvas()->getDevice()->height()))
{
bitmaps_changed = true;
@@ -546,30 +580,19 @@ void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect,
// preserves transparency information (and does the compositing itself)
// so plugins don't need access to the page background.
needs_background_store = false;
- if (transport_store_.get()) {
- // ResetWindowlessBitmaps inserts the old TransportDIBs into
- // old_transport_dibs_ using the transport store's file descriptor as
- // the key. The constraints on the keys are that -1 is reserved
- // to mean "no ACK required," and in-flight keys must be unique.
- // File descriptors will never be -1, and because they won't be closed
- // until receipt of the ACK, they're unique.
- param.ack_key = transport_store_->handle().fd;
- }
#endif
// Create a shared memory section that the plugin paints into
// asynchronously.
ResetWindowlessBitmaps();
if (!window_rect.IsEmpty()) {
- if (!CreateSharedBitmap(&transport_store_, &transport_store_canvas_) ||
-#if defined(OS_WIN)
- !CreateSharedBitmap(&backing_store_, &backing_store_canvas_) ||
-#else
- !CreateLocalBitmap(&backing_store_, &backing_store_canvas_) ||
-#endif
+ if (!CreateSharedBitmap(&transport_stores_[0].dib,
+ &transport_stores_[0].canvas) ||
+ !CreateSharedBitmap(&transport_stores_[1].dib,
+ &transport_stores_[1].canvas) ||
(needs_background_store &&
- !CreateSharedBitmap(&background_store_,
- &background_store_canvas_))) {
+ !CreateSharedBitmap(&background_store_.dib,
+ &background_store_.canvas))) {
DCHECK(false);
ResetWindowlessBitmaps();
return;
@@ -578,76 +601,19 @@ void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect,
}
}
- param.window_rect = window_rect;
- param.clip_rect = clip_rect;
- param.windowless_buffer = TransportDIB::DefaultHandleValue();
- param.background_buffer = TransportDIB::DefaultHandleValue();
- param.transparent = transparent_;
-
-#if defined(OS_POSIX)
- // If we're using POSIX mmap'd TransportDIBs, sending the handle across
- // IPC establishes a new mapping rather than just sending a window ID,
- // so only do so if we've actually recreated the shared memory bitmaps.
- if (bitmaps_changed)
-#endif
- {
- if (transport_store_.get())
- param.windowless_buffer = transport_store_->handle();
-
- if (background_store_.get())
- param.background_buffer = background_store_->handle();
- }
-
- IPC::Message* msg;
-#if defined (OS_WIN)
- if (UseSynchronousGeometryUpdates()) {
- msg = new PluginMsg_UpdateGeometrySync(instance_id_, param);
- } else // NOLINT
-#endif
- {
- msg = new PluginMsg_UpdateGeometry(instance_id_, param);
- msg->set_unblock(true);
- }
-
- Send(msg);
+ SendUpdateGeometry(bitmaps_changed);
}
void WebPluginDelegateProxy::ResetWindowlessBitmaps() {
-#if defined(OS_MACOSX)
- DCHECK(!background_store_.get());
- // The Mac TransportDIB implementation uses base::SharedMemory, which
- // cannot be disposed of if an in-flight UpdateGeometry message refers to
- // the shared memory file descriptor. The old_transport_dibs_ map holds
- // old TransportDIBs waiting to die, keyed by the |ack_key| values used in
- // UpdateGeometry messages. When an UpdateGeometry_ACK message arrives,
- // the associated TransportDIB can be released.
- if (transport_store_.get()) {
- int ack_key = transport_store_->handle().fd;
-
- DCHECK_NE(ack_key, -1);
-
- // DCHECK_EQ does not work with base::hash_map.
- DCHECK(old_transport_dibs_.find(ack_key) == old_transport_dibs_.end());
-
- // Stash the old TransportDIB in the map. It'll be released when an
- // ACK message comes in.
- old_transport_dibs_[ack_key] =
- linked_ptr<TransportDIB>(transport_store_.release());
- }
-#else
- transport_store_.reset();
- background_store_.reset();
-#endif
-#if defined(OS_WIN)
- backing_store_.reset();
-#else
- backing_store_.resize(0);
-#endif
+ transport_stores_[0].dib.reset();
+ transport_stores_[1].dib.reset();
+ background_store_.dib.reset();
- backing_store_canvas_.reset();
- transport_store_canvas_.reset();
- background_store_canvas_.reset();
- backing_store_painted_ = gfx::Rect();
+ transport_stores_[0].canvas.reset();
+ transport_stores_[1].canvas.reset();
+ background_store_.canvas.reset();
+ transport_store_painted_ = gfx::Rect();
+ front_buffer_diff_ = gfx::Rect();
}
static size_t BitmapSizeForPluginRect(const gfx::Rect& plugin_rect) {
@@ -681,7 +647,7 @@ bool WebPluginDelegateProxy::CreateSharedBitmap(
#endif
#if defined(OS_MACOSX)
TransportDIB::Handle handle;
- IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, true, &handle);
+ IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, false, &handle);
if (!RenderThread::current()->Send(msg))
return false;
if (handle.fd < 0)
@@ -700,7 +666,7 @@ bool WebPluginDelegateProxy::CreateSharedBitmap(
// Flips |rect| vertically within an enclosing rect with height |height|.
// Intended for converting rects between flipped and non-flipped contexts.
static void FlipRectVerticallyWithHeight(gfx::Rect* rect, int height) {
- rect->set_y(height - rect->y() - rect->height());
+ rect->set_y(height - rect->bottom());
}
#endif
@@ -722,7 +688,7 @@ void WebPluginDelegateProxy::Paint(WebKit::WebCanvas* canvas,
// We got a paint before the plugin's coordinates, so there's no buffer to
// copy from.
- if (!backing_store_canvas_.get())
+ if (!front_buffer_canvas())
return;
// We're using the native OS APIs from here on out.
@@ -752,20 +718,40 @@ void WebPluginDelegateProxy::Paint(WebKit::WebCanvas* canvas,
#endif
bool background_changed = false;
- if (background_store_canvas_.get() && BackgroundChanged(context, rect)) {
+ if (background_store_.canvas.get() && BackgroundChanged(context, rect)) {
background_changed = true;
- gfx::Rect flipped_offset_rect = offset_rect;
- BlitContextToCanvas(background_store_canvas_.get(), canvas_rect,
+ BlitContextToCanvas(background_store_.canvas.get(), canvas_rect,
context, rect.origin());
}
- if (background_changed || !backing_store_painted_.Contains(offset_rect)) {
+ // transport_store_painted_ is really a bounding box, so in principle this
+ // check could falsely indicate that we don't need to paint offset_rect, but
+ // in practice it works fine.
+ if (background_changed ||
+ !transport_store_painted_.Contains(offset_rect)) {
Send(new PluginMsg_Paint(instance_id_, offset_rect));
- CopyFromTransportToBacking(offset_rect);
+ // Since the plugin is not blocked on the renderer in this context, there is
+ // a chance that it will begin repainting the back-buffer before we complete
+ // capturing the data. Buffer flipping would increase that risk because
+ // geometry update is asynchronous, so we don't want to use buffer flipping
+ // here.
+ UpdateFrontBuffer(offset_rect, false);
}
- BlitCanvasToContext(context, rect, backing_store_canvas_.get(),
- canvas_rect.origin());
+#if defined(OS_MACOSX)
+ // The canvases are flipped relative to the context, so flip the context's
+ // coordinate space so that the blit unflips the content.
+ CGContextSaveGState(context);
+ CGContextScaleCTM(context, 1, -1);
+ rect.set_y(-rect.bottom());
+#endif
+ BlitCanvasToContext(context,
+ rect,
+ front_buffer_canvas(),
+ offset_rect.origin());
+#if defined(OS_MACOSX)
+ CGContextRestoreGState(context);
+#endif
if (invalidate_pending_) {
// Only send the PaintAck message if this paint is in response to an
@@ -813,7 +799,7 @@ bool WebPluginDelegateProxy::BackgroundChanged(
// getAddr32 doesn't use the translation units, so we have to subtract
// the plugin origin from the coordinates.
uint32_t* canvas_row_start =
- background_store_canvas_->getDevice()->accessBitmap(true).getAddr32(
+ background_store_.canvas->getDevice()->accessBitmap(true).getAddr32(
check_rect.x() - plugin_rect_.x(), y - plugin_rect_.y());
if (memcmp(hdc_row_start, canvas_row_start, row_byte_size) != 0)
return true;
@@ -857,7 +843,7 @@ bool WebPluginDelegateProxy::BackgroundChanged(
int page_start_y = content_rect.y() - context_offset_y;
skia::ScopedPlatformPaint scoped_platform_paint(
- background_store_canvas_.get());
+ background_store_.canvas.get());
CGContextRef bg_context = scoped_platform_paint.GetPlatformSurface();
DCHECK_EQ(CGBitmapContextGetBitsPerPixel(context),
@@ -878,7 +864,7 @@ bool WebPluginDelegateProxy::BackgroundChanged(
int page_start_y = static_cast<int>(page_y_double);
skia::ScopedPlatformPaint scoped_platform_paint(
- background_store_canvas_.get());
+ background_store_.canvas.get());
cairo_surface_t* bg_surface =cairo_get_target(
scoped_platform_paint.GetPlatformSurface());
DCHECK_EQ(cairo_surface_get_type(bg_surface), CAIRO_SURFACE_TYPE_IMAGE);
@@ -1075,7 +1061,8 @@ void WebPluginDelegateProxy::OnCancelResource(int id) {
plugin_->CancelResource(id);
}
-void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect& rect) {
+void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect& rect,
+ bool allow_buffer_flipping) {
if (!plugin_)
return;
@@ -1084,7 +1071,10 @@ void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect& rect) {
const gfx::Rect clipped_rect(rect.Intersect(gfx::Rect(plugin_rect_.size())));
invalidate_pending_ = true;
- CopyFromTransportToBacking(clipped_rect);
+ // The plugin is blocked on the renderer because the invalidate message it has
+ // sent us is synchronous, so we can use buffer flipping here if the caller
+ // allows it.
+ UpdateFrontBuffer(clipped_rect, allow_buffer_flipping);
plugin_->InvalidateRect(clipped_rect);
}
@@ -1149,36 +1139,69 @@ void WebPluginDelegateProxy::PaintSadPlugin(WebKit::WebCanvas* native_context,
webkit::PaintSadPlugin(native_context, plugin_rect_, *sad_plugin_);
}
-void WebPluginDelegateProxy::CopyFromTransportToBacking(const gfx::Rect& rect) {
- if (!backing_store_canvas_.get()) {
- return;
- }
-
- // Copy the damaged rect from the transport bitmap to the backing store.
+void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer(
+ const gfx::Rect& rect) {
#if defined(OS_MACOSX)
// Blitting the bits directly is much faster than going through CG, and since
- // since the goal is just to move the raw pixels between two bitmaps with the
- // same pixel format (no compositing, color correction, etc.), it's safe.
+ // the goal is just to move the raw pixels between two bitmaps with the same
+ // pixel format (no compositing, color correction, etc.), it's safe.
const size_t stride =
skia::PlatformCanvas::StrideForWidth(plugin_rect_.width());
const size_t chunk_size = 4 * rect.width();
- uint8* source_data = static_cast<uint8*>(transport_store_->memory()) +
+ DCHECK(back_buffer_dib() != NULL);
+ uint8* source_data = static_cast<uint8*>(back_buffer_dib()->memory()) +
+ rect.y() * stride + 4 * rect.x();
+ DCHECK(front_buffer_dib() != NULL);
+ uint8* target_data = static_cast<uint8*>(front_buffer_dib()->memory()) +
rect.y() * stride + 4 * rect.x();
- // The two bitmaps are flipped relative to each other.
- int dest_starting_row = plugin_rect_.height() - rect.y() - 1;
- DCHECK(!backing_store_.empty());
- uint8* target_data = &(backing_store_[0]) + dest_starting_row * stride +
- 4 * rect.x();
for (int row = 0; row < rect.height(); ++row) {
memcpy(target_data, source_data, chunk_size);
source_data += stride;
- target_data -= stride;
+ target_data += stride;
}
#else
- BlitCanvasToCanvas(backing_store_canvas_.get(), rect,
- transport_store_canvas_.get(), rect.origin());
+ BlitCanvasToCanvas(front_buffer_canvas(),
+ rect,
+ back_buffer_canvas(),
+ rect.origin());
#endif
- backing_store_painted_ = backing_store_painted_.Union(rect);
+}
+
+void WebPluginDelegateProxy::UpdateFrontBuffer(
+ const gfx::Rect& rect,
+ bool allow_buffer_flipping) {
+ if (!front_buffer_canvas()) {
+ return;
+ }
+
+#if defined(OS_WIN)
+ // If SendUpdateGeometry() would block on the plugin process then we don't
+ // want to use buffer flipping at all since it would add extra locking.
+ // (Alternatively we could probably safely use async updates for buffer
+ // flipping all the time since the size is not changing.)
+ if (UseSynchronousGeometryUpdates()) {
+ allow_buffer_flipping = false;
+ }
+#endif
+
+ // Plugin has just painted "rect" into the back-buffer, so the front-buffer
+ // no longer holds the latest content for that rectangle.
+ front_buffer_diff_ = front_buffer_diff_.Subtract(rect);
+ if (allow_buffer_flipping && front_buffer_diff_.IsEmpty()) {
+ // Back-buffer contains the latest content for all areas; simply flip
+ // the buffers.
+ front_buffer_index_ = back_buffer_index();
+ SendUpdateGeometry(false);
+ // The front-buffer now holds newer content for this region than the
+ // back-buffer.
+ front_buffer_diff_ = rect;
+ } else {
+ // Back-buffer contains the latest content for "rect" but the front-buffer
+ // contains the latest content for some other areas (or buffer flipping not
+ // allowed); fall back to copying the data.
+ CopyFromBackBufferToFrontBuffer(rect);
+ }
+ transport_store_painted_ = transport_store_painted_.Union(rect);
}
void WebPluginDelegateProxy::OnHandleURLRequest(
@@ -1297,22 +1320,6 @@ void WebPluginDelegateProxy::OnDeferResourceLoading(unsigned long resource_id,
}
#if defined(OS_MACOSX)
-void WebPluginDelegateProxy::OnUpdateGeometry_ACK(int ack_key) {
- DCHECK_NE(ack_key, -1);
-
- OldTransportDIBMap::iterator iterator = old_transport_dibs_.find(ack_key);
-
- // DCHECK_NE does not work with base::hash_map.
- DCHECK(iterator != old_transport_dibs_.end());
-
- // Now that the ACK has been received, the TransportDIB that was used
- // prior to the UpdateGeometry message now being acknowledged is known to
- // be no longer needed. Release it, and take the stale entry out of the map.
- ReleaseTransportDIB(iterator->second.get());
-
- old_transport_dibs_.erase(iterator);
-}
-
void WebPluginDelegateProxy::OnAcceleratedSurfaceSetIOSurface(
gfx::PluginWindowHandle window,
int32 width,
diff --git a/content/renderer/webplugin_delegate_proxy.h b/content/renderer/webplugin_delegate_proxy.h
index 3318140..22d2bd3 100644
--- a/content/renderer/webplugin_delegate_proxy.h
+++ b/content/renderer/webplugin_delegate_proxy.h
@@ -127,6 +127,11 @@ class WebPluginDelegateProxy
virtual ~WebPluginDelegateProxy();
private:
+ struct SharedBitmap {
+ scoped_ptr<TransportDIB> dib;
+ scoped_ptr<skia::PlatformCanvas> canvas;
+ };
+
// Message handlers for messages that proxy WebPlugin methods, which
// we translate into calls to the real WebPlugin.
void OnSetWindow(gfx::PluginWindowHandle window);
@@ -137,7 +142,7 @@ class WebPluginDelegateProxy
bool* result);
void OnHandleURLRequest(const PluginHostMsg_URLRequest_Params& params);
void OnCancelResource(int id);
- void OnInvalidateRect(const gfx::Rect& rect);
+ void OnInvalidateRect(const gfx::Rect& rect, bool allow_buffer_flipping);
void OnGetWindowScriptNPObject(int route_id, bool* success);
void OnGetPluginElement(int route_id, bool* success);
void OnSetCookie(const GURL& url,
@@ -156,7 +161,6 @@ class WebPluginDelegateProxy
void OnFocusChanged(bool focused);
void OnStartIme();
void OnBindFakePluginWindowHandle(bool opaque);
- void OnUpdateGeometry_ACK(int ack_key);
void OnAcceleratedSurfaceSetIOSurface(gfx::PluginWindowHandle window,
int32 width,
int32 height,
@@ -174,6 +178,9 @@ class WebPluginDelegateProxy
void OnURLRedirectResponse(bool allow, int resource_id);
+ // Helper function that sends the UpdateGeometry message.
+ void SendUpdateGeometry(bool bitmaps_changed);
+
// Draw a graphic indicating a crashed plugin.
void PaintSadPlugin(WebKit::WebCanvas* canvas, const gfx::Rect& rect);
@@ -182,12 +189,41 @@ class WebPluginDelegateProxy
bool BackgroundChanged(gfx::NativeDrawingContext context,
const gfx::Rect& rect);
- // Copies the given rectangle from the transport bitmap to the backing store.
- void CopyFromTransportToBacking(const gfx::Rect& rect);
+ // Copies the given rectangle from the back-buffer transport_stores_ bitmap to
+ // the front-buffer transport_stores_ bitmap.
+ void CopyFromBackBufferToFrontBuffer(const gfx::Rect& rect);
+
+ // Updates the front-buffer with the given rectangle from the back-buffer,
+ // either by copying the rectangle or flipping the buffers.
+ void UpdateFrontBuffer(const gfx::Rect& rect, bool allow_buffer_flipping);
// Clears the shared memory section and canvases used for windowless plugins.
void ResetWindowlessBitmaps();
+ int front_buffer_index() const {
+ return front_buffer_index_;
+ }
+
+ int back_buffer_index() const {
+ return 1 - front_buffer_index_;
+ }
+
+ skia::PlatformCanvas* front_buffer_canvas() const {
+ return transport_stores_[front_buffer_index()].canvas.get();
+ }
+
+ skia::PlatformCanvas* back_buffer_canvas() const {
+ return transport_stores_[back_buffer_index()].canvas.get();
+ }
+
+ TransportDIB* front_buffer_dib() const {
+ return transport_stores_[front_buffer_index()].dib.get();
+ }
+
+ TransportDIB* back_buffer_dib() const {
+ return transport_stores_[back_buffer_index()].dib.get();
+ }
+
#if !defined(OS_WIN)
// Creates a process-local memory section and canvas. PlatformCanvas on
// Windows only works with a DIB, not arbitrary memory.
@@ -211,10 +247,6 @@ class WebPluginDelegateProxy
// the plug-in. Returns true if it successfully sets the window handle on the
// plug-in.
bool BindFakePluginWindowHandle(bool opaque);
-
- typedef base::hash_map<int, linked_ptr<TransportDIB> > OldTransportDIBMap;
-
- OldTransportDIBMap old_transport_dibs_;
#endif // OS_MACOSX
#if defined(OS_WIN)
@@ -248,23 +280,23 @@ class WebPluginDelegateProxy
bool invalidate_pending_;
// Used to desynchronize windowless painting. When WebKit paints, we bitblt
- // from our backing store of what the plugin rectangle looks like. The
- // plugin paints into the transport store, and we copy that to our backing
- // store when we get an invalidate from it. The background bitmap is used
- // for transparent plugins, as they need the backgroud data during painting.
+ // from our front buffer store of what the plugin rectangle looks like. The
+ // plugin paints into the back buffer store, and we swap the buffers when we
+ // get an invalidate from it. The background bitmap is used for transparent
+ // plugins, as they need the background data during painting.
bool transparent_;
-#if defined(OS_WIN)
- scoped_ptr<TransportDIB> backing_store_;
-#else
- std::vector<uint8> backing_store_;
-#endif
- scoped_ptr<skia::PlatformCanvas> backing_store_canvas_;
- scoped_ptr<TransportDIB> transport_store_;
- scoped_ptr<skia::PlatformCanvas> transport_store_canvas_;
- scoped_ptr<TransportDIB> background_store_;
- scoped_ptr<skia::PlatformCanvas> background_store_canvas_;
- // This lets us know which portion of the backing store has been painted into.
- gfx::Rect backing_store_painted_;
+ // The index in the transport_stores_ array of the current front buffer
+ // (i.e., the buffer to display).
+ int front_buffer_index_;
+ SharedBitmap transport_stores_[2];
+ SharedBitmap background_store_;
+ // This lets us know the total portion of the transport store that has been
+ // painted since the buffers were created.
+ gfx::Rect transport_store_painted_;
+ // This is a bounding box on the portion of the front-buffer that was painted
+ // on the last buffer flip and which has not yet been re-painted in the
+ // back-buffer.
+ gfx::Rect front_buffer_diff_;
// The url of the main frame hosting the plugin.
GURL page_url_;
diff --git a/webkit/plugins/npapi/webplugin_delegate_impl.h b/webkit/plugins/npapi/webplugin_delegate_impl.h
index 26bfb65..0b9359c 100644
--- a/webkit/plugins/npapi/webplugin_delegate_impl.h
+++ b/webkit/plugins/npapi/webplugin_delegate_impl.h
@@ -185,6 +185,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate {
// and all callers will use the Paint defined above.
void CGPaint(CGContextRef context, const gfx::Rect& rect);
+ bool AllowBufferFlipping();
#endif // OS_MACOSX
gfx::PluginWindowHandle windowed_handle() const {
diff --git a/webkit/plugins/npapi/webplugin_delegate_impl_mac.mm b/webkit/plugins/npapi/webplugin_delegate_impl_mac.mm
index a2b021a..c2062a6 100644
--- a/webkit/plugins/npapi/webplugin_delegate_impl_mac.mm
+++ b/webkit/plugins/npapi/webplugin_delegate_impl_mac.mm
@@ -960,6 +960,17 @@ void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
current_windowless_cursor_.InitFromNSCursor(cursor);
}
+bool WebPluginDelegateImpl::AllowBufferFlipping() {
+#ifndef NP_NO_CARBON
+ // Using buffer flipping for Carbon plugins would require issuing an
+ // NPP_SetWindow() call for every frame, which would likely expose plugin
+ // bugs and/or suboptimal behaviour. So we don't allow it.
+ return instance()->event_model() != NPEventModelCarbon;
+#else
+ return true;
+#endif
+}
+
#pragma mark -
#pragma mark Internal Tracking