diff options
author | tschmelcher@chromium.org <tschmelcher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-25 22:13:52 +0000 |
---|---|---|
committer | tschmelcher@chromium.org <tschmelcher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-25 22:13:52 +0000 |
commit | 9cf755682fcff0aa79e64c6f4fcb7ced80547d67 (patch) | |
tree | 227975248d1e781ac949dcde1a2291c11919d838 | |
parent | b8fede4e01b906a65ca9eb816e2cab0694810270 (diff) | |
download | chromium_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.h | 20 | ||||
-rw-r--r-- | content/plugin/webplugin_delegate_stub.cc | 10 | ||||
-rw-r--r-- | content/plugin/webplugin_proxy.cc | 340 | ||||
-rw-r--r-- | content/plugin/webplugin_proxy.h | 80 | ||||
-rw-r--r-- | content/renderer/webplugin_delegate_proxy.cc | 337 | ||||
-rw-r--r-- | content/renderer/webplugin_delegate_proxy.h | 80 | ||||
-rw-r--r-- | webkit/plugins/npapi/webplugin_delegate_impl.h | 1 | ||||
-rw-r--r-- | webkit/plugins/npapi/webplugin_delegate_impl_mac.mm | 11 |
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(), + ¶m.windowless_buffer0); + + if (transport_stores_[1].dib.get()) + CopyTransportDIBHandleForMessage(transport_stores_[1].dib->handle(), + ¶m.windowless_buffer1); + + if (background_store_.dib.get()) + CopyTransportDIBHandleForMessage(background_store_.dib->handle(), + ¶m.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 |