| // Copyright 2012 The Chromium Authors |
| // 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 <utility> |
| |
| #include "base/numerics/safe_conversions.h" |
| #include "ppapi/proxy/ppapi_messages.h" |
| #include "ppapi/shared_impl/api_id.h" |
| #include "ppapi/shared_impl/host_resource.h" |
| #include "ppapi/shared_impl/proxy_lock.h" |
| |
| namespace ppapi { |
| namespace proxy { |
| |
| PpapiCommandBufferProxy::PpapiCommandBufferProxy( |
| const ppapi::HostResource& resource, |
| InstanceData::FlushInfo* flush_info, |
| LockedSender* sender, |
| const gpu::Capabilities& capabilities, |
| SerializedHandle shared_state, |
| gpu::CommandBufferId command_buffer_id) |
| : command_buffer_id_(command_buffer_id), |
| capabilities_(capabilities), |
| resource_(resource), |
| flush_info_(flush_info), |
| sender_(sender), |
| next_fence_sync_release_(1), |
| pending_fence_sync_release_(0), |
| flushed_fence_sync_release_(0), |
| validated_fence_sync_release_(0) { |
| base::UnsafeSharedMemoryRegion shmem_region = |
| base::UnsafeSharedMemoryRegion::Deserialize( |
| shared_state.TakeSharedMemoryRegion()); |
| shared_state_mapping_ = shmem_region.Map(); |
| } |
| |
| PpapiCommandBufferProxy::~PpapiCommandBufferProxy() { |
| // gpu::Buffers are no longer referenced, allowing shared memory objects to be |
| // deleted, closing the handle in this process. |
| } |
| |
| gpu::CommandBuffer::State PpapiCommandBufferProxy::GetLastState() { |
| ppapi::ProxyLock::AssertAcquiredDebugOnly(); |
| TryUpdateState(); |
| return last_state_; |
| } |
| |
| void PpapiCommandBufferProxy::Flush(int32_t put_offset) { |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| |
| OrderingBarrier(put_offset); |
| FlushInternal(); |
| } |
| |
| void PpapiCommandBufferProxy::OrderingBarrier(int32_t put_offset) { |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| |
| if (flush_info_->flush_pending && flush_info_->resource != resource_) { |
| FlushInternal(); |
| } |
| |
| flush_info_->flush_pending = true; |
| flush_info_->resource = resource_; |
| flush_info_->put_offset = put_offset; |
| pending_fence_sync_release_ = next_fence_sync_release_ - 1; |
| } |
| |
| gpu::CommandBuffer::State PpapiCommandBufferProxy::WaitForTokenInRange( |
| int32_t start, |
| int32_t end) { |
| TryUpdateState(); |
| if (!InRange(start, end, last_state_.token) && |
| last_state_.error == gpu::error::kNoError) { |
| bool success = false; |
| gpu::CommandBuffer::State state; |
| if (Send(new PpapiHostMsg_PPBGraphics3D_WaitForTokenInRange( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, start, end, &state, |
| &success))) |
| UpdateState(state, success); |
| } |
| DCHECK(InRange(start, end, last_state_.token) || |
| last_state_.error != gpu::error::kNoError); |
| return last_state_; |
| } |
| |
| gpu::CommandBuffer::State PpapiCommandBufferProxy::WaitForGetOffsetInRange( |
| uint32_t set_get_buffer_count, |
| int32_t start, |
| int32_t end) { |
| TryUpdateState(); |
| if (((set_get_buffer_count != last_state_.set_get_buffer_count) || |
| !InRange(start, end, last_state_.get_offset)) && |
| last_state_.error == gpu::error::kNoError) { |
| bool success = false; |
| gpu::CommandBuffer::State state; |
| if (Send(new PpapiHostMsg_PPBGraphics3D_WaitForGetOffsetInRange( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, set_get_buffer_count, |
| start, end, &state, &success))) |
| UpdateState(state, success); |
| } |
| DCHECK(((set_get_buffer_count == last_state_.set_get_buffer_count) && |
| InRange(start, end, last_state_.get_offset)) || |
| last_state_.error != gpu::error::kNoError); |
| return last_state_; |
| } |
| |
| void PpapiCommandBufferProxy::SetGetBuffer(int32_t 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)); |
| } |
| } |
| |
| scoped_refptr<gpu::Buffer> PpapiCommandBufferProxy::CreateTransferBuffer( |
| uint32_t size, |
| int32_t* id, |
| uint32_t alignment, |
| gpu::TransferBufferAllocationOption option) { |
| *id = -1; |
| |
| if (last_state_.error != gpu::error::kNoError) |
| return nullptr; |
| |
| // 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_REGION); |
| if (!Send(new PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, size, id, &handle))) { |
| if (last_state_.error == gpu::error::kNoError) |
| last_state_.error = gpu::error::kLostContext; |
| return nullptr; |
| } |
| |
| if (*id <= 0 || !handle.is_shmem_region()) { |
| if (last_state_.error == gpu::error::kNoError) |
| last_state_.error = gpu::error::kOutOfBounds; |
| return nullptr; |
| } |
| |
| base::UnsafeSharedMemoryRegion shared_memory_region = |
| base::UnsafeSharedMemoryRegion::Deserialize( |
| handle.TakeSharedMemoryRegion()); |
| |
| base::WritableSharedMemoryMapping shared_memory_mapping = |
| shared_memory_region.Map(); |
| if (!shared_memory_mapping.IsValid() || |
| (shared_memory_mapping.size() > UINT32_MAX)) { |
| if (last_state_.error == gpu::error::kNoError) |
| last_state_.error = gpu::error::kOutOfBounds; |
| *id = -1; |
| return nullptr; |
| } |
| |
| return gpu::MakeBufferFromSharedMemory(std::move(shared_memory_region), |
| std::move(shared_memory_mapping)); |
| } |
| |
| void PpapiCommandBufferProxy::DestroyTransferBuffer(int32_t id) { |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| |
| if (flush_info_->flush_pending) |
| FlushInternal(); |
| |
| Send(new PpapiHostMsg_PPBGraphics3D_DestroyTransferBuffer( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_, id)); |
| } |
| |
| void PpapiCommandBufferProxy::ForceLostContext(gpu::error::ContextLostReason) { |
| // This entry point was added to CommandBuffer well after PPAPI's |
| // deprecation. No current clients determined its necessity, so it |
| // will not be implemented. |
| } |
| |
| void PpapiCommandBufferProxy::SetLock(base::Lock*) { |
| NOTREACHED(); |
| } |
| |
| void PpapiCommandBufferProxy::EnsureWorkVisible() { |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| |
| if (flush_info_->flush_pending) |
| FlushInternal(); |
| DCHECK_GE(flushed_fence_sync_release_, validated_fence_sync_release_); |
| Send(new PpapiHostMsg_PPBGraphics3D_EnsureWorkVisible( |
| ppapi::API_ID_PPB_GRAPHICS_3D, resource_)); |
| validated_fence_sync_release_ = flushed_fence_sync_release_; |
| } |
| |
| gpu::CommandBufferNamespace PpapiCommandBufferProxy::GetNamespaceID() const { |
| return gpu::CommandBufferNamespace::GPU_IO; |
| } |
| |
| gpu::CommandBufferId PpapiCommandBufferProxy::GetCommandBufferID() const { |
| return command_buffer_id_; |
| } |
| |
| void PpapiCommandBufferProxy::FlushPendingWork() { |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| if (flush_info_->flush_pending) |
| FlushInternal(); |
| } |
| |
| uint64_t PpapiCommandBufferProxy::GenerateFenceSyncRelease() { |
| return next_fence_sync_release_++; |
| } |
| |
| bool PpapiCommandBufferProxy::IsFenceSyncReleased(uint64_t release) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void PpapiCommandBufferProxy::SignalSyncToken(const gpu::SyncToken& sync_token, |
| base::OnceClosure callback) { |
| NOTREACHED(); |
| } |
| |
| // Pepper plugin does not expose or call WaitSyncTokenCHROMIUM. |
| void PpapiCommandBufferProxy::WaitSyncToken(const gpu::SyncToken& sync_token) { |
| NOTREACHED(); |
| } |
| |
| bool PpapiCommandBufferProxy::CanWaitUnverifiedSyncToken( |
| const gpu::SyncToken& sync_token) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void PpapiCommandBufferProxy::SignalQuery(uint32_t query, |
| base::OnceClosure callback) { |
| NOTREACHED(); |
| } |
| |
| void PpapiCommandBufferProxy::CreateGpuFence(uint32_t gpu_fence_id, |
| ClientGpuFence source) { |
| NOTREACHED(); |
| } |
| |
| void PpapiCommandBufferProxy::GetGpuFence( |
| uint32_t gpu_fence_id, |
| base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) { |
| NOTREACHED(); |
| } |
| |
| void PpapiCommandBufferProxy::SetGpuControlClient(gpu::GpuControlClient*) { |
| // TODO(piman): The lost context callback skips past here and goes directly |
| // to the plugin instance. Make it more uniform and use the GpuControlClient. |
| } |
| |
| const gpu::Capabilities& PpapiCommandBufferProxy::GetCapabilities() const { |
| return capabilities_; |
| } |
| |
| bool PpapiCommandBufferProxy::Send(IPC::Message* msg) { |
| DCHECK(last_state_.error == gpu::error::kNoError); |
| |
| // We need to hold the Pepper proxy lock for sync IPC, because the GPU command |
| // buffer may use a sync IPC with another lock held which could lead to lock |
| // and deadlock if we dropped the proxy lock here. |
| // http://crbug.com/418651 |
| if (sender_->SendAndStayLocked(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; |
| } |
| } |
| |
| void PpapiCommandBufferProxy::TryUpdateState() { |
| if (last_state_.error == gpu::error::kNoError) |
| shared_state()->Read(&last_state_); |
| } |
| |
| gpu::CommandBufferSharedState* PpapiCommandBufferProxy::shared_state() const { |
| return reinterpret_cast<gpu::CommandBufferSharedState*>( |
| shared_state_mapping_.memory()); |
| } |
| |
| void PpapiCommandBufferProxy::FlushInternal() { |
| DCHECK(last_state_.error == gpu::error::kNoError); |
| |
| DCHECK(flush_info_->flush_pending); |
| DCHECK_GE(pending_fence_sync_release_, flushed_fence_sync_release_); |
| |
| IPC::Message* message = new PpapiHostMsg_PPBGraphics3D_AsyncFlush( |
| ppapi::API_ID_PPB_GRAPHICS_3D, flush_info_->resource, |
| flush_info_->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); |
| |
| flush_info_->flush_pending = false; |
| flush_info_->resource.SetHostResource(0, 0); |
| flushed_fence_sync_release_ = pending_fence_sync_release_; |
| } |
| |
| } // namespace proxy |
| } // namespace ppapi |