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
diff --git a/content/browser/renderer_host/render_widget_helper.cc b/content/browser/renderer_host/render_widget_helper.cc
index 8639565..2290b4ea 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 @@
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() {
+ 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 @@
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 @@
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 @@
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 @@
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::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 =