| // Copyright 2014 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 "mojo/gles2/command_buffer_client_impl.h" |
| |
| #include <limits> |
| |
| #include "base/logging.h" |
| #include "base/process/process_handle.h" |
| #include "services/gles2/command_buffer_type_conversions.h" |
| #include "services/gles2/mojo_buffer_backing.h" |
| |
| namespace mojo { |
| namespace gles2 { |
| |
| namespace { |
| |
| bool CreateMapAndDupSharedBuffer(size_t size, |
| void** memory, |
| mojo::ScopedSharedBufferHandle* handle, |
| mojo::ScopedSharedBufferHandle* duped) { |
| MojoResult result = mojo::CreateSharedBuffer(NULL, size, handle); |
| if (result != MOJO_RESULT_OK) |
| return false; |
| DCHECK(handle->is_valid()); |
| |
| result = mojo::DuplicateBuffer(handle->get(), NULL, duped); |
| if (result != MOJO_RESULT_OK) |
| return false; |
| DCHECK(duped->is_valid()); |
| |
| result = mojo::MapBuffer( |
| handle->get(), 0, size, memory, MOJO_MAP_BUFFER_FLAG_NONE); |
| if (result != MOJO_RESULT_OK) |
| return false; |
| DCHECK(*memory); |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| CommandBufferDelegate::~CommandBufferDelegate() {} |
| |
| void CommandBufferDelegate::ContextLost() {} |
| |
| class CommandBufferClientImpl::SyncClientImpl : public CommandBufferSyncClient { |
| public: |
| SyncClientImpl(CommandBufferSyncClientPtr* ptr, |
| const MojoAsyncWaiter* async_waiter) |
| : initialized_successfully_(false), binding_(this, ptr, async_waiter) {} |
| |
| bool WaitForInitialization() { |
| if (!binding_.WaitForIncomingMethodCall()) |
| return false; |
| return initialized_successfully_; |
| } |
| |
| CommandBufferStatePtr WaitForProgress() { |
| if (!binding_.WaitForIncomingMethodCall()) |
| return CommandBufferStatePtr(); |
| return command_buffer_state_.Pass(); |
| } |
| |
| gpu::Capabilities GetCapabilities() { |
| return capabilities_.To<gpu::Capabilities>(); |
| } |
| |
| private: |
| // CommandBufferSyncClient methods: |
| void DidInitialize(bool success, GpuCapabilitiesPtr capabilities) override { |
| initialized_successfully_ = success; |
| capabilities_ = capabilities.Pass(); |
| } |
| void DidMakeProgress(CommandBufferStatePtr state) override { |
| command_buffer_state_ = state.Pass(); |
| } |
| |
| bool initialized_successfully_; |
| GpuCapabilitiesPtr capabilities_; |
| CommandBufferStatePtr command_buffer_state_; |
| Binding<CommandBufferSyncClient> binding_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SyncClientImpl); |
| }; |
| |
| class CommandBufferClientImpl::SyncPointClientImpl |
| : public CommandBufferSyncPointClient { |
| public: |
| SyncPointClientImpl(CommandBufferSyncPointClientPtr* ptr, |
| const MojoAsyncWaiter* async_waiter) |
| : sync_point_(0u), binding_(this, ptr, async_waiter) {} |
| |
| uint32_t WaitForInsertSyncPoint() { |
| if (!binding_.WaitForIncomingMethodCall()) |
| return 0u; |
| uint32_t result = sync_point_; |
| sync_point_ = 0u; |
| return result; |
| } |
| |
| private: |
| void DidInsertSyncPoint(uint32_t sync_point) override { |
| sync_point_ = sync_point; |
| } |
| |
| uint32_t sync_point_; |
| |
| Binding<CommandBufferSyncPointClient> binding_; |
| }; |
| |
| CommandBufferClientImpl::CommandBufferClientImpl( |
| CommandBufferDelegate* delegate, |
| const MojoAsyncWaiter* async_waiter, |
| ScopedMessagePipeHandle command_buffer_handle) |
| : delegate_(delegate), |
| shared_state_(NULL), |
| last_put_offset_(-1), |
| next_transfer_buffer_id_(0), |
| async_waiter_(async_waiter) { |
| command_buffer_.Bind(command_buffer_handle.Pass(), async_waiter); |
| command_buffer_.set_error_handler(this); |
| command_buffer_.set_client(this); |
| } |
| |
| CommandBufferClientImpl::~CommandBufferClientImpl() {} |
| |
| bool CommandBufferClientImpl::Initialize() { |
| const size_t kSharedStateSize = sizeof(gpu::CommandBufferSharedState); |
| void* memory = NULL; |
| mojo::ScopedSharedBufferHandle duped; |
| bool result = CreateMapAndDupSharedBuffer( |
| kSharedStateSize, &memory, &shared_state_handle_, &duped); |
| if (!result) |
| return false; |
| |
| shared_state_ = static_cast<gpu::CommandBufferSharedState*>(memory); |
| |
| shared_state()->Initialize(); |
| |
| CommandBufferSyncClientPtr sync_client; |
| sync_client_impl_.reset(new SyncClientImpl(&sync_client, async_waiter_)); |
| |
| CommandBufferSyncPointClientPtr sync_point_client; |
| sync_point_client_impl_.reset( |
| new SyncPointClientImpl(&sync_point_client, async_waiter_)); |
| |
| command_buffer_->Initialize(sync_client.Pass(), sync_point_client.Pass(), |
| duped.Pass()); |
| |
| // Wait for DidInitialize to come on the sync client pipe. |
| if (!sync_client_impl_->WaitForInitialization()) { |
| VLOG(1) << "Channel encountered error while creating command buffer"; |
| return false; |
| } |
| capabilities_ = sync_client_impl_->GetCapabilities(); |
| return true; |
| } |
| |
| gpu::CommandBuffer::State CommandBufferClientImpl::GetLastState() { |
| return last_state_; |
| } |
| |
| int32 CommandBufferClientImpl::GetLastToken() { |
| TryUpdateState(); |
| return last_state_.token; |
| } |
| |
| void CommandBufferClientImpl::Flush(int32 put_offset) { |
| if (last_put_offset_ == put_offset) |
| return; |
| |
| last_put_offset_ = put_offset; |
| command_buffer_->Flush(put_offset); |
| } |
| |
| void CommandBufferClientImpl::WaitForTokenInRange(int32 start, int32 end) { |
| TryUpdateState(); |
| while (!InRange(start, end, last_state_.token) && |
| last_state_.error == gpu::error::kNoError) { |
| MakeProgressAndUpdateState(); |
| TryUpdateState(); |
| } |
| } |
| |
| void CommandBufferClientImpl::WaitForGetOffsetInRange(int32 start, int32 end) { |
| TryUpdateState(); |
| while (!InRange(start, end, last_state_.get_offset) && |
| last_state_.error == gpu::error::kNoError) { |
| MakeProgressAndUpdateState(); |
| TryUpdateState(); |
| } |
| } |
| |
| void CommandBufferClientImpl::SetGetBuffer(int32 shm_id) { |
| command_buffer_->SetGetBuffer(shm_id); |
| last_put_offset_ = -1; |
| } |
| |
| scoped_refptr<gpu::Buffer> CommandBufferClientImpl::CreateTransferBuffer( |
| size_t size, |
| int32* id) { |
| if (size >= std::numeric_limits<uint32_t>::max()) |
| return NULL; |
| |
| void* memory = NULL; |
| mojo::ScopedSharedBufferHandle handle; |
| mojo::ScopedSharedBufferHandle duped; |
| if (!CreateMapAndDupSharedBuffer(size, &memory, &handle, &duped)) |
| return NULL; |
| |
| *id = ++next_transfer_buffer_id_; |
| |
| command_buffer_->RegisterTransferBuffer( |
| *id, duped.Pass(), static_cast<uint32_t>(size)); |
| |
| scoped_ptr<gpu::BufferBacking> backing( |
| new MojoBufferBacking(handle.Pass(), memory, size)); |
| scoped_refptr<gpu::Buffer> buffer(new gpu::Buffer(backing.Pass())); |
| return buffer; |
| } |
| |
| void CommandBufferClientImpl::DestroyTransferBuffer(int32 id) { |
| command_buffer_->DestroyTransferBuffer(id); |
| } |
| |
| gpu::Capabilities CommandBufferClientImpl::GetCapabilities() { |
| return capabilities_; |
| } |
| |
| int32_t CommandBufferClientImpl::CreateImage(ClientBuffer buffer, |
| size_t width, |
| size_t height, |
| unsigned internalformat) { |
| // TODO(piman) |
| NOTIMPLEMENTED(); |
| return -1; |
| } |
| |
| void CommandBufferClientImpl::DestroyImage(int32 id) { |
| // TODO(piman) |
| NOTIMPLEMENTED(); |
| } |
| |
| int32_t CommandBufferClientImpl::CreateGpuMemoryBufferImage( |
| size_t width, |
| size_t height, |
| unsigned internalformat, |
| unsigned usage) { |
| // TODO(piman) |
| NOTIMPLEMENTED(); |
| return -1; |
| } |
| |
| uint32_t CommandBufferClientImpl::InsertSyncPoint() { |
| command_buffer_->InsertSyncPoint(true); |
| return sync_point_client_impl_->WaitForInsertSyncPoint(); |
| } |
| |
| uint32_t CommandBufferClientImpl::InsertFutureSyncPoint() { |
| command_buffer_->InsertSyncPoint(false); |
| return sync_point_client_impl_->WaitForInsertSyncPoint(); |
| } |
| |
| void CommandBufferClientImpl::RetireSyncPoint(uint32_t sync_point) { |
| command_buffer_->RetireSyncPoint(sync_point); |
| } |
| |
| void CommandBufferClientImpl::SignalSyncPoint(uint32_t sync_point, |
| const base::Closure& callback) { |
| // TODO(piman) |
| } |
| |
| void CommandBufferClientImpl::SignalQuery(uint32_t query, |
| const base::Closure& callback) { |
| // TODO(piman) |
| NOTIMPLEMENTED(); |
| } |
| |
| void CommandBufferClientImpl::SetSurfaceVisible(bool visible) { |
| // TODO(piman) |
| NOTIMPLEMENTED(); |
| } |
| |
| uint32_t CommandBufferClientImpl::CreateStreamTexture(uint32_t texture_id) { |
| // TODO(piman) |
| NOTIMPLEMENTED(); |
| return 0; |
| } |
| |
| void CommandBufferClientImpl::DidDestroy() { |
| LostContext(gpu::error::kUnknown); |
| } |
| |
| void CommandBufferClientImpl::LostContext(int32_t lost_reason) { |
| last_state_.error = gpu::error::kLostContext; |
| last_state_.context_lost_reason = |
| static_cast<gpu::error::ContextLostReason>(lost_reason); |
| delegate_->ContextLost(); |
| } |
| |
| void CommandBufferClientImpl::OnConnectionError() { |
| LostContext(gpu::error::kUnknown); |
| } |
| |
| void CommandBufferClientImpl::TryUpdateState() { |
| if (last_state_.error == gpu::error::kNoError) |
| shared_state()->Read(&last_state_); |
| } |
| |
| void CommandBufferClientImpl::MakeProgressAndUpdateState() { |
| command_buffer_->MakeProgress(last_state_.get_offset); |
| |
| CommandBufferStatePtr state = sync_client_impl_->WaitForProgress(); |
| if (!state) { |
| VLOG(1) << "Channel encountered error while waiting for command buffer"; |
| // TODO(piman): is it ok for this to re-enter? |
| DidDestroy(); |
| return; |
| } |
| |
| if (state->generation - last_state_.generation < 0x80000000U) |
| last_state_ = state.To<State>(); |
| } |
| |
| } // namespace gles2 |
| } // namespace mojo |