summaryrefslogtreecommitdiffstats
path: root/content/renderer/gpu
diff options
context:
space:
mode:
authormkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-22 17:05:11 +0000
committermkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-22 17:05:11 +0000
commit586871b039414476e9127d888a1b6ca3a02d3722 (patch)
tree48656558bb2ebe925a24400662535386839022eb /content/renderer/gpu
parent9788b787b14b333dc93d6d13d4c881046a58db63 (diff)
downloadchromium_src-586871b039414476e9127d888a1b6ca3a02d3722.zip
chromium_src-586871b039414476e9127d888a1b6ca3a02d3722.tar.gz
chromium_src-586871b039414476e9127d888a1b6ca3a02d3722.tar.bz2
Deliver IPC messages together with SwapCompositorFrame
This makes it possible to send IPC messages in such a way that they're delivered together with the SwapCompositorFrame corresponding to the next commit. BUG=364612 Review URL: https://codereview.chromium.org/240163005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284712 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/renderer/gpu')
-rw-r--r--content/renderer/gpu/compositor_output_surface.cc21
-rw-r--r--content/renderer/gpu/compositor_output_surface.h3
-rw-r--r--content/renderer/gpu/delegated_compositor_output_surface.cc5
-rw-r--r--content/renderer/gpu/delegated_compositor_output_surface.h5
-rw-r--r--content/renderer/gpu/frame_swap_message_queue.cc202
-rw-r--r--content/renderer/gpu/frame_swap_message_queue.h108
-rw-r--r--content/renderer/gpu/frame_swap_message_queue_unittest.cc294
-rw-r--r--content/renderer/gpu/mailbox_output_surface.cc3
-rw-r--r--content/renderer/gpu/mailbox_output_surface.h3
-rw-r--r--content/renderer/gpu/queue_message_swap_promise.cc65
-rw-r--r--content/renderer/gpu/queue_message_swap_promise.h45
-rw-r--r--content/renderer/gpu/queue_message_swap_promise_unittest.cc322
-rw-r--r--content/renderer/gpu/render_widget_compositor.cc9
-rw-r--r--content/renderer/gpu/render_widget_compositor.h2
14 files changed, 1082 insertions, 5 deletions
diff --git a/content/renderer/gpu/compositor_output_surface.cc b/content/renderer/gpu/compositor_output_surface.cc
index 218b42f..bc89848 100644
--- a/content/renderer/gpu/compositor_output_surface.cc
+++ b/content/renderer/gpu/compositor_output_surface.cc
@@ -15,6 +15,7 @@
#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
#include "content/common/view_messages.h"
#include "content/public/common/content_switches.h"
+#include "content/renderer/gpu/frame_swap_message_queue.h"
#include "content/renderer/render_thread_impl.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
@@ -54,12 +55,14 @@ CompositorOutputSurface::CompositorOutputSurface(
uint32 output_surface_id,
const scoped_refptr<ContextProviderCommandBuffer>& context_provider,
scoped_ptr<cc::SoftwareOutputDevice> software_device,
+ scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue,
bool use_swap_compositor_frame_message)
: OutputSurface(context_provider, software_device.Pass()),
output_surface_id_(output_surface_id),
use_swap_compositor_frame_message_(use_swap_compositor_frame_message),
output_surface_filter_(
RenderThreadImpl::current()->compositor_output_surface_filter()),
+ frame_swap_message_queue_(swap_frame_message_queue),
routing_id_(routing_id),
prefers_smoothness_(false),
#if defined(OS_WIN)
@@ -71,6 +74,7 @@ CompositorOutputSurface::CompositorOutputSurface(
layout_test_mode_(RenderThreadImpl::current()->layout_test_mode()),
weak_ptrs_(this) {
DCHECK(output_surface_filter_.get());
+ DCHECK(frame_swap_message_queue_.get());
DetachFromThread();
message_sender_ = RenderThreadImpl::current()->sync_message_filter();
DCHECK(message_sender_.get());
@@ -163,9 +167,20 @@ void CompositorOutputSurface::SwapBuffers(cc::CompositorFrame* frame) {
}
if (use_swap_compositor_frame_message_) {
- Send(new ViewHostMsg_SwapCompositorFrame(routing_id_,
- output_surface_id_,
- *frame));
+ {
+ ScopedVector<IPC::Message> messages;
+ std::vector<IPC::Message> messages_to_deliver_with_frame;
+ scoped_ptr<FrameSwapMessageQueue::SendMessageScope> send_message_scope =
+ frame_swap_message_queue_->AcquireSendMessageScope();
+ frame_swap_message_queue_->DrainMessages(&messages);
+ FrameSwapMessageQueue::TransferMessages(messages,
+ &messages_to_deliver_with_frame);
+ Send(new ViewHostMsg_SwapCompositorFrame(routing_id_,
+ output_surface_id_,
+ *frame,
+ messages_to_deliver_with_frame));
+ // ~send_message_scope.
+ }
client_->DidSwapBuffers();
return;
}
diff --git a/content/renderer/gpu/compositor_output_surface.h b/content/renderer/gpu/compositor_output_surface.h
index df91047..0b369b2 100644
--- a/content/renderer/gpu/compositor_output_surface.h
+++ b/content/renderer/gpu/compositor_output_surface.h
@@ -35,6 +35,7 @@ class SoftwareFrameData;
namespace content {
class ContextProviderCommandBuffer;
+class FrameSwapMessageQueue;
// This class can be created only on the main thread, but then becomes pinned
// to a fixed thread when bindToClient is called.
@@ -50,6 +51,7 @@ class CompositorOutputSurface
uint32 output_surface_id,
const scoped_refptr<ContextProviderCommandBuffer>& context_provider,
scoped_ptr<cc::SoftwareOutputDevice> software,
+ scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue,
bool use_swap_compositor_frame_message);
virtual ~CompositorOutputSurface();
@@ -108,6 +110,7 @@ class CompositorOutputSurface
scoped_refptr<IPC::ForwardingMessageFilter> output_surface_filter_;
scoped_refptr<CompositorOutputSurfaceProxy> output_surface_proxy_;
scoped_refptr<IPC::SyncMessageFilter> message_sender_;
+ scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue_;
int routing_id_;
bool prefers_smoothness_;
base::PlatformThreadHandle main_thread_handle_;
diff --git a/content/renderer/gpu/delegated_compositor_output_surface.cc b/content/renderer/gpu/delegated_compositor_output_surface.cc
index 263d5f4..d9ed496 100644
--- a/content/renderer/gpu/delegated_compositor_output_surface.cc
+++ b/content/renderer/gpu/delegated_compositor_output_surface.cc
@@ -3,17 +3,20 @@
// found in the LICENSE file.
#include "content/renderer/gpu/delegated_compositor_output_surface.h"
+#include "content/renderer/gpu/frame_swap_message_queue.h"
namespace content {
DelegatedCompositorOutputSurface::DelegatedCompositorOutputSurface(
int32 routing_id,
uint32 output_surface_id,
- const scoped_refptr<ContextProviderCommandBuffer>& context_provider)
+ const scoped_refptr<ContextProviderCommandBuffer>& context_provider,
+ scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue)
: CompositorOutputSurface(routing_id,
output_surface_id,
context_provider,
scoped_ptr<cc::SoftwareOutputDevice>(),
+ swap_frame_message_queue,
true) {
capabilities_.delegated_rendering = true;
capabilities_.max_frames_pending = 1;
diff --git a/content/renderer/gpu/delegated_compositor_output_surface.h b/content/renderer/gpu/delegated_compositor_output_surface.h
index f971f1c..b8ece97 100644
--- a/content/renderer/gpu/delegated_compositor_output_surface.h
+++ b/content/renderer/gpu/delegated_compositor_output_surface.h
@@ -5,16 +5,19 @@
#ifndef CONTENT_RENDERER_GPU_DELEGATED_COMPOSITOR_OUTPUT_SURFACE_H_
#define CONTENT_RENDERER_GPU_DELEGATED_COMPOSITOR_OUTPUT_SURFACE_H_
+#include "base/memory/ref_counted.h"
#include "content/renderer/gpu/compositor_output_surface.h"
namespace content {
+class FrameSwapMessageQueue;
class DelegatedCompositorOutputSurface : public CompositorOutputSurface {
public:
DelegatedCompositorOutputSurface(
int32 routing_id,
uint32 output_surface_id,
- const scoped_refptr<ContextProviderCommandBuffer>& context_provider);
+ const scoped_refptr<ContextProviderCommandBuffer>& context_provider,
+ scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue);
virtual ~DelegatedCompositorOutputSurface() {}
};
diff --git a/content/renderer/gpu/frame_swap_message_queue.cc b/content/renderer/gpu/frame_swap_message_queue.cc
new file mode 100644
index 0000000..df4185d
--- /dev/null
+++ b/content/renderer/gpu/frame_swap_message_queue.cc
@@ -0,0 +1,202 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/gpu/frame_swap_message_queue.h"
+
+#include <limits>
+
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "ipc/ipc_message.h"
+
+using std::vector;
+
+namespace content {
+
+class FrameSwapMessageSubQueue {
+ public:
+ FrameSwapMessageSubQueue() {}
+ virtual ~FrameSwapMessageSubQueue() {}
+ virtual bool Empty() const = 0;
+ virtual void QueueMessage(int source_frame_number,
+ scoped_ptr<IPC::Message> msg,
+ bool* is_first) = 0;
+ virtual void DrainMessages(int source_frame_number,
+ ScopedVector<IPC::Message>* messages) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FrameSwapMessageSubQueue);
+};
+
+namespace {
+
+// Queue specific to MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE.
+class SendMessageScopeImpl : public FrameSwapMessageQueue::SendMessageScope {
+ public:
+ SendMessageScopeImpl(base::Lock* lock) : auto_lock_(*lock) {}
+ virtual ~SendMessageScopeImpl() OVERRIDE {}
+
+ private:
+ base::AutoLock auto_lock_;
+};
+
+class VisualStateQueue : public FrameSwapMessageSubQueue {
+ public:
+ VisualStateQueue() {}
+
+ virtual ~VisualStateQueue() {
+ for (VisualStateQueueMap::iterator i = queue_.begin(); i != queue_.end();
+ i++) {
+ STLDeleteElements(&i->second);
+ }
+ }
+
+ virtual bool Empty() const OVERRIDE { return queue_.empty(); }
+
+ virtual void QueueMessage(int source_frame_number,
+ scoped_ptr<IPC::Message> msg,
+ bool* is_first) OVERRIDE {
+ if (is_first)
+ *is_first = (queue_.count(source_frame_number) == 0);
+
+ queue_[source_frame_number].push_back(msg.release());
+ }
+
+ virtual void DrainMessages(int source_frame_number,
+ ScopedVector<IPC::Message>* messages) OVERRIDE {
+ VisualStateQueueMap::iterator end = queue_.upper_bound(source_frame_number);
+ for (VisualStateQueueMap::iterator i = queue_.begin(); i != end; i++) {
+ DCHECK(i->first <= source_frame_number);
+ messages->insert(messages->end(), i->second.begin(), i->second.end());
+ i->second.clear();
+ }
+ queue_.erase(queue_.begin(), end);
+ }
+
+ private:
+ typedef std::map<int, std::vector<IPC::Message*> > VisualStateQueueMap;
+ VisualStateQueueMap queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisualStateQueue);
+};
+
+// Queue specific to MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP.
+class SwapQueue : public FrameSwapMessageSubQueue {
+ public:
+ SwapQueue() {}
+ virtual bool Empty() const OVERRIDE { return queue_.empty(); }
+
+ virtual void QueueMessage(int source_frame_number,
+ scoped_ptr<IPC::Message> msg,
+ bool* is_first) OVERRIDE {
+ if (is_first)
+ *is_first = Empty();
+ queue_.push_back(msg.release());
+ }
+
+ virtual void DrainMessages(int source_frame_number,
+ ScopedVector<IPC::Message>* messages) OVERRIDE {
+ messages->insert(messages->end(), queue_.begin(), queue_.end());
+ queue_.weak_clear();
+ }
+
+ private:
+ ScopedVector<IPC::Message> queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(SwapQueue);
+};
+
+} // namespace
+
+FrameSwapMessageQueue::FrameSwapMessageQueue()
+ : visual_state_queue_(new VisualStateQueue()),
+ swap_queue_(new SwapQueue()) {
+}
+
+FrameSwapMessageQueue::~FrameSwapMessageQueue() {
+}
+
+bool FrameSwapMessageQueue::Empty() const {
+ base::AutoLock lock(lock_);
+ return next_drain_messages_.empty() && visual_state_queue_->Empty() &&
+ swap_queue_->Empty();
+}
+
+FrameSwapMessageSubQueue* FrameSwapMessageQueue::GetSubQueue(
+ MessageDeliveryPolicy policy) {
+ switch (policy) {
+ case MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP:
+ return swap_queue_.get();
+ break;
+ case MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE:
+ return visual_state_queue_.get();
+ break;
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+void FrameSwapMessageQueue::QueueMessageForFrame(MessageDeliveryPolicy policy,
+ int source_frame_number,
+ scoped_ptr<IPC::Message> msg,
+ bool* is_first) {
+ base::AutoLock lock(lock_);
+ GetSubQueue(policy)->QueueMessage(source_frame_number, msg.Pass(), is_first);
+}
+
+void FrameSwapMessageQueue::DidSwap(int source_frame_number) {
+ base::AutoLock lock(lock_);
+
+ visual_state_queue_->DrainMessages(source_frame_number,
+ &next_drain_messages_);
+}
+
+void FrameSwapMessageQueue::DidNotSwap(int source_frame_number,
+ cc::SwapPromise::DidNotSwapReason reason,
+ ScopedVector<IPC::Message>* messages) {
+ base::AutoLock lock(lock_);
+ switch (reason) {
+ case cc::SwapPromise::SWAP_FAILS:
+ case cc::SwapPromise::COMMIT_NO_UPDATE:
+ swap_queue_->DrainMessages(source_frame_number, messages);
+ // fallthrough
+ case cc::SwapPromise::COMMIT_FAILS:
+ visual_state_queue_->DrainMessages(source_frame_number, messages);
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void FrameSwapMessageQueue::DrainMessages(
+ ScopedVector<IPC::Message>* messages) {
+ lock_.AssertAcquired();
+
+ swap_queue_->DrainMessages(0, messages);
+ messages->insert(messages->end(),
+ next_drain_messages_.begin(),
+ next_drain_messages_.end());
+ next_drain_messages_.weak_clear();
+}
+
+scoped_ptr<FrameSwapMessageQueue::SendMessageScope>
+FrameSwapMessageQueue::AcquireSendMessageScope() {
+ return make_scoped_ptr(new SendMessageScopeImpl(&lock_))
+ .PassAs<SendMessageScope>();
+}
+
+// static
+void FrameSwapMessageQueue::TransferMessages(ScopedVector<IPC::Message>& source,
+ vector<IPC::Message>* dest) {
+ for (vector<IPC::Message*>::iterator i = source.begin(); i != source.end();
+ ++i) {
+ IPC::Message* m(*i);
+ dest->push_back(*m);
+ delete m;
+ }
+ source.weak_clear();
+}
+
+} // namespace content
diff --git a/content/renderer/gpu/frame_swap_message_queue.h b/content/renderer/gpu/frame_swap_message_queue.h
new file mode 100644
index 0000000..6ebaf16
--- /dev/null
+++ b/content/renderer/gpu/frame_swap_message_queue.h
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_GPU_FRAME_SWAP_MESSAGE_QUEUE_H_
+#define CONTENT_RENDERER_GPU_FRAME_SWAP_MESSAGE_QUEUE_H_
+
+#include <map>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/synchronization/lock.h"
+#include "cc/base/swap_promise.h"
+#include "content/common/content_export.h"
+#include "content/renderer/message_delivery_policy.h"
+
+namespace IPC {
+class Message;
+};
+
+namespace content {
+
+class FrameSwapMessageSubQueue;
+
+// Queue used to keep track of which IPC::Messages should be sent along with a
+// particular compositor frame swap.
+class CONTENT_EXPORT FrameSwapMessageQueue
+ : public base::RefCountedThreadSafe<FrameSwapMessageQueue> {
+ public:
+ class CONTENT_EXPORT SendMessageScope {
+ public:
+ virtual ~SendMessageScope() {}
+ };
+
+ FrameSwapMessageQueue();
+
+ // Queues message to be returned on a matching DrainMessages call.
+ //
+ // |policy| determines how messages are matched with DrainMessages calls.
+ // |source_frame_number| frame number to queue |msg| for.
+ // |msg| - message to queue. The method takes ownership of |msg|.
+ // |is_first| - output parameter. Set to true if this was the first message
+ // enqueued for the given source_frame_number.
+ void QueueMessageForFrame(MessageDeliveryPolicy policy,
+ int source_frame_number,
+ scoped_ptr<IPC::Message> msg,
+ bool* is_first);
+
+ // Returns true if there are no messages in the queue.
+ bool Empty() const;
+
+ // Should be called when a successful swap occurs. The messages for that swap
+ // can be obtained by calling DrainMessages.
+ //
+ // |source_frame_number| frame number for which the swap occurred.
+ void DidSwap(int source_frame_number);
+
+ // Should be called when we know a swap will not occur. This also means we
+ // won't be expecting a DrainMessages call.
+ //
+ // |source_frame_number| frame number for which the swap will not occur.
+ // |reason| reason for the which the swap will not occur.
+ // |messages| depending on |reason| it may make sense to deliver certain
+ // messages asynchronously. This vector will contain those
+ // messages.
+ void DidNotSwap(int source_frame_number,
+ cc::SwapPromise::DidNotSwapReason reason,
+ ScopedVector<IPC::Message>* messages);
+
+ // A SendMessageScope object must be held by the caller when this method is
+ // called.
+ //
+ // |messages| vector to store messages, it's not cleared, only appended to.
+ // The method will append messages queued for frame numbers lower
+ // or equal to |source_frame_number|
+ void DrainMessages(ScopedVector<IPC::Message>* messages);
+
+ // SendMessageScope is used to make sure that messages sent from different
+ // threads (impl/main) are scheduled in the right order on the IO threads.
+ //
+ // Returns an object that must be kept in scope till an IPC message containing
+ // |messages| is sent.
+ scoped_ptr<SendMessageScope> AcquireSendMessageScope();
+
+ static void TransferMessages(ScopedVector<IPC::Message>& source,
+ std::vector<IPC::Message>* dest);
+
+ private:
+ friend class base::RefCountedThreadSafe<FrameSwapMessageQueue>;
+
+ FrameSwapMessageSubQueue* GetSubQueue(MessageDeliveryPolicy policy);
+
+ ~FrameSwapMessageQueue();
+
+ mutable base::Lock lock_;
+ scoped_ptr<FrameSwapMessageSubQueue> visual_state_queue_;
+ scoped_ptr<FrameSwapMessageSubQueue> swap_queue_;
+ ScopedVector<IPC::Message> next_drain_messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameSwapMessageQueue);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_GPU_FRAME_SWAP_MESSAGE_QUEUE_H_
diff --git a/content/renderer/gpu/frame_swap_message_queue_unittest.cc b/content/renderer/gpu/frame_swap_message_queue_unittest.cc
new file mode 100644
index 0000000..c3ea468
--- /dev/null
+++ b/content/renderer/gpu/frame_swap_message_queue_unittest.cc
@@ -0,0 +1,294 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/gpu/frame_swap_message_queue.h"
+#include "ipc/ipc_message.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class FrameSwapMessageQueueTest : public testing::Test {
+ public:
+ FrameSwapMessageQueueTest()
+ : first_message_(41, 1, IPC::Message::PRIORITY_NORMAL),
+ second_message_(42, 2, IPC::Message::PRIORITY_NORMAL),
+ third_message_(43, 3, IPC::Message::PRIORITY_NORMAL),
+ queue_(new FrameSwapMessageQueue()) {}
+
+ protected:
+ void QueueNextSwapMessage(scoped_ptr<IPC::Message> msg) {
+ queue_->QueueMessageForFrame(
+ MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 0, msg.Pass(), NULL);
+ }
+
+ void QueueNextSwapMessage(scoped_ptr<IPC::Message> msg, bool* first) {
+ queue_->QueueMessageForFrame(
+ MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 0, msg.Pass(), first);
+ }
+
+ void QueueVisualStateMessage(int source_frame_number,
+ scoped_ptr<IPC::Message> msg) {
+ queue_->QueueMessageForFrame(MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE,
+ source_frame_number,
+ msg.Pass(),
+ NULL);
+ }
+
+ void QueueVisualStateMessage(int source_frame_number,
+ scoped_ptr<IPC::Message> msg,
+ bool* first) {
+ queue_->QueueMessageForFrame(MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE,
+ source_frame_number,
+ msg.Pass(),
+ first);
+ }
+
+ void DrainMessages(int source_frame_number,
+ ScopedVector<IPC::Message>* messages) {
+ messages->clear();
+ queue_->DidSwap(source_frame_number);
+ scoped_ptr<FrameSwapMessageQueue::SendMessageScope> send_message_scope =
+ queue_->AcquireSendMessageScope();
+ queue_->DrainMessages(messages);
+ }
+
+ bool HasMessageForId(const ScopedVector<IPC::Message>& messages,
+ int routing_id) {
+ for (ScopedVector<IPC::Message>::const_iterator i = messages.begin();
+ i != messages.end();
+ ++i) {
+ if ((*i)->routing_id() == routing_id)
+ return true;
+ }
+ return false;
+ }
+
+ scoped_ptr<IPC::Message> CloneMessage(const IPC::Message& other) {
+ return make_scoped_ptr(new IPC::Message(other)).Pass();
+ }
+
+ void TestDidNotSwap(cc::SwapPromise::DidNotSwapReason reason);
+
+ IPC::Message first_message_;
+ IPC::Message second_message_;
+ IPC::Message third_message_;
+ scoped_refptr<FrameSwapMessageQueue> queue_;
+};
+
+TEST_F(FrameSwapMessageQueueTest, TestEmptyQueueDrain) {
+ ScopedVector<IPC::Message> messages;
+
+ DrainMessages(0, &messages);
+ ASSERT_TRUE(messages.empty());
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestEmpty) {
+ ScopedVector<IPC::Message> messages;
+ ASSERT_TRUE(queue_->Empty());
+ QueueNextSwapMessage(CloneMessage(first_message_));
+ ASSERT_FALSE(queue_->Empty());
+ DrainMessages(0, &messages);
+ ASSERT_TRUE(queue_->Empty());
+ QueueVisualStateMessage(1, CloneMessage(first_message_));
+ ASSERT_FALSE(queue_->Empty());
+ queue_->DidSwap(1);
+ ASSERT_FALSE(queue_->Empty());
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestQueueMessageFirst) {
+ ScopedVector<IPC::Message> messages;
+ bool visual_state_first = false;
+ bool next_swap_first = false;
+
+ // Queuing the first time should result in true.
+ QueueVisualStateMessage(1, CloneMessage(first_message_), &visual_state_first);
+ ASSERT_TRUE(visual_state_first);
+ // Queuing the second time should result in true.
+ QueueVisualStateMessage(
+ 1, CloneMessage(second_message_), &visual_state_first);
+ ASSERT_FALSE(visual_state_first);
+ // Queuing for a different frame should result in true.
+ QueueVisualStateMessage(2, CloneMessage(first_message_), &visual_state_first);
+ ASSERT_TRUE(visual_state_first);
+
+ // Queuing for a different policy should result in true.
+ QueueNextSwapMessage(CloneMessage(first_message_), &next_swap_first);
+ ASSERT_TRUE(next_swap_first);
+ // Second time for the same policy is still false.
+ QueueNextSwapMessage(CloneMessage(first_message_), &next_swap_first);
+ ASSERT_FALSE(next_swap_first);
+
+ DrainMessages(4, &messages);
+ // Queuing after all messages are drained is a true again.
+ QueueVisualStateMessage(4, CloneMessage(first_message_), &visual_state_first);
+ ASSERT_TRUE(visual_state_first);
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestNextSwapMessageSentWithNextFrame) {
+ ScopedVector<IPC::Message> messages;
+
+ DrainMessages(1, &messages);
+ QueueNextSwapMessage(CloneMessage(first_message_));
+ DrainMessages(2, &messages);
+ ASSERT_EQ(1u, messages.size());
+ ASSERT_EQ(first_message_.routing_id(), messages.front()->routing_id());
+ messages.clear();
+
+ DrainMessages(2, &messages);
+ ASSERT_TRUE(messages.empty());
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestNextSwapMessageSentWithCurrentFrame) {
+ ScopedVector<IPC::Message> messages;
+
+ DrainMessages(1, &messages);
+ QueueNextSwapMessage(CloneMessage(first_message_));
+ DrainMessages(1, &messages);
+ ASSERT_EQ(1u, messages.size());
+ ASSERT_EQ(first_message_.routing_id(), messages.front()->routing_id());
+ messages.clear();
+
+ DrainMessages(1, &messages);
+ ASSERT_TRUE(messages.empty());
+}
+
+TEST_F(FrameSwapMessageQueueTest,
+ TestDrainsVisualStateMessagesForCorrespondingFrames) {
+ ScopedVector<IPC::Message> messages;
+
+ QueueVisualStateMessage(1, CloneMessage(first_message_));
+ QueueVisualStateMessage(2, CloneMessage(second_message_));
+ QueueVisualStateMessage(3, CloneMessage(third_message_));
+ DrainMessages(0, &messages);
+ ASSERT_TRUE(messages.empty());
+
+ DrainMessages(2, &messages);
+ ASSERT_EQ(2u, messages.size());
+ ASSERT_TRUE(HasMessageForId(messages, first_message_.routing_id()));
+ ASSERT_TRUE(HasMessageForId(messages, second_message_.routing_id()));
+ messages.clear();
+
+ DrainMessages(2, &messages);
+ ASSERT_TRUE(messages.empty());
+
+ DrainMessages(5, &messages);
+ ASSERT_EQ(1u, messages.size());
+ ASSERT_EQ(third_message_.routing_id(), messages.front()->routing_id());
+}
+
+TEST_F(FrameSwapMessageQueueTest,
+ TestQueueNextSwapMessagePreservesFifoOrdering) {
+ ScopedVector<IPC::Message> messages;
+
+ QueueNextSwapMessage(CloneMessage(first_message_));
+ QueueNextSwapMessage(CloneMessage(second_message_));
+ DrainMessages(1, &messages);
+ ASSERT_EQ(2u, messages.size());
+ ASSERT_EQ(first_message_.routing_id(), messages[0]->routing_id());
+ ASSERT_EQ(second_message_.routing_id(), messages[1]->routing_id());
+}
+
+TEST_F(FrameSwapMessageQueueTest,
+ TestQueueVisualStateMessagePreservesFifoOrdering) {
+ ScopedVector<IPC::Message> messages;
+
+ QueueVisualStateMessage(1, CloneMessage(first_message_));
+ QueueVisualStateMessage(1, CloneMessage(second_message_));
+ DrainMessages(1, &messages);
+ ASSERT_EQ(2u, messages.size());
+ ASSERT_EQ(first_message_.routing_id(), messages[0]->routing_id());
+ ASSERT_EQ(second_message_.routing_id(), messages[1]->routing_id());
+}
+
+void FrameSwapMessageQueueTest::TestDidNotSwap(
+ cc::SwapPromise::DidNotSwapReason reason) {
+ ScopedVector<IPC::Message> messages;
+
+ QueueNextSwapMessage(CloneMessage(first_message_));
+ QueueVisualStateMessage(2, CloneMessage(second_message_));
+ QueueVisualStateMessage(3, CloneMessage(third_message_));
+
+ queue_->DidNotSwap(2, cc::SwapPromise::COMMIT_NO_UPDATE, &messages);
+ ASSERT_EQ(2u, messages.size());
+ ASSERT_TRUE(HasMessageForId(messages, first_message_.routing_id()));
+ ASSERT_TRUE(HasMessageForId(messages, second_message_.routing_id()));
+ messages.clear();
+
+ queue_->DidNotSwap(3, cc::SwapPromise::COMMIT_NO_UPDATE, &messages);
+ ASSERT_EQ(1u, messages.size());
+ ASSERT_TRUE(HasMessageForId(messages, third_message_.routing_id()));
+ messages.clear();
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestDidNotSwapNoUpdate) {
+ TestDidNotSwap(cc::SwapPromise::COMMIT_NO_UPDATE);
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestDidNotSwapSwapFails) {
+ TestDidNotSwap(cc::SwapPromise::SWAP_FAILS);
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestDidNotSwapCommitFails) {
+ ScopedVector<IPC::Message> messages;
+
+ QueueNextSwapMessage(CloneMessage(first_message_));
+ QueueVisualStateMessage(2, CloneMessage(second_message_));
+ QueueVisualStateMessage(3, CloneMessage(third_message_));
+
+ queue_->DidNotSwap(2, cc::SwapPromise::COMMIT_FAILS, &messages);
+ ASSERT_EQ(1u, messages.size());
+ ASSERT_TRUE(HasMessageForId(messages, second_message_.routing_id()));
+ messages.clear();
+
+ queue_->DidNotSwap(3, cc::SwapPromise::COMMIT_FAILS, &messages);
+ ASSERT_EQ(1u, messages.size());
+ ASSERT_TRUE(HasMessageForId(messages, third_message_.routing_id()));
+ messages.clear();
+
+ DrainMessages(1, &messages);
+ ASSERT_EQ(1u, messages.size());
+ ASSERT_TRUE(HasMessageForId(messages, first_message_.routing_id()));
+}
+
+class NotifiesDeletionMessage : public IPC::Message {
+ public:
+ NotifiesDeletionMessage(bool* deleted, const IPC::Message& other)
+ : IPC::Message(other), deleted_(deleted) {}
+ virtual ~NotifiesDeletionMessage() { *deleted_ = true; }
+
+ private:
+ bool* deleted_;
+};
+
+TEST_F(FrameSwapMessageQueueTest, TestDeletesNextSwapMessage) {
+ bool message_deleted = false;
+ QueueNextSwapMessage(make_scoped_ptr(new NotifiesDeletionMessage(
+ &message_deleted, first_message_))
+ .PassAs<IPC::Message>());
+ queue_ = NULL;
+ ASSERT_TRUE(message_deleted);
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestDeletesVisualStateMessage) {
+ bool message_deleted = false;
+ QueueVisualStateMessage(1,
+ make_scoped_ptr(new NotifiesDeletionMessage(
+ &message_deleted, first_message_))
+ .PassAs<IPC::Message>());
+ queue_ = NULL;
+ ASSERT_TRUE(message_deleted);
+}
+
+TEST_F(FrameSwapMessageQueueTest, TestDeletesQueuedVisualStateMessage) {
+ bool message_deleted = false;
+ QueueVisualStateMessage(1,
+ make_scoped_ptr(new NotifiesDeletionMessage(
+ &message_deleted, first_message_))
+ .PassAs<IPC::Message>());
+ queue_->DidSwap(1);
+ queue_ = NULL;
+ ASSERT_TRUE(message_deleted);
+}
+
+} // namespace content
diff --git a/content/renderer/gpu/mailbox_output_surface.cc b/content/renderer/gpu/mailbox_output_surface.cc
index ec5d00b..6389f93 100644
--- a/content/renderer/gpu/mailbox_output_surface.cc
+++ b/content/renderer/gpu/mailbox_output_surface.cc
@@ -9,6 +9,7 @@
#include "cc/output/compositor_frame_ack.h"
#include "cc/output/gl_frame_data.h"
#include "cc/resources/resource_provider.h"
+#include "content/renderer/gpu/frame_swap_message_queue.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
@@ -26,11 +27,13 @@ MailboxOutputSurface::MailboxOutputSurface(
uint32 output_surface_id,
const scoped_refptr<ContextProviderCommandBuffer>& context_provider,
scoped_ptr<cc::SoftwareOutputDevice> software_device,
+ scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue,
cc::ResourceFormat format)
: CompositorOutputSurface(routing_id,
output_surface_id,
context_provider,
software_device.Pass(),
+ swap_frame_message_queue,
true),
fbo_(0),
is_backbuffer_discarded_(false),
diff --git a/content/renderer/gpu/mailbox_output_surface.h b/content/renderer/gpu/mailbox_output_surface.h
index 2a8e278..8ba90b4 100644
--- a/content/renderer/gpu/mailbox_output_surface.h
+++ b/content/renderer/gpu/mailbox_output_surface.h
@@ -7,6 +7,7 @@
#include <queue>
+#include "base/memory/ref_counted.h"
#include "cc/resources/resource_format.h"
#include "cc/resources/transferable_resource.h"
#include "content/renderer/gpu/compositor_output_surface.h"
@@ -17,6 +18,7 @@ class CompositorFrameAck;
}
namespace content {
+class FrameSwapMessageQueue;
// Implementation of CompositorOutputSurface that renders to textures which
// are sent to the browser through the mailbox extension.
@@ -29,6 +31,7 @@ class MailboxOutputSurface : public CompositorOutputSurface {
uint32 output_surface_id,
const scoped_refptr<ContextProviderCommandBuffer>& context_provider,
scoped_ptr<cc::SoftwareOutputDevice> software_device,
+ scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue,
cc::ResourceFormat format);
virtual ~MailboxOutputSurface();
diff --git a/content/renderer/gpu/queue_message_swap_promise.cc b/content/renderer/gpu/queue_message_swap_promise.cc
new file mode 100644
index 0000000..f184277
--- /dev/null
+++ b/content/renderer/gpu/queue_message_swap_promise.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/gpu/queue_message_swap_promise.h"
+
+#include "content/renderer/gpu/frame_swap_message_queue.h"
+#include "ipc/ipc_sync_message_filter.h"
+
+namespace content {
+
+QueueMessageSwapPromise::QueueMessageSwapPromise(
+ scoped_refptr<IPC::SyncMessageFilter> message_sender,
+ scoped_refptr<content::FrameSwapMessageQueue> message_queue,
+ int source_frame_number)
+ : message_sender_(message_sender),
+ message_queue_(message_queue),
+ source_frame_number_(source_frame_number)
+#if DCHECK_IS_ON
+ ,
+ completed_(false)
+#endif
+{
+ DCHECK(message_sender_.get());
+ DCHECK(message_queue_.get());
+}
+
+QueueMessageSwapPromise::~QueueMessageSwapPromise() {
+ // The promise should have either been kept or broken before it's deleted.
+#if DCHECK_IS_ON
+ DCHECK(completed_);
+#endif
+}
+
+void QueueMessageSwapPromise::DidSwap(cc::CompositorFrameMetadata* metadata) {
+#if DCHECK_IS_ON
+ DCHECK(!completed_);
+#endif
+ message_queue_->DidSwap(source_frame_number_);
+ // The OutputSurface will take care of the Drain+Send.
+ PromiseCompleted();
+}
+
+void QueueMessageSwapPromise::DidNotSwap(DidNotSwapReason reason) {
+#if DCHECK_IS_ON
+ DCHECK(!completed_);
+#endif
+ ScopedVector<IPC::Message> messages;
+ message_queue_->DidNotSwap(source_frame_number_, reason, &messages);
+ for (ScopedVector<IPC::Message>::iterator i = messages.begin();
+ i != messages.end();
+ ++i) {
+ message_sender_->Send(*i);
+ }
+ messages.weak_clear();
+ PromiseCompleted();
+}
+
+void QueueMessageSwapPromise::PromiseCompleted() {
+#if DCHECK_IS_ON
+ completed_ = true;
+#endif
+}
+
+} // namespace content
diff --git a/content/renderer/gpu/queue_message_swap_promise.h b/content/renderer/gpu/queue_message_swap_promise.h
new file mode 100644
index 0000000..20a519b
--- /dev/null
+++ b/content/renderer/gpu/queue_message_swap_promise.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_QUEUE_MESSAGE_SWAP_PROMISE_H_
+#define CONTENT_RENDERER_QUEUE_MESSAGE_SWAP_PROMISE_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/swap_promise.h"
+
+namespace IPC {
+class SyncMessageFilter;
+}
+
+namespace content {
+
+class FrameSwapMessageQueue;
+
+class QueueMessageSwapPromise : public cc::SwapPromise {
+ public:
+ QueueMessageSwapPromise(scoped_refptr<IPC::SyncMessageFilter> message_sender,
+ scoped_refptr<FrameSwapMessageQueue> message_queue,
+ int source_frame_number);
+
+ virtual ~QueueMessageSwapPromise();
+
+ virtual void DidSwap(cc::CompositorFrameMetadata* metadata) OVERRIDE;
+
+ virtual void DidNotSwap(DidNotSwapReason reason) OVERRIDE;
+
+ private:
+ void PromiseCompleted();
+
+ scoped_refptr<IPC::SyncMessageFilter> message_sender_;
+ scoped_refptr<content::FrameSwapMessageQueue> message_queue_;
+ int source_frame_number_;
+#if DCHECK_IS_ON
+ bool completed_;
+#endif
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_QUEUE_MESSAGE_SWAP_PROMISE_H_
diff --git a/content/renderer/gpu/queue_message_swap_promise_unittest.cc b/content/renderer/gpu/queue_message_swap_promise_unittest.cc
new file mode 100644
index 0000000..809f5c7
--- /dev/null
+++ b/content/renderer/gpu/queue_message_swap_promise_unittest.cc
@@ -0,0 +1,322 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/gpu/queue_message_swap_promise.h"
+
+#include <vector>
+
+#include "base/memory/scoped_vector.h"
+#include "cc/base/swap_promise.h"
+#include "content/renderer/gpu/frame_swap_message_queue.h"
+#include "content/renderer/gpu/render_widget_compositor.h"
+#include "content/renderer/render_widget.h"
+#include "content/test/mock_render_process.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_sync_message_filter.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class TestRenderWidget : public RenderWidget {
+ public:
+ using RenderWidget::QueueMessageImpl;
+
+ private:
+ virtual ~TestRenderWidget() {}
+
+ DISALLOW_COPY_AND_ASSIGN(TestRenderWidget);
+};
+
+class TestSyncMessageFilter : public IPC::SyncMessageFilter {
+ public:
+ TestSyncMessageFilter() : IPC::SyncMessageFilter(NULL) {}
+
+ virtual bool Send(IPC::Message* message) OVERRIDE {
+ messages_.push_back(message);
+ return true;
+ }
+
+ ScopedVector<IPC::Message>& messages() { return messages_; }
+
+ private:
+ virtual ~TestSyncMessageFilter() {}
+
+ ScopedVector<IPC::Message> messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestSyncMessageFilter);
+};
+
+struct QueueMessageData {
+ MessageDeliveryPolicy policy;
+ bool commit_requested;
+ int source_frame_number;
+};
+
+class QueueMessageSwapPromiseTest : public testing::Test {
+ public:
+ QueueMessageSwapPromiseTest()
+ : frame_swap_message_queue_(new FrameSwapMessageQueue()),
+ sync_message_filter_(new TestSyncMessageFilter()) {}
+
+ virtual ~QueueMessageSwapPromiseTest() {}
+
+ scoped_ptr<cc::SwapPromise> QueueMessageImpl(IPC::Message* msg,
+ MessageDeliveryPolicy policy,
+ bool commit_requested,
+ int source_frame_number) {
+ return TestRenderWidget::QueueMessageImpl(msg,
+ policy,
+ frame_swap_message_queue_,
+ sync_message_filter_,
+ commit_requested,
+ source_frame_number).Pass();
+ }
+
+ ScopedVector<IPC::Message>& DirectSendMessages() {
+ return sync_message_filter_->messages();
+ }
+
+ ScopedVector<IPC::Message>& NextSwapMessages() {
+ next_swap_messages_.clear();
+ scoped_ptr<FrameSwapMessageQueue::SendMessageScope> send_message_scope =
+ frame_swap_message_queue_->AcquireSendMessageScope();
+ frame_swap_message_queue_->DrainMessages(&next_swap_messages_);
+ return next_swap_messages_;
+ }
+
+ bool ContainsMessage(const ScopedVector<IPC::Message>& messages,
+ const IPC::Message& message) {
+ if (messages.empty())
+ return false;
+ for (ScopedVector<IPC::Message>::const_iterator i = messages.begin();
+ i != messages.end();
+ ++i) {
+ if ((*i)->type() == message.type())
+ return true;
+ }
+ return false;
+ }
+
+ bool NextSwapHasMessage(const IPC::Message& message) {
+ return ContainsMessage(NextSwapMessages(), message);
+ }
+
+ void QueueMessages(QueueMessageData data[], size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ messages_.push_back(
+ IPC::Message(0, i + 1, IPC::Message::PRIORITY_NORMAL));
+ promises_.push_back(
+ QueueMessageImpl(new IPC::Message(messages_[i]),
+ data[i].policy,
+ data[i].commit_requested,
+ data[i].source_frame_number).release());
+ }
+ }
+
+ void CleanupPromises() {
+ for (ScopedVector<cc::SwapPromise>::iterator i = promises_.begin();
+ i != promises_.end();
+ ++i) {
+ if (*i)
+ (*i)->DidSwap(NULL);
+ }
+ }
+
+ protected:
+ void VisualStateSwapPromiseDidNotSwap(
+ cc::SwapPromise::DidNotSwapReason reason);
+
+ scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue_;
+ scoped_refptr<TestSyncMessageFilter> sync_message_filter_;
+ std::vector<IPC::Message> messages_;
+ ScopedVector<cc::SwapPromise> promises_;
+
+ private:
+ ScopedVector<IPC::Message> next_swap_messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(QueueMessageSwapPromiseTest);
+};
+
+TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySchedulesMessageForNextSwap) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, false, 1},
+ };
+ QueueMessages(data, arraysize(data));
+
+ ASSERT_TRUE(promises_[0]);
+ EXPECT_TRUE(DirectSendMessages().empty());
+ EXPECT_FALSE(frame_swap_message_queue_->Empty());
+ EXPECT_TRUE(NextSwapHasMessage(messages_[0]));
+
+ CleanupPromises();
+}
+
+TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicyNeedsAtMostOnePromise) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, false, 1},
+ {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, false, 1},
+ };
+ QueueMessages(data, arraysize(data));
+
+ ASSERT_TRUE(promises_[0]);
+ ASSERT_FALSE(promises_[1]);
+
+ CleanupPromises();
+}
+
+TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySendsMessageOnNoUpdate) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, false, 1},
+ };
+ QueueMessages(data, arraysize(data));
+
+ promises_[0]->DidNotSwap(cc::SwapPromise::COMMIT_NO_UPDATE);
+ EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0]));
+ EXPECT_TRUE(NextSwapMessages().empty());
+ EXPECT_TRUE(frame_swap_message_queue_->Empty());
+}
+
+TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySendsMessageOnSwapFails) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, false, 1},
+ };
+ QueueMessages(data, arraysize(data));
+
+ promises_[0]->DidNotSwap(cc::SwapPromise::SWAP_FAILS);
+ EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0]));
+ EXPECT_TRUE(NextSwapMessages().empty());
+ EXPECT_TRUE(frame_swap_message_queue_->Empty());
+}
+
+TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicyRetainsMessageOnCommitFails) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, false, 1},
+ };
+ QueueMessages(data, arraysize(data));
+
+ promises_[0]->DidNotSwap(cc::SwapPromise::COMMIT_FAILS);
+ EXPECT_TRUE(DirectSendMessages().empty());
+ EXPECT_FALSE(frame_swap_message_queue_->Empty());
+ EXPECT_TRUE(NextSwapHasMessage(messages_[0]));
+}
+
+TEST_F(QueueMessageSwapPromiseTest, VisualStateDirectSend) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, false, 1},
+ };
+ QueueMessages(data, arraysize(data));
+
+ ASSERT_FALSE(promises_[0]);
+ EXPECT_FALSE(DirectSendMessages().empty());
+ EXPECT_TRUE(frame_swap_message_queue_->Empty());
+ EXPECT_TRUE(NextSwapMessages().empty());
+}
+
+TEST_F(QueueMessageSwapPromiseTest,
+ VisualStateQueuesMessageWhenCommitRequested) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, true, 1},
+ };
+ QueueMessages(data, arraysize(data));
+
+ ASSERT_TRUE(promises_[0]);
+ EXPECT_TRUE(DirectSendMessages().empty());
+ EXPECT_FALSE(frame_swap_message_queue_->Empty());
+ EXPECT_TRUE(NextSwapMessages().empty());
+
+ CleanupPromises();
+}
+
+TEST_F(QueueMessageSwapPromiseTest,
+ VisualStateQueuesMessageWhenOtherMessageAlreadyQueued) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, true, 1},
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, true, 1},
+ };
+ QueueMessages(data, arraysize(data));
+
+ EXPECT_TRUE(DirectSendMessages().empty());
+ EXPECT_FALSE(frame_swap_message_queue_->Empty());
+ EXPECT_FALSE(NextSwapHasMessage(messages_[1]));
+
+ CleanupPromises();
+}
+
+TEST_F(QueueMessageSwapPromiseTest, VisualStateSwapPromiseDidSwap) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, true, 1},
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, false, 1},
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, false, 2},
+ };
+ QueueMessages(data, arraysize(data));
+
+ promises_[0]->DidSwap(NULL);
+ ASSERT_FALSE(promises_[1]);
+ ScopedVector<IPC::Message> messages;
+ messages.swap(NextSwapMessages());
+ EXPECT_EQ(2u, messages.size());
+ EXPECT_TRUE(ContainsMessage(messages, messages_[0]));
+ EXPECT_TRUE(ContainsMessage(messages, messages_[1]));
+ EXPECT_FALSE(ContainsMessage(messages, messages_[2]));
+
+ promises_[2]->DidSwap(NULL);
+ messages.swap(NextSwapMessages());
+ EXPECT_EQ(1u, messages.size());
+ EXPECT_TRUE(ContainsMessage(messages, messages_[2]));
+
+ EXPECT_TRUE(DirectSendMessages().empty());
+ EXPECT_TRUE(NextSwapMessages().empty());
+ EXPECT_TRUE(frame_swap_message_queue_->Empty());
+}
+
+void QueueMessageSwapPromiseTest::VisualStateSwapPromiseDidNotSwap(
+ cc::SwapPromise::DidNotSwapReason reason) {
+ QueueMessageData data[] = {
+ /* { policy, commit_requested, source_frame_number } */
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, true, 1},
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, false, 1},
+ {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, false, 2},
+ };
+ QueueMessages(data, arraysize(data));
+
+ promises_[0]->DidNotSwap(reason);
+ ASSERT_FALSE(promises_[1]);
+ EXPECT_TRUE(NextSwapMessages().empty());
+ EXPECT_EQ(2u, DirectSendMessages().size());
+ EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0]));
+ EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[1]));
+ EXPECT_FALSE(ContainsMessage(DirectSendMessages(), messages_[2]));
+
+ promises_[2]->DidNotSwap(reason);
+ EXPECT_TRUE(NextSwapMessages().empty());
+ EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[2]));
+
+ EXPECT_TRUE(NextSwapMessages().empty());
+ EXPECT_TRUE(frame_swap_message_queue_->Empty());
+}
+
+TEST_F(QueueMessageSwapPromiseTest, VisalStateSwapPromiseDidNotSwapNoUpdate) {
+ VisualStateSwapPromiseDidNotSwap(cc::SwapPromise::COMMIT_NO_UPDATE);
+}
+
+TEST_F(QueueMessageSwapPromiseTest,
+ VisalStateSwapPromiseDidNotSwapCommitFails) {
+ VisualStateSwapPromiseDidNotSwap(cc::SwapPromise::COMMIT_FAILS);
+}
+
+TEST_F(QueueMessageSwapPromiseTest, VisalStateSwapPromiseDidNotSwapSwapFails) {
+ VisualStateSwapPromiseDidNotSwap(cc::SwapPromise::SWAP_FAILS);
+}
+
+} // namespace content
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 64789d2..ca32fe3 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -16,6 +16,7 @@
#include "base/values.h"
#include "cc/base/latency_info_swap_promise.h"
#include "cc/base/latency_info_swap_promise_monitor.h"
+#include "cc/base/swap_promise.h"
#include "cc/base/switches.h"
#include "cc/debug/layer_tree_debug_state.h"
#include "cc/debug/micro_benchmark.h"
@@ -492,6 +493,14 @@ int RenderWidgetCompositor::GetLayerTreeId() const {
return layer_tree_host_->id();
}
+int RenderWidgetCompositor::GetSourceFrameNumber() const {
+ return layer_tree_host_->source_frame_number();
+}
+
+void RenderWidgetCompositor::SetNeedsCommit() {
+ layer_tree_host_->SetNeedsCommit();
+}
+
void RenderWidgetCompositor::NotifyInputThrottledUntilCommit() {
layer_tree_host_->NotifyInputThrottledUntilCommit();
}
diff --git a/content/renderer/gpu/render_widget_compositor.h b/content/renderer/gpu/render_widget_compositor.h
index 1328c8d..e0541ec 100644
--- a/content/renderer/gpu/render_widget_compositor.h
+++ b/content/renderer/gpu/render_widget_compositor.h
@@ -68,6 +68,8 @@ class RenderWidgetCompositor : public blink::WebLayerTreeView,
// LayerTreeHost.
void QueueSwapPromise(scoped_ptr<cc::SwapPromise> swap_promise);
int GetLayerTreeId() const;
+ int GetSourceFrameNumber() const;
+ void SetNeedsCommit();
void NotifyInputThrottledUntilCommit();
const cc::Layer* GetRootLayer() const;
int ScheduleMicroBenchmark(