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/common/gpu/client/gpu_channel_host.h"
6
7#include <algorithm>
8
9#include "base/bind.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"
18#include "url/gurl.h"
19
20#if defined(OS_WIN)
21#include "content/public/common/sandbox_init.h"
22#endif
23
24using base::AutoLock;
25using base::MessageLoopProxy;
26
27namespace content {
28
29GpuListenerInfo::GpuListenerInfo() {}
30
31GpuListenerInfo::~GpuListenerInfo() {}
32
33// static
34scoped_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);
42  return host;
43}
44
45// static
46bool 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:
52#endif
53#if defined(OS_ANDROID)
54    case gfx::SURFACE_TEXTURE_BUFFER:
55#endif
56#if defined(USE_X11)
57    case gfx::X11_PIXMAP_BUFFER:
58#endif
59      return true;
60    default:
61      return false;
62  }
63}
64
65GpuChannelHost::GpuChannelHost(GpuChannelHostFactory* factory,
66                               const gpu::GPUInfo& gpu_info)
67    : factory_(factory),
68      gpu_info_(gpu_info) {
69  next_transfer_buffer_id_.GetNext();
70  next_gpu_memory_buffer_id_.GetNext();
71  next_route_id_.GetNext();
72}
73
74void 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,
81                                      NULL,
82                                      io_loop.get(),
83                                      true,
84                                      shutdown_event);
85
86  sync_filter_ = new IPC::SyncMessageFilter(shutdown_event);
87
88  channel_->AddFilter(sync_filter_.get());
89
90  channel_filter_ = new MessageFilter();
91
92  // Install the filter last, because we intercept all leftover
93  // messages.
94  channel_->AddFilter(channel_filter_.get());
95}
96
97bool 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
102  // preserve order.
103  message->set_unblock(false);
104
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).
110  //
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());
117    if (!result)
118      DVLOG(1) << "GpuChannelHost::Send failed: Channel::Send failed";
119    return result;
120  } else if (base::MessageLoop::current()) {
121    bool result = sync_filter_->Send(message.release());
122    if (!result)
123      DVLOG(1) << "GpuChannelHost::Send failed: SyncMessageFilter::Send failed";
124    return result;
125  }
126
127  return false;
128}
129
130CommandBufferProxyImpl* GpuChannelHost::CreateViewCommandBuffer(
131    int32 surface_id,
132    CommandBufferProxyImpl* share_group,
133    const std::vector<int32>& attribs,
134    const GURL& active_url,
135    gfx::GpuPreference gpu_preference) {
136  TRACE_EVENT1("gpu",
137               "GpuChannelHost::CreateViewCommandBuffer",
138               "surface_id",
139               surface_id);
140
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.";
152
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
157      // process.
158      DCHECK(MessageLoopProxy::current().get());
159
160      scoped_refptr<base::MessageLoopProxy> io_loop =
161          factory_->GetIOLoopProxy();
162      io_loop->PostTask(
163          FROM_HERE,
164          base::Bind(&GpuChannelHost::MessageFilter::OnChannelError,
165                     channel_filter_.get()));
166    }
167
168    return NULL;
169  }
170
171  CommandBufferProxyImpl* command_buffer =
172      new CommandBufferProxyImpl(this, route_id);
173  AddRoute(route_id, command_buffer->AsWeakPtr());
174
175  AutoLock lock(context_lock_);
176  proxies_[route_id] = command_buffer;
177  return command_buffer;
178}
179
180CommandBufferProxyImpl* 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");
187
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,
197                                                           init_params,
198                                                           route_id,
199                                                           &succeeded))) {
200    LOG(ERROR) << "Failed to send GpuChannelMsg_CreateOffscreenCommandBuffer.";
201    return NULL;
202  }
203
204  if (!succeeded) {
205    LOG(ERROR)
206        << "GpuChannelMsg_CreateOffscreenCommandBuffer returned failure.";
207    return NULL;
208  }
209
210  CommandBufferProxyImpl* command_buffer =
211      new CommandBufferProxyImpl(this, route_id);
212  AddRoute(route_id, command_buffer->AsWeakPtr());
213
214  AutoLock lock(context_lock_);
215  proxies_[route_id] = command_buffer;
216  return command_buffer;
217}
218
219scoped_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();
226}
227
228scoped_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();
235}
236
237void GpuChannelHost::DestroyCommandBuffer(
238    CommandBufferProxyImpl* command_buffer) {
239  TRACE_EVENT0("gpu", "GpuChannelHost::DestroyCommandBuffer");
240
241  int route_id = command_buffer->GetRouteID();
242  Send(new GpuChannelMsg_DestroyCommandBuffer(route_id));
243  RemoveRoute(route_id);
244
245  AutoLock lock(context_lock_);
246  proxies_.erase(route_id);
247  delete command_buffer;
248}
249
250void GpuChannelHost::AddRoute(
251    int route_id, base::WeakPtr<IPC::Listener> listener) {
252  DCHECK(MessageLoopProxy::current().get());
253
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()));
259}
260
261void 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));
266}
267
268base::SharedMemoryHandle GpuChannelHost::ShareToGpuProcess(
269    base::SharedMemoryHandle source_handle) {
270  if (IsLost())
271    return base::SharedMemory::NULLHandle();
272
273#if defined(OS_WIN)
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(),
278                             &target_handle,
279                             FILE_GENERIC_READ | FILE_GENERIC_WRITE,
280                             0)) {
281    return base::SharedMemory::NULLHandle();
282  }
283
284  return target_handle;
285#else
286  int duped_handle = HANDLE_EINTR(dup(source_handle.fd));
287  if (duped_handle < 0)
288    return base::SharedMemory::NULLHandle();
289
290  return base::FileDescriptor(duped_handle, true);
291#endif
292}
293
294int32 GpuChannelHost::ReserveTransferBufferId() {
295  return next_transfer_buffer_id_.GetNext();
296}
297
298gfx::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);
305      return handle;
306    }
307#if defined(USE_OZONE)
308    case gfx::OZONE_NATIVE_BUFFER:
309      return source_handle;
310#endif
311#if defined(OS_MACOSX)
312    case gfx::IO_SURFACE_BUFFER:
313      return source_handle;
314#endif
315#if defined(OS_ANDROID)
316    case gfx::SURFACE_TEXTURE_BUFFER:
317      return source_handle;
318#endif
319#if defined(USE_X11)
320    case gfx::X11_PIXMAP_BUFFER:
321      return source_handle;
322#endif
323    default:
324      NOTREACHED();
325      return gfx::GpuMemoryBufferHandle();
326  }
327}
328
329int32 GpuChannelHost::ReserveGpuMemoryBufferId() {
330  return next_gpu_memory_buffer_id_.GetNext();
331}
332
333int32 GpuChannelHost::GenerateRouteID() {
334  return next_route_id_.GetNext();
335}
336
337GpuChannelHost::~GpuChannelHost() {
338  // channel_ must be destroyed on the main thread.
339  if (!factory_->IsMainThread())
340    factory_->GetMainLoop()->DeleteSoon(FROM_HERE, channel_.release());
341}
342
343
344GpuChannelHost::MessageFilter::MessageFilter()
345    : lost_(false) {
346}
347
348GpuChannelHost::MessageFilter::~MessageFilter() {}
349
350void GpuChannelHost::MessageFilter::AddRoute(
351    int route_id,
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;
357  info.loop = loop;
358  listeners_[route_id] = info;
359}
360
361void GpuChannelHost::MessageFilter::RemoveRoute(int route_id) {
362  ListenerMap::iterator it = listeners_.find(route_id);
363  if (it != listeners_.end())
364    listeners_.erase(it);
365}
366
367bool GpuChannelHost::MessageFilter::OnMessageReceived(
368    const IPC::Message& message) {
369  // Never handle sync message replies or we will deadlock here.
370  if (message.is_reply())
371    return false;
372
373  ListenerMap::iterator it = listeners_.find(message.routing_id());
374  if (it == listeners_.end())
375    return false;
376
377  const GpuListenerInfo& info = it->second;
378  info.loop->PostTask(
379      FROM_HERE,
380      base::Bind(
381          base::IgnoreResult(&IPC::Listener::OnMessageReceived),
382          info.listener,
383          message));
384  return true;
385}
386
387void 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.
391  {
392    AutoLock lock(lock_);
393    lost_ = true;
394  }
395
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();
400       it++) {
401    const GpuListenerInfo& info = it->second;
402    info.loop->PostTask(
403        FROM_HERE,
404        base::Bind(&IPC::Listener::OnChannelError, info.listener));
405  }
406
407  listeners_.clear();
408}
409
410bool GpuChannelHost::MessageFilter::IsLost() const {
411  AutoLock lock(lock_);
412  return lost_;
413}
414
415}  // namespace content
416