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