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 return true; 57 default: 58 return false; 59 } 60} 61 62GpuChannelHost::GpuChannelHost(GpuChannelHostFactory* factory, 63 const gpu::GPUInfo& gpu_info) 64 : factory_(factory), 65 gpu_info_(gpu_info) { 66 next_transfer_buffer_id_.GetNext(); 67 next_gpu_memory_buffer_id_.GetNext(); 68 next_route_id_.GetNext(); 69} 70 71void GpuChannelHost::Connect(const IPC::ChannelHandle& channel_handle, 72 base::WaitableEvent* shutdown_event) { 73 // Open a channel to the GPU process. We pass NULL as the main listener here 74 // since we need to filter everything to route it to the right thread. 75 scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy(); 76 channel_ = IPC::SyncChannel::Create(channel_handle, 77 IPC::Channel::MODE_CLIENT, 78 NULL, 79 io_loop.get(), 80 true, 81 shutdown_event); 82 83 sync_filter_ = new IPC::SyncMessageFilter(shutdown_event); 84 85 channel_->AddFilter(sync_filter_.get()); 86 87 channel_filter_ = new MessageFilter(); 88 89 // Install the filter last, because we intercept all leftover 90 // messages. 91 channel_->AddFilter(channel_filter_.get()); 92} 93 94bool GpuChannelHost::Send(IPC::Message* msg) { 95 // Callee takes ownership of message, regardless of whether Send is 96 // successful. See IPC::Sender. 97 scoped_ptr<IPC::Message> message(msg); 98 // The GPU process never sends synchronous IPCs so clear the unblock flag to 99 // preserve order. 100 message->set_unblock(false); 101 102 // Currently we need to choose between two different mechanisms for sending. 103 // On the main thread we use the regular channel Send() method, on another 104 // thread we use SyncMessageFilter. We also have to be careful interpreting 105 // IsMainThread() since it might return false during shutdown, 106 // impl we are actually calling from the main thread (discard message then). 107 // 108 // TODO: Can we just always use sync_filter_ since we setup the channel 109 // without a main listener? 110 if (factory_->IsMainThread()) { 111 // http://crbug.com/125264 112 base::ThreadRestrictions::ScopedAllowWait allow_wait; 113 bool result = channel_->Send(message.release()); 114 if (!result) 115 DVLOG(1) << "GpuChannelHost::Send failed: Channel::Send failed"; 116 return result; 117 } else if (base::MessageLoop::current()) { 118 bool result = sync_filter_->Send(message.release()); 119 if (!result) 120 DVLOG(1) << "GpuChannelHost::Send failed: SyncMessageFilter::Send failed"; 121 return result; 122 } 123 124 return false; 125} 126 127CommandBufferProxyImpl* GpuChannelHost::CreateViewCommandBuffer( 128 int32 surface_id, 129 CommandBufferProxyImpl* share_group, 130 const std::vector<int32>& attribs, 131 const GURL& active_url, 132 gfx::GpuPreference gpu_preference) { 133 TRACE_EVENT1("gpu", 134 "GpuChannelHost::CreateViewCommandBuffer", 135 "surface_id", 136 surface_id); 137 138 GPUCreateCommandBufferConfig init_params; 139 init_params.share_group_id = 140 share_group ? share_group->GetRouteID() : MSG_ROUTING_NONE; 141 init_params.attribs = attribs; 142 init_params.active_url = active_url; 143 init_params.gpu_preference = gpu_preference; 144 int32 route_id = GenerateRouteID(); 145 CreateCommandBufferResult result = factory_->CreateViewCommandBuffer( 146 surface_id, init_params, route_id); 147 if (result != CREATE_COMMAND_BUFFER_SUCCEEDED) { 148 LOG(ERROR) << "GpuChannelHost::CreateViewCommandBuffer failed."; 149 150 if (result == CREATE_COMMAND_BUFFER_FAILED_AND_CHANNEL_LOST) { 151 // The GPU channel needs to be considered lost. The caller will 152 // then set up a new connection, and the GPU channel and any 153 // view command buffers will all be associated with the same GPU 154 // process. 155 DCHECK(MessageLoopProxy::current().get()); 156 157 scoped_refptr<base::MessageLoopProxy> io_loop = 158 factory_->GetIOLoopProxy(); 159 io_loop->PostTask( 160 FROM_HERE, 161 base::Bind(&GpuChannelHost::MessageFilter::OnChannelError, 162 channel_filter_.get())); 163 } 164 165 return NULL; 166 } 167 168 CommandBufferProxyImpl* command_buffer = 169 new CommandBufferProxyImpl(this, route_id); 170 AddRoute(route_id, command_buffer->AsWeakPtr()); 171 172 AutoLock lock(context_lock_); 173 proxies_[route_id] = command_buffer; 174 return command_buffer; 175} 176 177CommandBufferProxyImpl* GpuChannelHost::CreateOffscreenCommandBuffer( 178 const gfx::Size& size, 179 CommandBufferProxyImpl* share_group, 180 const std::vector<int32>& attribs, 181 const GURL& active_url, 182 gfx::GpuPreference gpu_preference) { 183 TRACE_EVENT0("gpu", "GpuChannelHost::CreateOffscreenCommandBuffer"); 184 185 GPUCreateCommandBufferConfig init_params; 186 init_params.share_group_id = 187 share_group ? share_group->GetRouteID() : MSG_ROUTING_NONE; 188 init_params.attribs = attribs; 189 init_params.active_url = active_url; 190 init_params.gpu_preference = gpu_preference; 191 int32 route_id = GenerateRouteID(); 192 bool succeeded = false; 193 if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size, 194 init_params, 195 route_id, 196 &succeeded))) { 197 LOG(ERROR) << "Failed to send GpuChannelMsg_CreateOffscreenCommandBuffer."; 198 return NULL; 199 } 200 201 if (!succeeded) { 202 LOG(ERROR) 203 << "GpuChannelMsg_CreateOffscreenCommandBuffer returned failure."; 204 return NULL; 205 } 206 207 CommandBufferProxyImpl* command_buffer = 208 new CommandBufferProxyImpl(this, route_id); 209 AddRoute(route_id, command_buffer->AsWeakPtr()); 210 211 AutoLock lock(context_lock_); 212 proxies_[route_id] = command_buffer; 213 return command_buffer; 214} 215 216scoped_ptr<media::VideoDecodeAccelerator> GpuChannelHost::CreateVideoDecoder( 217 int command_buffer_route_id) { 218 TRACE_EVENT0("gpu", "GpuChannelHost::CreateVideoDecoder"); 219 AutoLock lock(context_lock_); 220 ProxyMap::iterator it = proxies_.find(command_buffer_route_id); 221 DCHECK(it != proxies_.end()); 222 return it->second->CreateVideoDecoder(); 223} 224 225scoped_ptr<media::VideoEncodeAccelerator> GpuChannelHost::CreateVideoEncoder( 226 int command_buffer_route_id) { 227 TRACE_EVENT0("gpu", "GpuChannelHost::CreateVideoEncoder"); 228 AutoLock lock(context_lock_); 229 ProxyMap::iterator it = proxies_.find(command_buffer_route_id); 230 DCHECK(it != proxies_.end()); 231 return it->second->CreateVideoEncoder(); 232} 233 234void GpuChannelHost::DestroyCommandBuffer( 235 CommandBufferProxyImpl* command_buffer) { 236 TRACE_EVENT0("gpu", "GpuChannelHost::DestroyCommandBuffer"); 237 238 int route_id = command_buffer->GetRouteID(); 239 Send(new GpuChannelMsg_DestroyCommandBuffer(route_id)); 240 RemoveRoute(route_id); 241 242 AutoLock lock(context_lock_); 243 proxies_.erase(route_id); 244 delete command_buffer; 245} 246 247void GpuChannelHost::AddRoute( 248 int route_id, base::WeakPtr<IPC::Listener> listener) { 249 DCHECK(MessageLoopProxy::current().get()); 250 251 scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy(); 252 io_loop->PostTask(FROM_HERE, 253 base::Bind(&GpuChannelHost::MessageFilter::AddRoute, 254 channel_filter_.get(), route_id, listener, 255 MessageLoopProxy::current())); 256} 257 258void GpuChannelHost::RemoveRoute(int route_id) { 259 scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy(); 260 io_loop->PostTask(FROM_HERE, 261 base::Bind(&GpuChannelHost::MessageFilter::RemoveRoute, 262 channel_filter_.get(), route_id)); 263} 264 265base::SharedMemoryHandle GpuChannelHost::ShareToGpuProcess( 266 base::SharedMemoryHandle source_handle) { 267 if (IsLost()) 268 return base::SharedMemory::NULLHandle(); 269 270#if defined(OS_WIN) 271 // Windows needs to explicitly duplicate the handle out to another process. 272 base::SharedMemoryHandle target_handle; 273 if (!BrokerDuplicateHandle(source_handle, 274 channel_->GetPeerPID(), 275 &target_handle, 276 FILE_GENERIC_READ | FILE_GENERIC_WRITE, 277 0)) { 278 return base::SharedMemory::NULLHandle(); 279 } 280 281 return target_handle; 282#else 283 int duped_handle = HANDLE_EINTR(dup(source_handle.fd)); 284 if (duped_handle < 0) 285 return base::SharedMemory::NULLHandle(); 286 287 return base::FileDescriptor(duped_handle, true); 288#endif 289} 290 291int32 GpuChannelHost::ReserveTransferBufferId() { 292 return next_transfer_buffer_id_.GetNext(); 293} 294 295gfx::GpuMemoryBufferHandle GpuChannelHost::ShareGpuMemoryBufferToGpuProcess( 296 gfx::GpuMemoryBufferHandle source_handle) { 297 switch (source_handle.type) { 298 case gfx::SHARED_MEMORY_BUFFER: { 299 gfx::GpuMemoryBufferHandle handle; 300 handle.type = gfx::SHARED_MEMORY_BUFFER; 301 handle.handle = ShareToGpuProcess(source_handle.handle); 302 return handle; 303 } 304#if defined(OS_MACOSX) 305 case gfx::IO_SURFACE_BUFFER: 306 return source_handle; 307#endif 308#if defined(OS_ANDROID) 309 case gfx::SURFACE_TEXTURE_BUFFER: 310 return source_handle; 311#endif 312 default: 313 NOTREACHED(); 314 return gfx::GpuMemoryBufferHandle(); 315 } 316} 317 318int32 GpuChannelHost::ReserveGpuMemoryBufferId() { 319 return next_gpu_memory_buffer_id_.GetNext(); 320} 321 322int32 GpuChannelHost::GenerateRouteID() { 323 return next_route_id_.GetNext(); 324} 325 326GpuChannelHost::~GpuChannelHost() { 327 // channel_ must be destroyed on the main thread. 328 if (!factory_->IsMainThread()) 329 factory_->GetMainLoop()->DeleteSoon(FROM_HERE, channel_.release()); 330} 331 332 333GpuChannelHost::MessageFilter::MessageFilter() 334 : lost_(false) { 335} 336 337GpuChannelHost::MessageFilter::~MessageFilter() {} 338 339void GpuChannelHost::MessageFilter::AddRoute( 340 int route_id, 341 base::WeakPtr<IPC::Listener> listener, 342 scoped_refptr<MessageLoopProxy> loop) { 343 DCHECK(listeners_.find(route_id) == listeners_.end()); 344 GpuListenerInfo info; 345 info.listener = listener; 346 info.loop = loop; 347 listeners_[route_id] = info; 348} 349 350void GpuChannelHost::MessageFilter::RemoveRoute(int route_id) { 351 ListenerMap::iterator it = listeners_.find(route_id); 352 if (it != listeners_.end()) 353 listeners_.erase(it); 354} 355 356bool GpuChannelHost::MessageFilter::OnMessageReceived( 357 const IPC::Message& message) { 358 // Never handle sync message replies or we will deadlock here. 359 if (message.is_reply()) 360 return false; 361 362 ListenerMap::iterator it = listeners_.find(message.routing_id()); 363 if (it == listeners_.end()) 364 return false; 365 366 const GpuListenerInfo& info = it->second; 367 info.loop->PostTask( 368 FROM_HERE, 369 base::Bind( 370 base::IgnoreResult(&IPC::Listener::OnMessageReceived), 371 info.listener, 372 message)); 373 return true; 374} 375 376void GpuChannelHost::MessageFilter::OnChannelError() { 377 // Set the lost state before signalling the proxies. That way, if they 378 // themselves post a task to recreate the context, they will not try to re-use 379 // this channel host. 380 { 381 AutoLock lock(lock_); 382 lost_ = true; 383 } 384 385 // Inform all the proxies that an error has occurred. This will be reported 386 // via OpenGL as a lost context. 387 for (ListenerMap::iterator it = listeners_.begin(); 388 it != listeners_.end(); 389 it++) { 390 const GpuListenerInfo& info = it->second; 391 info.loop->PostTask( 392 FROM_HERE, 393 base::Bind(&IPC::Listener::OnChannelError, info.listener)); 394 } 395 396 listeners_.clear(); 397} 398 399bool GpuChannelHost::MessageFilter::IsLost() const { 400 AutoLock lock(lock_); 401 return lost_; 402} 403 404} // namespace content 405