diff options
author | piman@chromium.org <piman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-09 22:19:21 +0000 |
---|---|---|
committer | piman@chromium.org <piman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-09 22:19:21 +0000 |
commit | aa4117f9d083dead1c9c764f0ad14e1c14d00524 (patch) | |
tree | 766cf5af27597abb38a94b16a1644a2c894b1436 | |
parent | 209f20c8a51c83c2344e5c638f391a9f5dfb827c (diff) | |
download | chromium_src-aa4117f9d083dead1c9c764f0ad14e1c14d00524.zip chromium_src-aa4117f9d083dead1c9c764f0ad14e1c14d00524.tar.gz chromium_src-aa4117f9d083dead1c9c764f0ad14e1c14d00524.tar.bz2 |
Delay UpdateRect until the SwapBuffers callback when accelerated compositing is on.
This is also removing the UpdateRect ack in the accelerated compositing case,
because it is not needed (and adds scheduling constraints that reduce
throughput).
This also sends a "dummy" message to the browser to unblock the UI thread if it's waiting on an UpdateRect when the transition from non-accelerated to accelerated happens: the GPU process may need to round trip to the browser UI thread before sending the SwapBuffers callback.
BUG=58782
TEST=reduced jankiness on aura builds when resizing.
Review URL: http://codereview.chromium.org/8498036
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113861 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/renderer_host/render_message_filter.cc | 14 | ||||
-rw-r--r-- | content/browser/renderer_host/render_message_filter.h | 2 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_helper.cc | 38 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_helper.h | 6 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host.cc | 19 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host.h | 3 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_unittest.cc | 1 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_gtk.cc | 5 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_win.cc | 46 | ||||
-rw-r--r-- | content/common/gpu/image_transport_surface.cc | 38 | ||||
-rw-r--r-- | content/common/gpu/image_transport_surface.h | 2 | ||||
-rw-r--r-- | content/common/view_messages.h | 8 | ||||
-rw-r--r-- | content/renderer/render_widget.cc | 122 | ||||
-rw-r--r-- | content/renderer/render_widget.h | 20 |
14 files changed, 209 insertions, 115 deletions
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index fe937b2..238a1a2 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -357,6 +357,7 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message, OnOpenChannelToPpapiBroker) IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_UpdateRect, render_widget_helper_->DidReceiveUpdateMsg(message)) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateIsDelayed, OnUpdateIsDelayed) IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_CheckPermission, OnCheckNotificationPermission) IPC_MESSAGE_HANDLER(ChildProcessHostMsg_SyncAllocateSharedMemory, @@ -889,3 +890,16 @@ void RenderMessageFilter::OnCompletedOpenChannelToNpapiPlugin( DCHECK(ContainsKey(plugin_host_clients_, client)); plugin_host_clients_.erase(client); } + +void RenderMessageFilter::OnUpdateIsDelayed(const IPC::Message& msg) { + // When not in accelerated compositing mode, in certain cases (e.g. waiting + // for a resize or if no backing store) the RenderWidgetHost is blocking the + // UI thread for some time, waiting for an UpdateRect from the renderer. If we + // are going to switch to accelerated compositing, the GPU process may need + // round-trips to the UI thread before finishing the frame, causing deadlocks + // if we delay the UpdateRect until we receive the OnSwapBuffersComplete. So + // the renderer sent us this message, so that we can unblock the UI thread. + // We will simply re-use the UpdateRect unblock mechanism, just with a + // different message. + render_widget_helper_->DidReceiveUpdateMsg(msg); +} diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h index d9e2072..e61ffa4 100644 --- a/content/browser/renderer_host/render_message_filter.h +++ b/content/browser/renderer_host/render_message_filter.h @@ -224,6 +224,8 @@ class RenderMessageFilter : public BrowserMessageFilter { void OnCompletedOpenChannelToNpapiPlugin( OpenChannelToNpapiPluginCallback* client); + void OnUpdateIsDelayed(const IPC::Message& msg); + // Cached resource request dispatcher host and plugin service, guaranteed to // be non-null if Init succeeds. We do not own the objects, they are managed // by the BrowserProcess, which has a wider scope than we do. diff --git a/content/browser/renderer_host/render_widget_helper.cc b/content/browser/renderer_host/render_widget_helper.cc index 9228e0f..2a791a7 100644 --- a/content/browser/renderer_host/render_widget_helper.cc +++ b/content/browser/renderer_host/render_widget_helper.cc @@ -109,13 +109,17 @@ bool RenderWidgetHelper::WaitForUpdateMsg(int render_widget_id, UpdateMsgProxyMap::iterator it = pending_paints_.find(render_widget_id); if (it != pending_paints_.end()) { - proxy = it->second; + UpdateMsgProxyQueue &queue = it->second; + DCHECK(!queue.empty()); + proxy = queue.front(); // Flag the proxy as cancelled so that when it is run as a task it will // do nothing. proxy->cancelled = true; - pending_paints_.erase(it); + queue.pop_front(); + if (queue.empty()) + pending_paints_.erase(it); } } @@ -140,30 +144,11 @@ bool RenderWidgetHelper::WaitForUpdateMsg(int render_widget_id, void RenderWidgetHelper::DidReceiveUpdateMsg(const IPC::Message& msg) { int render_widget_id = msg.routing_id(); - UpdateMsgProxy* proxy = NULL; + UpdateMsgProxy* proxy = new UpdateMsgProxy(this, msg); { base::AutoLock lock(pending_paints_lock_); - // Visual Studio 2010 has problems converting NULL to the null pointer for - // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/details/520043/error-converting-from-null-to-a-pointer-type-in-std-pair - // It will work if we pass nullptr. -#if defined(_MSC_VER) && _MSC_VER >= 1600 - RenderWidgetHelper::UpdateMsgProxy* null_proxy = nullptr; -#else - RenderWidgetHelper::UpdateMsgProxy* null_proxy = NULL; -#endif - UpdateMsgProxyMap::value_type new_value(render_widget_id, null_proxy); - - // We expect only a single PaintRect message at a time. Optimize for the - // case that we don't already have an entry by using the 'insert' method. - std::pair<UpdateMsgProxyMap::iterator, bool> result = - pending_paints_.insert(new_value); - if (!result.second) { - NOTREACHED() << "Unexpected PaintRect message!"; - return; - } - - result.first->second = (proxy = new UpdateMsgProxy(this, msg)); + pending_paints_[render_widget_id].push_back(proxy); } // Notify anyone waiting on the UI thread that there is a new entry in the @@ -184,9 +169,12 @@ void RenderWidgetHelper::OnDiscardUpdateMsg(UpdateMsgProxy* proxy) { UpdateMsgProxyMap::iterator it = pending_paints_.find(msg.routing_id()); DCHECK(it != pending_paints_.end()); - DCHECK(it->second == proxy); + UpdateMsgProxyQueue &queue = it->second; + DCHECK(queue.front() == proxy); - pending_paints_.erase(it); + queue.pop_front(); + if (queue.empty()) + pending_paints_.erase(it); } } diff --git a/content/browser/renderer_host/render_widget_helper.h b/content/browser/renderer_host/render_widget_helper.h index 605a317..766566a 100644 --- a/content/browser/renderer_host/render_widget_helper.h +++ b/content/browser/renderer_host/render_widget_helper.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HELPER_H_ #pragma once +#include <deque> #include <map> #include "base/atomic_sequence_num.h" @@ -160,8 +161,9 @@ class RenderWidgetHelper friend class UpdateMsgProxy; friend class base::RefCountedThreadSafe<RenderWidgetHelper>; - // Map from render_widget_id to live PaintMsgProxy instance. - typedef base::hash_map<int, UpdateMsgProxy*> UpdateMsgProxyMap; + typedef std::deque<UpdateMsgProxy*> UpdateMsgProxyQueue; + // Map from render_widget_id to a queue of live PaintMsgProxy instances. + typedef base::hash_map<int, UpdateMsgProxyQueue > UpdateMsgProxyMap; ~RenderWidgetHelper(); diff --git a/content/browser/renderer_host/render_widget_host.cc b/content/browser/renderer_host/render_widget_host.cc index bfc5ce8..e15bc63 100644 --- a/content/browser/renderer_host/render_widget_host.cc +++ b/content/browser/renderer_host/render_widget_host.cc @@ -197,6 +197,7 @@ bool RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_SetTooltipText, OnMsgSetTooltipText) IPC_MESSAGE_HANDLER(ViewHostMsg_PaintAtSize_ACK, OnMsgPaintAtSizeAck) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnMsgUpdateRect) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateIsDelayed, OnMsgUpdateIsDelayed) IPC_MESSAGE_HANDLER(ViewHostMsg_HandleInputEvent_ACK, OnMsgInputEventAck) IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnMsgFocus) IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnMsgBlur) @@ -1065,17 +1066,23 @@ void RenderWidgetHost::OnMsgUpdateRect( UMA_HISTOGRAM_TIMES("MPArch.RWH_OnMsgUpdateRect", delta); } +void RenderWidgetHost::OnMsgUpdateIsDelayed() { + // Nothing to do, this message was just to unblock the UI thread. +} + void RenderWidgetHost::DidUpdateBackingStore( const ViewHostMsg_UpdateRect_Params& params, const TimeTicks& paint_start) { TRACE_EVENT0("renderer_host", "RenderWidgetHost::DidUpdateBackingStore"); TimeTicks update_start = TimeTicks::Now(); - // ACK early so we can prefetch the next PaintRect if there is a next one. - // This must be done AFTER we're done painting with the bitmap supplied by the - // renderer. This ACK is a signal to the renderer that the backing store can - // be re-used, so the bitmap may be invalid after this call. - Send(new ViewMsg_UpdateRect_ACK(routing_id_)); + if (params.needs_ack) { + // ACK early so we can prefetch the next PaintRect if there is a next one. + // This must be done AFTER we're done painting with the bitmap supplied by + // the renderer. This ACK is a signal to the renderer that the backing store + // can be re-used, so the bitmap may be invalid after this call. + Send(new ViewMsg_UpdateRect_ACK(routing_id_)); + } // Move the plugins if the view hasn't already been destroyed. Plugin moves // will not be re-issued, so must move them now, regardless of whether we @@ -1511,7 +1518,6 @@ bool RenderWidgetHost::GotResponseToLockMouseRequest(bool allowed) { } } -#if defined(OS_MACOSX) || defined(UI_COMPOSITOR_IMAGE_TRANSPORT) // static void RenderWidgetHost::AcknowledgeSwapBuffers(int32 route_id, int gpu_host_id) { GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(gpu_host_id); @@ -1526,4 +1532,3 @@ void RenderWidgetHost::AcknowledgePostSubBuffer(int32 route_id, if (ui_shim) ui_shim->Send(new AcceleratedSurfaceMsg_PostSubBufferACK(route_id)); } -#endif diff --git a/content/browser/renderer_host/render_widget_host.h b/content/browser/renderer_host/render_widget_host.h index 49b71fd..8d27e23 100644 --- a/content/browser/renderer_host/render_widget_host.h +++ b/content/browser/renderer_host/render_widget_host.h @@ -449,11 +449,9 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener, // locked. bool GotResponseToLockMouseRequest(bool allowed); -#if defined(OS_MACOSX) || defined(UI_COMPOSITOR_IMAGE_TRANSPORT) // Called by the view in response to AcceleratedSurfaceBuffersSwapped. static void AcknowledgeSwapBuffers(int32 route_id, int gpu_host_id); static void AcknowledgePostSubBuffer(int32 route_id, int gpu_host_id); -#endif protected: // Internal implementation of the public Forward*Event() methods. @@ -557,6 +555,7 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener, WebKit::WebTextDirection text_direction_hint); void OnMsgPaintAtSizeAck(int tag, const gfx::Size& size); void OnMsgUpdateRect(const ViewHostMsg_UpdateRect_Params& params); + void OnMsgUpdateIsDelayed(); void OnMsgInputEventAck(WebKit::WebInputEvent::Type event_type, bool processed); virtual void OnMsgFocus(); diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index 189cb5f..fd363bb 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc @@ -96,6 +96,7 @@ void RenderWidgetHostProcess::InitUpdateRectParams( params->copy_rects.push_back(params->bitmap_rect); params->view_size = gfx::Size(w, h); params->flags = update_msg_reply_flags_; + params->needs_ack = true; } bool RenderWidgetHostProcess::WaitForUpdateMsg(int render_widget_id, diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.cc b/content/browser/renderer_host/render_widget_host_view_gtk.cc index 2efe680..d79db66 100644 --- a/content/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/content/browser/renderer_host/render_widget_host_view_gtk.cc @@ -31,6 +31,7 @@ #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_widget_host.h" +#include "content/common/gpu/gpu_messages.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/common/content_switches.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" @@ -1005,13 +1006,13 @@ BackingStore* RenderWidgetHostViewGtk::AllocBackingStore( void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped( const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, int gpu_host_id) { - NOTREACHED(); + RenderWidgetHost::AcknowledgeSwapBuffers(params.route_id, gpu_host_id); } void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer( const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, int gpu_host_id) { - NOTREACHED(); + RenderWidgetHost::AcknowledgePostSubBuffer(params.route_id, gpu_host_id); } void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) { diff --git a/content/browser/renderer_host/render_widget_host_view_win.cc b/content/browser/renderer_host/render_widget_host_view_win.cc index 997e145..c807814 100644 --- a/content/browser/renderer_host/render_widget_host_view_win.cc +++ b/content/browser/renderer_host/render_widget_host_view_win.cc @@ -182,21 +182,6 @@ LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message, return ::DefWindowProc(window, message, wparam, lparam); } -void SendToGpuProcessHost(int gpu_host_id, IPC::Message* message) { - GpuProcessHost* gpu_process_host = GpuProcessHost::FromID(gpu_host_id); - if (!gpu_process_host) { - delete message; - return; - } - - gpu_process_host->Send(message); -} - -void PostTaskOnIOThread(const tracked_objects::Location& from_here, - base::Closure task) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task); -} - bool DecodeZoomGesture(HWND hwnd, const GESTUREINFO& gi, content::PageZoom* zoom, POINT* zoom_center) { @@ -2044,28 +2029,27 @@ void RenderWidgetHostViewWin::OnAcceleratedCompositingStateChange() { void RenderWidgetHostViewWin::AcceleratedSurfaceBuffersSwapped( const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, int gpu_host_id) { - if (!accelerated_surface_.get() && compositor_host_window_) { - accelerated_surface_ = new AcceleratedSurface(compositor_host_window_); - accelerated_surface_->Initialize(); - } - - base::Closure acknowledge_task = - base::Bind(SendToGpuProcessHost, - gpu_host_id, - new AcceleratedSurfaceMsg_BuffersSwappedACK(params.route_id)); + if (params.surface_id) { + if (!accelerated_surface_.get() && compositor_host_window_) { + accelerated_surface_ = new AcceleratedSurface(compositor_host_window_); + accelerated_surface_->Initialize(); + } - accelerated_surface_->AsyncPresentAndAcknowledge( - params.size, - params.surface_id, - base::Bind(PostTaskOnIOThread, - FROM_HERE, - acknowledge_task)); + accelerated_surface_->AsyncPresentAndAcknowledge( + params.size, + params.surface_id, + base::Bind(&RenderWidgetHost::AcknowledgeSwapBuffers, + gpu_host_id, + params.route_id)); + } else { + RenderWidgetHost::AcknowledgeSwapBuffers(params.route_id, gpu_host_id); + } } void RenderWidgetHostViewWin::AcceleratedSurfacePostSubBuffer( const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, int gpu_host_id) { - NOTREACHED(); + RenderWidgetHost::AcknowledgePostSubBuffer(params.route_id, gpu_host_id); } void RenderWidgetHostViewWin::SetAccessibilityFocus(int acc_obj_id) { diff --git a/content/common/gpu/image_transport_surface.cc b/content/common/gpu/image_transport_surface.cc index 94669cf..d1939f2 100644 --- a/content/common/gpu/image_transport_surface.cc +++ b/content/common/gpu/image_transport_surface.cc @@ -245,12 +245,46 @@ void PassThroughImageTransportSurface::OnNewSurfaceACK( uint64 surface_id, TransportDIB::Handle surface_handle) { } +bool PassThroughImageTransportSurface::SwapBuffers() { + bool result = gfx::GLSurfaceAdapter::SwapBuffers(); + + // Round trip to the browser UI thread, for throttling, by sending a dummy + // SwapBuffers message. + GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; + params.surface_id = 0; +#if defined(OS_WIN) + params.size = GetSize(); +#endif + helper_->SendAcceleratedSurfaceBuffersSwapped(params); + + helper_->SetScheduled(false); + return result; +} + +bool PassThroughImageTransportSurface::PostSubBuffer( + int x, int y, int width, int height) { + bool result = gfx::GLSurfaceAdapter::PostSubBuffer(x, y, width, height); + + // Round trip to the browser UI thread, for throttling, by sending a dummy + // PostSubBuffer message. + GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; + params.surface_id = 0; + params.x = x; + params.y = y; + params.width = width; + params.height = height; + helper_->SendAcceleratedSurfacePostSubBuffer(params); + + helper_->SetScheduled(false); + return result; +} + void PassThroughImageTransportSurface::OnBuffersSwappedACK() { - NOTREACHED(); + helper_->SetScheduled(true); } void PassThroughImageTransportSurface::OnPostSubBufferACK() { - NOTREACHED(); + helper_->SetScheduled(true); } void PassThroughImageTransportSurface::OnResizeViewACK() { diff --git a/content/common/gpu/image_transport_surface.h b/content/common/gpu/image_transport_surface.h index 69ce265..a34ae33 100644 --- a/content/common/gpu/image_transport_surface.h +++ b/content/common/gpu/image_transport_surface.h @@ -156,6 +156,8 @@ class PassThroughImageTransportSurface // GLSurface implementation. virtual bool Initialize() OVERRIDE; virtual void Destroy() OVERRIDE; + virtual bool SwapBuffers() OVERRIDE; + virtual bool PostSubBuffer(int x, int y, int width, int height) OVERRIDE; // ImageTransportSurface implementation. virtual void OnNewSurfaceACK( diff --git a/content/common/view_messages.h b/content/common/view_messages.h index ac2c512..5b863eb 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -522,6 +522,10 @@ IPC_STRUCT_BEGIN(ViewHostMsg_UpdateRect_Params) // which would indicate that this paint message is an ACK for multiple // request messages. IPC_STRUCT_MEMBER(int, flags) + + // Whether or not the renderer expects a ViewMsg_UpdateRect_ACK for this + // update. True for 2D painting, but false for accelerated compositing. + IPC_STRUCT_MEMBER(bool, needs_ack) IPC_STRUCT_END() IPC_STRUCT_BEGIN(ViewHostMsg_DidFailProvisionalLoadWithError_Params) @@ -1431,6 +1435,10 @@ IPC_MESSAGE_ROUTED2(ViewHostMsg_PaintAtSize_ACK, IPC_MESSAGE_ROUTED1(ViewHostMsg_UpdateRect, ViewHostMsg_UpdateRect_Params) +// Sent to unblock the browser's UI thread if it is waiting on an UpdateRect, +// which may get delayed until the browser's UI unblocks. +IPC_MESSAGE_ROUTED0(ViewHostMsg_UpdateIsDelayed) + // Sent by the renderer when accelerated compositing is enabled or disabled to // notify the browser whether or not is should do painting. IPC_MESSAGE_ROUTED1(ViewHostMsg_DidActivateAcceleratedCompositing, diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 5980b18..be794cd5 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/metrics/histogram.h" +#include "base/stl_util.h" #include "base/utf_string_conversions.h" #include "build/build_config.h" #include "content/common/swapped_out_messages.h" @@ -104,6 +105,7 @@ RenderWidget::RenderWidget(WebKit::WebPopupType popup_type) RenderWidget::~RenderWidget() { DCHECK(!webwidget_) << "Leaking our WebWidget!"; + STLDeleteElements(&updates_pending_swap_); if (current_paint_buf_) { RenderProcess::current()->ReleaseTransportDIB(current_paint_buf_); current_paint_buf_ = NULL; @@ -356,7 +358,7 @@ void RenderWidget::OnRequestMoveAck() { void RenderWidget::OnUpdateRectAck() { TRACE_EVENT0("renderer", "RenderWidget::OnUpdateRectAck"); - DCHECK(update_reply_pending()); + DCHECK(update_reply_pending_); update_reply_pending_ = false; // If we sent an UpdateRect message with a zero-sized bitmap, then we should @@ -389,6 +391,14 @@ bool RenderWidget::SupportsAsynchronousSwapBuffers() void RenderWidget::OnSwapBuffersAborted() { TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersAborted"); + while (!updates_pending_swap_.empty()) { + ViewHostMsg_UpdateRect* msg = updates_pending_swap_.front(); + updates_pending_swap_.pop_front(); + // msg can be NULL if the swap doesn't correspond to an DoDeferredUpdate + // compositing pass, hence doesn't require an UpdateRect message. + if (msg) + Send(msg); + } num_swapbuffers_complete_pending_ = 0; using_asynchronous_swapbuffers_ = false; // Schedule another frame so the compositor learns about it. @@ -397,8 +407,19 @@ void RenderWidget::OnSwapBuffersAborted() void RenderWidget::OnSwapBuffersPosted() { TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersPosted"); - if (using_asynchronous_swapbuffers_) + + if (using_asynchronous_swapbuffers_) { + ViewHostMsg_UpdateRect* msg = NULL; + // pending_update_params_ can be NULL if the swap doesn't correspond to an + // DoDeferredUpdate compositing pass, hence doesn't require an UpdateRect + // message. + if (pending_update_params_.get()) { + msg = new ViewHostMsg_UpdateRect(routing_id_, *pending_update_params_); + pending_update_params_.reset(); + } + updates_pending_swap_.push_back(msg); num_swapbuffers_complete_pending_++; + } } void RenderWidget::OnSwapBuffersComplete() { @@ -409,6 +430,13 @@ void RenderWidget::OnSwapBuffersComplete() { TRACE_EVENT0("renderer", "EarlyOut_ZeroSwapbuffersPending"); return; } + DCHECK(!updates_pending_swap_.empty()); + ViewHostMsg_UpdateRect* msg = updates_pending_swap_.front(); + updates_pending_swap_.pop_front(); + // msg can be NULL if the swap doesn't correspond to an DoDeferredUpdate + // compositing pass, hence doesn't require an UpdateRect message. + if (msg) + Send(msg); num_swapbuffers_complete_pending_--; // If update reply is still pending, then defer the update until that reply @@ -709,7 +737,7 @@ void RenderWidget::DoDeferredUpdate() { if (!webwidget_) return; - if (update_reply_pending()) { + if (update_reply_pending_) { TRACE_EVENT0("renderer", "EarlyOut_UpdateReplyPending"); return; } @@ -773,9 +801,6 @@ void RenderWidget::DoDeferredUpdate() { gfx::Rect scroll_damage = update.GetScrollDamage(); gfx::Rect bounds = update.GetPaintBounds().Union(scroll_damage); - // Compositing the page may disable accelerated compositing. - bool accelerated_compositing_was_active = is_accelerated_compositing_active_; - // A plugin may be able to do an optimized paint. First check this, in which // case we can skip all of the bitmap generation and regular paint code. // This optimization allows PPAPI plugins that declare themselves on top of @@ -787,19 +812,30 @@ void RenderWidget::DoDeferredUpdate() { // This optimization only works when the entire invalid region is contained // within the plugin. There is a related optimization in PaintRect for the // case where there may be multiple invalid regions. - TransportDIB::Id dib_id = TransportDIB::Id(); TransportDIB* dib = NULL; - std::vector<gfx::Rect> copy_rects; gfx::Rect optimized_copy_rect, optimized_copy_location; + DCHECK(!pending_update_params_.get()); + pending_update_params_.reset(new ViewHostMsg_UpdateRect_Params); + pending_update_params_->dx = update.scroll_delta.x(); + pending_update_params_->dy = update.scroll_delta.y(); + pending_update_params_->scroll_rect = update.scroll_rect; + pending_update_params_->view_size = size_; + pending_update_params_->resizer_rect = resizer_rect_; + pending_update_params_->plugin_window_moves.swap(plugin_window_moves_); + pending_update_params_->flags = next_paint_flags_; + pending_update_params_->scroll_offset = GetScrollOffset(); + pending_update_params_->needs_ack = true; + next_paint_flags_ = 0; + if (update.scroll_rect.IsEmpty() && !is_accelerated_compositing_active_ && GetBitmapForOptimizedPluginPaint(bounds, &dib, &optimized_copy_location, &optimized_copy_rect)) { // Only update the part of the plugin that actually changed. optimized_copy_rect = optimized_copy_rect.Intersect(bounds); - bounds = optimized_copy_location; - copy_rects.push_back(optimized_copy_rect); - dib_id = dib->id(); + pending_update_params_->bitmap = dib->id(); + pending_update_params_->bitmap_rect = optimized_copy_location; + pending_update_params_->copy_rects.push_back(optimized_copy_rect); } else if (!is_accelerated_compositing_active_) { // Compute a buffer for painting and cache it. scoped_ptr<skia::PlatformCanvas> canvas( @@ -819,6 +855,10 @@ void RenderWidget::DoDeferredUpdate() { HISTOGRAM_COUNTS_100("MPArch.RW_PaintRectCount", update.paint_rects.size()); + pending_update_params_->bitmap = current_paint_buf_->id(); + pending_update_params_->bitmap_rect = bounds; + + std::vector<gfx::Rect>& copy_rects = pending_update_params_->copy_rects; // The scroll damage is just another rectangle to paint and copy. copy_rects.swap(update.paint_rects); if (!scroll_damage.IsEmpty()) @@ -826,37 +866,29 @@ void RenderWidget::DoDeferredUpdate() { for (size_t i = 0; i < copy_rects.size(); ++i) PaintRect(copy_rects[i], bounds.origin(), canvas.get()); - - dib_id = current_paint_buf_->id(); } else { // Accelerated compositing path // Begin painting. + // If painting is done via the gpu process then we don't set any damage + // rects to save the browser process from doing unecessary work. + pending_update_params_->bitmap_rect = bounds; + pending_update_params_->scroll_rect = gfx::Rect(); + // We don't need an ack, because we're not sharing a DIB with the browser. + // If it needs to (e.g. composited UI), the GPU process does its own ACK + // with the browser for the GPU surface. + pending_update_params_->needs_ack = false; webwidget_->composite(false); } - // sending an ack to browser process that the paint is complete... - ViewHostMsg_UpdateRect_Params params; - params.bitmap = dib_id; - params.bitmap_rect = bounds; - params.dx = update.scroll_delta.x(); - params.dy = update.scroll_delta.y(); - if (accelerated_compositing_was_active) { - // If painting is done via the gpu process then we clear out all damage - // rects to save the browser process from doing unecessary work. - params.scroll_rect = gfx::Rect(); - params.copy_rects.clear(); - } else { - params.scroll_rect = update.scroll_rect; - params.copy_rects.swap(copy_rects); // TODO(darin): clip to bounds? + // If composite() called SwapBuffers, pending_update_params_ will be reset (in + // OnSwapBuffersPosted), meaning a message has been added to the + // updates_pending_swap_ queue, that will be sent later. Otherwise, we send + // the message now. + if (pending_update_params_.get()) { + // sending an ack to browser process that the paint is complete... + update_reply_pending_ = pending_update_params_->needs_ack; + Send(new ViewHostMsg_UpdateRect(routing_id_, *pending_update_params_)); + pending_update_params_.reset(); } - params.view_size = size_; - params.resizer_rect = resizer_rect_; - params.plugin_window_moves.swap(plugin_window_moves_); - params.flags = next_paint_flags_; - params.scroll_offset = GetScrollOffset(); - - update_reply_pending_ = true; - Send(new ViewHostMsg_UpdateRect(routing_id_, params)); - next_paint_flags_ = 0; UpdateTextInputState(); UpdateSelectionBounds(); @@ -882,7 +914,7 @@ void RenderWidget::didInvalidateRect(const WebRect& rect) { return; if (!paint_aggregator_.HasPendingUpdate()) return; - if (update_reply_pending() || + if (update_reply_pending_ || num_swapbuffers_complete_pending_ >= kMaxSwapBuffersPending) return; @@ -920,7 +952,7 @@ void RenderWidget::didScrollRect(int dx, int dy, const WebRect& clip_rect) { return; if (!paint_aggregator_.HasPendingUpdate()) return; - if (update_reply_pending() || + if (update_reply_pending_ || num_swapbuffers_complete_pending_ >= kMaxSwapBuffersPending) return; @@ -951,6 +983,18 @@ void RenderWidget::didActivateCompositor(int compositor_identifier) { if (compositor_thread) compositor_thread->AddCompositor(routing_id_, compositor_identifier); + if (!is_accelerated_compositing_active_) { + // When not in accelerated compositing mode, in certain cases (e.g. waiting + // for a resize or if no backing store) the RenderWidgetHost is blocking the + // browser's UI thread for some time, waiting for an UpdateRect. If we are + // going to switch to accelerated compositing, the GPU process may need + // round-trips to the browser's UI thread before finishing the frame, + // causing deadlocks if we delay the UpdateRect until we receive the + // OnSwapBuffersComplete. So send a dummy message that will unblock the + // browser's UI thread. + Send(new ViewHostMsg_UpdateIsDelayed(routing_id_)); + } + is_accelerated_compositing_active_ = true; Send(new ViewHostMsg_DidActivateAcceleratedCompositing( routing_id_, is_accelerated_compositing_active_)); @@ -978,7 +1022,7 @@ void RenderWidget::didCommitAndDrawCompositorFrame() { } void RenderWidget::didCompleteSwapBuffers() { - if (update_reply_pending()) + if (update_reply_pending_) return; if (!next_paint_flags_ && !plugin_window_moves_.size()) diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index aa03719..5dc7962 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h @@ -6,6 +6,7 @@ #define CONTENT_RENDERER_RENDER_WIDGET_H_ #pragma once +#include <deque> #include <vector> #include "base/basictypes.h" @@ -29,6 +30,9 @@ #include "ui/gfx/surface/transport_dib.h" #include "webkit/glue/webcursor.h" +struct ViewHostMsg_UpdateRect_Params; +class ViewHostMsg_UpdateRect; + namespace IPC { class SyncMessage; } @@ -274,11 +278,6 @@ class CONTENT_EXPORT RenderWidget void WillToggleFullscreen(); void DidToggleFullscreen(); - // True if an UpdateRect_ACK message is pending. - bool update_reply_pending() const { - return update_reply_pending_; - } - bool next_paint_is_resize_ack() const; bool next_paint_is_restore_ack() const; void set_next_paint_is_resize_ack(); @@ -474,6 +473,17 @@ class CONTENT_EXPORT RenderWidget bool has_disable_gpu_vsync_switch_; base::TimeTicks last_do_deferred_update_time_; + // UpdateRect parameters for the current compositing pass. This is used to + // pass state between DoDeferredUpdate and OnSwapBuffersPosted. + scoped_ptr<ViewHostMsg_UpdateRect_Params> pending_update_params_; + + // Queue of UpdateRect messages corresponding to a SwapBuffers. We want to + // delay sending of UpdateRect until the corresponding SwapBuffers has been + // executed. Since we can have several in flight, we need to keep them in a + // queue. Note: some SwapBuffers may not correspond to an update, in which + // case NULL is added to the queue. + std::deque<ViewHostMsg_UpdateRect*> updates_pending_swap_; + DISALLOW_COPY_AND_ASSIGN(RenderWidget); }; |