diff options
author | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-22 17:05:11 +0000 |
---|---|---|
committer | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-22 17:05:11 +0000 |
commit | 586871b039414476e9127d888a1b6ca3a02d3722 (patch) | |
tree | 48656558bb2ebe925a24400662535386839022eb /content/renderer/gpu | |
parent | 9788b787b14b333dc93d6d13d4c881046a58db63 (diff) | |
download | chromium_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.cc | 21 | ||||
-rw-r--r-- | content/renderer/gpu/compositor_output_surface.h | 3 | ||||
-rw-r--r-- | content/renderer/gpu/delegated_compositor_output_surface.cc | 5 | ||||
-rw-r--r-- | content/renderer/gpu/delegated_compositor_output_surface.h | 5 | ||||
-rw-r--r-- | content/renderer/gpu/frame_swap_message_queue.cc | 202 | ||||
-rw-r--r-- | content/renderer/gpu/frame_swap_message_queue.h | 108 | ||||
-rw-r--r-- | content/renderer/gpu/frame_swap_message_queue_unittest.cc | 294 | ||||
-rw-r--r-- | content/renderer/gpu/mailbox_output_surface.cc | 3 | ||||
-rw-r--r-- | content/renderer/gpu/mailbox_output_surface.h | 3 | ||||
-rw-r--r-- | content/renderer/gpu/queue_message_swap_promise.cc | 65 | ||||
-rw-r--r-- | content/renderer/gpu/queue_message_swap_promise.h | 45 | ||||
-rw-r--r-- | content/renderer/gpu/queue_message_swap_promise_unittest.cc | 322 | ||||
-rw-r--r-- | content/renderer/gpu/render_widget_compositor.cc | 9 | ||||
-rw-r--r-- | content/renderer/gpu/render_widget_compositor.h | 2 |
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( |