| // Copyright 2013 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 "gpu/command_buffer/service/in_process_command_buffer.h" |
| |
| #include <queue> |
| #include <set> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/lazy_instance.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/sequence_checker.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/thread_task_runner_handle.h" |
| #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" |
| #include "gpu/command_buffer/common/sync_token.h" |
| #include "gpu/command_buffer/common/value_state.h" |
| #include "gpu/command_buffer/service/command_buffer_service.h" |
| #include "gpu/command_buffer/service/context_group.h" |
| #include "gpu/command_buffer/service/gl_context_virtual.h" |
| #include "gpu/command_buffer/service/gpu_scheduler.h" |
| #include "gpu/command_buffer/service/gpu_switches.h" |
| #include "gpu/command_buffer/service/image_factory.h" |
| #include "gpu/command_buffer/service/image_manager.h" |
| #include "gpu/command_buffer/service/mailbox_manager.h" |
| #include "gpu/command_buffer/service/memory_program_cache.h" |
| #include "gpu/command_buffer/service/memory_tracking.h" |
| #include "gpu/command_buffer/service/query_manager.h" |
| #include "gpu/command_buffer/service/sync_point_manager.h" |
| #include "gpu/command_buffer/service/transfer_buffer_manager.h" |
| #include "gpu/command_buffer/service/valuebuffer_manager.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_image.h" |
| #include "ui/gl/gl_image_shared_memory.h" |
| #include "ui/gl/gl_share_group.h" |
| |
| #if defined(OS_ANDROID) |
| #include "gpu/command_buffer/service/stream_texture_manager_in_process_android.h" |
| #include "ui/gl/android/surface_texture.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include <windows.h> |
| #include "base/process/process_handle.h" |
| #endif |
| |
| namespace gpu { |
| |
| namespace { |
| |
| base::StaticAtomicSequenceNumber g_next_command_buffer_id; |
| |
| template <typename T> |
| static void RunTaskWithResult(base::Callback<T(void)> task, |
| T* result, |
| base::WaitableEvent* completion) { |
| *result = task.Run(); |
| completion->Signal(); |
| } |
| |
| struct ScopedOrderNumberProcessor { |
| ScopedOrderNumberProcessor(SyncPointOrderData* order_data, uint32_t order_num) |
| : order_data_(order_data), order_num_(order_num) { |
| order_data_->BeginProcessingOrderNumber(order_num_); |
| } |
| |
| ~ScopedOrderNumberProcessor() { |
| order_data_->FinishProcessingOrderNumber(order_num_); |
| } |
| |
| private: |
| SyncPointOrderData* order_data_; |
| uint32_t order_num_; |
| }; |
| |
| struct GpuInProcessThreadHolder { |
| GpuInProcessThreadHolder() |
| : sync_point_manager(new SyncPointManager(false)), |
| gpu_thread(new GpuInProcessThread(sync_point_manager.get())) {} |
| scoped_ptr<SyncPointManager> sync_point_manager; |
| scoped_refptr<InProcessCommandBuffer::Service> gpu_thread; |
| }; |
| |
| base::LazyInstance<GpuInProcessThreadHolder> g_default_service = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| class ScopedEvent { |
| public: |
| explicit ScopedEvent(base::WaitableEvent* event) : event_(event) {} |
| ~ScopedEvent() { event_->Signal(); } |
| |
| private: |
| base::WaitableEvent* event_; |
| }; |
| |
| base::SharedMemoryHandle ShareToGpuThread( |
| base::SharedMemoryHandle source_handle) { |
| return base::SharedMemory::DuplicateHandle(source_handle); |
| } |
| |
| gfx::GpuMemoryBufferHandle ShareGpuMemoryBufferToGpuThread( |
| const gfx::GpuMemoryBufferHandle& source_handle, |
| bool* requires_sync_point) { |
| switch (source_handle.type) { |
| case gfx::SHARED_MEMORY_BUFFER: { |
| gfx::GpuMemoryBufferHandle handle; |
| handle.type = gfx::SHARED_MEMORY_BUFFER; |
| handle.handle = ShareToGpuThread(source_handle.handle); |
| handle.offset = source_handle.offset; |
| handle.stride = source_handle.stride; |
| *requires_sync_point = false; |
| return handle; |
| } |
| case gfx::IO_SURFACE_BUFFER: |
| case gfx::SURFACE_TEXTURE_BUFFER: |
| case gfx::OZONE_NATIVE_PIXMAP: |
| *requires_sync_point = true; |
| return source_handle; |
| default: |
| NOTREACHED(); |
| return gfx::GpuMemoryBufferHandle(); |
| } |
| } |
| |
| scoped_refptr<InProcessCommandBuffer::Service> GetInitialService( |
| const scoped_refptr<InProcessCommandBuffer::Service>& service) { |
| if (service) |
| return service; |
| |
| // Call base::ThreadTaskRunnerHandle::IsSet() to ensure that it is |
| // instantiated before we create the GPU thread, otherwise shutdown order will |
| // delete the ThreadTaskRunnerHandle before the GPU thread's message loop, |
| // and when the message loop is shutdown, it will recreate |
| // ThreadTaskRunnerHandle, which will re-add a new task to the, AtExitManager, |
| // which causes a deadlock because it's already locked. |
| base::ThreadTaskRunnerHandle::IsSet(); |
| return g_default_service.Get().gpu_thread; |
| } |
| |
| } // anonyous namespace |
| |
| InProcessCommandBuffer::Service::Service() {} |
| |
| InProcessCommandBuffer::Service::~Service() {} |
| |
| scoped_refptr<gfx::GLShareGroup> |
| InProcessCommandBuffer::Service::share_group() { |
| if (!share_group_.get()) |
| share_group_ = new gfx::GLShareGroup; |
| return share_group_; |
| } |
| |
| scoped_refptr<gles2::MailboxManager> |
| InProcessCommandBuffer::Service::mailbox_manager() { |
| if (!mailbox_manager_.get()) { |
| mailbox_manager_ = gles2::MailboxManager::Create(); |
| } |
| return mailbox_manager_; |
| } |
| |
| scoped_refptr<gles2::SubscriptionRefSet> |
| InProcessCommandBuffer::Service::subscription_ref_set() { |
| if (!subscription_ref_set_.get()) { |
| subscription_ref_set_ = new gles2::SubscriptionRefSet(); |
| } |
| return subscription_ref_set_; |
| } |
| |
| scoped_refptr<ValueStateMap> |
| InProcessCommandBuffer::Service::pending_valuebuffer_state() { |
| if (!pending_valuebuffer_state_.get()) { |
| pending_valuebuffer_state_ = new ValueStateMap(); |
| } |
| return pending_valuebuffer_state_; |
| } |
| |
| gpu::gles2::ProgramCache* InProcessCommandBuffer::Service::program_cache() { |
| if (!program_cache_.get() && |
| (gfx::g_driver_gl.ext.b_GL_ARB_get_program_binary || |
| gfx::g_driver_gl.ext.b_GL_OES_get_program_binary) && |
| !base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableGpuProgramCache)) { |
| program_cache_.reset(new gpu::gles2::MemoryProgramCache()); |
| } |
| return program_cache_.get(); |
| } |
| |
| InProcessCommandBuffer::InProcessCommandBuffer( |
| const scoped_refptr<Service>& service) |
| : command_buffer_id_(g_next_command_buffer_id.GetNext()), |
| context_lost_(false), |
| delayed_work_pending_(false), |
| image_factory_(nullptr), |
| last_put_offset_(-1), |
| gpu_memory_buffer_manager_(nullptr), |
| next_fence_sync_release_(1), |
| flushed_fence_sync_release_(0), |
| flush_event_(false, false), |
| service_(GetInitialService(service)), |
| fence_sync_wait_event_(false, false), |
| gpu_thread_weak_ptr_factory_(this) { |
| DCHECK(service_.get()); |
| next_image_id_.GetNext(); |
| } |
| |
| InProcessCommandBuffer::~InProcessCommandBuffer() { |
| Destroy(); |
| } |
| |
| bool InProcessCommandBuffer::MakeCurrent() { |
| CheckSequencedThread(); |
| command_buffer_lock_.AssertAcquired(); |
| |
| if (!context_lost_ && decoder_->MakeCurrent()) |
| return true; |
| DLOG(ERROR) << "Context lost because MakeCurrent failed."; |
| command_buffer_->SetContextLostReason(decoder_->GetContextLostReason()); |
| command_buffer_->SetParseError(gpu::error::kLostContext); |
| return false; |
| } |
| |
| void InProcessCommandBuffer::PumpCommands() { |
| CheckSequencedThread(); |
| command_buffer_lock_.AssertAcquired(); |
| |
| if (!MakeCurrent()) |
| return; |
| |
| gpu_scheduler_->PutChanged(); |
| } |
| |
| bool InProcessCommandBuffer::GetBufferChanged(int32 transfer_buffer_id) { |
| CheckSequencedThread(); |
| command_buffer_lock_.AssertAcquired(); |
| command_buffer_->SetGetBuffer(transfer_buffer_id); |
| return true; |
| } |
| |
| bool InProcessCommandBuffer::Initialize( |
| scoped_refptr<gfx::GLSurface> surface, |
| bool is_offscreen, |
| gfx::AcceleratedWidget window, |
| const gfx::Size& size, |
| const std::vector<int32>& attribs, |
| gfx::GpuPreference gpu_preference, |
| const base::Closure& context_lost_callback, |
| InProcessCommandBuffer* share_group, |
| GpuMemoryBufferManager* gpu_memory_buffer_manager, |
| ImageFactory* image_factory) { |
| DCHECK(!share_group || service_.get() == share_group->service_.get()); |
| context_lost_callback_ = WrapCallback(context_lost_callback); |
| |
| if (surface.get()) { |
| // GPU thread must be the same as client thread due to GLSurface not being |
| // thread safe. |
| sequence_checker_.reset(new base::SequenceChecker); |
| surface_ = surface; |
| } |
| |
| gpu::Capabilities capabilities; |
| InitializeOnGpuThreadParams params(is_offscreen, |
| window, |
| size, |
| attribs, |
| gpu_preference, |
| &capabilities, |
| share_group, |
| image_factory); |
| |
| base::Callback<bool(void)> init_task = |
| base::Bind(&InProcessCommandBuffer::InitializeOnGpuThread, |
| base::Unretained(this), |
| params); |
| |
| base::WaitableEvent completion(true, false); |
| bool result = false; |
| QueueTask( |
| base::Bind(&RunTaskWithResult<bool>, init_task, &result, &completion)); |
| completion.Wait(); |
| |
| gpu_memory_buffer_manager_ = gpu_memory_buffer_manager; |
| |
| if (result) { |
| capabilities_ = capabilities; |
| capabilities_.image = capabilities_.image && gpu_memory_buffer_manager_; |
| } |
| |
| return result; |
| } |
| |
| bool InProcessCommandBuffer::InitializeOnGpuThread( |
| const InitializeOnGpuThreadParams& params) { |
| CheckSequencedThread(); |
| gpu_thread_weak_ptr_ = gpu_thread_weak_ptr_factory_.GetWeakPtr(); |
| |
| DCHECK(params.size.width() >= 0 && params.size.height() >= 0); |
| |
| TransferBufferManager* manager = new TransferBufferManager(nullptr); |
| transfer_buffer_manager_ = manager; |
| manager->Initialize(); |
| |
| scoped_ptr<CommandBufferService> command_buffer( |
| new CommandBufferService(transfer_buffer_manager_.get())); |
| command_buffer->SetPutOffsetChangeCallback(base::Bind( |
| &InProcessCommandBuffer::PumpCommands, gpu_thread_weak_ptr_)); |
| command_buffer->SetParseErrorCallback(base::Bind( |
| &InProcessCommandBuffer::OnContextLost, gpu_thread_weak_ptr_)); |
| |
| if (!command_buffer->Initialize()) { |
| LOG(ERROR) << "Could not initialize command buffer."; |
| DestroyOnGpuThread(); |
| return false; |
| } |
| |
| gl_share_group_ = params.context_group |
| ? params.context_group->gl_share_group_ |
| : service_->share_group(); |
| |
| #if defined(OS_ANDROID) |
| stream_texture_manager_.reset(new StreamTextureManagerInProcess); |
| #endif |
| |
| bool bind_generates_resource = false; |
| decoder_.reset(gles2::GLES2Decoder::Create( |
| params.context_group |
| ? params.context_group->decoder_->GetContextGroup() |
| : new gles2::ContextGroup(service_->mailbox_manager(), NULL, |
| service_->shader_translator_cache(), |
| service_->framebuffer_completeness_cache(), |
| NULL, service_->subscription_ref_set(), |
| service_->pending_valuebuffer_state(), |
| bind_generates_resource))); |
| |
| gpu_scheduler_.reset( |
| new GpuScheduler(command_buffer.get(), decoder_.get(), decoder_.get())); |
| command_buffer->SetGetBufferChangeCallback(base::Bind( |
| &GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get()))); |
| command_buffer_ = command_buffer.Pass(); |
| |
| decoder_->set_engine(gpu_scheduler_.get()); |
| |
| if (!surface_.get()) { |
| if (params.is_offscreen) |
| surface_ = gfx::GLSurface::CreateOffscreenGLSurface(params.size); |
| else |
| surface_ = gfx::GLSurface::CreateViewGLSurface(params.window); |
| } |
| |
| if (!surface_.get()) { |
| LOG(ERROR) << "Could not create GLSurface."; |
| DestroyOnGpuThread(); |
| return false; |
| } |
| |
| sync_point_order_data_ = SyncPointOrderData::Create(); |
| sync_point_client_ = service_->sync_point_manager()->CreateSyncPointClient( |
| sync_point_order_data_, GetNamespaceID(), GetCommandBufferID()); |
| |
| if (service_->UseVirtualizedGLContexts() || |
| decoder_->GetContextGroup() |
| ->feature_info() |
| ->workarounds() |
| .use_virtualized_gl_contexts) { |
| context_ = gl_share_group_->GetSharedContext(); |
| if (!context_.get()) { |
| context_ = gfx::GLContext::CreateGLContext( |
| gl_share_group_.get(), surface_.get(), params.gpu_preference); |
| gl_share_group_->SetSharedContext(context_.get()); |
| } |
| |
| context_ = new GLContextVirtual( |
| gl_share_group_.get(), context_.get(), decoder_->AsWeakPtr()); |
| if (context_->Initialize(surface_.get(), params.gpu_preference)) { |
| VLOG(1) << "Created virtual GL context."; |
| } else { |
| context_ = NULL; |
| } |
| } else { |
| context_ = gfx::GLContext::CreateGLContext( |
| gl_share_group_.get(), surface_.get(), params.gpu_preference); |
| } |
| |
| if (!context_.get()) { |
| LOG(ERROR) << "Could not create GLContext."; |
| DestroyOnGpuThread(); |
| return false; |
| } |
| |
| if (!context_->MakeCurrent(surface_.get())) { |
| LOG(ERROR) << "Could not make context current."; |
| DestroyOnGpuThread(); |
| return false; |
| } |
| |
| if (!decoder_->GetContextGroup()->has_program_cache() && |
| !decoder_->GetContextGroup() |
| ->feature_info() |
| ->workarounds() |
| .disable_program_cache) { |
| decoder_->GetContextGroup()->set_program_cache(service_->program_cache()); |
| } |
| |
| gles2::DisallowedFeatures disallowed_features; |
| disallowed_features.gpu_memory_manager = true; |
| if (!decoder_->Initialize(surface_, |
| context_, |
| params.is_offscreen, |
| params.size, |
| disallowed_features, |
| params.attribs)) { |
| LOG(ERROR) << "Could not initialize decoder."; |
| DestroyOnGpuThread(); |
| return false; |
| } |
| *params.capabilities = decoder_->GetCapabilities(); |
| |
| decoder_->SetWaitSyncPointCallback( |
| base::Bind(&InProcessCommandBuffer::WaitSyncPointOnGpuThread, |
| base::Unretained(this))); |
| decoder_->SetFenceSyncReleaseCallback( |
| base::Bind(&InProcessCommandBuffer::FenceSyncReleaseOnGpuThread, |
| base::Unretained(this))); |
| decoder_->SetWaitFenceSyncCallback( |
| base::Bind(&InProcessCommandBuffer::WaitFenceSyncOnGpuThread, |
| base::Unretained(this))); |
| |
| image_factory_ = params.image_factory; |
| |
| return true; |
| } |
| |
| void InProcessCommandBuffer::Destroy() { |
| CheckSequencedThread(); |
| |
| base::WaitableEvent completion(true, false); |
| bool result = false; |
| base::Callback<bool(void)> destroy_task = base::Bind( |
| &InProcessCommandBuffer::DestroyOnGpuThread, base::Unretained(this)); |
| QueueTask( |
| base::Bind(&RunTaskWithResult<bool>, destroy_task, &result, &completion)); |
| completion.Wait(); |
| } |
| |
| bool InProcessCommandBuffer::DestroyOnGpuThread() { |
| CheckSequencedThread(); |
| gpu_thread_weak_ptr_factory_.InvalidateWeakPtrs(); |
| command_buffer_.reset(); |
| // Clean up GL resources if possible. |
| bool have_context = context_.get() && context_->MakeCurrent(surface_.get()); |
| if (decoder_) { |
| decoder_->Destroy(have_context); |
| decoder_.reset(); |
| } |
| context_ = NULL; |
| surface_ = NULL; |
| sync_point_client_ = NULL; |
| if (sync_point_order_data_) { |
| sync_point_order_data_->Destroy(); |
| sync_point_order_data_ = nullptr; |
| } |
| gl_share_group_ = NULL; |
| #if defined(OS_ANDROID) |
| stream_texture_manager_.reset(); |
| #endif |
| |
| return true; |
| } |
| |
| void InProcessCommandBuffer::CheckSequencedThread() { |
| DCHECK(!sequence_checker_ || |
| sequence_checker_->CalledOnValidSequencedThread()); |
| } |
| |
| void InProcessCommandBuffer::OnContextLost() { |
| CheckSequencedThread(); |
| if (!context_lost_callback_.is_null()) { |
| context_lost_callback_.Run(); |
| context_lost_callback_.Reset(); |
| } |
| |
| context_lost_ = true; |
| } |
| |
| CommandBuffer::State InProcessCommandBuffer::GetStateFast() { |
| CheckSequencedThread(); |
| base::AutoLock lock(state_after_last_flush_lock_); |
| if (state_after_last_flush_.generation - last_state_.generation < 0x80000000U) |
| last_state_ = state_after_last_flush_; |
| return last_state_; |
| } |
| |
| CommandBuffer::State InProcessCommandBuffer::GetLastState() { |
| CheckSequencedThread(); |
| return last_state_; |
| } |
| |
| int32 InProcessCommandBuffer::GetLastToken() { |
| CheckSequencedThread(); |
| GetStateFast(); |
| return last_state_.token; |
| } |
| |
| void InProcessCommandBuffer::FlushOnGpuThread(int32 put_offset, |
| uint32_t order_num) { |
| CheckSequencedThread(); |
| ScopedEvent handle_flush(&flush_event_); |
| base::AutoLock lock(command_buffer_lock_); |
| |
| { |
| ScopedOrderNumberProcessor scoped_order_num(sync_point_order_data_.get(), |
| order_num); |
| command_buffer_->Flush(put_offset); |
| { |
| // Update state before signaling the flush event. |
| base::AutoLock lock(state_after_last_flush_lock_); |
| state_after_last_flush_ = command_buffer_->GetLastState(); |
| } |
| DCHECK((!error::IsError(state_after_last_flush_.error) && !context_lost_) || |
| (error::IsError(state_after_last_flush_.error) && context_lost_)); |
| |
| // Currently the in process command buffer does not support being |
| // descheduled, if it does we would need to back off on calling the finish |
| // processing number function until the message is rescheduled and finished |
| // processing. This DCHECK is to enforce this. |
| DCHECK(context_lost_ || put_offset == state_after_last_flush_.get_offset); |
| } |
| |
| // If we've processed all pending commands but still have pending queries, |
| // pump idle work until the query is passed. |
| if (put_offset == state_after_last_flush_.get_offset && |
| (gpu_scheduler_->HasMoreIdleWork() || |
| gpu_scheduler_->HasPendingQueries())) { |
| ScheduleDelayedWorkOnGpuThread(); |
| } |
| } |
| |
| void InProcessCommandBuffer::PerformDelayedWork() { |
| CheckSequencedThread(); |
| delayed_work_pending_ = false; |
| base::AutoLock lock(command_buffer_lock_); |
| if (MakeCurrent()) { |
| gpu_scheduler_->PerformIdleWork(); |
| gpu_scheduler_->ProcessPendingQueries(); |
| if (gpu_scheduler_->HasMoreIdleWork() || |
| gpu_scheduler_->HasPendingQueries()) { |
| ScheduleDelayedWorkOnGpuThread(); |
| } |
| } |
| } |
| |
| void InProcessCommandBuffer::ScheduleDelayedWorkOnGpuThread() { |
| CheckSequencedThread(); |
| if (delayed_work_pending_) |
| return; |
| delayed_work_pending_ = true; |
| service_->ScheduleDelayedWork(base::Bind( |
| &InProcessCommandBuffer::PerformDelayedWork, gpu_thread_weak_ptr_)); |
| } |
| |
| void InProcessCommandBuffer::Flush(int32 put_offset) { |
| CheckSequencedThread(); |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| |
| if (last_put_offset_ == put_offset) |
| return; |
| |
| SyncPointManager* sync_manager = service_->sync_point_manager(); |
| const uint32_t order_num = |
| sync_point_order_data_->GenerateUnprocessedOrderNumber(sync_manager); |
| last_put_offset_ = put_offset; |
| base::Closure task = base::Bind(&InProcessCommandBuffer::FlushOnGpuThread, |
| gpu_thread_weak_ptr_, |
| put_offset, |
| order_num); |
| QueueTask(task); |
| |
| flushed_fence_sync_release_ = next_fence_sync_release_ - 1; |
| } |
| |
| void InProcessCommandBuffer::OrderingBarrier(int32 put_offset) { |
| Flush(put_offset); |
| } |
| |
| void InProcessCommandBuffer::WaitForTokenInRange(int32 start, int32 end) { |
| CheckSequencedThread(); |
| while (!InRange(start, end, GetLastToken()) && |
| last_state_.error == gpu::error::kNoError) |
| flush_event_.Wait(); |
| } |
| |
| void InProcessCommandBuffer::WaitForGetOffsetInRange(int32 start, int32 end) { |
| CheckSequencedThread(); |
| |
| GetStateFast(); |
| while (!InRange(start, end, last_state_.get_offset) && |
| last_state_.error == gpu::error::kNoError) { |
| flush_event_.Wait(); |
| GetStateFast(); |
| } |
| } |
| |
| void InProcessCommandBuffer::SetGetBuffer(int32 shm_id) { |
| CheckSequencedThread(); |
| if (last_state_.error != gpu::error::kNoError) |
| return; |
| |
| base::WaitableEvent completion(true, false); |
| base::Closure task = |
| base::Bind(&InProcessCommandBuffer::SetGetBufferOnGpuThread, |
| base::Unretained(this), shm_id, &completion); |
| QueueTask(task); |
| completion.Wait(); |
| |
| { |
| base::AutoLock lock(state_after_last_flush_lock_); |
| state_after_last_flush_ = command_buffer_->GetLastState(); |
| } |
| } |
| |
| void InProcessCommandBuffer::SetGetBufferOnGpuThread( |
| int32 shm_id, |
| base::WaitableEvent* completion) { |
| base::AutoLock lock(command_buffer_lock_); |
| command_buffer_->SetGetBuffer(shm_id); |
| last_put_offset_ = 0; |
| completion->Signal(); |
| } |
| |
| scoped_refptr<Buffer> InProcessCommandBuffer::CreateTransferBuffer(size_t size, |
| int32* id) { |
| CheckSequencedThread(); |
| base::AutoLock lock(command_buffer_lock_); |
| return command_buffer_->CreateTransferBuffer(size, id); |
| } |
| |
| void InProcessCommandBuffer::DestroyTransferBuffer(int32 id) { |
| CheckSequencedThread(); |
| base::Closure task = |
| base::Bind(&InProcessCommandBuffer::DestroyTransferBufferOnGpuThread, |
| base::Unretained(this), |
| id); |
| |
| QueueTask(task); |
| } |
| |
| void InProcessCommandBuffer::DestroyTransferBufferOnGpuThread(int32 id) { |
| base::AutoLock lock(command_buffer_lock_); |
| command_buffer_->DestroyTransferBuffer(id); |
| } |
| |
| gpu::Capabilities InProcessCommandBuffer::GetCapabilities() { |
| return capabilities_; |
| } |
| |
| int32 InProcessCommandBuffer::CreateImage(ClientBuffer buffer, |
| size_t width, |
| size_t height, |
| unsigned internalformat) { |
| CheckSequencedThread(); |
| |
| DCHECK(gpu_memory_buffer_manager_); |
| gfx::GpuMemoryBuffer* gpu_memory_buffer = |
| gpu_memory_buffer_manager_->GpuMemoryBufferFromClientBuffer(buffer); |
| DCHECK(gpu_memory_buffer); |
| |
| int32 new_id = next_image_id_.GetNext(); |
| |
| DCHECK(gpu::ImageFactory::IsGpuMemoryBufferFormatSupported( |
| gpu_memory_buffer->GetFormat(), capabilities_)); |
| DCHECK(gpu::ImageFactory::IsImageFormatCompatibleWithGpuMemoryBufferFormat( |
| internalformat, gpu_memory_buffer->GetFormat())); |
| |
| // This handle is owned by the GPU thread and must be passed to it or it |
| // will leak. In otherwords, do not early out on error between here and the |
| // queuing of the CreateImage task below. |
| bool requires_sync_point = false; |
| gfx::GpuMemoryBufferHandle handle = |
| ShareGpuMemoryBufferToGpuThread(gpu_memory_buffer->GetHandle(), |
| &requires_sync_point); |
| |
| SyncPointManager* sync_manager = service_->sync_point_manager(); |
| const uint32_t order_num = |
| sync_point_order_data_->GenerateUnprocessedOrderNumber(sync_manager); |
| |
| uint64_t fence_sync = 0; |
| if (requires_sync_point) { |
| fence_sync = GenerateFenceSyncRelease(); |
| |
| // Previous fence syncs should be flushed already. |
| DCHECK_EQ(fence_sync - 1, flushed_fence_sync_release_); |
| } |
| |
| QueueTask(base::Bind(&InProcessCommandBuffer::CreateImageOnGpuThread, |
| base::Unretained(this), new_id, handle, |
| gfx::Size(width, height), gpu_memory_buffer->GetFormat(), |
| internalformat, order_num, fence_sync)); |
| |
| if (fence_sync) { |
| flushed_fence_sync_release_ = fence_sync; |
| SyncToken sync_token(GetNamespaceID(), GetCommandBufferID(), fence_sync); |
| sync_token.SetVerifyFlush(); |
| gpu_memory_buffer_manager_->SetDestructionSyncToken(gpu_memory_buffer, |
| sync_token); |
| } |
| |
| return new_id; |
| } |
| |
| void InProcessCommandBuffer::CreateImageOnGpuThread( |
| int32 id, |
| const gfx::GpuMemoryBufferHandle& handle, |
| const gfx::Size& size, |
| gfx::BufferFormat format, |
| uint32 internalformat, |
| uint32_t order_num, |
| uint64_t fence_sync) { |
| ScopedOrderNumberProcessor scoped_order_num(sync_point_order_data_.get(), |
| order_num); |
| if (!decoder_) |
| return; |
| |
| gpu::gles2::ImageManager* image_manager = decoder_->GetImageManager(); |
| DCHECK(image_manager); |
| if (image_manager->LookupImage(id)) { |
| LOG(ERROR) << "Image already exists with same ID."; |
| return; |
| } |
| |
| switch (handle.type) { |
| case gfx::SHARED_MEMORY_BUFFER: { |
| if (!base::IsValueInRangeForNumericType<size_t>(handle.stride)) { |
| LOG(ERROR) << "Invalid stride for image."; |
| return; |
| } |
| scoped_refptr<gl::GLImageSharedMemory> image( |
| new gl::GLImageSharedMemory(size, internalformat)); |
| if (!image->Initialize(handle.handle, handle.id, format, handle.offset, |
| handle.stride)) { |
| LOG(ERROR) << "Failed to initialize image."; |
| return; |
| } |
| |
| image_manager->AddImage(image.get(), id); |
| break; |
| } |
| default: { |
| if (!image_factory_) { |
| LOG(ERROR) << "Image factory missing but required by buffer type."; |
| return; |
| } |
| |
| // Note: this assumes that client ID is always 0. |
| const int kClientId = 0; |
| |
| scoped_refptr<gl::GLImage> image = |
| image_factory_->CreateImageForGpuMemoryBuffer( |
| handle, size, format, internalformat, kClientId); |
| if (!image.get()) { |
| LOG(ERROR) << "Failed to create image for buffer."; |
| return; |
| } |
| |
| image_manager->AddImage(image.get(), id); |
| break; |
| } |
| } |
| |
| if (fence_sync) { |
| sync_point_client_->ReleaseFenceSync(fence_sync); |
| } |
| } |
| |
| void InProcessCommandBuffer::DestroyImage(int32 id) { |
| CheckSequencedThread(); |
| |
| QueueTask(base::Bind(&InProcessCommandBuffer::DestroyImageOnGpuThread, |
| base::Unretained(this), |
| id)); |
| } |
| |
| void InProcessCommandBuffer::DestroyImageOnGpuThread(int32 id) { |
| if (!decoder_) |
| return; |
| |
| gpu::gles2::ImageManager* image_manager = decoder_->GetImageManager(); |
| DCHECK(image_manager); |
| if (!image_manager->LookupImage(id)) { |
| LOG(ERROR) << "Image with ID doesn't exist."; |
| return; |
| } |
| |
| image_manager->RemoveImage(id); |
| } |
| |
| int32 InProcessCommandBuffer::CreateGpuMemoryBufferImage( |
| size_t width, |
| size_t height, |
| unsigned internalformat, |
| unsigned usage) { |
| CheckSequencedThread(); |
| |
| DCHECK(gpu_memory_buffer_manager_); |
| scoped_ptr<gfx::GpuMemoryBuffer> buffer( |
| gpu_memory_buffer_manager_->AllocateGpuMemoryBuffer( |
| gfx::Size(width, height), |
| gpu::ImageFactory::DefaultBufferFormatForImageFormat(internalformat), |
| gfx::BufferUsage::SCANOUT)); |
| if (!buffer) |
| return -1; |
| |
| return CreateImage(buffer->AsClientBuffer(), width, height, internalformat); |
| } |
| |
| uint32 InProcessCommandBuffer::InsertSyncPoint() { |
| uint32 sync_point = service_->sync_point_manager()->GenerateSyncPoint(); |
| QueueTask(base::Bind(&InProcessCommandBuffer::RetireSyncPointOnGpuThread, |
| base::Unretained(this), |
| sync_point)); |
| return sync_point; |
| } |
| |
| uint32 InProcessCommandBuffer::InsertFutureSyncPoint() { |
| return service_->sync_point_manager()->GenerateSyncPoint(); |
| } |
| |
| void InProcessCommandBuffer::RetireSyncPoint(uint32 sync_point) { |
| QueueTask(base::Bind(&InProcessCommandBuffer::RetireSyncPointOnGpuThread, |
| base::Unretained(this), |
| sync_point)); |
| } |
| |
| void InProcessCommandBuffer::RetireSyncPointOnGpuThread(uint32 sync_point) { |
| gles2::MailboxManager* mailbox_manager = |
| decoder_->GetContextGroup()->mailbox_manager(); |
| if (mailbox_manager->UsesSync()) { |
| bool make_current_success = false; |
| { |
| base::AutoLock lock(command_buffer_lock_); |
| make_current_success = MakeCurrent(); |
| } |
| if (make_current_success) { |
| // Old sync points are global and do not have a command buffer ID, |
| // We can simply use the GPUIO namespace with 0 for the command buffer ID |
| // (under normal circumstances 0 is invalid so will not be used) until |
| // the old sync points are replaced. |
| SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, 0, sync_point); |
| mailbox_manager->PushTextureUpdates(sync_token); |
| } |
| } |
| service_->sync_point_manager()->RetireSyncPoint(sync_point); |
| } |
| |
| void InProcessCommandBuffer::SignalSyncPoint(unsigned sync_point, |
| const base::Closure& callback) { |
| service_->sync_point_manager()->AddSyncPointCallback(sync_point, |
| WrapCallback(callback)); |
| } |
| |
| bool InProcessCommandBuffer::WaitSyncPointOnGpuThread(unsigned sync_point) { |
| service_->sync_point_manager()->WaitSyncPoint(sync_point); |
| gles2::MailboxManager* mailbox_manager = |
| decoder_->GetContextGroup()->mailbox_manager(); |
| // Old sync points are global and do not have a command buffer ID, |
| // We can simply use the GPUIO namespace with 0 for the command buffer ID |
| // (under normal circumstances 0 is invalid so will not be used) until |
| // the old sync points are replaced. |
| SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, 0, sync_point); |
| mailbox_manager->PullTextureUpdates(sync_token); |
| return true; |
| } |
| |
| void InProcessCommandBuffer::FenceSyncReleaseOnGpuThread(uint64_t release) { |
| DCHECK(!sync_point_client_->client_state()->IsFenceSyncReleased(release)); |
| gles2::MailboxManager* mailbox_manager = |
| decoder_->GetContextGroup()->mailbox_manager(); |
| if (mailbox_manager->UsesSync()) { |
| bool make_current_success = false; |
| { |
| base::AutoLock lock(command_buffer_lock_); |
| make_current_success = MakeCurrent(); |
| } |
| if (make_current_success) { |
| SyncToken sync_token(GetNamespaceID(), GetCommandBufferID(), release); |
| mailbox_manager->PushTextureUpdates(sync_token); |
| } |
| } |
| |
| sync_point_client_->ReleaseFenceSync(release); |
| } |
| |
| bool InProcessCommandBuffer::WaitFenceSyncOnGpuThread( |
| gpu::CommandBufferNamespace namespace_id, |
| uint64_t command_buffer_id, |
| uint64_t release) { |
| gpu::SyncPointManager* sync_point_manager = service_->sync_point_manager(); |
| DCHECK(sync_point_manager); |
| |
| scoped_refptr<gpu::SyncPointClientState> release_state = |
| sync_point_manager->GetSyncPointClientState(namespace_id, |
| command_buffer_id); |
| |
| if (!release_state) |
| return true; |
| |
| if (!release_state->IsFenceSyncReleased(release)) { |
| // Use waitable event which is signalled when the release fence is released. |
| sync_point_client_->Wait( |
| release_state.get(), release, |
| base::Bind(&base::WaitableEvent::Signal, |
| base::Unretained(&fence_sync_wait_event_))); |
| fence_sync_wait_event_.Wait(); |
| } |
| |
| gles2::MailboxManager* mailbox_manager = |
| decoder_->GetContextGroup()->mailbox_manager(); |
| SyncToken sync_token(namespace_id, command_buffer_id, release); |
| mailbox_manager->PullTextureUpdates(sync_token); |
| return true; |
| } |
| |
| void InProcessCommandBuffer::SignalSyncTokenOnGpuThread( |
| const SyncToken& sync_token, const base::Closure& callback) { |
| gpu::SyncPointManager* sync_point_manager = service_->sync_point_manager(); |
| DCHECK(sync_point_manager); |
| |
| scoped_refptr<gpu::SyncPointClientState> release_state = |
| sync_point_manager->GetSyncPointClientState( |
| sync_token.namespace_id(), sync_token.command_buffer_id()); |
| |
| if (!release_state) { |
| callback.Run(); |
| return; |
| } |
| |
| sync_point_client_->Wait(release_state.get(), sync_token.release_count(), |
| WrapCallback(callback)); |
| } |
| |
| void InProcessCommandBuffer::SignalQuery(unsigned query_id, |
| const base::Closure& callback) { |
| CheckSequencedThread(); |
| QueueTask(base::Bind(&InProcessCommandBuffer::SignalQueryOnGpuThread, |
| base::Unretained(this), |
| query_id, |
| WrapCallback(callback))); |
| } |
| |
| void InProcessCommandBuffer::SignalQueryOnGpuThread( |
| unsigned query_id, |
| const base::Closure& callback) { |
| gles2::QueryManager* query_manager_ = decoder_->GetQueryManager(); |
| DCHECK(query_manager_); |
| |
| gles2::QueryManager::Query* query = query_manager_->GetQuery(query_id); |
| if (!query) |
| callback.Run(); |
| else |
| query->AddCallback(callback); |
| } |
| |
| void InProcessCommandBuffer::SetLock(base::Lock*) { |
| } |
| |
| bool InProcessCommandBuffer::IsGpuChannelLost() { |
| // There is no such channel to lose for in-process contexts. This only |
| // makes sense for out-of-process command buffers. |
| return false; |
| } |
| |
| CommandBufferNamespace InProcessCommandBuffer::GetNamespaceID() const { |
| return CommandBufferNamespace::IN_PROCESS; |
| } |
| |
| uint64_t InProcessCommandBuffer::GetCommandBufferID() const { |
| return command_buffer_id_; |
| } |
| |
| uint64_t InProcessCommandBuffer::GenerateFenceSyncRelease() { |
| return next_fence_sync_release_++; |
| } |
| |
| bool InProcessCommandBuffer::IsFenceSyncRelease(uint64_t release) { |
| return release != 0 && release < next_fence_sync_release_; |
| } |
| |
| bool InProcessCommandBuffer::IsFenceSyncFlushed(uint64_t release) { |
| return release <= flushed_fence_sync_release_; |
| } |
| |
| bool InProcessCommandBuffer::IsFenceSyncFlushReceived(uint64_t release) { |
| return IsFenceSyncFlushed(release); |
| } |
| |
| void InProcessCommandBuffer::SignalSyncToken(const SyncToken& sync_token, |
| const base::Closure& callback) { |
| CheckSequencedThread(); |
| QueueTask(base::Bind(&InProcessCommandBuffer::SignalSyncTokenOnGpuThread, |
| base::Unretained(this), |
| sync_token, |
| WrapCallback(callback))); |
| } |
| |
| bool InProcessCommandBuffer::CanWaitUnverifiedSyncToken( |
| const SyncToken* sync_token) { |
| return false; |
| } |
| |
| uint32 InProcessCommandBuffer::CreateStreamTextureOnGpuThread( |
| uint32 client_texture_id) { |
| #if defined(OS_ANDROID) |
| return stream_texture_manager_->CreateStreamTexture( |
| client_texture_id, decoder_->GetContextGroup()->texture_manager()); |
| #else |
| return 0; |
| #endif |
| } |
| |
| gpu::error::Error InProcessCommandBuffer::GetLastError() { |
| CheckSequencedThread(); |
| return last_state_.error; |
| } |
| |
| bool InProcessCommandBuffer::Initialize() { |
| NOTREACHED(); |
| return false; |
| } |
| |
| namespace { |
| |
| void PostCallback( |
| const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| const base::Closure& callback) { |
| // The task_runner.get() check is to support using InProcessCommandBuffer on |
| // a thread without a message loop. |
| if (task_runner.get() && !task_runner->BelongsToCurrentThread()) { |
| task_runner->PostTask(FROM_HERE, callback); |
| } else { |
| callback.Run(); |
| } |
| } |
| |
| void RunOnTargetThread(scoped_ptr<base::Closure> callback) { |
| DCHECK(callback.get()); |
| callback->Run(); |
| } |
| |
| } // anonymous namespace |
| |
| base::Closure InProcessCommandBuffer::WrapCallback( |
| const base::Closure& callback) { |
| // Make sure the callback gets deleted on the target thread by passing |
| // ownership. |
| scoped_ptr<base::Closure> scoped_callback(new base::Closure(callback)); |
| base::Closure callback_on_client_thread = |
| base::Bind(&RunOnTargetThread, base::Passed(&scoped_callback)); |
| base::Closure wrapped_callback = |
| base::Bind(&PostCallback, base::ThreadTaskRunnerHandle::IsSet() |
| ? base::ThreadTaskRunnerHandle::Get() |
| : nullptr, |
| callback_on_client_thread); |
| return wrapped_callback; |
| } |
| |
| #if defined(OS_ANDROID) |
| scoped_refptr<gfx::SurfaceTexture> |
| InProcessCommandBuffer::GetSurfaceTexture(uint32 stream_id) { |
| DCHECK(stream_texture_manager_); |
| return stream_texture_manager_->GetSurfaceTexture(stream_id); |
| } |
| |
| uint32 InProcessCommandBuffer::CreateStreamTexture(uint32 texture_id) { |
| base::WaitableEvent completion(true, false); |
| uint32 stream_id = 0; |
| base::Callback<uint32(void)> task = |
| base::Bind(&InProcessCommandBuffer::CreateStreamTextureOnGpuThread, |
| base::Unretained(this), texture_id); |
| QueueTask( |
| base::Bind(&RunTaskWithResult<uint32>, task, &stream_id, &completion)); |
| completion.Wait(); |
| return stream_id; |
| } |
| #endif |
| |
| GpuInProcessThread::GpuInProcessThread(SyncPointManager* sync_point_manager) |
| : base::Thread("GpuThread"), sync_point_manager_(sync_point_manager) { |
| Start(); |
| } |
| |
| GpuInProcessThread::~GpuInProcessThread() { |
| Stop(); |
| } |
| |
| void GpuInProcessThread::AddRef() const { |
| base::RefCountedThreadSafe<GpuInProcessThread>::AddRef(); |
| } |
| void GpuInProcessThread::Release() const { |
| base::RefCountedThreadSafe<GpuInProcessThread>::Release(); |
| } |
| |
| void GpuInProcessThread::ScheduleTask(const base::Closure& task) { |
| task_runner()->PostTask(FROM_HERE, task); |
| } |
| |
| void GpuInProcessThread::ScheduleDelayedWork(const base::Closure& callback) { |
| // Match delay with GpuCommandBufferStub. |
| task_runner()->PostDelayedTask(FROM_HERE, callback, |
| base::TimeDelta::FromMilliseconds(2)); |
| } |
| |
| bool GpuInProcessThread::UseVirtualizedGLContexts() { |
| return false; |
| } |
| |
| scoped_refptr<gles2::ShaderTranslatorCache> |
| GpuInProcessThread::shader_translator_cache() { |
| if (!shader_translator_cache_.get()) |
| shader_translator_cache_ = new gpu::gles2::ShaderTranslatorCache; |
| return shader_translator_cache_; |
| } |
| |
| scoped_refptr<gles2::FramebufferCompletenessCache> |
| GpuInProcessThread::framebuffer_completeness_cache() { |
| if (!framebuffer_completeness_cache_.get()) |
| framebuffer_completeness_cache_ = |
| new gpu::gles2::FramebufferCompletenessCache; |
| return framebuffer_completeness_cache_; |
| } |
| |
| SyncPointManager* GpuInProcessThread::sync_point_manager() { |
| return sync_point_manager_; |
| } |
| |
| } // namespace gpu |