// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/client/shared_image_interface.h"

#include <GLES2/gl2.h>

#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/notimplemented.h"
#include "base/notreached.h"
#include "base/process/memory.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/common/shared_image_usage.h"

#if BUILDFLAG(IS_WIN)
#include "ui/gfx/win/d3d_shared_fence.h"
#endif

namespace gpu {

// static
void SharedImageInterface::CreateSharedMemoryRegionFromSIInfo(
    const SharedImageInfo& si_info,
    base::WritableSharedMemoryMapping& mapping,
    gfx::GpuMemoryBufferHandle& handle) {
  DCHECK(gpu::IsValidClientUsage(si_info.usage))
      << static_cast<uint32_t>(si_info.usage);
  DCHECK_EQ(si_info.usage,
            gpu::SharedImageUsageSet(gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY));
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
  CHECK(!si_info.format.PrefersExternalSampler()) << si_info.format.ToString();
#endif

  const size_t buffer_size =
      viz::SharedMemorySizeForSharedImageFormat(si_info.format, si_info.size)
          .value();
  auto shared_memory_region =
      base::UnsafeSharedMemoryRegion::Create(buffer_size);

  if (!shared_memory_region.IsValid()) {
    DLOG(ERROR) << "base::UnsafeSharedMemoryRegion::Create() for SharedImage "
                   "with SHARED_IMAGE_USAGE_CPU_WRITE_ONLY fails!";
    base::TerminateBecauseOutOfMemory(buffer_size);
  }

  mapping = shared_memory_region.Map();
  if (!mapping.IsValid()) {
    DLOG(ERROR) << "shared_memory_region.Map() for "
                   "SHARED_IMAGE_USAGE_CPU_WRITE_ONLY fails!";
    base::TerminateBecauseOutOfMemory(buffer_size);
  }

  handle = gfx::GpuMemoryBufferHandle(std::move(shared_memory_region));
  handle.offset = 0;
  handle.stride = static_cast<int32_t>(
      viz::SharedMemoryRowSizeForSharedImageFormat(
          si_info.format, /*plane_index=*/0, si_info.size.width())
          .value());
}

gpu::SharedImageUsageSet SharedImageInterface::GetCpuSIUsage(
    gfx::BufferUsage buffer_usage) {
  switch (buffer_usage) {
    case gfx::BufferUsage::GPU_READ:
    case gfx::BufferUsage::SCANOUT:
    case gfx::BufferUsage::SCANOUT_FRONT_RENDERING:
    case gfx::BufferUsage::SCANOUT_VDA_WRITE:
    case gfx::BufferUsage::PROTECTED_SCANOUT:
    case gfx::BufferUsage::PROTECTED_SCANOUT_VDA_WRITE:
      return gpu::SharedImageUsageSet();
    case gfx::BufferUsage::SCANOUT_VEA_CPU_READ:
      return gpu::SHARED_IMAGE_USAGE_CPU_READ;
    case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE:
    case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE:
    case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
    case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE:
    case gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE:
      return gpu::SHARED_IMAGE_USAGE_CPU_READ |
             gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY;
  }
}

SharedImageInterface::SharedImageInterface()
    : holder_(base::MakeRefCounted<SharedImageInterfaceHolder>(this)) {}
SharedImageInterface::~SharedImageInterface() = default;

scoped_refptr<ClientSharedImage> SharedImageInterface::CreateSharedImage(
    const SharedImageInfo& si_info,
    gpu::SurfaceHandle surface_handle,
    gfx::BufferUsage buffer_usage,
    std::optional<SharedImagePoolId> pool_id) {
  NOTREACHED();
}

scoped_refptr<ClientSharedImage> SharedImageInterface::NotifyMailboxAdded(
    const Mailbox& /*mailbox*/,
    viz::SharedImageFormat /*format*/,
    const gfx::Size& /*size*/,
    const gfx::ColorSpace& /*color_space*/,
    GrSurfaceOrigin /*surface_origin*/,
    SkAlphaType /*alpha_type*/,
    SharedImageUsageSet /*usage*/,
    uint32_t /*texture_target*/,
    std::string_view /*debug_label*/) {
  return nullptr;
}

void SharedImageInterface::CopyToGpuMemoryBuffer(const SyncToken& sync_token,
                                                 const Mailbox& mailbox) {
  NOTREACHED();
}

void SharedImageInterface::CopyToGpuMemoryBufferAsync(
    const SyncToken& sync_token,
    const Mailbox& mailbox,
    base::OnceCallback<void(bool)> callback) {
  NOTREACHED();
}

void SharedImageInterface::CopyNativeGmbToSharedMemoryAsync(
    gfx::GpuMemoryBufferHandle buffer_handle,
    base::UnsafeSharedMemoryRegion memory_region,
    base::OnceCallback<void(bool)> callback) {
  NOTREACHED();
}

void SharedImageInterface::Release() const {
  bool should_destroy = false;

  {
    base::AutoLock auto_lock(holder_->lock_);
    if (base::subtle::RefCountedThreadSafeBase::Release()) {
      ANALYZER_SKIP_THIS_PATH();
      holder_->OnDestroy();
      should_destroy = true;
    }
  }

  if (should_destroy) {
    delete this;
  }
}

#if BUILDFLAG(IS_WIN)
void SharedImageInterface::UpdateSharedImage(
    const SyncToken& sync_token,
    scoped_refptr<gfx::D3DSharedFence> d3d_shared_fence,
    const Mailbox& mailbox) {
  NOTIMPLEMENTED_LOG_ONCE();
}
#endif  // BUILDFLAG(IS_WIN)

void SharedImageInterface::CreateSharedImagePool(
    const SharedImagePoolId& pool_id,
    mojo::PendingRemote<mojom::SharedImagePoolClientInterface> client_remote) {
  NOTREACHED();
}

void SharedImageInterface::DestroySharedImagePool(
    const SharedImagePoolId& pool_id) {
  NOTREACHED();
}

bool SharedImageInterface::IsLost() const {
  NOTREACHED();
}

bool SharedImageInterface::AddGpuChannelLostObserver(
    GpuChannelLostObserver* observer) {
  NOTREACHED();
}

void SharedImageInterface::RemoveGpuChannelLostObserver(
    GpuChannelLostObserver* observer) {
  NOTREACHED();
}

void SharedImageInterface::SignalSyncToken(std::vector<SyncToken> sync_tokens,
                                           base::OnceClosure callback) {
  NOTREACHED();
}

SharedImageInterfaceHolder::SharedImageInterfaceHolder(
    SharedImageInterface* sii)
    : sii_(sii) {}
SharedImageInterfaceHolder::~SharedImageInterfaceHolder() = default;

scoped_refptr<SharedImageInterface> SharedImageInterfaceHolder::Get() {
  base::AutoLock auto_lock(lock_);
  return scoped_refptr<SharedImageInterface>(sii_);
}

void SharedImageInterfaceHolder::OnDestroy() {
  lock_.AssertAcquired();
  sii_ = nullptr;
}

}  // namespace gpu
