OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/gpu/gpu_channel_host.h" | 5 #include "content/renderer/gpu/gpu_channel_host.h" |
6 | 6 |
| 7 #include "base/message_loop_proxy.h" |
7 #include "content/common/child_process.h" | 8 #include "content/common/child_process.h" |
8 #include "content/common/gpu/gpu_messages.h" | 9 #include "content/common/gpu/gpu_messages.h" |
9 #include "content/renderer/gpu/command_buffer_proxy.h" | 10 #include "content/renderer/gpu/command_buffer_proxy.h" |
10 #include "content/renderer/gpu/gpu_surface_proxy.h" | 11 #include "content/renderer/gpu/gpu_surface_proxy.h" |
11 #include "content/renderer/gpu/transport_texture_service.h" | 12 #include "content/renderer/gpu/transport_texture_service.h" |
| 13 #include "content/renderer/render_process.h" |
12 #include "content/renderer/render_thread.h" | 14 #include "content/renderer/render_thread.h" |
13 #include "googleurl/src/gurl.h" | 15 #include "googleurl/src/gurl.h" |
| 16 #include "ipc/ipc_sync_message_filter.h" |
| 17 |
| 18 using base::AutoLock; |
| 19 using base::MessageLoopProxy; |
| 20 |
| 21 GpuChannelHost::Listener::Listener( |
| 22 base::WeakPtr<IPC::Channel::Listener> listener, |
| 23 scoped_refptr<base::MessageLoopProxy> loop) |
| 24 : listener_(listener), |
| 25 loop_(loop) { |
| 26 |
| 27 } |
| 28 |
| 29 GpuChannelHost::Listener::~Listener() { |
| 30 |
| 31 } |
| 32 |
| 33 void GpuChannelHost::Listener::DispatchMessage(const IPC::Message& msg) { |
| 34 if (listener_.get()) |
| 35 listener_->OnMessageReceived(msg); |
| 36 } |
| 37 |
| 38 void GpuChannelHost::Listener::DispatchError() { |
| 39 if (listener_.get()) |
| 40 listener_->OnChannelError(); |
| 41 } |
| 42 |
| 43 GpuChannelHost::MessageFilter::MessageFilter(GpuChannelHost* parent) |
| 44 : parent_(parent) { |
| 45 DetachFromThread(); |
| 46 } |
| 47 |
| 48 GpuChannelHost::MessageFilter::~MessageFilter() { |
| 49 |
| 50 } |
| 51 |
| 52 void GpuChannelHost::MessageFilter::AddRoute( |
| 53 int route_id, |
| 54 base::WeakPtr<IPC::Channel::Listener> listener, |
| 55 scoped_refptr<MessageLoopProxy> loop) { |
| 56 DCHECK(CalledOnValidThread()); |
| 57 DCHECK(listeners_.find(route_id) == listeners_.end()); |
| 58 listeners_[route_id] = new GpuChannelHost::Listener(listener, loop); |
| 59 } |
| 60 |
| 61 void GpuChannelHost::MessageFilter::RemoveRoute(int route_id) { |
| 62 DCHECK(CalledOnValidThread()); |
| 63 ListenerMap::iterator it = listeners_.find(route_id); |
| 64 if (it != listeners_.end()) |
| 65 listeners_.erase(it); |
| 66 } |
| 67 |
| 68 bool GpuChannelHost::MessageFilter::OnMessageReceived( |
| 69 const IPC::Message& message) { |
| 70 DCHECK(CalledOnValidThread()); |
| 71 |
| 72 // Never handle sync message replies or we will deadlock here. |
| 73 if (message.is_reply()) |
| 74 return false; |
| 75 |
| 76 DCHECK(message.routing_id() != MSG_ROUTING_CONTROL); |
| 77 |
| 78 ListenerMap::iterator it = listeners_.find(message.routing_id()); |
| 79 |
| 80 if (it != listeners_.end()) { |
| 81 const scoped_refptr<GpuChannelHost::Listener>& listener = it->second; |
| 82 listener->loop()->PostTask( |
| 83 FROM_HERE, |
| 84 NewRunnableMethod( |
| 85 listener.get(), |
| 86 &GpuChannelHost::Listener::DispatchMessage, |
| 87 message)); |
| 88 } |
| 89 |
| 90 return true; |
| 91 } |
| 92 |
| 93 void GpuChannelHost::MessageFilter::OnChannelError() { |
| 94 DCHECK(CalledOnValidThread()); |
| 95 |
| 96 // Inform all the proxies that an error has occurred. This will be reported |
| 97 // via OpenGL as a lost context. |
| 98 for (ListenerMap::iterator it = listeners_.begin(); |
| 99 it != listeners_.end(); |
| 100 it++) { |
| 101 const scoped_refptr<GpuChannelHost::Listener>& listener = it->second; |
| 102 listener->loop()->PostTask( |
| 103 FROM_HERE, |
| 104 NewRunnableMethod( |
| 105 listener.get(), |
| 106 &GpuChannelHost::Listener::DispatchError)); |
| 107 } |
| 108 |
| 109 listeners_.clear(); |
| 110 |
| 111 ChildThread* main_thread = RenderProcess::current()->main_thread(); |
| 112 MessageLoop* main_loop = main_thread->message_loop(); |
| 113 main_loop->PostTask(FROM_HERE, |
| 114 NewRunnableMethod(parent_, |
| 115 &GpuChannelHost::OnChannelError)); |
| 116 } |
14 | 117 |
15 GpuChannelHost::GpuChannelHost() | 118 GpuChannelHost::GpuChannelHost() |
16 : state_(kUnconnected), | 119 : state_(kUnconnected), |
17 transport_texture_service_(new TransportTextureService()) { | 120 transport_texture_service_(new TransportTextureService()) { |
18 } | 121 } |
19 | 122 |
20 GpuChannelHost::~GpuChannelHost() { | 123 GpuChannelHost::~GpuChannelHost() { |
21 } | 124 } |
22 | 125 |
23 void GpuChannelHost::Connect( | 126 void GpuChannelHost::Connect( |
24 const IPC::ChannelHandle& channel_handle, | 127 const IPC::ChannelHandle& channel_handle, |
25 base::ProcessHandle renderer_process_for_gpu) { | 128 base::ProcessHandle renderer_process_for_gpu) { |
26 // Open a channel to the GPU process. | 129 DCHECK(RenderThread::current()); |
| 130 // Open a channel to the GPU process. We pass NULL as the main listener here |
| 131 // since we need to filter everything to route it to the right thread. |
27 channel_.reset(new IPC::SyncChannel( | 132 channel_.reset(new IPC::SyncChannel( |
28 channel_handle, IPC::Channel::MODE_CLIENT, this, | 133 channel_handle, IPC::Channel::MODE_CLIENT, NULL, |
29 ChildProcess::current()->io_message_loop_proxy(), true, | 134 ChildProcess::current()->io_message_loop_proxy(), true, |
30 ChildProcess::current()->GetShutDownEvent())); | 135 ChildProcess::current()->GetShutDownEvent())); |
31 | 136 |
| 137 sync_filter_ = new IPC::SyncMessageFilter( |
| 138 ChildProcess::current()->GetShutDownEvent()); |
| 139 |
| 140 channel_->AddFilter(sync_filter_.get()); |
| 141 |
| 142 channel_->AddFilter(transport_texture_service_.get()); |
| 143 |
| 144 channel_filter_ = new MessageFilter(this); |
| 145 |
| 146 // Install the filter last, because we intercept all leftover |
| 147 // messages. |
| 148 channel_->AddFilter(channel_filter_.get()); |
| 149 |
32 // It is safe to send IPC messages before the channel completes the connection | 150 // It is safe to send IPC messages before the channel completes the connection |
33 // and receives the hello message from the GPU process. The messages get | 151 // and receives the hello message from the GPU process. The messages get |
34 // cached. | 152 // cached. |
35 state_ = kConnected; | 153 state_ = kConnected; |
36 | 154 |
37 // Notify the GPU process of our process handle. This gives it the ability | 155 // Notify the GPU process of our process handle. This gives it the ability |
38 // to map renderer handles into the GPU process. | 156 // to map renderer handles into the GPU process. |
39 Send(new GpuChannelMsg_Initialize(renderer_process_for_gpu)); | 157 Send(new GpuChannelMsg_Initialize(renderer_process_for_gpu)); |
40 } | 158 } |
41 | 159 |
42 void GpuChannelHost::set_gpu_info(const GPUInfo& gpu_info) { | 160 void GpuChannelHost::set_gpu_info(const GPUInfo& gpu_info) { |
43 gpu_info_ = gpu_info; | 161 gpu_info_ = gpu_info; |
44 } | 162 } |
45 | 163 |
46 const GPUInfo& GpuChannelHost::gpu_info() const { | 164 const GPUInfo& GpuChannelHost::gpu_info() const { |
47 return gpu_info_; | 165 return gpu_info_; |
48 } | 166 } |
49 | 167 |
50 void GpuChannelHost::SetStateLost() { | 168 void GpuChannelHost::SetStateLost() { |
51 state_ = kLost; | 169 state_ = kLost; |
52 } | 170 } |
53 | 171 |
54 bool GpuChannelHost::OnMessageReceived(const IPC::Message& message) { | |
55 DCHECK(message.routing_id() != MSG_ROUTING_CONTROL); | |
56 | |
57 return router_.RouteMessage(message); | |
58 } | |
59 | |
60 void GpuChannelHost::OnChannelConnected(int32 peer_pid) { | |
61 channel_->AddFilter(transport_texture_service_.get()); | |
62 } | |
63 | |
64 void GpuChannelHost::OnChannelError() { | 172 void GpuChannelHost::OnChannelError() { |
65 state_ = kLost; | 173 state_ = kLost; |
66 | 174 |
67 // Channel is invalid and will be reinitialized if this host is requested | 175 // Channel is invalid and will be reinitialized if this host is requested |
68 // again. | 176 // again. |
69 channel_.reset(); | 177 channel_.reset(); |
70 | |
71 // Inform all the proxies that an error has occured. This will be reported via | |
72 // OpenGL as a lost context. | |
73 for (ProxyMap::iterator iter = proxies_.begin(); | |
74 iter != proxies_.end(); iter++) { | |
75 router_.RemoveRoute(iter->first); | |
76 iter->second->OnChannelError(); | |
77 } | |
78 | |
79 // The proxies are reference counted so this will not result in their | |
80 // destruction if the client still holds a reference. The proxy will report | |
81 // a lost context, indicating to the client that it needs to be recreated. | |
82 proxies_.clear(); | |
83 } | 178 } |
84 | 179 |
85 bool GpuChannelHost::Send(IPC::Message* message) { | 180 bool GpuChannelHost::Send(IPC::Message* message) { |
86 // The GPU process never sends synchronous IPCs so clear the unblock flag to | 181 // The GPU process never sends synchronous IPCs so clear the unblock flag to |
87 // preserve order. | 182 // preserve order. |
88 message->set_unblock(false); | 183 message->set_unblock(false); |
89 if (channel_.get()) | 184 |
90 return channel_->Send(message); | 185 // Unfortunately a sync filter cannot be used on the main (listener) thread. |
| 186 // TODO: Is that true even when we don't install a listener? |
| 187 if (RenderThread::current()) { |
| 188 if (channel_.get()) |
| 189 return channel_->Send(message); |
| 190 } else { |
| 191 return sync_filter_->Send(message); |
| 192 } |
91 | 193 |
92 // Callee takes ownership of message, regardless of whether Send is | 194 // Callee takes ownership of message, regardless of whether Send is |
93 // successful. See IPC::Message::Sender. | 195 // successful. See IPC::Message::Sender. |
94 delete message; | 196 delete message; |
95 return false; | 197 return false; |
96 } | 198 } |
97 | 199 |
98 CommandBufferProxy* GpuChannelHost::CreateViewCommandBuffer( | 200 CommandBufferProxy* GpuChannelHost::CreateViewCommandBuffer( |
99 int render_view_id, | 201 int render_view_id, |
100 CommandBufferProxy* share_group, | 202 CommandBufferProxy* share_group, |
101 const std::string& allowed_extensions, | 203 const std::string& allowed_extensions, |
102 const std::vector<int32>& attribs, | 204 const std::vector<int32>& attribs, |
103 const GURL& active_url) { | 205 const GURL& active_url) { |
104 #if defined(ENABLE_GPU) | 206 #if defined(ENABLE_GPU) |
| 207 AutoLock lock(context_lock_); |
105 // An error occurred. Need to get the host again to reinitialize it. | 208 // An error occurred. Need to get the host again to reinitialize it. |
106 if (!channel_.get()) | 209 if (!channel_.get()) |
107 return NULL; | 210 return NULL; |
108 | 211 |
109 GPUCreateCommandBufferConfig init_params; | 212 GPUCreateCommandBufferConfig init_params; |
110 init_params.share_group_id = | 213 init_params.share_group_id = |
111 share_group ? share_group->route_id() : MSG_ROUTING_NONE; | 214 share_group ? share_group->route_id() : MSG_ROUTING_NONE; |
112 init_params.allowed_extensions = allowed_extensions; | 215 init_params.allowed_extensions = allowed_extensions; |
113 init_params.attribs = attribs; | 216 init_params.attribs = attribs; |
114 init_params.active_url = active_url; | 217 init_params.active_url = active_url; |
115 int32 route_id; | 218 int32 route_id; |
116 if (!RenderThread::current()->Send( | 219 if (!RenderThread::current()->Send( |
117 new GpuHostMsg_CreateViewCommandBuffer( | 220 new GpuHostMsg_CreateViewCommandBuffer( |
118 render_view_id, | 221 render_view_id, |
119 init_params, | 222 init_params, |
120 &route_id))) { | 223 &route_id))) { |
121 return NULL; | 224 return NULL; |
122 } | 225 } |
123 | 226 |
124 if (route_id == MSG_ROUTING_NONE) | 227 if (route_id == MSG_ROUTING_NONE) |
125 return NULL; | 228 return NULL; |
126 | 229 |
127 CommandBufferProxy* command_buffer = new CommandBufferProxy(this, route_id); | 230 CommandBufferProxy* command_buffer = new CommandBufferProxy(this, route_id); |
128 router_.AddRoute(route_id, command_buffer); | 231 AddRoute(route_id, command_buffer->AsWeakPtr()); |
129 proxies_[route_id] = command_buffer; | 232 proxies_[route_id] = command_buffer; |
130 return command_buffer; | 233 return command_buffer; |
131 #else | 234 #else |
132 return NULL; | 235 return NULL; |
133 #endif | 236 #endif |
134 } | 237 } |
135 | 238 |
136 GpuVideoDecodeAcceleratorHost* GpuChannelHost::CreateVideoDecoder( | 239 GpuVideoDecodeAcceleratorHost* GpuChannelHost::CreateVideoDecoder( |
137 int command_buffer_route_id, | 240 int command_buffer_route_id, |
138 const std::vector<int32>& configs, | 241 const std::vector<int32>& configs, |
139 media::VideoDecodeAccelerator::Client* client) { | 242 media::VideoDecodeAccelerator::Client* client) { |
| 243 AutoLock lock(context_lock_); |
140 ProxyMap::iterator it = proxies_.find(command_buffer_route_id); | 244 ProxyMap::iterator it = proxies_.find(command_buffer_route_id); |
141 DCHECK(it != proxies_.end()); | 245 DCHECK(it != proxies_.end()); |
142 CommandBufferProxy* proxy = it->second; | 246 CommandBufferProxy* proxy = it->second; |
143 return proxy->CreateVideoDecoder(configs, client); | 247 return proxy->CreateVideoDecoder(configs, client); |
144 } | 248 } |
145 | 249 |
146 CommandBufferProxy* GpuChannelHost::CreateOffscreenCommandBuffer( | 250 CommandBufferProxy* GpuChannelHost::CreateOffscreenCommandBuffer( |
147 const gfx::Size& size, | 251 const gfx::Size& size, |
148 CommandBufferProxy* share_group, | 252 CommandBufferProxy* share_group, |
149 const std::string& allowed_extensions, | 253 const std::string& allowed_extensions, |
150 const std::vector<int32>& attribs, | 254 const std::vector<int32>& attribs, |
151 const GURL& active_url) { | 255 const GURL& active_url) { |
152 #if defined(ENABLE_GPU) | 256 #if defined(ENABLE_GPU) |
| 257 AutoLock lock(context_lock_); |
153 // An error occurred. Need to get the host again to reinitialize it. | 258 // An error occurred. Need to get the host again to reinitialize it. |
154 if (!channel_.get()) | 259 if (!channel_.get()) |
155 return NULL; | 260 return NULL; |
156 | 261 |
157 GPUCreateCommandBufferConfig init_params; | 262 GPUCreateCommandBufferConfig init_params; |
158 init_params.share_group_id = | 263 init_params.share_group_id = |
159 share_group ? share_group->route_id() : MSG_ROUTING_NONE; | 264 share_group ? share_group->route_id() : MSG_ROUTING_NONE; |
160 init_params.allowed_extensions = allowed_extensions; | 265 init_params.allowed_extensions = allowed_extensions; |
161 init_params.attribs = attribs; | 266 init_params.attribs = attribs; |
162 init_params.active_url = active_url; | 267 init_params.active_url = active_url; |
163 int32 route_id; | 268 int32 route_id; |
164 if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size, | 269 if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size, |
165 init_params, | 270 init_params, |
166 &route_id))) { | 271 &route_id))) { |
167 return NULL; | 272 return NULL; |
168 } | 273 } |
169 | 274 |
170 if (route_id == MSG_ROUTING_NONE) | 275 if (route_id == MSG_ROUTING_NONE) |
171 return NULL; | 276 return NULL; |
172 | 277 |
173 CommandBufferProxy* command_buffer = new CommandBufferProxy(this, route_id); | 278 CommandBufferProxy* command_buffer = new CommandBufferProxy(this, route_id); |
174 router_.AddRoute(route_id, command_buffer); | 279 AddRoute(route_id, command_buffer->AsWeakPtr()); |
175 proxies_[route_id] = command_buffer; | 280 proxies_[route_id] = command_buffer; |
176 return command_buffer; | 281 return command_buffer; |
177 #else | 282 #else |
178 return NULL; | 283 return NULL; |
179 #endif | 284 #endif |
180 } | 285 } |
181 | 286 |
182 void GpuChannelHost::AddRoute(int32 route_id, | |
183 IPC::Channel::Listener* listener) { | |
184 router_.AddRoute(route_id, listener); | |
185 } | |
186 | |
187 void GpuChannelHost::RemoveRoute(int32 route_id) { | |
188 router_.RemoveRoute(route_id); | |
189 } | |
190 | |
191 void GpuChannelHost::DestroyCommandBuffer(CommandBufferProxy* command_buffer) { | 287 void GpuChannelHost::DestroyCommandBuffer(CommandBufferProxy* command_buffer) { |
192 #if defined(ENABLE_GPU) | 288 #if defined(ENABLE_GPU) |
| 289 AutoLock lock(context_lock_); |
193 Send(new GpuChannelMsg_DestroyCommandBuffer(command_buffer->route_id())); | 290 Send(new GpuChannelMsg_DestroyCommandBuffer(command_buffer->route_id())); |
194 | 291 |
195 // Check the proxy has not already been removed after a channel error. | 292 // Check the proxy has not already been removed after a channel error. |
196 int route_id = command_buffer->route_id(); | 293 int route_id = command_buffer->route_id(); |
197 if (proxies_.find(command_buffer->route_id()) != proxies_.end()) { | 294 if (proxies_.find(command_buffer->route_id()) != proxies_.end()) |
198 proxies_.erase(route_id); | 295 proxies_.erase(route_id); |
199 router_.RemoveRoute(route_id); | 296 RemoveRoute(route_id); |
200 } | |
201 | |
202 delete command_buffer; | 297 delete command_buffer; |
203 #endif | 298 #endif |
204 } | 299 } |
205 | 300 |
206 GpuSurfaceProxy* GpuChannelHost::CreateOffscreenSurface(const gfx::Size& size) { | 301 GpuSurfaceProxy* GpuChannelHost::CreateOffscreenSurface( |
| 302 const gfx::Size& size) { |
207 #if defined(ENABLE_GPU) | 303 #if defined(ENABLE_GPU) |
| 304 AutoLock lock(context_lock_); |
208 int route_id; | 305 int route_id; |
209 if (!Send(new GpuChannelMsg_CreateOffscreenSurface(size, &route_id))) | 306 if (!Send(new GpuChannelMsg_CreateOffscreenSurface(size, &route_id))) |
210 return NULL; | 307 return NULL; |
211 | 308 |
212 scoped_ptr<GpuSurfaceProxy> surface(new GpuSurfaceProxy(this, route_id)); | 309 scoped_ptr<GpuSurfaceProxy> surface(new GpuSurfaceProxy(this, route_id)); |
213 router_.AddRoute(route_id, surface.get()); | 310 AddRoute(route_id, surface->AsWeakPtr()); |
214 | 311 |
215 return surface.release(); | 312 return surface.release(); |
216 #endif | 313 #endif |
217 } | 314 } |
218 | 315 |
219 void GpuChannelHost::DestroySurface(GpuSurfaceProxy* surface) { | 316 void GpuChannelHost::DestroySurface(GpuSurfaceProxy* surface) { |
220 #if defined(ENABLE_GPU) | 317 #if defined(ENABLE_GPU) |
| 318 AutoLock lock(context_lock_); |
221 Send(new GpuChannelMsg_DestroySurface(surface->route_id())); | 319 Send(new GpuChannelMsg_DestroySurface(surface->route_id())); |
222 if (router_.ResolveRoute(surface->route_id())) | |
223 router_.RemoveRoute(surface->route_id()); | |
224 | 320 |
| 321 RemoveRoute(surface->route_id()); |
225 delete surface; | 322 delete surface; |
226 #endif | 323 #endif |
227 } | 324 } |
| 325 |
| 326 void GpuChannelHost::AddRoute( |
| 327 int route_id, base::WeakPtr<IPC::Channel::Listener> listener) { |
| 328 DCHECK(MessageLoopProxy::current()); |
| 329 |
| 330 MessageLoopProxy* io_loop = RenderProcess::current()->io_message_loop_proxy(); |
| 331 io_loop->PostTask(FROM_HERE, |
| 332 NewRunnableMethod( |
| 333 channel_filter_.get(), |
| 334 &GpuChannelHost::MessageFilter::AddRoute, |
| 335 route_id, listener, MessageLoopProxy::current())); |
| 336 } |
| 337 |
| 338 void GpuChannelHost::RemoveRoute(int route_id) { |
| 339 MessageLoopProxy* io_loop = RenderProcess::current()->io_message_loop_proxy(); |
| 340 io_loop->PostTask(FROM_HERE, |
| 341 NewRunnableMethod( |
| 342 channel_filter_.get(), |
| 343 &GpuChannelHost::MessageFilter::RemoveRoute, |
| 344 route_id)); |
| 345 } |
OLD | NEW |