Index: content/common/gpu/gpu_channel.cc |
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc |
index 64e6a1943216664be4528788aeab5b60ac8dd0d0..364260c817e0fb44f5106476ec0279c4da9c81e7 100644 |
--- a/content/common/gpu/gpu_channel.cc |
+++ b/content/common/gpu/gpu_channel.cc |
@@ -15,6 +15,7 @@ |
#include "base/process_util.h" |
#include "base/rand_util.h" |
#include "base/string_util.h" |
+#include "base/timer.h" |
#include "content/common/child_process.h" |
#include "content/common/gpu/gpu_channel_manager.h" |
#include "content/common/gpu/gpu_messages.h" |
@@ -40,6 +41,87 @@ |
namespace content { |
namespace { |
+ |
+// How long to wait for an IPC on this channel to be processed before |
+// signaling the need for a preemption. |
+// TODO(backer): This should really be the time between vblank on |
+// platforms that provide this information. |
+const int64 kPreemptPeriodMs = 16; |
+ |
+// Generates mailbox names for clients of the GPU process on the IO thread. |
+class MailboxMessageFilter : public IPC::ChannelProxy::MessageFilter { |
+ public: |
+ explicit MailboxMessageFilter(const std::string& private_key) |
+ : channel_(NULL), |
+ hmac_(crypto::HMAC::SHA256) { |
+ bool success = hmac_.Init(base::StringPiece(private_key)); |
+ DCHECK(success); |
+ } |
+ |
+ virtual void OnFilterAdded(IPC::Channel* channel) { |
+ DCHECK(!channel_); |
+ channel_ = channel; |
+ } |
+ |
+ virtual void OnFilterRemoved() { |
+ DCHECK(channel_); |
+ channel_ = NULL; |
+ } |
+ |
+ virtual bool OnMessageReceived(const IPC::Message& message) { |
+ DCHECK(channel_); |
+ |
+ bool handled = true; |
+ IPC_BEGIN_MESSAGE_MAP(MailboxMessageFilter, message) |
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNames, |
+ OnGenerateMailboxNames) |
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNamesAsync, |
+ OnGenerateMailboxNamesAsync) |
+ IPC_MESSAGE_UNHANDLED(handled = false) |
+ IPC_END_MESSAGE_MAP() |
+ |
+ return handled; |
+ } |
+ |
+ bool Send(IPC::Message* message) { |
+ return channel_->Send(message); |
+ } |
+ |
+ private: |
+ ~MailboxMessageFilter() { |
+ } |
+ |
+ // Message handlers. |
+ void OnGenerateMailboxNames(unsigned num, std::vector<std::string>* result) { |
+ TRACE_EVENT1("gpu", "OnGenerateMailboxNames", "num", num); |
+ |
+ result->resize(num); |
+ |
+ for (unsigned i = 0; i < num; ++i) { |
+ char name[GL_MAILBOX_SIZE_CHROMIUM]; |
+ base::RandBytes(name, sizeof(name) / 2); |
+ |
+ bool success = hmac_.Sign( |
+ base::StringPiece(name, sizeof(name) / 2), |
+ reinterpret_cast<unsigned char*>(name) + sizeof(name) / 2, |
+ sizeof(name) / 2); |
+ DCHECK(success); |
+ |
+ (*result)[i].assign(name, sizeof(name)); |
+ } |
+ } |
+ |
+ void OnGenerateMailboxNamesAsync(unsigned num) { |
+ std::vector<std::string> names; |
+ OnGenerateMailboxNames(num, &names); |
+ Send(new GpuChannelMsg_GenerateMailboxNamesReply(names)); |
+ } |
+ |
+ IPC::Channel* channel_; |
+ crypto::HMAC hmac_; |
+}; |
+} // anonymous namespace |
+ |
// This filter does two things: |
// - it counts the number of messages coming in on the channel |
// - it handles the GpuCommandBufferMsg_InsertSyncPoint message on the IO |
@@ -52,12 +134,13 @@ class SyncPointMessageFilter : public IPC::ChannelProxy::MessageFilter { |
SyncPointMessageFilter(base::WeakPtr<GpuChannel>* gpu_channel, |
scoped_refptr<SyncPointManager> sync_point_manager, |
scoped_refptr<base::MessageLoopProxy> message_loop, |
- scoped_refptr<gpu::RefCountedCounter> |
- unprocessed_messages) |
+ scoped_refptr<gpu::PreemptionFlag> processing_stalled, |
+ base::AtomicRefCount* unprocessed_messages) |
: gpu_channel_(gpu_channel), |
channel_(NULL), |
sync_point_manager_(sync_point_manager), |
message_loop_(message_loop), |
+ processing_stalled_(processing_stalled), |
unprocessed_messages_(unprocessed_messages) { |
} |
@@ -73,7 +156,11 @@ class SyncPointMessageFilter : public IPC::ChannelProxy::MessageFilter { |
virtual bool OnMessageReceived(const IPC::Message& message) { |
DCHECK(channel_); |
- unprocessed_messages_->IncCount(); |
+ base::AtomicRefCountInc(unprocessed_messages_.get()); |
+ if (!timer_.IsRunning()) |
+ timer_.Start(FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kPreemptPeriodMs), |
+ this, &SyncPointMessageFilter::TriggerPreemption); |
if (message.type() == GpuCommandBufferMsg_InsertSyncPoint::ID) { |
uint32 sync_point = sync_point_manager_->GenerateSyncPoint(); |
IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); |
@@ -85,18 +172,29 @@ class SyncPointMessageFilter : public IPC::ChannelProxy::MessageFilter { |
sync_point_manager_, |
message.routing_id(), |
sync_point, |
- unprocessed_messages_)); |
+ unprocessed_messages_.get())); |
return true; |
} else if (message.type() == GpuCommandBufferMsg_RetireSyncPoint::ID) { |
// This message should not be sent explicitly by the renderer. |
NOTREACHED(); |
- unprocessed_messages_->DecCount(); |
+ base::AtomicRefCountDec(unprocessed_messages_.get()); |
return true; |
} else { |
return false; |
} |
} |
+ void TriggerPreemption() { |
+ if (!base::AtomicRefCountIsZero(unprocessed_messages_.get())) |
+ processing_stalled_->Set(); |
+ } |
+ |
+ void ReschedulePreemption() { |
+ processing_stalled_->Reset(); |
+ if (timer_.IsRunning()) |
+ timer_.Reset(); |
+ } |
+ |
protected: |
virtual ~SyncPointMessageFilter() { |
message_loop_->PostTask(FROM_HERE, base::Bind( |
@@ -109,7 +207,7 @@ class SyncPointMessageFilter : public IPC::ChannelProxy::MessageFilter { |
scoped_refptr<SyncPointManager> manager, |
int32 routing_id, |
uint32 sync_point, |
- scoped_refptr<gpu::RefCountedCounter> unprocessed_messages_) { |
+ base::AtomicRefCount* unprocessed_messages) { |
// This function must ensure that the sync point will be retired. Normally |
// we'll find the stub based on the routing ID, and associate the sync point |
// with it, but if that fails for any reason (channel or stub already |
@@ -124,7 +222,7 @@ class SyncPointMessageFilter : public IPC::ChannelProxy::MessageFilter { |
gpu_channel->get()->OnMessageReceived(message); |
return; |
} else { |
- unprocessed_messages_->DecCount(); |
+ base::AtomicRefCountDec(unprocessed_messages); |
} |
} |
manager->RetireSyncPoint(sync_point); |
@@ -142,83 +240,11 @@ class SyncPointMessageFilter : public IPC::ChannelProxy::MessageFilter { |
IPC::Channel* channel_; |
scoped_refptr<SyncPointManager> sync_point_manager_; |
scoped_refptr<base::MessageLoopProxy> message_loop_; |
- scoped_refptr<gpu::RefCountedCounter> unprocessed_messages_; |
+ scoped_refptr<gpu::PreemptionFlag> processing_stalled_; |
+ scoped_ptr<base::AtomicRefCount> unprocessed_messages_; |
+ base::OneShotTimer<SyncPointMessageFilter> timer_; |
}; |
-// Generates mailbox names for clients of the GPU process on the IO thread. |
-class MailboxMessageFilter : public IPC::ChannelProxy::MessageFilter { |
- public: |
- explicit MailboxMessageFilter(const std::string& private_key) |
- : channel_(NULL), |
- hmac_(crypto::HMAC::SHA256) { |
- bool success = hmac_.Init(base::StringPiece(private_key)); |
- DCHECK(success); |
- } |
- |
- virtual void OnFilterAdded(IPC::Channel* channel) { |
- DCHECK(!channel_); |
- channel_ = channel; |
- } |
- |
- virtual void OnFilterRemoved() { |
- DCHECK(channel_); |
- channel_ = NULL; |
- } |
- |
- virtual bool OnMessageReceived(const IPC::Message& message) { |
- DCHECK(channel_); |
- |
- bool handled = true; |
- IPC_BEGIN_MESSAGE_MAP(MailboxMessageFilter, message) |
- IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNames, |
- OnGenerateMailboxNames) |
- IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNamesAsync, |
- OnGenerateMailboxNamesAsync) |
- IPC_MESSAGE_UNHANDLED(handled = false) |
- IPC_END_MESSAGE_MAP() |
- |
- return handled; |
- } |
- |
- bool Send(IPC::Message* message) { |
- return channel_->Send(message); |
- } |
- |
- private: |
- ~MailboxMessageFilter() { |
- } |
- |
- // Message handlers. |
- void OnGenerateMailboxNames(unsigned num, std::vector<std::string>* result) { |
- TRACE_EVENT1("gpu", "OnGenerateMailboxNames", "num", num); |
- |
- result->resize(num); |
- |
- for (unsigned i = 0; i < num; ++i) { |
- char name[GL_MAILBOX_SIZE_CHROMIUM]; |
- base::RandBytes(name, sizeof(name) / 2); |
- |
- bool success = hmac_.Sign( |
- base::StringPiece(name, sizeof(name) / 2), |
- reinterpret_cast<unsigned char*>(name) + sizeof(name) / 2, |
- sizeof(name) / 2); |
- DCHECK(success); |
- |
- (*result)[i].assign(name, sizeof(name)); |
- } |
- } |
- |
- void OnGenerateMailboxNamesAsync(unsigned num) { |
- std::vector<std::string> names; |
- OnGenerateMailboxNames(num, &names); |
- Send(new GpuChannelMsg_GenerateMailboxNamesReply(names)); |
- } |
- |
- IPC::Channel* channel_; |
- crypto::HMAC hmac_; |
-}; |
-} // anonymous namespace |
- |
GpuChannel::GpuChannel(GpuChannelManager* gpu_channel_manager, |
GpuWatchdog* watchdog, |
gfx::GLShareGroup* share_group, |
@@ -226,7 +252,8 @@ GpuChannel::GpuChannel(GpuChannelManager* gpu_channel_manager, |
int client_id, |
bool software) |
: gpu_channel_manager_(gpu_channel_manager), |
- unprocessed_messages_(new gpu::RefCountedCounter), |
+ unprocessed_messages_(NULL), |
+ processing_stalled_(new gpu::PreemptionFlag), |
client_id_(client_id), |
share_group_(share_group ? share_group : new gfx::GLShareGroup), |
mailbox_manager_(mailbox ? mailbox : new gpu::gles2::MailboxManager), |
@@ -266,12 +293,15 @@ bool GpuChannel::Init(base::MessageLoopProxy* io_message_loop, |
base::WeakPtr<GpuChannel>* weak_ptr(new base::WeakPtr<GpuChannel>( |
weak_factory_.GetWeakPtr())); |
- scoped_refptr<SyncPointMessageFilter> filter(new SyncPointMessageFilter( |
+ unprocessed_messages_ = new base::AtomicRefCount(0); |
+ filter_ = new SyncPointMessageFilter( |
weak_ptr, |
gpu_channel_manager_->sync_point_manager(), |
base::MessageLoopProxy::current(), |
- unprocessed_messages_)); |
- channel_->AddFilter(filter); |
+ processing_stalled_, |
+ unprocessed_messages_); |
+ io_message_loop_ = io_message_loop; |
+ channel_->AddFilter(filter_); |
channel_->AddFilter( |
new MailboxMessageFilter(mailbox_manager_->private_key())); |
@@ -294,9 +324,6 @@ int GpuChannel::TakeRendererFileDescriptor() { |
#endif // defined(OS_POSIX) |
bool GpuChannel::OnMessageReceived(const IPC::Message& message) { |
- // Drop the count that was incremented on the IO thread because we are |
- // about to process that message. |
- unprocessed_messages_->DecCount(); |
if (log_messages_) { |
DVLOG(1) << "received message @" << &message << " on channel @" << this |
<< " with type " << message.type(); |
@@ -304,9 +331,12 @@ bool GpuChannel::OnMessageReceived(const IPC::Message& message) { |
// Control messages are not deferred and can be handled out of order with |
// respect to routed ones. |
- if (message.routing_id() == MSG_ROUTING_CONTROL) |
+ if (message.routing_id() == MSG_ROUTING_CONTROL) { |
+ MessageProcessed(); |
return OnControlMessageReceived(message); |
+ } |
+ bool message_processed = true; |
if (message.type() == GpuCommandBufferMsg_GetStateFast::ID) { |
if (processed_get_state_fast_) { |
// Require a non-GetStateFast message in between two GetStateFast |
@@ -323,18 +353,21 @@ bool GpuChannel::OnMessageReceived(const IPC::Message& message) { |
} |
deferred_messages_.insert(point, new IPC::Message(message)); |
- unprocessed_messages_->IncCount(); |
+ message_processed = false; |
} else { |
// Move GetStateFast commands to the head of the queue, so the renderer |
// doesn't have to wait any longer than necessary. |
deferred_messages_.push_front(new IPC::Message(message)); |
- unprocessed_messages_->IncCount(); |
+ message_processed = false; |
} |
} else { |
deferred_messages_.push_back(new IPC::Message(message)); |
- unprocessed_messages_->IncCount(); |
+ message_processed = false; |
} |
+ if (message_processed) |
+ MessageProcessed(); |
+ |
OnScheduled(); |
return true; |
@@ -365,7 +398,7 @@ void GpuChannel::RequeueMessage() { |
DCHECK(currently_processing_message_); |
deferred_messages_.push_front( |
new IPC::Message(*currently_processing_message_)); |
- unprocessed_messages_->IncCount(); |
+ base::AtomicRefCountInc(unprocessed_messages_); |
currently_processing_message_ = NULL; |
} |
@@ -416,8 +449,8 @@ void GpuChannel::CreateViewCommandBuffer( |
watchdog_, |
software_, |
init_params.active_url)); |
- if (preempt_by_counter_.get()) |
- stub->SetPreemptByCounter(preempt_by_counter_); |
+ if (preemption_flag_.get()) |
+ stub->SetPreemptByFlag(preemption_flag_); |
router_.AddRoute(*route_id, stub.get()); |
stubs_.AddWithID(stub.release(), *route_id); |
#endif // ENABLE_GPU |
@@ -482,18 +515,18 @@ void GpuChannel::RemoveRoute(int32 route_id) { |
router_.RemoveRoute(route_id); |
} |
-void GpuChannel::SetPreemptByCounter( |
- scoped_refptr<gpu::RefCountedCounter> preempt_by_counter) { |
- preempt_by_counter_ = preempt_by_counter; |
+void GpuChannel::SetPreemptByFlag( |
+ scoped_refptr<gpu::PreemptionFlag> preemption_flag) { |
+ preemption_flag_ = preemption_flag; |
for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_); |
!it.IsAtEnd(); it.Advance()) { |
- it.GetCurrentValue()->SetPreemptByCounter(preempt_by_counter_); |
+ it.GetCurrentValue()->SetPreemptByFlag(preemption_flag_); |
} |
} |
GpuChannel::~GpuChannel() { |
- unprocessed_messages_->Reset(); |
+ processing_stalled_->Reset(); |
} |
void GpuChannel::OnDestroy() { |
@@ -536,16 +569,21 @@ void GpuChannel::HandleMessage() { |
if (m->type() == GpuCommandBufferMsg_Echo::ID) { |
stub->DelayEcho(m); |
deferred_messages_.pop_front(); |
- unprocessed_messages_->DecCount(); |
+ MessageProcessed(); |
if (!deferred_messages_.empty()) |
OnScheduled(); |
} |
return; |
} |
+ if (stub && stub->IsPreempted()) { |
+ OnScheduled(); |
+ return; |
+ } |
+ |
scoped_ptr<IPC::Message> message(m); |
deferred_messages_.pop_front(); |
- unprocessed_messages_->DecCount(); |
+ bool message_processed = true; |
processed_get_state_fast_ = |
(message->type() == GpuCommandBufferMsg_GetStateFast::ID); |
@@ -570,10 +608,12 @@ void GpuChannel::HandleMessage() { |
if (stub->HasUnprocessedCommands()) { |
deferred_messages_.push_front(new GpuCommandBufferMsg_Rescheduled( |
stub->route_id())); |
- unprocessed_messages_->IncCount(); |
+ message_processed = false; |
} |
} |
} |
+ if (message_processed) |
+ MessageProcessed(); |
} |
if (!deferred_messages_.empty()) { |
@@ -609,8 +649,8 @@ void GpuChannel::OnCreateOffscreenCommandBuffer( |
0, watchdog_, |
software_, |
init_params.active_url)); |
- if (preempt_by_counter_.get()) |
- stub->SetPreemptByCounter(preempt_by_counter_); |
+ if (preemption_flag_.get()) |
+ stub->SetPreemptByFlag(preemption_flag_); |
router_.AddRoute(route_id, stub.get()); |
stubs_.AddWithID(stub.release(), route_id); |
TRACE_EVENT1("gpu", "GpuChannel::OnCreateOffscreenCommandBuffer", |
@@ -692,4 +732,11 @@ void GpuChannel::OnCollectRenderingStatsForSurface( |
Send(reply_message); |
} |
+void GpuChannel::MessageProcessed() { |
+ base::AtomicRefCountDec(unprocessed_messages_); |
+ io_message_loop_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&SyncPointMessageFilter::ReschedulePreemption, filter_)); |
+} |
+ |
} // namespace content |