| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ppapi/proxy/ppapi_command_buffer_proxy.h" |
| |
| #include "ppapi/proxy/ppapi_messages.h" |
| #include "ppapi/proxy/proxy_channel.h" |
| #include "ppapi/shared_impl/api_id.h" |
| #include "ppapi/shared_impl/host_resource.h" |
| |
| namespace ppapi { |
| namespace proxy { |
| |
| PpapiCommandBufferProxy::PpapiCommandBufferProxy( |
| const ppapi::HostResource& resource, |
| ProxyChannel* channel) |
| : resource_(resource), |
| channel_(channel) { |
| } |
| |
| PpapiCommandBufferProxy::~PpapiCommandBufferProxy() { |
| // Delete all the locally cached shared memory objects, closing the handle |
| // in this process. |
| for (TransferBufferMap::iterator it = transfer_buffers_.begin(); |
| it != transfer_buffers_.end(); ++it) { |
| delete it->second.shared_memory; |
| it->second.shared_memory = NULL; |
| } |
| } |
| |
| void PpapiCommandBufferProxy::ReportChannelError() { |
| if (!channel_error_callback_.is_null()) { |
| channel_error_callback_.Run(); |
| channel_error_callback_.Reset(); |
| } |
| } |
| |
| int PpapiCommandBufferProxy::GetRouteID() const { |
| NOTIMPLEMENTED(); |
| return 0; |
| } |
| |
| bool PpapiCommandBufferProxy::Echo(const base::Closure& callback) { |
| return false; |
| } |
| |
| bool PpapiCommandBufferProxy::ProduceFrontBuffer(const gpu::Mailbox& mailbox) { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| void PpapiCommandBufferProxy::SetChannelErrorCallback( |
| const base::Closure& callback) { |
| channel_error_callback_ = callback; |
| } |
| |
| bool PpapiCommandBufferProxy::Initialize() { |
| return true; |
| } |
| |
| gpu::CommandBuffer::State PpapiCommandBufferProxy::GetState() { |
| // Send will flag state with lost context if IPC fails. |
| if (last_state_.error == gpu::error::kNoError) { |
| gpu::CommandBuffer::State state; |
| bool success = false; |
| if (Send(new PpapiHostMsg_PPBGraphics3D_GetState( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, &state, &success))) { |
| UpdateState(state, success); |
| } |
| } |
| |
| return last_state_; |
| } |
| |
| gpu::CommandBuffer::State PpapiCommandBufferProxy::GetLastState() { |
| // Note: The locking command buffer wrapper does not take a global lock before |
| // calling this function. |
| return last_state_; |
| } |
| |
| int32 PpapiCommandBufferProxy::GetLastToken() { |
| // Note: The locking command buffer wrapper does not take a global lock before |
| // calling this function. |
| return last_state_.token; |
| } |
| |
| void PpapiCommandBufferProxy::Flush(int32 put_offset) { |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| |
| IPC::Message* message = new PpapiHostMsg_PPBGraphics3D_AsyncFlush( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, put_offset); |
| |
| // Do not let a synchronous flush hold up this message. If this handler is |
| // deferred until after the synchronous flush completes, it will overwrite the |
| // cached last_state_ with out-of-date data. |
| message->set_unblock(true); |
| Send(message); |
| } |
| |
| gpu::CommandBuffer::State PpapiCommandBufferProxy::FlushSync(int32 put_offset, |
| int32 last_known_get) { |
| if (last_known_get == last_state_.get_offset) { |
| // Send will flag state with lost context if IPC fails. |
| if (last_state_.error == gpu::error::kNoError) { |
| gpu::CommandBuffer::State state; |
| bool success = false; |
| if (Send(new PpapiHostMsg_PPBGraphics3D_Flush( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, put_offset, |
| last_known_get, &state, &success))) { |
| UpdateState(state, success); |
| } |
| } |
| } else { |
| Flush(put_offset); |
| } |
| return last_state_; |
| } |
| |
| void PpapiCommandBufferProxy::SetGetBuffer(int32 transfer_buffer_id) { |
| if (last_state_.error == gpu::error::kNoError) { |
| Send(new PpapiHostMsg_PPBGraphics3D_SetGetBuffer( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, transfer_buffer_id)); |
| } |
| } |
| |
| void PpapiCommandBufferProxy::SetGetOffset(int32 get_offset) { |
| // Not implemented in proxy. |
| NOTREACHED(); |
| } |
| |
| gpu::Buffer PpapiCommandBufferProxy::CreateTransferBuffer(size_t size, |
| int32* id) { |
| *id = -1; |
| |
| if (last_state_.error != gpu::error::kNoError) |
| return gpu::Buffer(); |
| |
| if (!Send(new PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, size, id))) { |
| return gpu::Buffer(); |
| } |
| |
| if ((*id) <= 0) |
| return gpu::Buffer(); |
| |
| return GetTransferBuffer(*id); |
| } |
| |
| void PpapiCommandBufferProxy::DestroyTransferBuffer(int32 id) { |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| |
| // Remove the transfer buffer from the client side4 cache. |
| TransferBufferMap::iterator it = transfer_buffers_.find(id); |
| |
| if (it != transfer_buffers_.end()) { |
| // Delete the shared memory object, closing the handle in this process. |
| delete it->second.shared_memory; |
| |
| transfer_buffers_.erase(it); |
| } |
| |
| Send(new PpapiHostMsg_PPBGraphics3D_DestroyTransferBuffer( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, id)); |
| } |
| |
| gpu::Buffer PpapiCommandBufferProxy::GetTransferBuffer(int32 id) { |
| if (last_state_.error != gpu::error::kNoError) |
| return gpu::Buffer(); |
| |
| // Check local cache to see if there is already a client side shared memory |
| // object for this id. |
| TransferBufferMap::iterator it = transfer_buffers_.find(id); |
| if (it != transfer_buffers_.end()) { |
| return it->second; |
| } |
| |
| // Assuming we are in the renderer process, the service is responsible for |
| // duplicating the handle. This might not be true for NaCl. |
| ppapi::proxy::SerializedHandle handle( |
| ppapi::proxy::SerializedHandle::SHARED_MEMORY); |
| if (!Send(new PpapiHostMsg_PPBGraphics3D_GetTransferBuffer( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, id, &handle))) { |
| return gpu::Buffer(); |
| } |
| if (!handle.is_shmem()) |
| return gpu::Buffer(); |
| |
| // Cache the transfer buffer shared memory object client side. |
| scoped_ptr<base::SharedMemory> shared_memory( |
| new base::SharedMemory(handle.shmem(), false)); |
| |
| // Map the shared memory on demand. |
| if (!shared_memory->memory()) { |
| if (!shared_memory->Map(handle.size())) { |
| return gpu::Buffer(); |
| } |
| } |
| |
| gpu::Buffer buffer; |
| buffer.ptr = shared_memory->memory(); |
| buffer.size = handle.size(); |
| buffer.shared_memory = shared_memory.release(); |
| transfer_buffers_[id] = buffer; |
| |
| return buffer; |
| } |
| |
| void PpapiCommandBufferProxy::SetToken(int32 token) { |
| NOTREACHED(); |
| } |
| |
| void PpapiCommandBufferProxy::SetParseError(gpu::error::Error error) { |
| NOTREACHED(); |
| } |
| |
| void PpapiCommandBufferProxy::SetContextLostReason( |
| gpu::error::ContextLostReason reason) { |
| NOTREACHED(); |
| } |
| |
| uint32 PpapiCommandBufferProxy::InsertSyncPoint() { |
| uint32 sync_point = 0; |
| if (last_state_.error == gpu::error::kNoError) { |
| Send(new PpapiHostMsg_PPBGraphics3D_InsertSyncPoint( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, &sync_point)); |
| } |
| return sync_point; |
| } |
| |
| bool PpapiCommandBufferProxy::Send(IPC::Message* msg) { |
| DCHECK(last_state_.error == gpu::error::kNoError); |
| |
| if (channel_->Send(msg)) |
| return true; |
| |
| last_state_.error = gpu::error::kLostContext; |
| return false; |
| } |
| |
| void PpapiCommandBufferProxy::UpdateState( |
| const gpu::CommandBuffer::State& state, |
| bool success) { |
| // Handle wraparound. It works as long as we don't have more than 2B state |
| // updates in flight across which reordering occurs. |
| if (success) { |
| if (state.generation - last_state_.generation < 0x80000000U) { |
| last_state_ = state; |
| } |
| } else { |
| last_state_.error = gpu::error::kLostContext; |
| ++last_state_.generation; |
| } |
| } |
| |
| } // namespace proxy |
| } // namespace ppapi |