Index: content/common/gpu/gpu_channel.cc |
=================================================================== |
--- content/common/gpu/gpu_channel.cc (revision 92876) |
+++ content/common/gpu/gpu_channel.cc (working copy) |
@@ -33,7 +33,8 @@ |
renderer_process_(base::kNullProcessHandle), |
renderer_pid_(base::kNullProcessId), |
share_group_(new gfx::GLShareGroup), |
- watchdog_(watchdog) { |
+ watchdog_(watchdog), |
+ task_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
DCHECK(gpu_channel_manager); |
DCHECK(renderer_id); |
const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
@@ -86,9 +87,20 @@ |
<< " with type " << message.type(); |
} |
+ // Control messages are not deferred and can be handled out of order with |
+ // respect to routed ones. |
if (message.routing_id() == MSG_ROUTING_CONTROL) |
return OnControlMessageReceived(message); |
+ // If the channel is unscheduled, defer sync and async messages until it is |
+ // rescheduled. Also, even if the channel is scheduled, do not allow newly |
+ // received messages to be handled before previously received deferred ones; |
+ // append them to the deferred queue as well. |
+ if (!IsScheduled() || !deferred_messages_.empty()) { |
+ deferred_messages_.push(new IPC::Message(message)); |
+ return true; |
+ } |
+ |
if (!router_.RouteMessage(message)) { |
// Respond to sync messages even if router failed to route. |
if (message.is_sync()) { |
@@ -99,6 +111,20 @@ |
return false; |
} |
+ // If the channel becomes unscheduled as a result of handling the message, |
+ // synthesize an IPC message to flush the command buffer that became |
+ // unscheduled. |
+ for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_); |
+ !it.IsAtEnd(); |
+ it.Advance()) { |
+ GpuCommandBufferStub* stub = it.GetCurrentValue(); |
+ if (!stub->IsScheduled()) { |
+ DCHECK(deferred_messages_.empty()); |
+ deferred_messages_.push(new GpuCommandBufferMsg_Rescheduled( |
+ stub->route_id())); |
+ } |
+ } |
+ |
return true; |
} |
@@ -127,6 +153,30 @@ |
return channel_->Send(message); |
} |
+bool GpuChannel::IsScheduled() { |
+ for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_); |
+ !it.IsAtEnd(); |
+ it.Advance()) { |
+ GpuCommandBufferStub* stub = it.GetCurrentValue(); |
+ if (!stub->IsScheduled()) |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+void GpuChannel::OnScheduled() { |
+ // Post a task to handle any deferred messages. The deferred message queue is |
+ // not emptied here, which ensures that OnMessageReceived will continue to |
+ // defer newly received messages until the ones in the queue have all been |
+ // handled by HandleDeferredMessages. HandleDeferredMessages is invoked as a |
+ // task to prevent reentrancy. |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ task_factory_.NewRunnableMethod( |
+ &GpuChannel::HandleDeferredMessages)); |
+} |
+ |
void GpuChannel::LoseAllContexts() { |
gpu_channel_manager_->LoseAllContexts(); |
} |
@@ -218,7 +268,7 @@ |
// that matches the given render_view_id and delete the route. |
for (StubMap::const_iterator iter(&stubs_); !iter.IsAtEnd(); iter.Advance()) { |
if (iter.GetCurrentValue()->render_view_id() == render_view_id) { |
- OnDestroyCommandBuffer(iter.GetCurrentKey()); |
+ OnDestroyCommandBuffer(iter.GetCurrentKey(), NULL); |
return; |
} |
} |
@@ -226,15 +276,17 @@ |
#endif |
bool GpuChannel::OnControlMessageReceived(const IPC::Message& msg) { |
+ // Always use IPC_MESSAGE_HANDLER_DELAY_REPLY for synchronous message handlers |
+ // here. This is so the reply can be delayed if the scheduler is unscheduled. |
bool handled = true; |
IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg) |
IPC_MESSAGE_HANDLER(GpuChannelMsg_Initialize, OnInitialize) |
- IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenCommandBuffer, |
- OnCreateOffscreenCommandBuffer) |
- IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer, |
- OnDestroyCommandBuffer) |
- IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenSurface, |
- OnCreateOffscreenSurface) |
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuChannelMsg_CreateOffscreenCommandBuffer, |
+ OnCreateOffscreenCommandBuffer) |
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuChannelMsg_DestroyCommandBuffer, |
+ OnDestroyCommandBuffer) |
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuChannelMsg_CreateOffscreenSurface, |
+ OnCreateOffscreenSurface) |
IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroySurface, OnDestroySurface) |
IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateTransportTexture, |
OnCreateTransportTexture) |
@@ -244,6 +296,21 @@ |
return handled; |
} |
+void GpuChannel::HandleDeferredMessages() { |
+ // Empty the deferred queue so OnMessageRecieved does not defer on that |
+ // account and to prevent an infinite loop if the scheduler is unscheduled |
+ // as a result of handling already deferred messages. |
+ std::queue<IPC::Message*> deferred_messages_copy; |
+ std::swap(deferred_messages_copy, deferred_messages_); |
+ |
+ while (!deferred_messages_copy.empty()) { |
+ scoped_ptr<IPC::Message> message(deferred_messages_copy.front()); |
+ deferred_messages_copy.pop(); |
+ |
+ OnMessageReceived(*message); |
+ } |
+} |
+ |
int GpuChannel::GenerateRouteID() { |
static int last_id = 0; |
return ++last_id; |
@@ -261,10 +328,12 @@ |
void GpuChannel::OnCreateOffscreenCommandBuffer( |
const gfx::Size& size, |
const GPUCreateCommandBufferConfig& init_params, |
- int32* route_id) { |
+ IPC::Message* reply_message) { |
+ int32 route_id = MSG_ROUTING_NONE; |
+ |
content::GetContentClient()->SetActiveURL(init_params.active_url); |
#if defined(ENABLE_GPU) |
- *route_id = GenerateRouteID(); |
+ route_id = GenerateRouteID(); |
scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub( |
this, |
@@ -273,36 +342,38 @@ |
disallowed_extensions_, |
init_params.allowed_extensions, |
init_params.attribs, |
- *route_id, |
+ route_id, |
0, 0, watchdog_)); |
- router_.AddRoute(*route_id, stub.get()); |
- stubs_.AddWithID(stub.release(), *route_id); |
+ router_.AddRoute(route_id, stub.get()); |
+ stubs_.AddWithID(stub.release(), route_id); |
TRACE_EVENT1("gpu", "GpuChannel::OnCreateOffscreenCommandBuffer", |
"route_id", route_id); |
-#else |
- *route_id = MSG_ROUTING_NONE; |
#endif |
+ |
+ GpuChannelMsg_CreateOffscreenCommandBuffer::WriteReplyParams( |
+ reply_message, |
+ route_id); |
+ Send(reply_message); |
} |
-void GpuChannel::OnDestroyCommandBuffer(int32 route_id) { |
+void GpuChannel::OnDestroyCommandBuffer(int32 route_id, |
+ IPC::Message* reply_message) { |
#if defined(ENABLE_GPU) |
TRACE_EVENT1("gpu", "GpuChannel::OnDestroyCommandBuffer", |
"route_id", route_id); |
if (router_.ResolveRoute(route_id)) { |
- GpuCommandBufferStub* stub = stubs_.Lookup(route_id); |
- // In case the renderer is currently blocked waiting for a sync reply from |
- // the stub, allow the stub to clean up and unblock pending messages here: |
- if (stub != NULL) |
- stub->CommandBufferWasDestroyed(); |
router_.RemoveRoute(route_id); |
stubs_.Remove(route_id); |
} |
#endif |
+ |
+ if (reply_message) |
+ Send(reply_message); |
} |
void GpuChannel::OnCreateOffscreenSurface(const gfx::Size& size, |
- int* route_id) { |
- *route_id = MSG_ROUTING_NONE; |
+ IPC::Message* reply_message) { |
+ int route_id = MSG_ROUTING_NONE; |
#if defined(ENABLE_GPU) |
scoped_refptr<gfx::GLSurface> surface( |
@@ -310,15 +381,19 @@ |
if (!surface.get()) |
return; |
- *route_id = GenerateRouteID(); |
+ route_id = GenerateRouteID(); |
scoped_ptr<GpuSurfaceStub> stub (new GpuSurfaceStub(this, |
- *route_id, |
+ route_id, |
surface.release())); |
- router_.AddRoute(*route_id, stub.get()); |
- surfaces_.AddWithID(stub.release(), *route_id); |
+ router_.AddRoute(route_id, stub.get()); |
+ surfaces_.AddWithID(stub.release(), route_id); |
#endif |
+ |
+ GpuChannelMsg_CreateOffscreenSurface::WriteReplyParams(reply_message, |
+ route_id); |
+ Send(reply_message); |
} |
void GpuChannel::OnDestroySurface(int route_id) { |