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/gpu_messages.h"
17 #include "ipc/ipc_sync_message_filter.h"
21 #include "content/public/common/sandbox_init.h"
25 using base::MessageLoopProxy
;
29 GpuListenerInfo::GpuListenerInfo() {}
31 GpuListenerInfo::~GpuListenerInfo() {}
34 scoped_refptr
<GpuChannelHost
> GpuChannelHost::Create(
35 GpuChannelHostFactory
* factory
,
36 const gpu::GPUInfo
& gpu_info
,
37 const IPC::ChannelHandle
& channel_handle
,
38 base::WaitableEvent
* shutdown_event
) {
39 DCHECK(factory
->IsMainThread());
40 scoped_refptr
<GpuChannelHost
> host
= new GpuChannelHost(factory
, gpu_info
);
41 host
->Connect(channel_handle
, shutdown_event
);
46 bool GpuChannelHost::IsValidGpuMemoryBuffer(
47 gfx::GpuMemoryBufferHandle handle
) {
48 switch (handle
.type
) {
49 case gfx::SHARED_MEMORY_BUFFER
:
50 #if defined(OS_MACOSX)
51 case gfx::IO_SURFACE_BUFFER
:
53 #if defined(OS_ANDROID)
54 case gfx::SURFACE_TEXTURE_BUFFER
:
57 case gfx::X11_PIXMAP_BUFFER
:
65 GpuChannelHost::GpuChannelHost(GpuChannelHostFactory
* factory
,
66 const gpu::GPUInfo
& gpu_info
)
69 next_transfer_buffer_id_
.GetNext();
70 next_gpu_memory_buffer_id_
.GetNext();
71 next_route_id_
.GetNext();
74 void GpuChannelHost::Connect(const IPC::ChannelHandle
& channel_handle
,
75 base::WaitableEvent
* shutdown_event
) {
76 // Open a channel to the GPU process. We pass NULL as the main listener here
77 // since we need to filter everything to route it to the right thread.
78 scoped_refptr
<base::MessageLoopProxy
> io_loop
= factory_
->GetIOLoopProxy();
79 channel_
= IPC::SyncChannel::Create(channel_handle
,
80 IPC::Channel::MODE_CLIENT
,
86 sync_filter_
= new IPC::SyncMessageFilter(shutdown_event
);
88 channel_
->AddFilter(sync_filter_
.get());
90 channel_filter_
= new MessageFilter();
92 // Install the filter last, because we intercept all leftover
94 channel_
->AddFilter(channel_filter_
.get());
97 bool GpuChannelHost::Send(IPC::Message
* msg
) {
98 // Callee takes ownership of message, regardless of whether Send is
99 // successful. See IPC::Sender.
100 scoped_ptr
<IPC::Message
> message(msg
);
101 // The GPU process never sends synchronous IPCs so clear the unblock flag to
103 message
->set_unblock(false);
105 // Currently we need to choose between two different mechanisms for sending.
106 // On the main thread we use the regular channel Send() method, on another
107 // thread we use SyncMessageFilter. We also have to be careful interpreting
108 // IsMainThread() since it might return false during shutdown,
109 // impl we are actually calling from the main thread (discard message then).
111 // TODO: Can we just always use sync_filter_ since we setup the channel
112 // without a main listener?
113 if (factory_
->IsMainThread()) {
114 // http://crbug.com/125264
115 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
116 bool result
= channel_
->Send(message
.release());
118 DVLOG(1) << "GpuChannelHost::Send failed: Channel::Send failed";
120 } else if (base::MessageLoop::current()) {
121 bool result
= sync_filter_
->Send(message
.release());
123 DVLOG(1) << "GpuChannelHost::Send failed: SyncMessageFilter::Send failed";
130 CommandBufferProxyImpl
* GpuChannelHost::CreateViewCommandBuffer(
132 CommandBufferProxyImpl
* share_group
,
133 const std::vector
<int32
>& attribs
,
134 const GURL
& active_url
,
135 gfx::GpuPreference gpu_preference
) {
137 "GpuChannelHost::CreateViewCommandBuffer",
141 GPUCreateCommandBufferConfig init_params
;
142 init_params
.share_group_id
=
143 share_group
? share_group
->GetRouteID() : MSG_ROUTING_NONE
;
144 init_params
.attribs
= attribs
;
145 init_params
.active_url
= active_url
;
146 init_params
.gpu_preference
= gpu_preference
;
147 int32 route_id
= GenerateRouteID();
148 CreateCommandBufferResult result
= factory_
->CreateViewCommandBuffer(
149 surface_id
, init_params
, route_id
);
150 if (result
!= CREATE_COMMAND_BUFFER_SUCCEEDED
) {
151 LOG(ERROR
) << "GpuChannelHost::CreateViewCommandBuffer failed.";
153 if (result
== CREATE_COMMAND_BUFFER_FAILED_AND_CHANNEL_LOST
) {
154 // The GPU channel needs to be considered lost. The caller will
155 // then set up a new connection, and the GPU channel and any
156 // view command buffers will all be associated with the same GPU
158 DCHECK(MessageLoopProxy::current().get());
160 scoped_refptr
<base::MessageLoopProxy
> io_loop
=
161 factory_
->GetIOLoopProxy();
164 base::Bind(&GpuChannelHost::MessageFilter::OnChannelError
,
165 channel_filter_
.get()));
171 CommandBufferProxyImpl
* command_buffer
=
172 new CommandBufferProxyImpl(this, route_id
);
173 AddRoute(route_id
, command_buffer
->AsWeakPtr());
175 AutoLock
lock(context_lock_
);
176 proxies_
[route_id
] = command_buffer
;
177 return command_buffer
;
180 CommandBufferProxyImpl
* GpuChannelHost::CreateOffscreenCommandBuffer(
181 const gfx::Size
& size
,
182 CommandBufferProxyImpl
* share_group
,
183 const std::vector
<int32
>& attribs
,
184 const GURL
& active_url
,
185 gfx::GpuPreference gpu_preference
) {
186 TRACE_EVENT0("gpu", "GpuChannelHost::CreateOffscreenCommandBuffer");
188 GPUCreateCommandBufferConfig init_params
;
189 init_params
.share_group_id
=
190 share_group
? share_group
->GetRouteID() : MSG_ROUTING_NONE
;
191 init_params
.attribs
= attribs
;
192 init_params
.active_url
= active_url
;
193 init_params
.gpu_preference
= gpu_preference
;
194 int32 route_id
= GenerateRouteID();
195 bool succeeded
= false;
196 if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size
,
200 LOG(ERROR
) << "Failed to send GpuChannelMsg_CreateOffscreenCommandBuffer.";
206 << "GpuChannelMsg_CreateOffscreenCommandBuffer returned failure.";
210 CommandBufferProxyImpl
* command_buffer
=
211 new CommandBufferProxyImpl(this, route_id
);
212 AddRoute(route_id
, command_buffer
->AsWeakPtr());
214 AutoLock
lock(context_lock_
);
215 proxies_
[route_id
] = command_buffer
;
216 return command_buffer
;
219 scoped_ptr
<media::VideoDecodeAccelerator
> GpuChannelHost::CreateVideoDecoder(
220 int command_buffer_route_id
) {
221 TRACE_EVENT0("gpu", "GpuChannelHost::CreateVideoDecoder");
222 AutoLock
lock(context_lock_
);
223 ProxyMap::iterator it
= proxies_
.find(command_buffer_route_id
);
224 DCHECK(it
!= proxies_
.end());
225 return it
->second
->CreateVideoDecoder();
228 scoped_ptr
<media::VideoEncodeAccelerator
> GpuChannelHost::CreateVideoEncoder(
229 int command_buffer_route_id
) {
230 TRACE_EVENT0("gpu", "GpuChannelHost::CreateVideoEncoder");
231 AutoLock
lock(context_lock_
);
232 ProxyMap::iterator it
= proxies_
.find(command_buffer_route_id
);
233 DCHECK(it
!= proxies_
.end());
234 return it
->second
->CreateVideoEncoder();
237 void GpuChannelHost::DestroyCommandBuffer(
238 CommandBufferProxyImpl
* command_buffer
) {
239 TRACE_EVENT0("gpu", "GpuChannelHost::DestroyCommandBuffer");
241 int route_id
= command_buffer
->GetRouteID();
242 Send(new GpuChannelMsg_DestroyCommandBuffer(route_id
));
243 RemoveRoute(route_id
);
245 AutoLock
lock(context_lock_
);
246 proxies_
.erase(route_id
);
247 delete command_buffer
;
250 void GpuChannelHost::AddRoute(
251 int route_id
, base::WeakPtr
<IPC::Listener
> listener
) {
252 DCHECK(MessageLoopProxy::current().get());
254 scoped_refptr
<base::MessageLoopProxy
> io_loop
= factory_
->GetIOLoopProxy();
255 io_loop
->PostTask(FROM_HERE
,
256 base::Bind(&GpuChannelHost::MessageFilter::AddRoute
,
257 channel_filter_
.get(), route_id
, listener
,
258 MessageLoopProxy::current()));
261 void GpuChannelHost::RemoveRoute(int route_id
) {
262 scoped_refptr
<base::MessageLoopProxy
> io_loop
= factory_
->GetIOLoopProxy();
263 io_loop
->PostTask(FROM_HERE
,
264 base::Bind(&GpuChannelHost::MessageFilter::RemoveRoute
,
265 channel_filter_
.get(), route_id
));
268 base::SharedMemoryHandle
GpuChannelHost::ShareToGpuProcess(
269 base::SharedMemoryHandle source_handle
) {
271 return base::SharedMemory::NULLHandle();
274 // Windows needs to explicitly duplicate the handle out to another process.
275 base::SharedMemoryHandle target_handle
;
276 if (!BrokerDuplicateHandle(source_handle
,
277 channel_
->GetPeerPID(),
279 FILE_GENERIC_READ
| FILE_GENERIC_WRITE
,
281 return base::SharedMemory::NULLHandle();
284 return target_handle
;
286 int duped_handle
= HANDLE_EINTR(dup(source_handle
.fd
));
287 if (duped_handle
< 0)
288 return base::SharedMemory::NULLHandle();
290 return base::FileDescriptor(duped_handle
, true);
294 int32
GpuChannelHost::ReserveTransferBufferId() {
295 return next_transfer_buffer_id_
.GetNext();
298 gfx::GpuMemoryBufferHandle
GpuChannelHost::ShareGpuMemoryBufferToGpuProcess(
299 gfx::GpuMemoryBufferHandle source_handle
) {
300 switch (source_handle
.type
) {
301 case gfx::SHARED_MEMORY_BUFFER
: {
302 gfx::GpuMemoryBufferHandle handle
;
303 handle
.type
= gfx::SHARED_MEMORY_BUFFER
;
304 handle
.handle
= ShareToGpuProcess(source_handle
.handle
);
307 #if defined(USE_OZONE)
308 case gfx::OZONE_NATIVE_BUFFER
:
309 return source_handle
;
311 #if defined(OS_MACOSX)
312 case gfx::IO_SURFACE_BUFFER
:
313 return source_handle
;
315 #if defined(OS_ANDROID)
316 case gfx::SURFACE_TEXTURE_BUFFER
:
317 return source_handle
;
320 case gfx::X11_PIXMAP_BUFFER
:
321 return source_handle
;
325 return gfx::GpuMemoryBufferHandle();
329 int32
GpuChannelHost::ReserveGpuMemoryBufferId() {
330 return next_gpu_memory_buffer_id_
.GetNext();
333 int32
GpuChannelHost::GenerateRouteID() {
334 return next_route_id_
.GetNext();
337 GpuChannelHost::~GpuChannelHost() {
338 // channel_ must be destroyed on the main thread.
339 if (!factory_
->IsMainThread())
340 factory_
->GetMainLoop()->DeleteSoon(FROM_HERE
, channel_
.release());
344 GpuChannelHost::MessageFilter::MessageFilter()
348 GpuChannelHost::MessageFilter::~MessageFilter() {}
350 void GpuChannelHost::MessageFilter::AddRoute(
352 base::WeakPtr
<IPC::Listener
> listener
,
353 scoped_refptr
<MessageLoopProxy
> loop
) {
354 DCHECK(listeners_
.find(route_id
) == listeners_
.end());
355 GpuListenerInfo info
;
356 info
.listener
= listener
;
358 listeners_
[route_id
] = info
;
361 void GpuChannelHost::MessageFilter::RemoveRoute(int route_id
) {
362 ListenerMap::iterator it
= listeners_
.find(route_id
);
363 if (it
!= listeners_
.end())
364 listeners_
.erase(it
);
367 bool GpuChannelHost::MessageFilter::OnMessageReceived(
368 const IPC::Message
& message
) {
369 // Never handle sync message replies or we will deadlock here.
370 if (message
.is_reply())
373 ListenerMap::iterator it
= listeners_
.find(message
.routing_id());
374 if (it
== listeners_
.end())
377 const GpuListenerInfo
& info
= it
->second
;
381 base::IgnoreResult(&IPC::Listener::OnMessageReceived
),
387 void GpuChannelHost::MessageFilter::OnChannelError() {
388 // Set the lost state before signalling the proxies. That way, if they
389 // themselves post a task to recreate the context, they will not try to re-use
390 // this channel host.
392 AutoLock
lock(lock_
);
396 // Inform all the proxies that an error has occurred. This will be reported
397 // via OpenGL as a lost context.
398 for (ListenerMap::iterator it
= listeners_
.begin();
399 it
!= listeners_
.end();
401 const GpuListenerInfo
& info
= it
->second
;
404 base::Bind(&IPC::Listener::OnChannelError
, info
.listener
));
410 bool GpuChannelHost::MessageFilter::IsLost() const {
411 AutoLock
lock(lock_
);
415 } // namespace content