blob: 4280cda2fe0d2affb186acc3468da417258fe9db [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
#include <utility>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "gpu/ipc/common/gpu_memory_buffer_impl.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "mojo/public/cpp/system/buffer.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/buffer_format_util.h"
namespace viz {
ClientGpuMemoryBufferManager::ClientGpuMemoryBufferManager(
mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu)
: thread_("GpuMemoryThread"),
gpu_memory_buffer_support_(
std::make_unique<gpu::GpuMemoryBufferSupport>()),
pool_(base::MakeRefCounted<base::UnsafeSharedMemoryPool>()) {
CHECK(thread_.Start());
// The thread is owned by this object. Which means the task will not run if
// the object has been destroyed. So Unretained() is safe.
thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&ClientGpuMemoryBufferManager::InitThread,
base::Unretained(this), std::move(gpu)));
}
ClientGpuMemoryBufferManager::~ClientGpuMemoryBufferManager() {
thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&ClientGpuMemoryBufferManager::TearDownThread,
base::Unretained(this)));
thread_.Stop();
}
void ClientGpuMemoryBufferManager::InitThread(
mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu_remote) {
gpu_.Bind(std::move(gpu_remote));
gpu_.set_disconnect_handler(
base::BindOnce(&ClientGpuMemoryBufferManager::DisconnectGpuOnThread,
base::Unretained(this)));
weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
}
void ClientGpuMemoryBufferManager::TearDownThread() {
weak_ptr_factory_.InvalidateWeakPtrs();
DisconnectGpuOnThread();
}
void ClientGpuMemoryBufferManager::DisconnectGpuOnThread() {
if (!gpu_.is_bound())
return;
gpu_.reset();
for (auto* waiter : pending_allocation_waiters_)
waiter->Signal();
pending_allocation_waiters_.clear();
}
void ClientGpuMemoryBufferManager::AllocateGpuMemoryBufferOnThread(
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
gfx::GpuMemoryBufferHandle* handle,
base::WaitableEvent* wait) {
DCHECK(thread_.task_runner()->BelongsToCurrentThread());
if (!gpu_) {
// The Gpu interface may have been disconnected, in which case we can't
// fulfill the request.
wait->Signal();
return;
}
// |handle| and |wait| are both on the stack, and will be alive until |wait|
// is signaled. So it is safe for OnGpuMemoryBufferAllocated() to operate on
// these.
pending_allocation_waiters_.insert(wait);
gpu_->CreateGpuMemoryBuffer(
gfx::GpuMemoryBufferId(++counter_), size, format, usage,
base::BindOnce(
&ClientGpuMemoryBufferManager::OnGpuMemoryBufferAllocatedOnThread,
base::Unretained(this), handle, wait));
}
void ClientGpuMemoryBufferManager::OnGpuMemoryBufferAllocatedOnThread(
gfx::GpuMemoryBufferHandle* ret_handle,
base::WaitableEvent* wait,
gfx::GpuMemoryBufferHandle handle) {
auto it = pending_allocation_waiters_.find(wait);
DCHECK(it != pending_allocation_waiters_.end());
pending_allocation_waiters_.erase(it);
*ret_handle = std::move(handle);
wait->Signal();
}
void ClientGpuMemoryBufferManager::DeletedGpuMemoryBuffer(
gfx::GpuMemoryBufferId id) {
if (!thread_.task_runner()->BelongsToCurrentThread()) {
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ClientGpuMemoryBufferManager::DeletedGpuMemoryBuffer,
base::Unretained(this), id));
return;
}
if (gpu_) {
gpu_->DestroyGpuMemoryBuffer(id);
}
}
std::unique_ptr<gfx::GpuMemoryBuffer>
ClientGpuMemoryBufferManager::CreateGpuMemoryBuffer(
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
gpu::SurfaceHandle surface_handle,
base::WaitableEvent* shutdown_event) {
// Note: this can be called from multiple threads at the same time. Some of
// those threads may not have a TaskRunner set.
base::ScopedAllowBaseSyncPrimitives allow;
DCHECK_EQ(gpu::kNullSurfaceHandle, surface_handle);
CHECK(!thread_.task_runner()->BelongsToCurrentThread());
gfx::GpuMemoryBufferHandle gmb_handle;
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
&ClientGpuMemoryBufferManager::AllocateGpuMemoryBufferOnThread,
base::Unretained(this), size, format, usage, &gmb_handle, &wait));
wait.Wait();
if (gmb_handle.is_null())
return nullptr;
auto gmb_handle_id = gmb_handle.id;
auto callback =
base::BindOnce(&ClientGpuMemoryBufferManager::DeletedGpuMemoryBuffer,
weak_ptr_, gmb_handle_id);
std::unique_ptr<gpu::GpuMemoryBufferImpl> buffer =
gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
std::move(gmb_handle), size, format, usage,
base::BindPostTask(thread_.task_runner(), std::move(callback)), this,
pool_);
if (!buffer) {
DeletedGpuMemoryBuffer(gmb_handle_id);
return nullptr;
}
return std::move(buffer);
}
void ClientGpuMemoryBufferManager::CopyGpuMemoryBufferAsync(
gfx::GpuMemoryBufferHandle buffer_handle,
base::UnsafeSharedMemoryRegion memory_region,
base::OnceCallback<void(bool)> callback) {
if (!thread_.task_runner()->BelongsToCurrentThread()) {
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ClientGpuMemoryBufferManager::CopyGpuMemoryBufferAsync,
base::Unretained(this), std::move(buffer_handle),
std::move(memory_region), std::move(callback)));
return;
}
gpu_->CopyGpuMemoryBuffer(std::move(buffer_handle), std::move(memory_region),
std::move(callback));
}
bool ClientGpuMemoryBufferManager::CopyGpuMemoryBufferSync(
gfx::GpuMemoryBufferHandle buffer_handle,
base::UnsafeSharedMemoryRegion memory_region) {
base::WaitableEvent event;
bool mapping_result = false;
base::ScopedAllowBaseSyncPrimitives allow;
CopyGpuMemoryBufferAsync(
std::move(buffer_handle), std::move(memory_region),
base::BindOnce(
[](base::WaitableEvent* event, bool* result_ptr, bool result) {
*result_ptr = result;
event->Signal();
},
&event, &mapping_result));
event.Wait();
return mapping_result;
}
} // namespace viz