| // Copyright 2016 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 "components/viz/host/gpu_client.h" |
| |
| #include "base/bind.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/numerics/checked_math.h" |
| #include "components/viz/host/gpu_host_impl.h" |
| #include "components/viz/host/host_gpu_memory_buffer_manager.h" |
| #include "gpu/ipc/client/gpu_channel_host.h" |
| #include "gpu/ipc/common/gpu_memory_buffer_impl.h" |
| #include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h" |
| #include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h" |
| |
| namespace viz { |
| namespace { |
| bool IsSizeValid(const gfx::Size& size) { |
| base::CheckedNumeric<int> bytes = size.width(); |
| bytes *= size.height(); |
| return bytes.IsValid(); |
| } |
| } // namespace |
| |
| GpuClient::GpuClient(std::unique_ptr<GpuClientDelegate> delegate, |
| int client_id, |
| uint64_t client_tracing_id, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : delegate_(std::move(delegate)), |
| client_id_(client_id), |
| client_tracing_id_(client_tracing_id), |
| task_runner_(std::move(task_runner)), |
| weak_factory_(this) { |
| DCHECK(delegate_); |
| gpu_bindings_.set_connection_error_handler( |
| base::BindRepeating(&GpuClient::OnError, base::Unretained(this), |
| ErrorReason::kConnectionLost)); |
| } |
| |
| GpuClient::~GpuClient() { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| gpu_bindings_.CloseAllBindings(); |
| OnError(ErrorReason::kInDestructor); |
| } |
| |
| void GpuClient::Add(ws::mojom::GpuRequest request) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| gpu_bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| void GpuClient::OnError(ErrorReason reason) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| ClearCallback(); |
| if (gpu_bindings_.empty() && delegate_) { |
| if (auto* gpu_memory_buffer_manager = |
| delegate_->GetGpuMemoryBufferManager()) { |
| gpu_memory_buffer_manager->DestroyAllGpuMemoryBufferForClient(client_id_); |
| } |
| } |
| if (reason == ErrorReason::kConnectionLost && connection_error_handler_) |
| std::move(connection_error_handler_).Run(this); |
| } |
| |
| void GpuClient::PreEstablishGpuChannel() { |
| if (task_runner_->RunsTasksInCurrentSequence()) { |
| EstablishGpuChannel(EstablishGpuChannelCallback()); |
| } else { |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&GpuClient::EstablishGpuChannel, base::Unretained(this), |
| EstablishGpuChannelCallback())); |
| } |
| } |
| |
| void GpuClient::SetConnectionErrorHandler( |
| ConnectionErrorHandlerClosure connection_error_handler) { |
| connection_error_handler_ = std::move(connection_error_handler); |
| } |
| |
| base::WeakPtr<GpuClient> GpuClient::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| void GpuClient::OnEstablishGpuChannel( |
| mojo::ScopedMessagePipeHandle channel_handle, |
| const gpu::GPUInfo& gpu_info, |
| const gpu::GpuFeatureInfo& gpu_feature_info, |
| GpuHostImpl::EstablishChannelStatus status) { |
| DCHECK_EQ(channel_handle.is_valid(), |
| status == GpuHostImpl::EstablishChannelStatus::kSuccess); |
| gpu_channel_requested_ = false; |
| EstablishGpuChannelCallback callback = std::move(callback_); |
| DCHECK(!callback_); |
| |
| if (status == GpuHostImpl::EstablishChannelStatus::kGpuHostInvalid) { |
| // GPU process may have crashed or been killed. Try again. |
| EstablishGpuChannel(std::move(callback)); |
| return; |
| } |
| if (callback) { |
| // A request is waiting. |
| std::move(callback).Run(client_id_, std::move(channel_handle), gpu_info, |
| gpu_feature_info); |
| return; |
| } |
| if (status == GpuHostImpl::EstablishChannelStatus::kSuccess) { |
| // This is the case we pre-establish a channel before a request arrives. |
| // Cache the channel for a future request. |
| channel_handle_ = std::move(channel_handle); |
| gpu_info_ = gpu_info; |
| gpu_feature_info_ = gpu_feature_info; |
| } |
| } |
| |
| void GpuClient::OnCreateGpuMemoryBuffer(CreateGpuMemoryBufferCallback callback, |
| gfx::GpuMemoryBufferHandle handle) { |
| std::move(callback).Run(std::move(handle)); |
| } |
| |
| void GpuClient::ClearCallback() { |
| if (!callback_) |
| return; |
| EstablishGpuChannelCallback callback = std::move(callback_); |
| std::move(callback).Run(client_id_, mojo::ScopedMessagePipeHandle(), |
| gpu::GPUInfo(), gpu::GpuFeatureInfo()); |
| DCHECK(!callback_); |
| } |
| |
| void GpuClient::EstablishGpuChannel(EstablishGpuChannelCallback callback) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| // At most one channel should be requested. So clear previous request first. |
| ClearCallback(); |
| |
| // TODO(crbug.com/874797): Gpu::EstablishGpuChannelSync() is blocking long |
| // enough that hung renderer detection code is killing the renderer. This |
| // UMA measures how long the request to establish a GPU channel takes after it |
| // arrives in the browser process. Remove this UMA after investigating. |
| if (callback) { |
| callback = base::BindOnce( |
| [](base::TimeTicks start_time, EstablishGpuChannelCallback callback, |
| int32_t client_id, mojo::ScopedMessagePipeHandle handle, |
| const gpu::GPUInfo& gpu_info, |
| const gpu::GpuFeatureInfo& gpu_feature_info) { |
| constexpr base::TimeDelta kMinTime = |
| base::TimeDelta::FromMilliseconds(1); |
| constexpr base::TimeDelta kMaxTime = base::TimeDelta::FromMinutes(10); |
| constexpr int kBuckets = 100; |
| |
| base::TimeDelta delta = base::TimeTicks::Now() - start_time; |
| if (handle.is_valid()) { |
| UMA_HISTOGRAM_CUSTOM_TIMES( |
| "GPU.EstablishGpuChannelDuration.Success", delta, kMinTime, |
| kMaxTime, kBuckets); |
| } else { |
| UMA_HISTOGRAM_CUSTOM_TIMES( |
| "GPU.EstablishGpuChannelDuration.Failure", delta, kMinTime, |
| kMaxTime, kBuckets); |
| } |
| |
| std::move(callback).Run(client_id, std::move(handle), gpu_info, |
| gpu_feature_info); |
| }, |
| base::TimeTicks::Now(), std::move(callback)); |
| } |
| |
| if (channel_handle_.is_valid()) { |
| // If a channel has been pre-established and cached, |
| // 1) if callback is valid, return it right away. |
| // 2) if callback is empty, it's PreEstablishGpyChannel() being called |
| // more than once, no need to do anything. |
| if (callback) { |
| std::move(callback).Run(client_id_, std::move(channel_handle_), gpu_info_, |
| gpu_feature_info_); |
| DCHECK(!channel_handle_.is_valid()); |
| } |
| return; |
| } |
| |
| GpuHostImpl* gpu_host = delegate_->EnsureGpuHost(); |
| if (!gpu_host) { |
| if (callback) { |
| std::move(callback).Run(client_id_, mojo::ScopedMessagePipeHandle(), |
| gpu::GPUInfo(), gpu::GpuFeatureInfo()); |
| } |
| return; |
| } |
| |
| callback_ = std::move(callback); |
| if (gpu_channel_requested_) |
| return; |
| gpu_channel_requested_ = true; |
| const bool is_gpu_host = false; |
| gpu_host->EstablishGpuChannel( |
| client_id_, client_tracing_id_, is_gpu_host, |
| base::BindOnce(&GpuClient::OnEstablishGpuChannel, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void GpuClient::CreateJpegDecodeAccelerator( |
| media::mojom::MjpegDecodeAcceleratorRequest jda_request) { |
| if (auto* gpu_host = delegate_->EnsureGpuHost()) { |
| gpu_host->gpu_service()->CreateJpegDecodeAccelerator( |
| std::move(jda_request)); |
| } |
| } |
| |
| void GpuClient::CreateVideoEncodeAcceleratorProvider( |
| media::mojom::VideoEncodeAcceleratorProviderRequest vea_provider_request) { |
| if (auto* gpu_host = delegate_->EnsureGpuHost()) { |
| gpu_host->gpu_service()->CreateVideoEncodeAcceleratorProvider( |
| std::move(vea_provider_request)); |
| } |
| } |
| |
| void GpuClient::CreateGpuMemoryBuffer( |
| gfx::GpuMemoryBufferId id, |
| const gfx::Size& size, |
| gfx::BufferFormat format, |
| gfx::BufferUsage usage, |
| ws::mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback) { |
| auto* gpu_memory_buffer_manager = delegate_->GetGpuMemoryBufferManager(); |
| |
| if (!gpu_memory_buffer_manager || !IsSizeValid(size)) { |
| OnCreateGpuMemoryBuffer(std::move(callback), gfx::GpuMemoryBufferHandle()); |
| return; |
| } |
| |
| gpu_memory_buffer_manager->AllocateGpuMemoryBuffer( |
| id, client_id_, size, format, usage, gpu::kNullSurfaceHandle, |
| base::BindOnce(&GpuClient::OnCreateGpuMemoryBuffer, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void GpuClient::DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id, |
| const gpu::SyncToken& sync_token) { |
| if (auto* gpu_memory_buffer_manager = |
| delegate_->GetGpuMemoryBufferManager()) { |
| gpu_memory_buffer_manager->DestroyGpuMemoryBuffer(id, client_id_, |
| sync_token); |
| } |
| } |
| |
| void GpuClient::CreateGpuMemoryBufferFactory( |
| ws::mojom::GpuMemoryBufferFactoryRequest request) { |
| gpu_memory_buffer_factory_bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| } // namespace viz |