// Copyright 2017 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/service/display_embedder/in_process_gpu_memory_buffer_manager.h"

#include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/ipc/common/gpu_client_ids.h"
#include "gpu/ipc/common/gpu_memory_buffer_impl.h"
#include "gpu/ipc/in_process_command_buffer.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"

namespace viz {
namespace {

void DestroyOnThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
                     gpu::GpuMemoryBufferImpl::DestructionCallback callback,
                     const gpu::SyncToken& sync_token) {
  if (task_runner->BelongsToCurrentThread()) {
    std::move(callback).Run(sync_token);
  } else {
    task_runner->PostTask(FROM_HERE,
                          base::BindOnce(std::move(callback), sync_token));
  }
}

}  // namespace

InProcessGpuMemoryBufferManager::InProcessGpuMemoryBufferManager(
    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
    gpu::SyncPointManager* sync_point_manager)
    : client_id_(gpu::kInProcessCommandBufferClientId),
      gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
      sync_point_manager_(sync_point_manager),
      task_runner_(base::ThreadTaskRunnerHandle::Get()),
      weak_ptr_factory_(this) {
  weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
}

InProcessGpuMemoryBufferManager::~InProcessGpuMemoryBufferManager() {
  DCHECK(task_runner_->BelongsToCurrentThread());
}

std::unique_ptr<gfx::GpuMemoryBuffer>
InProcessGpuMemoryBufferManager::CreateGpuMemoryBuffer(
    const gfx::Size& size,
    gfx::BufferFormat format,
    gfx::BufferUsage usage,
    gpu::SurfaceHandle surface_handle) {
  gfx::GpuMemoryBufferId id(next_gpu_memory_id_++);
  gfx::GpuMemoryBufferHandle buffer_handle =
      gpu_memory_buffer_factory_->CreateGpuMemoryBuffer(
          id, size, format, usage, client_id_, surface_handle);

  auto callback = base::BindOnce(
      &InProcessGpuMemoryBufferManager::ShouldDestroyGpuMemoryBuffer, weak_ptr_,
      id);
  return gpu_memory_buffer_support_.CreateGpuMemoryBufferImplFromHandle(
      std::move(buffer_handle), size, format, usage,
      base::BindOnce(&DestroyOnThread, task_runner_, std::move(callback)));
}

void InProcessGpuMemoryBufferManager::SetDestructionSyncToken(
    gfx::GpuMemoryBuffer* buffer,
    const gpu::SyncToken& sync_token) {
  static_cast<gpu::GpuMemoryBufferImpl*>(buffer)->set_destruction_sync_token(
      sync_token);
}

void InProcessGpuMemoryBufferManager::ShouldDestroyGpuMemoryBuffer(
    gfx::GpuMemoryBufferId id,
    const gpu::SyncToken& sync_token) {
  DCHECK(task_runner_->BelongsToCurrentThread());

  auto callback = base::BindOnce(
      &InProcessGpuMemoryBufferManager::DestroyGpuMemoryBuffer, weak_ptr_, id);
  // This is equivalent to calling SyncPointerManager::WaitOutOfOrder() except
  // |callback| will be run on this thread instead of the thread where the sync
  // point is released.
  bool will_run = sync_point_manager_->WaitNonThreadSafe(
      sync_token, gpu::SequenceId(), UINT32_MAX, task_runner_,
      std::move(callback));

  // No sync token or invalid sync token, destroy immediately.
  if (!will_run)
    DestroyGpuMemoryBuffer(id);
}

void InProcessGpuMemoryBufferManager::DestroyGpuMemoryBuffer(
    gfx::GpuMemoryBufferId id) {
  gpu_memory_buffer_factory_->DestroyGpuMemoryBuffer(id, client_id_);
}

}  // namespace viz
