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/command_buffer_proxy_impl.h" 6 7 #include "base/callback.h" 8 #include "base/debug/trace_event.h" 9 #include "base/logging.h" 10 #include "base/memory/shared_memory.h" 11 #include "base/stl_util.h" 12 #include "content/common/child_process_messages.h" 13 #include "content/common/gpu/client/gpu_channel_host.h" 14 #include "content/common/gpu/client/gpu_video_decode_accelerator_host.h" 15 #include "content/common/gpu/client/gpu_video_encode_accelerator_host.h" 16 #include "content/common/gpu/gpu_messages.h" 17 #include "content/common/view_messages.h" 18 #include "gpu/command_buffer/common/cmd_buffer_common.h" 19 #include "gpu/command_buffer/common/command_buffer_shared.h" 20 #include "gpu/command_buffer/common/gpu_memory_allocation.h" 21 #include "ui/gfx/size.h" 22 23 namespace content { 24 25 CommandBufferProxyImpl::CommandBufferProxyImpl( 26 GpuChannelHost* channel, 27 int route_id) 28 : channel_(channel), 29 route_id_(route_id), 30 flush_count_(0), 31 last_put_offset_(-1), 32 next_signal_id_(0) { 33 } 34 35 CommandBufferProxyImpl::~CommandBufferProxyImpl() { 36 FOR_EACH_OBSERVER(DeletionObserver, 37 deletion_observers_, 38 OnWillDeleteImpl()); 39 } 40 41 bool CommandBufferProxyImpl::OnMessageReceived(const IPC::Message& message) { 42 bool handled = true; 43 IPC_BEGIN_MESSAGE_MAP(CommandBufferProxyImpl, message) 44 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_Destroyed, OnDestroyed); 45 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_EchoAck, OnEchoAck); 46 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_ConsoleMsg, OnConsoleMessage); 47 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SetMemoryAllocation, 48 OnSetMemoryAllocation); 49 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalSyncPointAck, 50 OnSignalSyncPointAck); 51 IPC_MESSAGE_UNHANDLED(handled = false) 52 IPC_END_MESSAGE_MAP() 53 54 DCHECK(handled); 55 return handled; 56 } 57 58 void CommandBufferProxyImpl::OnChannelError() { 59 OnDestroyed(gpu::error::kUnknown); 60 } 61 62 void CommandBufferProxyImpl::OnDestroyed(gpu::error::ContextLostReason reason) { 63 // Prevent any further messages from being sent. 64 channel_ = NULL; 65 66 // When the client sees that the context is lost, they should delete this 67 // CommandBufferProxyImpl and create a new one. 68 last_state_.error = gpu::error::kLostContext; 69 last_state_.context_lost_reason = reason; 70 71 if (!channel_error_callback_.is_null()) { 72 channel_error_callback_.Run(); 73 // Avoid calling the error callback more than once. 74 channel_error_callback_.Reset(); 75 } 76 } 77 78 void CommandBufferProxyImpl::OnEchoAck() { 79 DCHECK(!echo_tasks_.empty()); 80 base::Closure callback = echo_tasks_.front(); 81 echo_tasks_.pop(); 82 callback.Run(); 83 } 84 85 void CommandBufferProxyImpl::OnConsoleMessage( 86 const GPUCommandBufferConsoleMessage& message) { 87 if (!console_message_callback_.is_null()) { 88 console_message_callback_.Run(message.message, message.id); 89 } 90 } 91 92 void CommandBufferProxyImpl::SetMemoryAllocationChangedCallback( 93 const MemoryAllocationChangedCallback& callback) { 94 if (last_state_.error != gpu::error::kNoError) 95 return; 96 97 memory_allocation_changed_callback_ = callback; 98 Send(new GpuCommandBufferMsg_SetClientHasMemoryAllocationChangedCallback( 99 route_id_, !memory_allocation_changed_callback_.is_null())); 100 } 101 102 void CommandBufferProxyImpl::AddDeletionObserver(DeletionObserver* observer) { 103 deletion_observers_.AddObserver(observer); 104 } 105 106 void CommandBufferProxyImpl::RemoveDeletionObserver( 107 DeletionObserver* observer) { 108 deletion_observers_.RemoveObserver(observer); 109 } 110 111 void CommandBufferProxyImpl::OnSetMemoryAllocation( 112 const gpu::MemoryAllocation& allocation) { 113 if (!memory_allocation_changed_callback_.is_null()) 114 memory_allocation_changed_callback_.Run(allocation); 115 } 116 117 void CommandBufferProxyImpl::OnSignalSyncPointAck(uint32 id) { 118 SignalTaskMap::iterator it = signal_tasks_.find(id); 119 DCHECK(it != signal_tasks_.end()); 120 base::Closure callback = it->second; 121 signal_tasks_.erase(it); 122 callback.Run(); 123 } 124 125 void CommandBufferProxyImpl::SetChannelErrorCallback( 126 const base::Closure& callback) { 127 channel_error_callback_ = callback; 128 } 129 130 bool CommandBufferProxyImpl::Initialize() { 131 TRACE_EVENT0("gpu", "CommandBufferProxyImpl::Initialize"); 132 shared_state_shm_.reset(channel_->factory()->AllocateSharedMemory( 133 sizeof(*shared_state())).release()); 134 if (!shared_state_shm_) 135 return false; 136 137 if (!shared_state_shm_->Map(sizeof(*shared_state()))) 138 return false; 139 140 shared_state()->Initialize(); 141 142 // This handle is owned by the GPU process and must be passed to it or it 143 // will leak. In otherwords, do not early out on error between here and the 144 // sending of the Initialize IPC below. 145 base::SharedMemoryHandle handle = 146 channel_->ShareToGpuProcess(shared_state_shm_->handle()); 147 if (!base::SharedMemory::IsHandleValid(handle)) 148 return false; 149 150 bool result = false; 151 if (!Send(new GpuCommandBufferMsg_Initialize( 152 route_id_, handle, &result, &capabilities_))) { 153 LOG(ERROR) << "Could not send GpuCommandBufferMsg_Initialize."; 154 return false; 155 } 156 157 if (!result) { 158 LOG(ERROR) << "Failed to initialize command buffer service."; 159 return false; 160 } 161 162 capabilities_.map_image = true; 163 164 return true; 165 } 166 167 gpu::CommandBuffer::State CommandBufferProxyImpl::GetLastState() { 168 return last_state_; 169 } 170 171 int32 CommandBufferProxyImpl::GetLastToken() { 172 TryUpdateState(); 173 return last_state_.token; 174 } 175 176 void CommandBufferProxyImpl::Flush(int32 put_offset) { 177 if (last_state_.error != gpu::error::kNoError) 178 return; 179 180 TRACE_EVENT1("gpu", 181 "CommandBufferProxyImpl::Flush", 182 "put_offset", 183 put_offset); 184 185 if (last_put_offset_ == put_offset) 186 return; 187 188 last_put_offset_ = put_offset; 189 190 Send(new GpuCommandBufferMsg_AsyncFlush(route_id_, 191 put_offset, 192 ++flush_count_, 193 latency_info_)); 194 latency_info_.clear(); 195 } 196 197 void CommandBufferProxyImpl::SetLatencyInfo( 198 const std::vector<ui::LatencyInfo>& latency_info) { 199 for (size_t i = 0; i < latency_info.size(); i++) 200 latency_info_.push_back(latency_info[i]); 201 } 202 203 void CommandBufferProxyImpl::WaitForTokenInRange(int32 start, int32 end) { 204 TRACE_EVENT2("gpu", 205 "CommandBufferProxyImpl::WaitForToken", 206 "start", 207 start, 208 "end", 209 end); 210 TryUpdateState(); 211 if (!InRange(start, end, last_state_.token) && 212 last_state_.error == gpu::error::kNoError) { 213 gpu::CommandBuffer::State state; 214 if (Send(new GpuCommandBufferMsg_WaitForTokenInRange( 215 route_id_, start, end, &state))) 216 OnUpdateState(state); 217 } 218 DCHECK(InRange(start, end, last_state_.token) || 219 last_state_.error != gpu::error::kNoError); 220 } 221 222 void CommandBufferProxyImpl::WaitForGetOffsetInRange(int32 start, int32 end) { 223 TRACE_EVENT2("gpu", 224 "CommandBufferProxyImpl::WaitForGetOffset", 225 "start", 226 start, 227 "end", 228 end); 229 TryUpdateState(); 230 if (!InRange(start, end, last_state_.get_offset) && 231 last_state_.error == gpu::error::kNoError) { 232 gpu::CommandBuffer::State state; 233 if (Send(new GpuCommandBufferMsg_WaitForGetOffsetInRange( 234 route_id_, start, end, &state))) 235 OnUpdateState(state); 236 } 237 DCHECK(InRange(start, end, last_state_.get_offset) || 238 last_state_.error != gpu::error::kNoError); 239 } 240 241 void CommandBufferProxyImpl::SetGetBuffer(int32 shm_id) { 242 if (last_state_.error != gpu::error::kNoError) 243 return; 244 245 Send(new GpuCommandBufferMsg_SetGetBuffer(route_id_, shm_id)); 246 last_put_offset_ = -1; 247 } 248 249 scoped_refptr<gpu::Buffer> CommandBufferProxyImpl::CreateTransferBuffer( 250 size_t size, 251 int32* id) { 252 *id = -1; 253 254 if (last_state_.error != gpu::error::kNoError) 255 return NULL; 256 257 int32 new_id = channel_->ReserveTransferBufferId(); 258 259 scoped_ptr<base::SharedMemory> shared_memory( 260 channel_->factory()->AllocateSharedMemory(size)); 261 if (!shared_memory) 262 return NULL; 263 264 DCHECK(!shared_memory->memory()); 265 if (!shared_memory->Map(size)) 266 return NULL; 267 268 // This handle is owned by the GPU process and must be passed to it or it 269 // will leak. In otherwords, do not early out on error between here and the 270 // sending of the RegisterTransferBuffer IPC below. 271 base::SharedMemoryHandle handle = 272 channel_->ShareToGpuProcess(shared_memory->handle()); 273 if (!base::SharedMemory::IsHandleValid(handle)) 274 return NULL; 275 276 if (!Send(new GpuCommandBufferMsg_RegisterTransferBuffer(route_id_, 277 new_id, 278 handle, 279 size))) { 280 return NULL; 281 } 282 283 *id = new_id; 284 scoped_refptr<gpu::Buffer> buffer( 285 gpu::MakeBufferFromSharedMemory(shared_memory.Pass(), size)); 286 return buffer; 287 } 288 289 void CommandBufferProxyImpl::DestroyTransferBuffer(int32 id) { 290 if (last_state_.error != gpu::error::kNoError) 291 return; 292 293 Send(new GpuCommandBufferMsg_DestroyTransferBuffer(route_id_, id)); 294 } 295 296 gpu::Capabilities CommandBufferProxyImpl::GetCapabilities() { 297 return capabilities_; 298 } 299 300 gfx::GpuMemoryBuffer* CommandBufferProxyImpl::CreateGpuMemoryBuffer( 301 size_t width, 302 size_t height, 303 unsigned internalformat, 304 unsigned usage, 305 int32* id) { 306 *id = -1; 307 308 if (last_state_.error != gpu::error::kNoError) 309 return NULL; 310 311 scoped_ptr<gfx::GpuMemoryBuffer> buffer( 312 channel_->factory()->AllocateGpuMemoryBuffer( 313 width, height, internalformat, usage)); 314 if (!buffer) 315 return NULL; 316 317 DCHECK(GpuChannelHost::IsValidGpuMemoryBuffer(buffer->GetHandle())); 318 319 int32 new_id = channel_->ReserveGpuMemoryBufferId(); 320 321 // This handle is owned by the GPU process and must be passed to it or it 322 // will leak. In otherwords, do not early out on error between here and the 323 // sending of the RegisterGpuMemoryBuffer IPC below. 324 gfx::GpuMemoryBufferHandle handle = 325 channel_->ShareGpuMemoryBufferToGpuProcess(buffer->GetHandle()); 326 327 if (!Send(new GpuCommandBufferMsg_RegisterGpuMemoryBuffer( 328 route_id_, 329 new_id, 330 handle, 331 width, 332 height, 333 internalformat))) { 334 return NULL; 335 } 336 337 *id = new_id; 338 DCHECK(gpu_memory_buffers_.find(new_id) == gpu_memory_buffers_.end()); 339 return gpu_memory_buffers_.add(new_id, buffer.Pass()).first->second; 340 } 341 342 void CommandBufferProxyImpl::DestroyGpuMemoryBuffer(int32 id) { 343 if (last_state_.error != gpu::error::kNoError) 344 return; 345 346 Send(new GpuCommandBufferMsg_UnregisterGpuMemoryBuffer(route_id_, id)); 347 348 // Remove the gpu memory buffer from the client side cache. 349 DCHECK(gpu_memory_buffers_.find(id) != gpu_memory_buffers_.end()); 350 gpu_memory_buffers_.take(id); 351 } 352 353 int CommandBufferProxyImpl::GetRouteID() const { 354 return route_id_; 355 } 356 357 void CommandBufferProxyImpl::Echo(const base::Closure& callback) { 358 if (last_state_.error != gpu::error::kNoError) { 359 return; 360 } 361 362 if (!Send(new GpuCommandBufferMsg_Echo( 363 route_id_, GpuCommandBufferMsg_EchoAck(route_id_)))) { 364 return; 365 } 366 367 echo_tasks_.push(callback); 368 } 369 370 uint32 CommandBufferProxyImpl::CreateStreamTexture(uint32 texture_id) { 371 if (last_state_.error != gpu::error::kNoError) 372 return 0; 373 374 int32 stream_id = channel_->GenerateRouteID(); 375 bool succeeded = false; 376 Send(new GpuCommandBufferMsg_CreateStreamTexture( 377 route_id_, texture_id, stream_id, &succeeded)); 378 if (!succeeded) { 379 DLOG(ERROR) << "GpuCommandBufferMsg_CreateStreamTexture returned failure"; 380 return 0; 381 } 382 return stream_id; 383 } 384 385 uint32 CommandBufferProxyImpl::InsertSyncPoint() { 386 if (last_state_.error != gpu::error::kNoError) 387 return 0; 388 389 uint32 sync_point = 0; 390 Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_, true, &sync_point)); 391 return sync_point; 392 } 393 394 uint32_t CommandBufferProxyImpl::InsertFutureSyncPoint() { 395 if (last_state_.error != gpu::error::kNoError) 396 return 0; 397 398 uint32 sync_point = 0; 399 Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_, false, &sync_point)); 400 return sync_point; 401 } 402 403 void CommandBufferProxyImpl::RetireSyncPoint(uint32_t sync_point) { 404 if (last_state_.error != gpu::error::kNoError) 405 return; 406 407 Send(new GpuCommandBufferMsg_RetireSyncPoint(route_id_, sync_point)); 408 } 409 410 void CommandBufferProxyImpl::SignalSyncPoint(uint32 sync_point, 411 const base::Closure& callback) { 412 if (last_state_.error != gpu::error::kNoError) 413 return; 414 415 uint32 signal_id = next_signal_id_++; 416 if (!Send(new GpuCommandBufferMsg_SignalSyncPoint(route_id_, 417 sync_point, 418 signal_id))) { 419 return; 420 } 421 422 signal_tasks_.insert(std::make_pair(signal_id, callback)); 423 } 424 425 void CommandBufferProxyImpl::SignalQuery(uint32 query, 426 const base::Closure& callback) { 427 if (last_state_.error != gpu::error::kNoError) 428 return; 429 430 // Signal identifiers are hidden, so nobody outside of this class will see 431 // them. (And thus, they cannot save them.) The IDs themselves only last 432 // until the callback is invoked, which will happen as soon as the GPU 433 // catches upwith the command buffer. 434 // A malicious caller trying to create a collision by making next_signal_id 435 // would have to make calls at an astounding rate (300B/s) and even if they 436 // could do that, all they would do is to prevent some callbacks from getting 437 // called, leading to stalled threads and/or memory leaks. 438 uint32 signal_id = next_signal_id_++; 439 if (!Send(new GpuCommandBufferMsg_SignalQuery(route_id_, 440 query, 441 signal_id))) { 442 return; 443 } 444 445 signal_tasks_.insert(std::make_pair(signal_id, callback)); 446 } 447 448 void CommandBufferProxyImpl::SetSurfaceVisible(bool visible) { 449 if (last_state_.error != gpu::error::kNoError) 450 return; 451 452 Send(new GpuCommandBufferMsg_SetSurfaceVisible(route_id_, visible)); 453 } 454 455 bool CommandBufferProxyImpl::ProduceFrontBuffer(const gpu::Mailbox& mailbox) { 456 if (last_state_.error != gpu::error::kNoError) 457 return false; 458 459 return Send(new GpuCommandBufferMsg_ProduceFrontBuffer(route_id_, mailbox)); 460 } 461 462 scoped_ptr<media::VideoDecodeAccelerator> 463 CommandBufferProxyImpl::CreateVideoDecoder() { 464 if (!channel_) 465 return scoped_ptr<media::VideoDecodeAccelerator>(); 466 return scoped_ptr<media::VideoDecodeAccelerator>( 467 new GpuVideoDecodeAcceleratorHost(channel_, this)); 468 } 469 470 scoped_ptr<media::VideoEncodeAccelerator> 471 CommandBufferProxyImpl::CreateVideoEncoder() { 472 if (!channel_) 473 return scoped_ptr<media::VideoEncodeAccelerator>(); 474 return scoped_ptr<media::VideoEncodeAccelerator>( 475 new GpuVideoEncodeAcceleratorHost(channel_, this)); 476 } 477 478 gpu::error::Error CommandBufferProxyImpl::GetLastError() { 479 return last_state_.error; 480 } 481 482 bool CommandBufferProxyImpl::Send(IPC::Message* msg) { 483 // Caller should not intentionally send a message if the context is lost. 484 DCHECK(last_state_.error == gpu::error::kNoError); 485 486 if (channel_) { 487 if (channel_->Send(msg)) { 488 return true; 489 } else { 490 // Flag the command buffer as lost. Defer deleting the channel until 491 // OnChannelError is called after returning to the message loop in case 492 // it is referenced elsewhere. 493 DVLOG(1) << "CommandBufferProxyImpl::Send failed. Losing context."; 494 last_state_.error = gpu::error::kLostContext; 495 return false; 496 } 497 } 498 499 // Callee takes ownership of message, regardless of whether Send is 500 // successful. See IPC::Sender. 501 delete msg; 502 return false; 503 } 504 505 void CommandBufferProxyImpl::OnUpdateState( 506 const gpu::CommandBuffer::State& state) { 507 // Handle wraparound. It works as long as we don't have more than 2B state 508 // updates in flight across which reordering occurs. 509 if (state.generation - last_state_.generation < 0x80000000U) 510 last_state_ = state; 511 } 512 513 void CommandBufferProxyImpl::SetOnConsoleMessageCallback( 514 const GpuConsoleMessageCallback& callback) { 515 console_message_callback_ = callback; 516 } 517 518 void CommandBufferProxyImpl::TryUpdateState() { 519 if (last_state_.error == gpu::error::kNoError) 520 shared_state()->Read(&last_state_); 521 } 522 523 gpu::CommandBufferSharedState* CommandBufferProxyImpl::shared_state() const { 524 return reinterpret_cast<gpu::CommandBufferSharedState*>( 525 shared_state_shm_->memory()); 526 } 527 528 } // namespace content 529