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