diff options
author | jbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-26 01:01:07 +0000 |
---|---|---|
committer | jbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-26 01:01:07 +0000 |
commit | c63b4d4f499671b7f55aa264e823ad521f5d6aae (patch) | |
tree | 36c62254048a0e3f82ac0f003905ae860db1a3cf | |
parent | 92adf1719ccb5f7734955998006bc22b03210723 (diff) | |
download | chromium_src-c63b4d4f499671b7f55aa264e823ad521f5d6aae.zip chromium_src-c63b4d4f499671b7f55aa264e823ad521f5d6aae.tar.gz chromium_src-c63b4d4f499671b7f55aa264e823ad521f5d6aae.tar.bz2 |
Delete background tab IOSurfaces on Mac.
The deleting part is easy: just handle the AcceleratedSurfaceSuspend call by unrefing the IOSurface.
The hard part is dealing with a NSView drawRect when we don't have the IOSurface (or software BackingStore). To solve this, I reuse the GetBackingStore code to wait for a new frame from the renderer. When the BuffersSwapped message arrives on the IO thread for Mac, an UpdateRect message is synthesized with the SwapBuffers data. The UpdateRect message wakes up the UI thread and allows GetBackingStore to resume.
The accelerated path can have multiple frames in the pipeline, so it is rarely enough to just wait for the next UpdateRect. Instead, the GetBackingStore method is updated to wait up to 40ms to get the correctly-sized frame (whether it's accelerated or software).
The original GetBackingStore code waits for a frame that matches current_size_. However, this CL makes GetBackingStore wait for a frame that matches the view_->GetViewBounds(). current_size_ is equal to the last UpdateRect, which may or may not match GetViewBounds. (Anyone know why the original code doesn't use GetViewBounds?)
This allows us to recover from missing BackingStores or IOSurfaces if the renderer/GPU can produce a new frame within 40ms. We probably want to increase 40 to something like 60 or 100ms though because of the deep GPU pipeline.
In addition, thanks to the blocking GetBackingStore, this fixes some additional bugs:
- no more resize gutter jank on accelerated pages (Mac only for now, but should work on Windows with a followup patch).
- no more white flash while resizing flicker-test2.html.
BUG=117624,58782,85519,124328,106586
Review URL: https://chromiumcodereview.appspot.com/9980016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134033 0039d316-1c4b-4281-b951-d872f2087c98
29 files changed, 645 insertions, 264 deletions
diff --git a/chrome/test/gpu/gpu_pixel_browsertest.cc b/chrome/test/gpu/gpu_pixel_browsertest.cc index 7ea66d3..8acbd22 100644 --- a/chrome/test/gpu/gpu_pixel_browsertest.cc +++ b/chrome/test/gpu/gpu_pixel_browsertest.cc @@ -247,7 +247,7 @@ class GpuPixelBrowserTest : public InProcessBrowserTest { const SkBitmap* ref_bmp; bool save_gen = false; - bool save_diff = false; + bool save_diff = true; bool rt = true; if ((ref_img_revision_ <= 0 && !use_checked_in_ref_imgs_) || diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index 35ac5f3..66f152b 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc @@ -18,9 +18,11 @@ #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/gpu/gpu_process_host_ui_shim.h" #include "content/browser/gpu/gpu_surface_tracker.h" +#include "content/browser/renderer_host/render_widget_helper.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/common/child_process_host_impl.h" #include "content/common/gpu/gpu_messages.h" +#include "content/common/view_messages.h" #include "content/gpu/gpu_child_thread.h" #include "content/gpu/gpu_process.h" #include "content/public/browser/browser_thread.h" @@ -410,6 +412,10 @@ bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished) IPC_MESSAGE_HANDLER(GpuHostMsg_CommandBufferCreated, OnCommandBufferCreated) IPC_MESSAGE_HANDLER(GpuHostMsg_DestroyCommandBuffer, OnDestroyCommandBuffer) +#if defined(OS_MACOSX) + IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped, + OnAcceleratedSurfaceBuffersSwapped) +#endif #if defined(OS_WIN) && !defined(USE_AURA) IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped, OnAcceleratedSurfaceBuffersSwapped) @@ -535,6 +541,51 @@ void GpuProcessHost::OnDestroyCommandBuffer(int32 surface_id) { #endif // defined(TOOLKIT_GTK) } +#if defined(OS_MACOSX) +void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( + const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) { + TRACE_EVENT0("renderer", + "GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped"); + + gfx::PluginWindowHandle handle = + GpuSurfaceTracker::Get()->GetSurfaceWindowHandle(params.surface_id); + // Compositor window is always gfx::kNullPluginWindow. + // TODO(jbates) http://crbug.com/105344 This will be removed when there are no + // plugin windows. + if (handle != gfx::kNullPluginWindow) { + RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceBuffersSwapped(params)); + return; + } + + base::ScopedClosureRunner scoped_completion_runner( + base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted, + host_id_, params.route_id, true)); + + int render_process_id = 0; + int render_widget_id = 0; + if (!GpuSurfaceTracker::Get()->GetRenderWidgetIDForSurface( + params.surface_id, &render_process_id, &render_widget_id)) { + return; + } + RenderWidgetHelper* helper = + RenderWidgetHelper::FromProcessHostID(render_process_id); + if (!helper) + return; + + // Pass the SwapBuffers on to the RenderWidgetHelper to wake up the UI thread + // if the browser is waiting for a new frame. Otherwise the RenderWidgetHelper + // will forward to the RenderWidgetHostView via RenderProcessHostImpl and + // RenderWidgetHostImpl. + scoped_completion_runner.Release(); + helper->DidReceiveBackingStoreMsg(ViewHostMsg_CompositorSurfaceBuffersSwapped( + render_widget_id, + params.surface_id, + params.surface_handle, + params.route_id, + host_id_)); +} +#endif // OS_MACOSX + #if defined(OS_WIN) && !defined(USE_AURA) void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h index f101c6c..8f0b05f 100644 --- a/content/browser/gpu/gpu_process_host.h +++ b/content/browser/gpu/gpu_process_host.h @@ -117,6 +117,10 @@ class GpuProcessHost : public content::BrowserChildProcessHostDelegate, void OnCommandBufferCreated(const int32 route_id); void OnDestroyCommandBuffer(int32 surface_id); +#if defined(OS_MACOSX) + void OnAcceleratedSurfaceBuffersSwapped( + const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params); +#endif #if defined(OS_WIN) && !defined(USE_AURA) void OnAcceleratedSurfaceBuffersSwapped( const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params); diff --git a/content/browser/renderer_host/compositing_iosurface_mac.mm b/content/browser/renderer_host/compositing_iosurface_mac.mm index f1754cf..1786fb6 100644 --- a/content/browser/renderer_host/compositing_iosurface_mac.mm +++ b/content/browser/renderer_host/compositing_iosurface_mac.mm @@ -100,11 +100,13 @@ void CompositingIOSurfaceMac::SetIOSurface(uint64 io_surface_handle) { } void CompositingIOSurfaceMac::DrawIOSurface(NSView* view) { - TRACE_EVENT0("browser", "CompositingIOSurfaceMac::DrawIOSurface"); CGLSetCurrentContext(cglContext_); bool has_io_surface = MapIOSurfaceToTexture(io_surface_handle_); + TRACE_EVENT1("browser", "CompositingIOSurfaceMac::DrawIOSurface", + "has_io_surface", has_io_surface); + [glContext_ setView:view]; NSSize window_size = [view frame].size; glViewport(0, 0, window_size.width, window_size.height); @@ -291,6 +293,11 @@ void CompositingIOSurfaceMac::UnrefIOSurfaceWithContextCurrent() { } io_surface_.reset(); + + // Forget the ID, because even if it is still around when we want to use it + // again, OSX may have reused the same ID for a new tab and we don't want to + // blit random tab contents. + io_surface_handle_ = 0; } void CompositingIOSurfaceMac::GlobalFrameDidChange() { diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index 0976394..879c2e6 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -361,7 +361,7 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message, IPC_MESSAGE_HANDLER(ViewHostMsg_OpenChannelToPpapiBroker, OnOpenChannelToPpapiBroker) IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_UpdateRect, - render_widget_helper_->DidReceiveUpdateMsg(message)) + render_widget_helper_->DidReceiveBackingStoreMsg(message)) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateIsDelayed, OnUpdateIsDelayed) IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_CheckPermission, OnCheckNotificationPermission) @@ -948,5 +948,5 @@ void RenderMessageFilter::OnUpdateIsDelayed(const IPC::Message& msg) { // 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); + render_widget_helper_->DidReceiveBackingStoreMsg(msg); } diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 2f26a73..b1f75dd 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -24,6 +24,7 @@ #include "base/bind_helpers.h" #include "base/callback.h" #include "base/command_line.h" +#include "base/debug/trace_event.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/metrics/field_trial.h" @@ -557,7 +558,7 @@ void RenderProcessHostImpl::SimulateSwapOutACK( widget_helper_->SimulateSwapOutACK(params); } -bool RenderProcessHostImpl::WaitForUpdateMsg( +bool RenderProcessHostImpl::WaitForBackingStoreMsg( int render_widget_id, const base::TimeDelta& max_delay, IPC::Message* msg) { @@ -566,7 +567,8 @@ bool RenderProcessHostImpl::WaitForUpdateMsg( if (child_process_launcher_.get() && child_process_launcher_->IsStarting()) return false; - return widget_helper_->WaitForUpdateMsg(render_widget_id, max_delay, msg); + return widget_helper_->WaitForBackingStoreMsg(render_widget_id, + max_delay, msg); } void RenderProcessHostImpl::ReceivedBadMessage() { @@ -923,6 +925,14 @@ bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) { reply->set_reply_error(); Send(reply); } + + // If this is a SwapBuffers, we need to ack it if we're not going to handle + // it so that the GPU process doesn't get stuck in unscheduled state. + bool msg_is_ok = true; + IPC_BEGIN_MESSAGE_MAP_EX(RenderProcessHostImpl, msg, msg_is_ok) + IPC_MESSAGE_HANDLER(ViewHostMsg_CompositorSurfaceBuffersSwapped, + OnCompositorSurfaceBuffersSwappedNoHost) + IPC_END_MESSAGE_MAP_EX() return true; } return RenderWidgetHostImpl::From(rwh)->OnMessageReceived(msg); @@ -1341,3 +1351,14 @@ void RenderProcessHostImpl::OnRevealFolderInOS(const FilePath& path) { void RenderProcessHostImpl::OnSavedPageAsMHTML(int job_id, int64 data_size) { MHTMLGenerationManager::GetInstance()->MHTMLGenerated(job_id, data_size); } + +void RenderProcessHostImpl::OnCompositorSurfaceBuffersSwappedNoHost( + int32 surface_id, + uint64 surface_handle, + int32 route_id, + int32 gpu_process_host_id) { + TRACE_EVENT0("renderer_host", + "RenderWidgetHostImpl::OnCompositorSurfaceBuffersSwappedNoHost"); + RenderWidgetHostImpl::AcknowledgeSwapBuffers(route_id, + gpu_process_host_id); +} diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index 09e861d..ef7e433 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h @@ -63,9 +63,9 @@ class CONTENT_EXPORT RenderProcessHostImpl virtual void CancelResourceRequests(int render_widget_id) OVERRIDE; virtual void SimulateSwapOutACK( const ViewMsg_SwapOut_Params& params) OVERRIDE; - virtual bool WaitForUpdateMsg(int render_widget_id, - const base::TimeDelta& max_delay, - IPC::Message* msg) OVERRIDE; + virtual bool WaitForBackingStoreMsg(int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg) OVERRIDE; virtual void ReceivedBadMessage() OVERRIDE; virtual void WidgetRestored() OVERRIDE; virtual void WidgetHidden() OVERRIDE; @@ -162,6 +162,12 @@ class CONTENT_EXPORT RenderProcessHostImpl void OnRevealFolderInOS(const FilePath& path); void OnSavedPageAsMHTML(int job_id, int64 mhtml_file_size); + // CompositorSurfaceBuffersSwapped handler when there's no RWH. + void OnCompositorSurfaceBuffersSwappedNoHost(int32 surface_id, + uint64 surface_handle, + int32 route_id, + int32 gpu_process_host_id); + // Generates a command line to be used to spawn a renderer and appends the // results to |*command_line|. void AppendRendererCommandLine(CommandLine* command_line) const; diff --git a/content/browser/renderer_host/render_widget_helper.cc b/content/browser/renderer_host/render_widget_helper.cc index 8639565..2290b4e 100644 --- a/content/browser/renderer_host/render_widget_helper.cc +++ b/content/browser/renderer_host/render_widget_helper.cc @@ -7,24 +7,40 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/eintr_wrapper.h" +#include "base/lazy_instance.h" #include "base/threading/thread.h" #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" #include "content/common/view_messages.h" -#include "content/public/browser/browser_thread.h" using content::BrowserThread; using content::RenderViewHostImpl; using content::ResourceDispatcherHostImpl; -// A helper used with DidReceiveUpdateMsg that we hold a pointer to in +namespace { + +typedef std::map<int, RenderWidgetHelper*> WidgetHelperMap; +base::LazyInstance<WidgetHelperMap> g_widget_helpers = + LAZY_INSTANCE_INITIALIZER; + +void AddWidgetHelper(int render_process_id, + const scoped_refptr<RenderWidgetHelper>& widget_helper) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // We don't care if RenderWidgetHelpers overwrite an existing process_id. Just + // want this to be up to date. + g_widget_helpers.Get()[render_process_id] = widget_helper.get(); +} + +} // namespace + +// A helper used with DidReceiveBackingStoreMsg that we hold a pointer to in // pending_paints_. -class RenderWidgetHelper::UpdateMsgProxy { +class RenderWidgetHelper::BackingStoreMsgProxy { public: - UpdateMsgProxy(RenderWidgetHelper* h, const IPC::Message& m); - ~UpdateMsgProxy(); + BackingStoreMsgProxy(RenderWidgetHelper* h, const IPC::Message& m); + ~BackingStoreMsgProxy(); void Run(); void Cancel() { cancelled_ = true; } @@ -35,26 +51,26 @@ class RenderWidgetHelper::UpdateMsgProxy { IPC::Message message_; bool cancelled_; // If true, then the message will not be dispatched. - DISALLOW_COPY_AND_ASSIGN(UpdateMsgProxy); + DISALLOW_COPY_AND_ASSIGN(BackingStoreMsgProxy); }; -RenderWidgetHelper::UpdateMsgProxy::UpdateMsgProxy( +RenderWidgetHelper::BackingStoreMsgProxy::BackingStoreMsgProxy( RenderWidgetHelper* h, const IPC::Message& m) : helper_(h), message_(m), cancelled_(false) { } -RenderWidgetHelper::UpdateMsgProxy::~UpdateMsgProxy() { +RenderWidgetHelper::BackingStoreMsgProxy::~BackingStoreMsgProxy() { // If the paint message was never dispatched, then we need to let the // helper know that we are going away. if (!cancelled_ && helper_) - helper_->OnDiscardUpdateMsg(this); + helper_->OnDiscardBackingStoreMsg(this); } -void RenderWidgetHelper::UpdateMsgProxy::Run() { +void RenderWidgetHelper::BackingStoreMsgProxy::Run() { if (!cancelled_) { - helper_->OnDispatchUpdateMsg(this); + helper_->OnDispatchBackingStoreMsg(this); helper_ = NULL; } } @@ -70,6 +86,9 @@ RenderWidgetHelper::RenderWidgetHelper() } RenderWidgetHelper::~RenderWidgetHelper() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + g_widget_helpers.Get().erase(render_process_id_); + // The elements of pending_paints_ each hold an owning reference back to this // object, so we should not be destroyed unless pending_paints_ is empty! DCHECK(pending_paints_.empty()); @@ -84,12 +103,26 @@ void RenderWidgetHelper::Init( ResourceDispatcherHostImpl* resource_dispatcher_host) { render_process_id_ = render_process_id; resource_dispatcher_host_ = resource_dispatcher_host; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&AddWidgetHelper, + render_process_id_, make_scoped_refptr(this))); } int RenderWidgetHelper::GetNextRoutingID() { return next_routing_id_.GetNext() + 1; } +// static +RenderWidgetHelper* RenderWidgetHelper::FromProcessHostID( + int render_process_host_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + WidgetHelperMap::const_iterator ci = g_widget_helpers.Get().find( + render_process_host_id); + return (ci == g_widget_helpers.Get().end())? NULL : ci->second; +} + void RenderWidgetHelper::CancelResourceRequests(int render_widget_id) { if (render_process_id_ == -1) return; @@ -110,19 +143,21 @@ void RenderWidgetHelper::SimulateSwapOutACK( params)); } -bool RenderWidgetHelper::WaitForUpdateMsg(int render_widget_id, - const base::TimeDelta& max_delay, - IPC::Message* msg) { +bool RenderWidgetHelper::WaitForBackingStoreMsg( + int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg) { base::TimeTicks time_start = base::TimeTicks::Now(); for (;;) { - UpdateMsgProxy* proxy = NULL; + BackingStoreMsgProxy* proxy = NULL; { base::AutoLock lock(pending_paints_lock_); - UpdateMsgProxyMap::iterator it = pending_paints_.find(render_widget_id); + BackingStoreMsgProxyMap::iterator it = + pending_paints_.find(render_widget_id); if (it != pending_paints_.end()) { - UpdateMsgProxyQueue &queue = it->second; + BackingStoreMsgProxyQueue &queue = it->second; DCHECK(!queue.empty()); proxy = queue.front(); @@ -154,10 +189,10 @@ bool RenderWidgetHelper::WaitForUpdateMsg(int render_widget_id, return false; } -void RenderWidgetHelper::DidReceiveUpdateMsg(const IPC::Message& msg) { +void RenderWidgetHelper::DidReceiveBackingStoreMsg(const IPC::Message& msg) { int render_widget_id = msg.routing_id(); - UpdateMsgProxy* proxy = new UpdateMsgProxy(this, msg); + BackingStoreMsgProxy* proxy = new BackingStoreMsgProxy(this, msg); { base::AutoLock lock(pending_paints_lock_); @@ -170,19 +205,20 @@ void RenderWidgetHelper::DidReceiveUpdateMsg(const IPC::Message& msg) { event_.Signal(); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&UpdateMsgProxy::Run, base::Owned(proxy))); + base::Bind(&BackingStoreMsgProxy::Run, base::Owned(proxy))); } -void RenderWidgetHelper::OnDiscardUpdateMsg(UpdateMsgProxy* proxy) { +void RenderWidgetHelper::OnDiscardBackingStoreMsg(BackingStoreMsgProxy* proxy) { const IPC::Message& msg = proxy->message(); // Remove the proxy from the map now that we are going to handle it normally. { base::AutoLock lock(pending_paints_lock_); - UpdateMsgProxyMap::iterator it = pending_paints_.find(msg.routing_id()); + BackingStoreMsgProxyMap::iterator it = + pending_paints_.find(msg.routing_id()); DCHECK(it != pending_paints_.end()); - UpdateMsgProxyQueue &queue = it->second; + BackingStoreMsgProxyQueue &queue = it->second; DCHECK(queue.front() == proxy); queue.pop_front(); @@ -191,8 +227,9 @@ void RenderWidgetHelper::OnDiscardUpdateMsg(UpdateMsgProxy* proxy) { } } -void RenderWidgetHelper::OnDispatchUpdateMsg(UpdateMsgProxy* proxy) { - OnDiscardUpdateMsg(proxy); +void RenderWidgetHelper::OnDispatchBackingStoreMsg( + BackingStoreMsgProxy* proxy) { + OnDiscardBackingStoreMsg(proxy); // It is reasonable for the host to no longer exist. content::RenderProcessHost* host = diff --git a/content/browser/renderer_host/render_widget_helper.h b/content/browser/renderer_host/render_widget_helper.h index a57f0e6..b2c4de5 100644 --- a/content/browser/renderer_host/render_widget_helper.h +++ b/content/browser/renderer_host/render_widget_helper.h @@ -15,6 +15,7 @@ #include "base/process.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/window_container_type.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h" @@ -47,44 +48,51 @@ struct ViewMsg_SwapOut_Params; // // RenderWidgetHelper is used to implement optimized resize. When the // RenderWidgetHost is resized, it sends a Resize message to its RenderWidget -// counterpart in the renderer process. The RenderWidget generates a -// UpdateRect message in response to the Resize message, and it sets the -// IS_RESIZE_ACK flag in the UpdateRect message to true. +// counterpart in the renderer process. In response to the Resize message, +// the RenderWidget generates a new BackingStore and sends an UpdateRect +// message (or BuffersSwapped via the GPU process in the case of accelerated +// compositing), and it sets the IS_RESIZE_ACK flag in the UpdateRect message +// to true. In the accelerated case, an UpdateRect is still sent from the +// renderer to the browser with acks and plugin moves even though the GPU +// BackingStore was sent earlier in the BuffersSwapped message. "BackingStore +// message" is used throughout this code and documentation to mean either a +// software UpdateRect or GPU BuffersSwapped message. // // Back in the browser process, when the RenderProcessHost's MessageFilter -// sees a UpdateRect message, it directs it to the RenderWidgetHelper by -// calling the DidReceiveUpdateMsg method. That method stores the data for -// the UpdateRect message in a map, where it can be directly accessed by the -// RenderWidgetHost on the UI thread during a call to RenderWidgetHost's -// GetBackingStore method. +// sees an UpdateRect message (or when the GpuProcessHost sees a +// BuffersSwapped message), it directs it to the RenderWidgetHelper by calling +// the DidReceiveBackingStoreMsg method. That method stores the data for the +// message in a map, where it can be directly accessed by the RenderWidgetHost +// on the UI thread during a call to RenderWidgetHost's GetBackingStore +// method. // // When the RenderWidgetHost's GetBackingStore method is called, it first // checks to see if it is waiting for a resize ack. If it is, then it calls -// the RenderWidgetHelper's WaitForUpdateMsg to check if there is already a -// resulting UpdateRect message (or to wait a short amount of time for one to -// arrive). The main goal of this mechanism is to short-cut the usual way in -// which IPC messages are proxied over to the UI thread via InvokeLater. -// This approach is necessary since window resize is followed up immediately -// by a request to repaint the window. +// the RenderWidgetHelper's WaitForBackingStoreMsg to check if there is +// already a resulting BackingStore message (or to wait a short amount of time +// for one to arrive). The main goal of this mechanism is to short-cut the +// usual way in which IPC messages are proxied over to the UI thread via +// InvokeLater. This approach is necessary since window resize is followed up +// immediately by a request to repaint the window. // // // OPTIMIZED TAB SWITCHING // // When a RenderWidgetHost is in a background tab, it is flagged as hidden. -// This causes the corresponding RenderWidget to stop sending UpdateRect +// This causes the corresponding RenderWidget to stop sending BackingStore // messages. The RenderWidgetHost also discards its backingstore when it is // hidden, which helps free up memory. As a result, when a RenderWidgetHost -// is restored, it can be momentarily without a backingstore. (Restoring a +// is restored, it can be momentarily be without a backingstore. (Restoring a // RenderWidgetHost results in a WasRestored message being sent to the -// RenderWidget, which triggers a full UpdateRect message.) This can lead to -// an observed rendering glitch as the WebContentsImpl will just have to fill -// white overtop the RenderWidgetHost until the RenderWidgetHost receives a -// UpdateRect message to refresh its backingstore. +// RenderWidget, which triggers a full BackingStore message.) This can lead +// to an observed rendering glitch as the WebContentsImpl will just have to +// fill white overtop the RenderWidgetHost until the RenderWidgetHost receives +// a BackingStore message to refresh its backingstore. // // To avoid this 'white flash', the RenderWidgetHost again makes use of the -// RenderWidgetHelper's WaitForUpdateMsg method. When the RenderWidgetHost's -// GetBackingStore method is called, it will call WaitForUpdateMsg if it has -// no backingstore. +// RenderWidgetHelper's WaitForBackingStoreMsg method. When the +// RenderWidgetHost's GetBackingStore method is called, it will call +// WaitForBackingStoreMsg if it has no backingstore. // // TRANSPORT DIB CREATION // @@ -96,7 +104,8 @@ struct ViewMsg_SwapOut_Params; // renderers can refer to. // class RenderWidgetHelper - : public base::RefCountedThreadSafe<RenderWidgetHelper> { + : public base::RefCountedThreadSafe< + RenderWidgetHelper, content::BrowserThread::DeleteOnIOThread> { public: RenderWidgetHelper(); @@ -106,6 +115,12 @@ class RenderWidgetHelper // Gets the next available routing id. This is thread safe. int GetNextRoutingID(); + // IO THREAD ONLY ----------------------------------------------------------- + + // Lookup the RenderWidgetHelper from the render_process_host_id. Returns NULL + // if not found. NOTE: The raw pointer is for temporary use only. To retain, + // store in a scoped_refptr. + static RenderWidgetHelper* FromProcessHostID(int render_process_host_id); // UI THREAD ONLY ----------------------------------------------------------- @@ -114,9 +129,9 @@ class RenderWidgetHelper // for documentation. void CancelResourceRequests(int render_widget_id); void SimulateSwapOutACK(const ViewMsg_SwapOut_Params& params); - bool WaitForUpdateMsg(int render_widget_id, - const base::TimeDelta& max_delay, - IPC::Message* msg); + bool WaitForBackingStoreMsg(int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg); #if defined(OS_MACOSX) // Given the id of a transport DIB, return a mapping to it or NULL on error. @@ -125,8 +140,8 @@ class RenderWidgetHelper // IO THREAD ONLY ----------------------------------------------------------- - // Called on the IO thread when a UpdateRect message is received. - void DidReceiveUpdateMsg(const IPC::Message& msg); + // Called on the IO thread when a BackingStore message is received. + void DidReceiveBackingStoreMsg(const IPC::Message& msg); void CreateNewWindow(const ViewHostMsg_CreateWindow_Params& params, bool no_javascript_access, @@ -156,21 +171,25 @@ class RenderWidgetHelper private: // A class used to proxy a paint message. PaintMsgProxy objects are created // on the IO thread and destroyed on the UI thread. - class UpdateMsgProxy; - friend class UpdateMsgProxy; + class BackingStoreMsgProxy; + friend class BackingStoreMsgProxy; friend class base::RefCountedThreadSafe<RenderWidgetHelper>; + friend struct content::BrowserThread::DeleteOnThread< + content::BrowserThread::IO>; + friend class base::DeleteHelper<RenderWidgetHelper>; - typedef std::deque<UpdateMsgProxy*> UpdateMsgProxyQueue; + typedef std::deque<BackingStoreMsgProxy*> BackingStoreMsgProxyQueue; // Map from render_widget_id to a queue of live PaintMsgProxy instances. - typedef base::hash_map<int, UpdateMsgProxyQueue > UpdateMsgProxyMap; + typedef base::hash_map<int, BackingStoreMsgProxyQueue > + BackingStoreMsgProxyMap; ~RenderWidgetHelper(); // Called on the UI thread to discard a paint message. - void OnDiscardUpdateMsg(UpdateMsgProxy* proxy); + void OnDiscardBackingStoreMsg(BackingStoreMsgProxy* proxy); // Called on the UI thread to dispatch a paint message if necessary. - void OnDispatchUpdateMsg(UpdateMsgProxy* proxy); + void OnDispatchBackingStoreMsg(BackingStoreMsgProxy* proxy); // Called on the UI thread to finish creating a window. void OnCreateWindowOnUI(const ViewHostMsg_CreateWindow_Params& params, @@ -205,14 +224,15 @@ class RenderWidgetHelper #endif // A map of live paint messages. Must hold pending_paints_lock_ to access. - // The UpdateMsgProxy objects are not owned by this map. (See UpdateMsgProxy - // for details about how the lifetime of instances are managed.) - UpdateMsgProxyMap pending_paints_; + // The BackingStoreMsgProxy objects are not owned by this map. (See + // BackingStoreMsgProxy for details about how the lifetime of instances are + // managed.) + BackingStoreMsgProxyMap pending_paints_; base::Lock pending_paints_lock_; int render_process_id_; - // Event used to implement WaitForUpdateMsg. + // Event used to implement WaitForBackingStoreMsg. base::WaitableEvent event_; // The next routing id to use. diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index c0c5293..85c53c5 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -65,7 +65,7 @@ namespace { // PaintRect message, when our backing-store is invalid, before giving up and // returning a null or incorrectly sized backing-store from GetBackingStore. // This timeout impacts the "choppiness" of our window resize perf. -static const int kPaintMsgTimeoutMS = 40; +static const int kPaintMsgTimeoutMS = 50; // How long to wait before we consider a renderer hung. static const int kHungRendererDelayMs = 20000; @@ -119,6 +119,7 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderProcessHost* process, is_unresponsive_(false), in_flight_event_count_(0), in_get_backing_store_(false), + abort_get_backing_store_(false), view_being_painted_(false), ignore_input_events_(false), text_direction_updated_(false), @@ -261,6 +262,8 @@ bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove) IPC_MESSAGE_HANDLER(ViewHostMsg_SetTooltipText, OnMsgSetTooltipText) IPC_MESSAGE_HANDLER(ViewHostMsg_PaintAtSize_ACK, OnMsgPaintAtSizeAck) + IPC_MESSAGE_HANDLER(ViewHostMsg_CompositorSurfaceBuffersSwapped, + OnCompositorSurfaceBuffersSwapped) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnMsgUpdateRect) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateIsDelayed, OnMsgUpdateIsDelayed) IPC_MESSAGE_HANDLER(ViewHostMsg_HandleInputEvent_ACK, OnMsgInputEventAck) @@ -578,10 +581,38 @@ void RenderWidgetHostImpl::PaintAtSize(TransportDIB::Handle dib_handle, page_size, desired_size)); } +bool RenderWidgetHostImpl::TryGetBackingStore(const gfx::Size& desired_size, + BackingStore** backing_store) { + // Check if the view has an accelerated surface of the desired size. + if (view_->HasAcceleratedSurface(desired_size)) { + *backing_store = NULL; + return true; + } + + // Check for a software backing store of the desired size. + *backing_store = BackingStoreManager::GetBackingStore(this, desired_size); + return !!*backing_store; +} + BackingStore* RenderWidgetHostImpl::GetBackingStore(bool force_create) { + if (!view_) + return NULL; + + // The view_size will be current_size_ for auto-sized views and otherwise the + // size of the view_. (For auto-sized views, current_size_ is updated during + // UpdateRect messages.) + gfx::Size view_size = current_size_; + if (!should_auto_resize_) { + // Get the desired size from the current view bounds. + gfx::Rect view_rect = view_->GetViewBounds(); + if (view_rect.IsEmpty()) + return NULL; + view_size = view_rect.size(); + } + TRACE_EVENT2("renderer_host", "RenderWidgetHostImpl::GetBackingStore", - "width", base::IntToString(current_size_.width()), - "height", base::IntToString(current_size_.height())); + "width", base::IntToString(view_size.width()), + "height", base::IntToString(view_size.height())); // We should not be asked to paint while we are hidden. If we are hidden, // then it means that our consumer failed to call WasRestored. If we're not @@ -596,32 +627,58 @@ BackingStore* RenderWidgetHostImpl::GetBackingStore(bool force_create) { AutoReset<bool> auto_reset_in_get_backing_store(&in_get_backing_store_, true); // We might have a cached backing store that we can reuse! - BackingStore* backing_store = - BackingStoreManager::GetBackingStore(this, current_size_); - if (!force_create) + BackingStore* backing_store = NULL; + if (TryGetBackingStore(view_size, &backing_store) || !force_create) return backing_store; - // If we fail to find a backing store in the cache, send out a request - // to the renderer to paint the view if required. - if (!backing_store && !repaint_ack_pending_ && !resize_ack_pending_ && - !view_being_painted_) { + // We do not have a suitable backing store in the cache, so send out a + // request to the renderer to paint the view if required. + if (!repaint_ack_pending_ && !resize_ack_pending_ && !view_being_painted_) { repaint_start_time_ = TimeTicks::Now(); repaint_ack_pending_ = true; - Send(new ViewMsg_Repaint(routing_id_, current_size_)); + Send(new ViewMsg_Repaint(routing_id_, view_size)); } - // When we have asked the RenderWidget to resize, and we are still waiting on - // a response, block for a little while to see if we can't get a response - // before returning the old (incorrectly sized) backing store. - if (resize_ack_pending_ || !backing_store) { + TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS); + TimeTicks end_time = TimeTicks::Now() + max_delay; + do { + TRACE_EVENT0("renderer_host", "GetBackingStore::WaitForUpdate"); + + // When we have asked the RenderWidget to resize, and we are still waiting + // on a response, block for a little while to see if we can't get a response + // before returning the old (incorrectly sized) backing store. IPC::Message msg; - TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS); - if (process_->WaitForUpdateMsg(routing_id_, max_delay, &msg)) { + if (process_->WaitForBackingStoreMsg(routing_id_, max_delay, &msg)) { OnMessageReceived(msg); - backing_store = BackingStoreManager::GetBackingStore(this, current_size_); + + // For auto-resized views, current_size_ determines the view_size and it + // may have changed during the handling of an UpdateRect message. + if (should_auto_resize_) + view_size = current_size_; + + // Break now if we got a backing store or accelerated surface of the + // correct size. + if (TryGetBackingStore(view_size, &backing_store) || + abort_get_backing_store_) { + abort_get_backing_store_ = false; + return backing_store; + } + } else { + TRACE_EVENT0("renderer_host", "GetBackingStore::Timeout"); + break; } - } + // Loop if we still have time left and haven't gotten a properly sized + // BackingStore yet. This is necessary to support the GPU path which + // typically has multiple frames pipelined -- we may need to skip one or two + // BackingStore messages to get to the latest. + max_delay = end_time - TimeTicks::Now(); + } while (max_delay > TimeDelta::FromSeconds(0)); + + // We have failed to get a backing store of view_size. Fall back on + // current_size_ to avoid a white flash while resizing slow pages. + if (view_size != current_size_) + TryGetBackingStore(current_size_, &backing_store); return backing_store; } @@ -1147,6 +1204,32 @@ void RenderWidgetHostImpl::OnMsgPaintAtSizeAck(int tag, const gfx::Size& size) { Details<std::pair<int, gfx::Size> >(&details)); } +void RenderWidgetHostImpl::OnCompositorSurfaceBuffersSwapped( + int32 surface_id, + uint64 surface_handle, + int32 route_id, + int32 gpu_process_host_id) { + TRACE_EVENT0("renderer_host", + "RenderWidgetHostImpl::OnCompositorSurfaceBuffersSwapped"); + if (!view_) { + RenderWidgetHostImpl::AcknowledgeSwapBuffers(route_id, + gpu_process_host_id); + return; + } + GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params gpu_params; + gpu_params.surface_id = surface_id; + gpu_params.surface_handle = surface_handle; + gpu_params.route_id = route_id; +#if defined(OS_MACOSX) + // Compositor window is always gfx::kNullPluginWindow. + // TODO(jbates) http://crbug.com/105344 This will be removed when there are no + // plugin windows. + gpu_params.window = gfx::kNullPluginWindow; +#endif + view_->AcceleratedSurfaceBuffersSwapped(gpu_params, + gpu_process_host_id); +} + void RenderWidgetHostImpl::OnMsgUpdateRect( const ViewHostMsg_UpdateRect_Params& params) { TRACE_EVENT0("renderer_host", "RenderWidgetHostImpl::OnMsgUpdateRect"); @@ -1179,44 +1262,44 @@ void RenderWidgetHostImpl::OnMsgUpdateRect( DCHECK(!params.view_size.IsEmpty()); bool was_async = false; - if (!is_accelerated_compositing_active_) { + + // If this is a GPU UpdateRect, params.bitmap is invalid and dib will be NULL. + TransportDIB* dib = process_->GetTransportDIB(params.bitmap); + + // If gpu process does painting, scroll_rect and copy_rects are always empty + // and backing store is never used. + if (dib) { DCHECK(!params.bitmap_rect.IsEmpty()); const size_t size = params.bitmap_rect.height() * params.bitmap_rect.width() * 4; - TransportDIB* dib = process_->GetTransportDIB(params.bitmap); - - // If gpu process does painting, scroll_rect and copy_rects are always empty - // and backing store is never used. - if (dib) { - if (dib->size() < size) { - DLOG(WARNING) << "Transport DIB too small for given rectangle"; - RecordAction(UserMetricsAction("BadMessageTerminate_RWH1")); - GetProcess()->ReceivedBadMessage(); - } else { - UNSHIPPED_TRACE_EVENT_INSTANT2("test_latency", "UpdateRect", - "x+y", params.bitmap_rect.x() + params.bitmap_rect.y(), - "color", 0xffffff & *static_cast<uint32*>(dib->memory())); - UNSHIPPED_TRACE_EVENT_INSTANT1("test_latency", "UpdateRectWidth", - "width", params.bitmap_rect.width()); - - // Scroll the backing store. - if (!params.scroll_rect.IsEmpty()) { - ScrollBackingStoreRect(params.dx, params.dy, - params.scroll_rect, - params.view_size); - } - - // Paint the backing store. This will update it with the - // renderer-supplied bits. The view will read out of the backing store - // later to actually draw to the screen. - was_async = PaintBackingStoreRect( - params.bitmap, - params.bitmap_rect, - params.copy_rects, - params.view_size, - base::Bind(&RenderWidgetHostImpl::DidUpdateBackingStore, - weak_factory_.GetWeakPtr(), params, paint_start)); + if (dib->size() < size) { + DLOG(WARNING) << "Transport DIB too small for given rectangle"; + RecordAction(UserMetricsAction("BadMessageTerminate_RWH1")); + GetProcess()->ReceivedBadMessage(); + } else { + UNSHIPPED_TRACE_EVENT_INSTANT2("test_latency", "UpdateRect", + "x+y", params.bitmap_rect.x() + params.bitmap_rect.y(), + "color", 0xffffff & *static_cast<uint32*>(dib->memory())); + UNSHIPPED_TRACE_EVENT_INSTANT1("test_latency", "UpdateRectWidth", + "width", params.bitmap_rect.width()); + + // Scroll the backing store. + if (!params.scroll_rect.IsEmpty()) { + ScrollBackingStoreRect(params.dx, params.dy, + params.scroll_rect, + params.view_size); } + + // Paint the backing store. This will update it with the + // renderer-supplied bits. The view will read out of the backing store + // later to actually draw to the screen. + was_async = PaintBackingStoreRect( + params.bitmap, + params.bitmap_rect, + params.copy_rects, + params.view_size, + base::Bind(&RenderWidgetHostImpl::DidUpdateBackingStore, + weak_factory_.GetWeakPtr(), params, paint_start)); } } @@ -1243,7 +1326,8 @@ void RenderWidgetHostImpl::OnMsgUpdateRect( } void RenderWidgetHostImpl::OnMsgUpdateIsDelayed() { - // Nothing to do, this message was just to unblock the UI thread. + if (in_get_backing_store_) + abort_get_backing_store_ = true; } void RenderWidgetHostImpl::DidUpdateBackingStore( @@ -1290,11 +1374,8 @@ void RenderWidgetHostImpl::DidUpdateBackingStore( // If we got a resize ack, then perhaps we have another resize to send? bool is_resize_ack = ViewHostMsg_UpdateRect_Flags::is_resize_ack(params.flags); - if (is_resize_ack && view_) { - gfx::Rect view_bounds = view_->GetViewBounds(); - if (current_size_ != view_bounds.size()) - WasResized(); - } + if (is_resize_ack) + WasResized(); // Log the time delta for processing a paint message. TimeTicks now = TimeTicks::Now(); diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index e3f95ca..9a0e175 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h @@ -181,12 +181,21 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // Indicates if the page has finished loading. void SetIsLoading(bool is_loading); - // Get access to the widget's backing store. If a resize is in progress, - // then the current size of the backing store may be less than the size of - // the widget's view. If you pass |force_create| as true, then the backing - // store will be created if it doesn't exist. Otherwise, NULL will be returned - // if the backing store doesn't already exist. It will also return NULL if the + // Check for the existance of a BackingStore of the given |desired_size| and + // return it if it exists. If the BackingStore is GPU, true is returned and + // |*backing_store| is set to NULL. + bool TryGetBackingStore(const gfx::Size& desired_size, + BackingStore** backing_store); + + // Get access to the widget's backing store matching the size of the widget's + // view. If you pass |force_create| as true, then GetBackingStore may block + // for the renderer to send a new frame. Otherwise, NULL will be returned if + // the backing store doesn't already exist. It will also return NULL if the // backing store could not be created. + // + // Mac only: NULL may also be returned if the last frame was GPU accelerated. + // Call GetView()->HasAcceleratedSurface to determine if the last frame was + // accelerated. BackingStore* GetBackingStore(bool force_create); // Allocate a new backing store of the given size. Returns NULL on failure @@ -466,6 +475,10 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, void OnMsgSetTooltipText(const string16& tooltip_text, WebKit::WebTextDirection text_direction_hint); void OnMsgPaintAtSizeAck(int tag, const gfx::Size& size); + void OnCompositorSurfaceBuffersSwapped(int32 surface_id, + uint64 surface_handle, + int32 route_id, + int32 gpu_process_host_id); void OnMsgUpdateRect(const ViewHostMsg_UpdateRect_Params& params); void OnMsgUpdateIsDelayed(); void OnMsgInputEventAck(WebKit::WebInputEvent::Type event_type, @@ -653,6 +666,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // Flag to detect recursive calls to GetBackingStore(). bool in_get_backing_store_; + // Flag to trigger the GetBackingStore method to abort early. + bool abort_get_backing_store_; + // Set when we call DidPaintRect/DidScrollRect on the view. bool view_being_painted_; diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index 8f5c465..eb48d85 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc @@ -66,14 +66,14 @@ class RenderWidgetHostProcess : public MockRenderProcessHost { virtual bool HasConnection() const { return true; } protected: - virtual bool WaitForUpdateMsg(int render_widget_id, - const base::TimeDelta& max_delay, - IPC::Message* msg); + virtual bool WaitForBackingStoreMsg(int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg); TransportDIB* current_update_buf_; - // Set to true when WaitForUpdateMsg should return a successful update message - // reply. False implies timeout. + // Set to true when WaitForBackingStoreMsg should return a successful update + // message reply. False implies timeout. bool update_msg_should_reply_; // Indicates the flags that should be sent with a the repaint request. This @@ -101,9 +101,10 @@ void RenderWidgetHostProcess::InitUpdateRectParams( params->needs_ack = true; } -bool RenderWidgetHostProcess::WaitForUpdateMsg(int render_widget_id, - const base::TimeDelta& max_delay, - IPC::Message* msg) { +bool RenderWidgetHostProcess::WaitForBackingStoreMsg( + int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg) { if (!update_msg_should_reply_) return false; @@ -508,6 +509,11 @@ TEST_F(RenderWidgetHostTest, Background) { // Tests getting the backing store with the renderer not setting repaint ack // flags. TEST_F(RenderWidgetHostTest, GetBackingStore_NoRepaintAck) { + // First set the view size to match what the renderer is rendering. + ViewHostMsg_UpdateRect_Params params; + process_->InitUpdateRectParams(¶ms); + view_->set_bounds(gfx::Rect(params.view_size)); + // We don't currently have a backing store, and if the renderer doesn't send // one in time, we should get nothing. process_->set_update_msg_should_reply(false); @@ -534,6 +540,11 @@ TEST_F(RenderWidgetHostTest, GetBackingStore_NoRepaintAck) { // Tests getting the backing store with the renderer sending a repaint ack. TEST_F(RenderWidgetHostTest, GetBackingStore_RepaintAck) { + // First set the view size to match what the renderer is rendering. + ViewHostMsg_UpdateRect_Params params; + process_->InitUpdateRectParams(¶ms); + view_->set_bounds(gfx::Rect(params.view_size)); + // Doing a request request with the update message allowed should work and // the repaint ack should work. process_->set_update_msg_should_reply(true); diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index f21e8f9..f094a62 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -637,6 +637,14 @@ void RenderWidgetHostViewAura::AcceleratedSurfacePostSubBuffer( void RenderWidgetHostViewAura::AcceleratedSurfaceSuspend() { } +bool RenderWidgetHostViewAura::HasAcceleratedSurface( + const gfx::Size& desired_size) { + // Aura doesn't use GetBackingStore for accelerated pages, so it doesn't + // matter what is returned here as GetBackingStore is the only caller of this + // method. TODO(jbates) implement this if other Aura code needs it. + return false; +} + void RenderWidgetHostViewAura::AcceleratedSurfaceNew( int32 width, int32 height, diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index f58aee9..3c4814e 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h @@ -109,6 +109,7 @@ class RenderWidgetHostViewAura const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, int gpu_host_id) OVERRIDE; virtual void AcceleratedSurfaceSuspend() OVERRIDE; + virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE; virtual void AcceleratedSurfaceNew( int32 width, int32 height, 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 466d1d1..15a0073 100644 --- a/content/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/content/browser/renderer_host/render_widget_host_view_gtk.cc @@ -1011,6 +1011,13 @@ void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer( void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() { } +bool RenderWidgetHostViewGtk::HasAcceleratedSurface( + const gfx::Size& desired_size) { + // TODO(jbates) Implement this so this view can use GetBackingStore for both + // software and GPU frames. Defaulting to false just makes GetBackingStore + // only useable for software frames. + return false; +} void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) { content::RenderWidgetHostViewBase::SetBackground(background); diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.h b/content/browser/renderer_host/render_widget_host_view_gtk.h index 1adaeee..0f7c00a 100644 --- a/content/browser/renderer_host/render_widget_host_view_gtk.h +++ b/content/browser/renderer_host/render_widget_host_view_gtk.h @@ -108,6 +108,7 @@ class RenderWidgetHostViewGtk : public content::RenderWidgetHostViewBase { const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, int gpu_host_id) OVERRIDE; virtual void AcceleratedSurfaceSuspend() OVERRIDE; + virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE; virtual void CreatePluginContainer(gfx::PluginWindowHandle id) OVERRIDE; virtual void DestroyPluginContainer(gfx::PluginWindowHandle id) OVERRIDE; virtual void ProcessTouchAck(WebKit::WebInputEvent::Type type, diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h index de4f6f4..7056b1c6 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h @@ -285,6 +285,7 @@ class RenderWidgetHostViewMac : public content::RenderWidgetHostViewBase { const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, int gpu_host_id) OVERRIDE; virtual void AcceleratedSurfaceSuspend() OVERRIDE; + virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE; virtual void GetScreenInfo(WebKit::WebScreenInfo* results) OVERRIDE; virtual gfx::Rect GetRootWindowBounds() OVERRIDE; virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE; @@ -321,15 +322,8 @@ class RenderWidgetHostViewMac : public content::RenderWidgetHostViewBase { const std::string& selected_text() const { return selected_text_; } // Call setNeedsDisplay on the cocoa_view_. The IOSurface will be drawn during - // the next drawRect. The route_id and gpu_host_id are added to a list to be - // acked when the SwapBuffers occurs in drawRect. - void CompositorSwapBuffers(uint64 surface_handle, - int32 route_id, - int32 gpu_host_id); - // Ack pending SwapBuffers requests (queued up by CompositorSwapBuffers), if - // any, to unblock the GPU process. Has no effect if there are no pending - // requests. - void AckPendingCompositorSwapBuffers(); + // the next drawRect. + void CompositorSwapBuffers(uint64 surface_handle); // These member variables should be private, but the associated ObjC class // needs access to them and can't be made a friend. @@ -398,6 +392,11 @@ class RenderWidgetHostViewMac : public content::RenderWidgetHostViewBase { // invoke it from the message loop. void ShutdownHost(); + // Called when a GPU SwapBuffers is received. + void GotAcceleratedFrame(); + // Called when a software DIB is received. + void GotSoftwareFrame(); + // The associated view. This is weak and is inserted into the view hierarchy // to own this RenderWidgetHostViewMac object. RenderWidgetHostViewCocoa* cocoa_view_; @@ -420,12 +419,6 @@ class RenderWidgetHostViewMac : public content::RenderWidgetHostViewBase { // selected text on the renderer. std::string selected_text_; - gfx::PluginWindowHandle compositing_surface_; - - // List of pending swaps for deferred acking: - // pairs of (route_id, gpu_host_id). - std::list<std::pair<int32, int32> > pending_swap_buffers_acks_; - // The fullscreen window used for pepper flash. scoped_nsobject<NSWindow> pepper_fullscreen_window_; scoped_nsobject<FullscreenWindowManager> fullscreen_window_manager_; diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index 4a09ade..155a2cfb 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -24,6 +24,7 @@ #include "content/browser/plugin_process_host.h" #import "content/browser/renderer_host/accelerated_plugin_view_mac.h" #include "content/browser/renderer_host/backing_store_mac.h" +#include "content/browser/renderer_host/backing_store_manager.h" #include "content/browser/renderer_host/compositing_iosurface_mac.h" #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -263,8 +264,7 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) can_compose_inline_(true), is_loading_(false), is_hidden_(false), - weak_factory_(this), - compositing_surface_(gfx::kNullPluginWindow) { + weak_factory_(this) { // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| // goes away. Since we autorelease it, our caller must put // |GetNativeView()| into the view hierarchy right after calling us. @@ -372,10 +372,6 @@ void RenderWidgetHostViewMac::WasHidden() { // everything again when we become selected again. is_hidden_ = true; - // Send ACKs for any pending SwapBuffers (if any) since we won't be displaying - // them and the GPU process is waiting. - AckPendingCompositorSwapBuffers(); - // If we have a renderer, then inform it that we are being hidden so it can // reduce its resource utilization. render_widget_host_->WasHidden(); @@ -613,7 +609,7 @@ void RenderWidgetHostViewMac::ImeCompositionRangeChanged( void RenderWidgetHostViewMac::DidUpdateBackingStore( const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, const std::vector<gfx::Rect>& copy_rects) { - last_frame_was_accelerated_ = false; + GotSoftwareFrame(); if (!is_hidden_) { std::vector<gfx::Rect> rects(copy_rects); @@ -683,9 +679,6 @@ void RenderWidgetHostViewMac::Destroy() { } } - // Ack pending swaps (if any). - AckPendingCompositorSwapBuffers(); - // We've been told to destroy. [cocoa_view_ retain]; [cocoa_view_ removeFromSuperview]; @@ -953,34 +946,22 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB( transport_dib); } -void RenderWidgetHostViewMac::CompositorSwapBuffers(uint64 surface_handle, - int32 route_id, - int32 gpu_host_id) { - pending_swap_buffers_acks_.push_back(std::make_pair(route_id, gpu_host_id)); - if (!compositing_iosurface_.get() && !is_hidden_) { +void RenderWidgetHostViewMac::CompositorSwapBuffers(uint64 surface_handle) { + if (is_hidden_) + return; + + if (!compositing_iosurface_.get()) compositing_iosurface_.reset(CompositingIOSurfaceMac::Create()); - } - if (compositing_iosurface_.get() && !is_hidden_) { - last_frame_was_accelerated_ = true; - compositing_iosurface_->SetIOSurface(surface_handle); - [cocoa_view_ setNeedsDisplay:YES]; - } else { - AckPendingCompositorSwapBuffers(); - } -} + if (!compositing_iosurface_.get()) + return; -void RenderWidgetHostViewMac::AckPendingCompositorSwapBuffers() { - TRACE_EVENT0("browser", - "RenderWidgetHostViewMac::AckPendingCompositorSwapBuffers"); - while (!pending_swap_buffers_acks_.empty()) { - if (pending_swap_buffers_acks_.front().first != 0) { - RenderWidgetHostImpl::AcknowledgeSwapBuffers( - pending_swap_buffers_acks_.front().first, - pending_swap_buffers_acks_.front().second); - } - pending_swap_buffers_acks_.erase(pending_swap_buffers_acks_.begin()); - } + compositing_iosurface_->SetIOSurface(surface_handle); + // No need to draw the surface if we are inside a drawRect. It will be done + // later. + if (!about_to_validate_and_paint_) + compositing_iosurface_->DrawIOSurface(cocoa_view_); + GotAcceleratedFrame(); } void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( @@ -990,8 +971,11 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( "RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped"); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (params.window == compositing_surface_) { - CompositorSwapBuffers(params.surface_handle, params.route_id, gpu_host_id); + // Compositor window is always gfx::kNullPluginWindow. + // TODO(jbates) http://crbug.com/105344 This will be removed when there are no + // plugin windows. + if (params.window == gfx::kNullPluginWindow) { + CompositorSwapBuffers(params.surface_handle); } else { // Deprecated accelerated plugin code path. AcceleratedPluginView* view = ViewForPluginWindowHandle(params.window); @@ -1005,11 +989,11 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( [view setHidden:NO]; [view drawView]; } + } - if (params.route_id != 0) { - RenderWidgetHostImpl::AcknowledgeSwapBuffers(params.route_id, - gpu_host_id); - } + if (params.route_id != 0) { + RenderWidgetHostImpl::AcknowledgeSwapBuffers(params.route_id, + gpu_host_id); } } @@ -1020,8 +1004,11 @@ void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer( "RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer"); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (params.window == compositing_surface_) { - CompositorSwapBuffers(params.surface_handle, params.route_id, gpu_host_id); + // Compositor window is always gfx::kNullPluginWindow. + // TODO(jbates) http://crbug.com/105344 This will be removed when there are no + // plugin windows. + if (params.window == gfx::kNullPluginWindow) { + CompositorSwapBuffers(params.surface_handle); } else { // Deprecated accelerated plugin code path. AcceleratedPluginView* view = ViewForPluginWindowHandle(params.window); @@ -1037,15 +1024,26 @@ void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer( [view setHidden:NO]; [view drawView]; } + } - if (params.route_id != 0) { - RenderWidgetHostImpl::AcknowledgePostSubBuffer( - params.route_id, gpu_host_id); - } + if (params.route_id != 0) { + RenderWidgetHostImpl::AcknowledgePostSubBuffer( + params.route_id, gpu_host_id); } } void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() { + if (compositing_iosurface_.get()) + compositing_iosurface_->UnrefIOSurface(); +} + +bool RenderWidgetHostViewMac::HasAcceleratedSurface( + const gfx::Size& desired_size) { + return last_frame_was_accelerated_ && + compositing_iosurface_.get() && + compositing_iosurface_->HasIOSurface() && + (desired_size.IsEmpty() || + compositing_iosurface_->io_surface_size() == desired_size); } void RenderWidgetHostViewMac::OnAcceleratedCompositingStateChange() { @@ -1068,8 +1066,10 @@ gfx::Rect RenderWidgetHostViewMac::GetRootWindowBounds() { } gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() { - // compositing_surface_ is always gfx::kNullPluginWindow. - return gfx::GLSurfaceHandle(compositing_surface_, true); + // Compositor window is always gfx::kNullPluginWindow. + // TODO(jbates) http://crbug.com/105344 This will be removed when there are no + // plugin windows. + return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, true); } void RenderWidgetHostViewMac::DrawAcceleratedSurfaceInstance( @@ -1151,6 +1151,34 @@ void RenderWidgetHostViewMac::ShutdownHost() { // Do not touch any members at this point, |this| has been deleted. } +void RenderWidgetHostViewMac::GotAcceleratedFrame() { + if (!last_frame_was_accelerated_) { + last_frame_was_accelerated_ = true; + + // Need to wipe the software view with transparency to expose the GL + // underlay. Invalidate the whole window to do that. + if (!about_to_validate_and_paint_) { + [cocoa_view_ setNeedsDisplay:YES]; + [cocoa_view_ displayIfNeeded]; + } + + // Delete software backingstore. + BackingStoreManager::RemoveBackingStore(render_widget_host_); + } +} + +void RenderWidgetHostViewMac::GotSoftwareFrame() { + if (last_frame_was_accelerated_) { + last_frame_was_accelerated_ = false; + + // Forget IOSurface since we are drawing a software frame now. + if (compositing_iosurface_.get() && + compositing_iosurface_->HasIOSurface()) { + compositing_iosurface_->UnrefIOSurface(); + } + } +} + gfx::Rect RenderWidgetHostViewMac::GetViewCocoaBounds() const { return gfx::Rect(NSRectToCGRect([cocoa_view_ bounds])); } @@ -1360,6 +1388,7 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { } - (void)mouseEvent:(NSEvent*)theEvent { + TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent"); if (delegate_ && [delegate_ respondsToSelector:@selector(handleEvent:)]) { BOOL handled = [delegate_ handleEvent:theEvent]; if (handled) @@ -1521,6 +1550,7 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { } - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { + TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::keyEvent"); DCHECK([theEvent type] != NSKeyDown || !equiv == !([theEvent modifierFlags] & NSCommandKeyMask)); @@ -1846,6 +1876,7 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { } - (void)drawRect:(NSRect)dirtyRect { + TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::drawRect"); if (!renderWidgetHostView_->render_widget_host_) { // TODO(shess): Consider using something more noticable? [[NSColor whiteColor] set]; @@ -1853,30 +1884,37 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { return; } - const gfx::Rect damagedRect([self flipNSRectToRect:dirtyRect]); - - if (renderWidgetHostView_->last_frame_was_accelerated_) { - // Draw transparency to expose the GL underlay: - // TODO(jbates) avoid doing this every frame. - [[NSColor clearColor] set]; - NSRectFill(dirtyRect); - - renderWidgetHostView_->compositing_iosurface_->DrawIOSurface(self); - renderWidgetHostView_->AckPendingCompositorSwapBuffers(); - return; - } - - // In case the last frame was accelerated, ack any pending swaps to unblock - // the GPU process. - renderWidgetHostView_->AckPendingCompositorSwapBuffers(); - DCHECK(!renderWidgetHostView_->about_to_validate_and_paint_); + // GetBackingStore works for both software and accelerated frames. If a + // SwapBuffers occurs while GetBackingStore is blocking, we will continue to + // blit the IOSurface below. renderWidgetHostView_->about_to_validate_and_paint_ = true; BackingStoreMac* backingStore = static_cast<BackingStoreMac*>( renderWidgetHostView_->render_widget_host_->GetBackingStore(true)); renderWidgetHostView_->about_to_validate_and_paint_ = false; + const gfx::Rect damagedRect([self flipNSRectToRect:dirtyRect]); + + if (renderWidgetHostView_->last_frame_was_accelerated_ && + renderWidgetHostView_->compositing_iosurface_.get()) { + // Note that this code path is only executed when there's window damage + // (when the window is foregrounded, for example). Normally, GPU frames + // arrive and are drawn during AcceleratedSurfaceBuffersSwapped. + { + TRACE_EVENT0("browser", "NSRectFill"); + // Draw transparency to expose the GL underlay. NSRectFill is extremely + // slow (15ms for a window on a fast MacPro), so this is only done for the + // dirty rect. The composited swap-buffers typically happens outside of + // drawRect to avoid invalidating the entire NSView. + [[NSColor clearColor] set]; + NSRectFill(dirtyRect); + } + + renderWidgetHostView_->compositing_iosurface_->DrawIOSurface(self); + return; + } + if (backingStore) { gfx::Rect bitmapRect(0, 0, backingStore->size().width(), 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 288439e..dd6cd15 100644 --- a/content/browser/renderer_host/render_widget_host_view_win.cc +++ b/content/browser/renderer_host/render_widget_host_view_win.cc @@ -2251,6 +2251,14 @@ void RenderWidgetHostViewWin::AcceleratedSurfaceSuspend() { accelerated_surface_->Suspend(); } +bool RenderWidgetHostViewWin::HasAcceleratedSurface( + const gfx::Size& desired_size) { + // TODO(jbates) Implement this so this view can use GetBackingStore for both + // software and GPU frames. Defaulting to false just makes GetBackingStore + // only useable for software frames. + return false; +} + void RenderWidgetHostViewWin::SetAccessibilityFocus(int acc_obj_id) { if (!render_widget_host_) return; diff --git a/content/browser/renderer_host/render_widget_host_view_win.h b/content/browser/renderer_host/render_widget_host_view_win.h index 7990345..e117894 100644 --- a/content/browser/renderer_host/render_widget_host_view_win.h +++ b/content/browser/renderer_host/render_widget_host_view_win.h @@ -222,6 +222,7 @@ class RenderWidgetHostViewWin const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, int gpu_host_id) OVERRIDE; virtual void AcceleratedSurfaceSuspend() OVERRIDE; + virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE; virtual void OnAccessibilityNotifications( const std::vector<AccessibilityHostMsg_NotificationParams>& params ) OVERRIDE; diff --git a/content/browser/renderer_host/test_render_view_host.cc b/content/browser/renderer_host/test_render_view_host.cc index 735ec82..b8c81d8 100644 --- a/content/browser/renderer_host/test_render_view_host.cc +++ b/content/browser/renderer_host/test_render_view_host.cc @@ -123,6 +123,11 @@ void TestRenderWidgetHostView::AcceleratedSurfacePostSubBuffer( void TestRenderWidgetHostView::AcceleratedSurfaceSuspend() { } +bool TestRenderWidgetHostView::HasAcceleratedSurface( + const gfx::Size& desired_size) { + return false; +} + #if defined(OS_MACOSX) gfx::Rect TestRenderWidgetHostView::GetViewCocoaBounds() const { diff --git a/content/browser/renderer_host/test_render_view_host.h b/content/browser/renderer_host/test_render_view_host.h index 39448ad..966edfd 100644 --- a/content/browser/renderer_host/test_render_view_host.h +++ b/content/browser/renderer_host/test_render_view_host.h @@ -112,6 +112,7 @@ class TestRenderWidgetHostView : public RenderWidgetHostViewBase { const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, int gpu_host_id) OVERRIDE; virtual void AcceleratedSurfaceSuspend() OVERRIDE; + virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE; #if defined(OS_MACOSX) virtual gfx::Rect GetViewCocoaBounds() const OVERRIDE; virtual void PluginFocusChanged(bool focused, int plugin_id) OVERRIDE; diff --git a/content/common/gpu/image_transport_surface_mac.cc b/content/common/gpu/image_transport_surface_mac.cc index 6dc3149..ed9a4aa 100644 --- a/content/common/gpu/image_transport_surface_mac.cc +++ b/content/common/gpu/image_transport_surface_mac.cc @@ -37,6 +37,7 @@ class IOSurfaceImageTransportSurface : public gfx::NoOpGLSurfaceCGL, virtual gfx::Size GetSize() OVERRIDE; virtual bool OnMakeCurrent(gfx::GLContext* context) OVERRIDE; virtual unsigned int GetBackingFrameBufferObject() OVERRIDE; + virtual void SetBufferAllocation(BufferAllocationState state) OVERRIDE; protected: // ImageTransportSurface implementation @@ -50,6 +51,11 @@ class IOSurfaceImageTransportSurface : public gfx::NoOpGLSurfaceCGL, private: virtual ~IOSurfaceImageTransportSurface() OVERRIDE; + void UnrefIOSurface(); + void CreateIOSurface(); + + BufferAllocationState buffer_allocation_state_; + uint32 fbo_id_; GLuint texture_id_; @@ -91,6 +97,7 @@ IOSurfaceImageTransportSurface::IOSurfaceImageTransportSurface( GpuCommandBufferStub* stub, gfx::PluginWindowHandle handle) : gfx::NoOpGLSurfaceCGL(gfx::Size(1, 1)), + buffer_allocation_state_(BUFFER_ALLOCATION_FRONT_AND_BACK), fbo_id_(0), texture_id_(0), io_surface_handle_(0), @@ -141,16 +148,8 @@ bool IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) { if (made_current_) return true; - glGenFramebuffersEXT(1, &fbo_id_); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); OnResize(gfx::Size(1, 1)); - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if (status != GL_FRAMEBUFFER_COMPLETE) { - DLOG(ERROR) << "Framebuffer incomplete."; - return false; - } - made_current_ = true; return true; } @@ -159,6 +158,30 @@ unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() { return fbo_id_; } +void IOSurfaceImageTransportSurface::SetBufferAllocation( + BufferAllocationState state) { + if (buffer_allocation_state_ == state) + return; + buffer_allocation_state_ = state; + + switch (state) { + case BUFFER_ALLOCATION_FRONT_AND_BACK: + CreateIOSurface(); + break; + + case BUFFER_ALLOCATION_FRONT_ONLY: + break; + + case BUFFER_ALLOCATION_NONE: + UnrefIOSurface(); + helper_->Suspend(); + break; + + default: + NOTREACHED(); + } +} + bool IOSurfaceImageTransportSurface::SwapBuffers() { glFlush(); @@ -217,23 +240,44 @@ void IOSurfaceImageTransportSurface::OnResizeViewACK() { } void IOSurfaceImageTransportSurface::OnResize(gfx::Size size) { - IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); - // Caching |context_| from OnMakeCurrent. It should still be current. DCHECK(context_->IsCurrent(this)); size_ = size; + CreateIOSurface(); +} + +void IOSurfaceImageTransportSurface::UnrefIOSurface() { + DCHECK(context_->IsCurrent(this)); + + if (fbo_id_) { + glDeleteFramebuffersEXT(1, &fbo_id_); + fbo_id_ = 0; + } + if (texture_id_) { glDeleteTextures(1, &texture_id_); texture_id_ = 0; } - glGenTextures(1, &texture_id_); + io_surface_.reset(); + io_surface_handle_ = 0; +} +void IOSurfaceImageTransportSurface::CreateIOSurface() { GLint previous_texture_id = 0; glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id); + UnrefIOSurface(); + + glGenFramebuffersEXT(1, &fbo_id_); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); + + IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); + + glGenTextures(1, &texture_id_); + // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on // Mac OS X and is required for IOSurface interoperability. GLenum target = GL_TEXTURE_RECTANGLE_ARB; @@ -243,11 +287,6 @@ void IOSurfaceImageTransportSurface::OnResize(gfx::Size size) { glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLint previous_fbo_id = 0; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previous_fbo_id); - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, @@ -278,22 +317,28 @@ void IOSurfaceImageTransportSurface::OnResize(gfx::Size size) { // Don't think we need to identify a plane. GLuint plane = 0; - io_surface_support->CGLTexImageIOSurface2D( - static_cast<CGLContextObj>(context_->GetHandle()), - target, - GL_RGBA, - size_.width(), - size_.height(), - GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV, - io_surface_.get(), - plane); + CGLError cglerror = + io_surface_support->CGLTexImageIOSurface2D( + static_cast<CGLContextObj>(context_->GetHandle()), + target, + GL_RGBA, + size_.width(), + size_.height(), + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, + io_surface_.get(), + plane); + if (cglerror != kCGLNoError) { + DLOG(ERROR) << "CGLTexImageIOSurface2D: " << cglerror; + UnrefIOSurface(); + return; + } io_surface_handle_ = io_surface_support->IOSurfaceGetID(io_surface_); glFlush(); glBindTexture(target, previous_texture_id); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, previous_fbo_id); + // The FBO remains bound for this GL context. } } // namespace diff --git a/content/common/view_messages.h b/content/common/view_messages.h index ecde49a..d94dff2 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -493,7 +493,8 @@ IPC_STRUCT_BEGIN(ViewHostMsg_UpdateRect_Params) // The size of the RenderView when this message was generated. This is // included so the host knows how large the view is from the perspective of // the renderer process. This is necessary in case a resize operation is in - // progress. + // progress. If auto-resize is enabled, this should update the corresponding + // view size. IPC_STRUCT_MEMBER(gfx::Size, view_size) // New window locations for plugin child windows. @@ -1906,6 +1907,15 @@ IPC_MESSAGE_ROUTED2(ViewHostMsg_AcceleratedSurfaceBuffersSwapped, uint64 /* surface_handle */) #endif +// This message is synthesized by GpuProcessHost to pass through a swap message +// to the RenderWidgetHelper. This allows GetBackingStore to block for either a +// software or GPU frame. +IPC_MESSAGE_ROUTED4(ViewHostMsg_CompositorSurfaceBuffersSwapped, + int32 /* surface id */, + uint64 /* surface_handle */, + int32 /* route_id */, + int32 /* gpu_process_host_id */) + // Opens a file asynchronously. The response returns a file descriptor // and an error code from base/platform_file.h. IPC_MESSAGE_ROUTED3(ViewHostMsg_AsyncOpenFile, diff --git a/content/port/browser/render_widget_host_view_port.h b/content/port/browser/render_widget_host_view_port.h index cf0d459..2c6b8cb 100644 --- a/content/port/browser/render_widget_host_view_port.h +++ b/content/port/browser/render_widget_host_view_port.h @@ -175,6 +175,11 @@ class CONTENT_EXPORT RenderWidgetHostViewPort : public RenderWidgetHostView { // next swap buffers or post sub buffer. virtual void AcceleratedSurfaceSuspend() = 0; + // Return true if the view has an accelerated surface that contains the last + // presented frame for the view. If |desired_size| is non-empty, true is + // returned only if the accelerated surface size matches. + virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) = 0; + #if defined(OS_MACOSX) // Retrieve the bounds of the view, in cocoa view coordinates. // If the UI scale factor is 2, |GetViewBounds()| will return a size of e.g. diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h index b044a11..194aa30 100644 --- a/content/public/browser/render_process_host.h +++ b/content/public/browser/render_process_host.h @@ -79,9 +79,9 @@ class CONTENT_EXPORT RenderProcessHost : public IPC::Message::Sender, // Called to wait for the next UpdateRect message for the specified render // widget. Returns true if successful, and the msg out-param will contain a // copy of the received UpdateRect message. - virtual bool WaitForUpdateMsg(int render_widget_id, - const base::TimeDelta& max_delay, - IPC::Message* msg) = 0; + virtual bool WaitForBackingStoreMsg(int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg) = 0; // Called when a received message cannot be decoded. virtual void ReceivedBadMessage() = 0; diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 4aa576c..e21aeda 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -1081,6 +1081,7 @@ void RenderWidget::didAutoResize(const WebSize& new_size) { void RenderWidget::didActivateCompositor(int input_handler_identifier) { TRACE_EVENT0("gpu", "RenderWidget::didActivateCompositor"); +#if !defined(OS_MACOSX) 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 @@ -1089,9 +1090,11 @@ void RenderWidget::didActivateCompositor(int input_handler_identifier) { // 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. + // browser's UI thread. This is not necessary on Mac, because SwapBuffers + // now unblocks GetBackingStore on Mac. Send(new ViewHostMsg_UpdateIsDelayed(routing_id_)); } +#endif is_accelerated_compositing_active_ = true; Send(new ViewHostMsg_DidActivateAcceleratedCompositing( diff --git a/content/test/mock_render_process_host.cc b/content/test/mock_render_process_host.cc index f555bec..2a16e00 100644 --- a/content/test/mock_render_process_host.cc +++ b/content/test/mock_render_process_host.cc @@ -58,9 +58,10 @@ void MockRenderProcessHost::SimulateSwapOutACK( const ViewMsg_SwapOut_Params& params) { } -bool MockRenderProcessHost::WaitForUpdateMsg(int render_widget_id, - const base::TimeDelta& max_delay, - IPC::Message* msg) { +bool MockRenderProcessHost::WaitForBackingStoreMsg( + int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg) { return false; } diff --git a/content/test/mock_render_process_host.h b/content/test/mock_render_process_host.h index 58be48c..b700697 100644 --- a/content/test/mock_render_process_host.h +++ b/content/test/mock_render_process_host.h @@ -39,9 +39,9 @@ class MockRenderProcessHost : public RenderProcessHost { virtual void CancelResourceRequests(int render_widget_id) OVERRIDE; virtual void SimulateSwapOutACK( const ViewMsg_SwapOut_Params& params) OVERRIDE; - virtual bool WaitForUpdateMsg(int render_widget_id, - const base::TimeDelta& max_delay, - IPC::Message* msg) OVERRIDE; + virtual bool WaitForBackingStoreMsg(int render_widget_id, + const base::TimeDelta& max_delay, + IPC::Message* msg) OVERRIDE; virtual void ReceivedBadMessage() OVERRIDE; virtual void WidgetRestored() OVERRIDE; virtual void WidgetHidden() OVERRIDE; |