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.
5 #include "content/common/gpu/client/gpu_channel_host.h"
10 #include "base/debug/trace_event.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/posix/eintr_wrapper.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "content/common/gpu/client/command_buffer_proxy_impl.h"
16 #include "content/common/gpu/client/gpu_video_encode_accelerator_host.h"
17 #include "content/common/gpu/gpu_messages.h"
18 #include "gpu/command_buffer/common/mailbox.h"
19 #include "ipc/ipc_sync_message_filter.h"
23 #include "content/public/common/sandbox_init.h"
27 using base::MessageLoopProxy
;
31 GpuListenerInfo::GpuListenerInfo() {}
33 GpuListenerInfo::~GpuListenerInfo() {}
36 scoped_refptr
<GpuChannelHost
> GpuChannelHost::Create(
37 GpuChannelHostFactory
* factory
,
40 const gpu::GPUInfo
& gpu_info
,
41 const IPC::ChannelHandle
& channel_handle
) {
42 DCHECK(factory
->IsMainThread());
43 scoped_refptr
<GpuChannelHost
> host
= new GpuChannelHost(
44 factory
, gpu_host_id
, client_id
, gpu_info
);
45 host
->Connect(channel_handle
);
49 GpuChannelHost::GpuChannelHost(GpuChannelHostFactory
* factory
,
52 const gpu::GPUInfo
& gpu_info
)
54 client_id_(client_id
),
55 gpu_host_id_(gpu_host_id
),
57 next_transfer_buffer_id_
.GetNext();
60 void GpuChannelHost::Connect(const IPC::ChannelHandle
& channel_handle
) {
61 // Open a channel to the GPU process. We pass NULL as the main listener here
62 // since we need to filter everything to route it to the right thread.
63 scoped_refptr
<base::MessageLoopProxy
> io_loop
= factory_
->GetIOLoopProxy();
64 channel_
.reset(new IPC::SyncChannel(channel_handle
,
65 IPC::Channel::MODE_CLIENT
,
69 factory_
->GetShutDownEvent()));
71 sync_filter_
= new IPC::SyncMessageFilter(
72 factory_
->GetShutDownEvent());
74 channel_
->AddFilter(sync_filter_
.get());
76 channel_filter_
= new MessageFilter();
78 // Install the filter last, because we intercept all leftover
80 channel_
->AddFilter(channel_filter_
.get());
83 bool GpuChannelHost::Send(IPC::Message
* msg
) {
84 // Callee takes ownership of message, regardless of whether Send is
85 // successful. See IPC::Sender.
86 scoped_ptr
<IPC::Message
> message(msg
);
87 // The GPU process never sends synchronous IPCs so clear the unblock flag to
89 message
->set_unblock(false);
91 // Currently we need to choose between two different mechanisms for sending.
92 // On the main thread we use the regular channel Send() method, on another
93 // thread we use SyncMessageFilter. We also have to be careful interpreting
94 // IsMainThread() since it might return false during shutdown,
95 // impl we are actually calling from the main thread (discard message then).
97 // TODO: Can we just always use sync_filter_ since we setup the channel
98 // without a main listener?
99 if (factory_
->IsMainThread()) {
100 // http://crbug.com/125264
101 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
102 return channel_
->Send(message
.release());
103 } else if (base::MessageLoop::current()) {
104 return sync_filter_
->Send(message
.release());
110 CommandBufferProxyImpl
* GpuChannelHost::CreateViewCommandBuffer(
112 CommandBufferProxyImpl
* share_group
,
113 const std::vector
<int32
>& attribs
,
114 const GURL
& active_url
,
115 gfx::GpuPreference gpu_preference
) {
117 "GpuChannelHost::CreateViewCommandBuffer",
121 GPUCreateCommandBufferConfig init_params
;
122 init_params
.share_group_id
=
123 share_group
? share_group
->GetRouteID() : MSG_ROUTING_NONE
;
124 init_params
.attribs
= attribs
;
125 init_params
.active_url
= active_url
;
126 init_params
.gpu_preference
= gpu_preference
;
127 int32 route_id
= factory_
->CreateViewCommandBuffer(surface_id
, init_params
);
128 if (route_id
== MSG_ROUTING_NONE
)
131 CommandBufferProxyImpl
* command_buffer
=
132 new CommandBufferProxyImpl(this, route_id
);
133 AddRoute(route_id
, command_buffer
->AsWeakPtr());
135 AutoLock
lock(context_lock_
);
136 proxies_
[route_id
] = command_buffer
;
137 return command_buffer
;
140 CommandBufferProxyImpl
* GpuChannelHost::CreateOffscreenCommandBuffer(
141 const gfx::Size
& size
,
142 CommandBufferProxyImpl
* share_group
,
143 const std::vector
<int32
>& attribs
,
144 const GURL
& active_url
,
145 gfx::GpuPreference gpu_preference
) {
146 TRACE_EVENT0("gpu", "GpuChannelHost::CreateOffscreenCommandBuffer");
148 GPUCreateCommandBufferConfig init_params
;
149 init_params
.share_group_id
=
150 share_group
? share_group
->GetRouteID() : MSG_ROUTING_NONE
;
151 init_params
.attribs
= attribs
;
152 init_params
.active_url
= active_url
;
153 init_params
.gpu_preference
= gpu_preference
;
155 if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size
,
161 if (route_id
== MSG_ROUTING_NONE
)
164 CommandBufferProxyImpl
* command_buffer
=
165 new CommandBufferProxyImpl(this, route_id
);
166 AddRoute(route_id
, command_buffer
->AsWeakPtr());
168 AutoLock
lock(context_lock_
);
169 proxies_
[route_id
] = command_buffer
;
170 return command_buffer
;
173 scoped_ptr
<media::VideoDecodeAccelerator
> GpuChannelHost::CreateVideoDecoder(
174 int command_buffer_route_id
,
175 media::VideoCodecProfile profile
,
176 media::VideoDecodeAccelerator::Client
* client
) {
177 AutoLock
lock(context_lock_
);
178 ProxyMap::iterator it
= proxies_
.find(command_buffer_route_id
);
179 DCHECK(it
!= proxies_
.end());
180 CommandBufferProxyImpl
* proxy
= it
->second
;
181 return proxy
->CreateVideoDecoder(profile
, client
).Pass();
184 scoped_ptr
<media::VideoEncodeAccelerator
> GpuChannelHost::CreateVideoEncoder(
185 media::VideoEncodeAccelerator::Client
* client
) {
186 TRACE_EVENT0("gpu", "GpuChannelHost::CreateVideoEncoder");
188 scoped_ptr
<media::VideoEncodeAccelerator
> vea
;
189 int32 route_id
= MSG_ROUTING_NONE
;
190 if (!Send(new GpuChannelMsg_CreateVideoEncoder(&route_id
)))
192 if (route_id
== MSG_ROUTING_NONE
)
195 vea
.reset(new GpuVideoEncodeAcceleratorHost(client
, this, route_id
));
199 void GpuChannelHost::DestroyCommandBuffer(
200 CommandBufferProxyImpl
* command_buffer
) {
201 TRACE_EVENT0("gpu", "GpuChannelHost::DestroyCommandBuffer");
203 int route_id
= command_buffer
->GetRouteID();
204 Send(new GpuChannelMsg_DestroyCommandBuffer(route_id
));
205 RemoveRoute(route_id
);
207 AutoLock
lock(context_lock_
);
208 proxies_
.erase(route_id
);
209 delete command_buffer
;
212 bool GpuChannelHost::CollectRenderingStatsForSurface(
213 int surface_id
, GpuRenderingStats
* stats
) {
214 TRACE_EVENT0("gpu", "GpuChannelHost::CollectRenderingStats");
216 return Send(new GpuChannelMsg_CollectRenderingStatsForSurface(surface_id
,
220 void GpuChannelHost::AddRoute(
221 int route_id
, base::WeakPtr
<IPC::Listener
> listener
) {
222 DCHECK(MessageLoopProxy::current().get());
224 scoped_refptr
<base::MessageLoopProxy
> io_loop
= factory_
->GetIOLoopProxy();
225 io_loop
->PostTask(FROM_HERE
,
226 base::Bind(&GpuChannelHost::MessageFilter::AddRoute
,
227 channel_filter_
.get(), route_id
, listener
,
228 MessageLoopProxy::current()));
231 void GpuChannelHost::RemoveRoute(int route_id
) {
232 scoped_refptr
<base::MessageLoopProxy
> io_loop
= factory_
->GetIOLoopProxy();
233 io_loop
->PostTask(FROM_HERE
,
234 base::Bind(&GpuChannelHost::MessageFilter::RemoveRoute
,
235 channel_filter_
.get(), route_id
));
238 base::SharedMemoryHandle
GpuChannelHost::ShareToGpuProcess(
239 base::SharedMemoryHandle source_handle
) {
241 return base::SharedMemory::NULLHandle();
244 // Windows needs to explicitly duplicate the handle out to another process.
245 base::SharedMemoryHandle target_handle
;
246 if (!BrokerDuplicateHandle(source_handle
,
247 channel_
->peer_pid(),
250 DUPLICATE_SAME_ACCESS
)) {
251 return base::SharedMemory::NULLHandle();
254 return target_handle
;
256 int duped_handle
= HANDLE_EINTR(dup(source_handle
.fd
));
257 if (duped_handle
< 0)
258 return base::SharedMemory::NULLHandle();
260 return base::FileDescriptor(duped_handle
, true);
264 bool GpuChannelHost::GenerateMailboxNames(unsigned num
,
265 std::vector
<gpu::Mailbox
>* names
) {
266 DCHECK(names
->empty());
267 TRACE_EVENT0("gpu", "GenerateMailboxName");
268 size_t generate_count
= channel_filter_
->GetMailboxNames(num
, names
);
270 if (names
->size() < num
) {
271 std::vector
<gpu::Mailbox
> new_names
;
272 if (!Send(new GpuChannelMsg_GenerateMailboxNames(num
- names
->size(),
275 names
->insert(names
->end(), new_names
.begin(), new_names
.end());
278 if (generate_count
> 0)
279 Send(new GpuChannelMsg_GenerateMailboxNamesAsync(generate_count
));
284 int32
GpuChannelHost::ReserveTransferBufferId() {
285 return next_transfer_buffer_id_
.GetNext();
288 GpuChannelHost::~GpuChannelHost() {
289 // channel_ must be destroyed on the main thread.
290 if (!factory_
->IsMainThread())
291 factory_
->GetMainLoop()->DeleteSoon(FROM_HERE
, channel_
.release());
295 GpuChannelHost::MessageFilter::MessageFilter()
297 requested_mailboxes_(0) {
300 GpuChannelHost::MessageFilter::~MessageFilter() {}
302 void GpuChannelHost::MessageFilter::AddRoute(
304 base::WeakPtr
<IPC::Listener
> listener
,
305 scoped_refptr
<MessageLoopProxy
> loop
) {
306 DCHECK(listeners_
.find(route_id
) == listeners_
.end());
307 GpuListenerInfo info
;
308 info
.listener
= listener
;
310 listeners_
[route_id
] = info
;
313 void GpuChannelHost::MessageFilter::RemoveRoute(int route_id
) {
314 ListenerMap::iterator it
= listeners_
.find(route_id
);
315 if (it
!= listeners_
.end())
316 listeners_
.erase(it
);
319 bool GpuChannelHost::MessageFilter::OnMessageReceived(
320 const IPC::Message
& message
) {
321 // Never handle sync message replies or we will deadlock here.
322 if (message
.is_reply())
325 if (message
.routing_id() == MSG_ROUTING_CONTROL
)
326 return OnControlMessageReceived(message
);
328 ListenerMap::iterator it
= listeners_
.find(message
.routing_id());
330 if (it
!= listeners_
.end()) {
331 const GpuListenerInfo
& info
= it
->second
;
335 base::IgnoreResult(&IPC::Listener::OnMessageReceived
),
343 void GpuChannelHost::MessageFilter::OnChannelError() {
344 // Set the lost state before signalling the proxies. That way, if they
345 // themselves post a task to recreate the context, they will not try to re-use
346 // this channel host.
348 AutoLock
lock(lock_
);
352 // Inform all the proxies that an error has occurred. This will be reported
353 // via OpenGL as a lost context.
354 for (ListenerMap::iterator it
= listeners_
.begin();
355 it
!= listeners_
.end();
357 const GpuListenerInfo
& info
= it
->second
;
360 base::Bind(&IPC::Listener::OnChannelError
, info
.listener
));
366 bool GpuChannelHost::MessageFilter::IsLost() const {
367 AutoLock
lock(lock_
);
371 size_t GpuChannelHost::MessageFilter::GetMailboxNames(
372 size_t num
, std::vector
<gpu::Mailbox
>* names
) {
373 AutoLock
lock(lock_
);
374 size_t count
= std::min(num
, mailbox_name_pool_
.size());
375 names
->insert(names
->begin(),
376 mailbox_name_pool_
.end() - count
,
377 mailbox_name_pool_
.end());
378 mailbox_name_pool_
.erase(mailbox_name_pool_
.end() - count
,
379 mailbox_name_pool_
.end());
381 const size_t ideal_mailbox_pool_size
= 100;
382 size_t total
= mailbox_name_pool_
.size() + requested_mailboxes_
;
383 DCHECK_LE(total
, ideal_mailbox_pool_size
);
384 if (total
>= ideal_mailbox_pool_size
/ 2)
386 size_t request
= ideal_mailbox_pool_size
- total
;
387 requested_mailboxes_
+= request
;
391 bool GpuChannelHost::MessageFilter::OnControlMessageReceived(
392 const IPC::Message
& message
) {
395 IPC_BEGIN_MESSAGE_MAP(GpuChannelHost::MessageFilter
, message
)
396 IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNamesReply
,
397 OnGenerateMailboxNamesReply
)
398 IPC_MESSAGE_UNHANDLED(handled
= false)
399 IPC_END_MESSAGE_MAP()
405 void GpuChannelHost::MessageFilter::OnGenerateMailboxNamesReply(
406 const std::vector
<gpu::Mailbox
>& names
) {
407 TRACE_EVENT0("gpu", "OnGenerateMailboxNamesReply");
408 AutoLock
lock(lock_
);
409 DCHECK_LE(names
.size(), requested_mailboxes_
);
410 requested_mailboxes_
-= names
.size();
411 mailbox_name_pool_
.insert(mailbox_name_pool_
.end(),
417 } // namespace content