// Copyright 2023 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/client_shared_image.h"

#include <GLES2/gl2.h>
#include <GLES2/gl2extchromium.h>

#include <optional>
#include <utility>

#include "base/check_is_test.h"
#include "base/containers/to_vector.h"
#include "base/debug/crash_logging.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/client/internal/mappable_buffer.h"
#include "gpu/command_buffer/client/internal/mappable_buffer_shared_memory.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/client/webgpu_interface.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/shared_image_capabilities.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/config/gpu_finch_features.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "third_party/dawn/include/dawn/wire/client/webgpu_cpp.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/buffer_usage_util.h"
#include "ui/gfx/gpu_fence.h"

#if BUILDFLAG(IS_APPLE)
#include "gpu/command_buffer/client/internal/mappable_buffer_io_surface.h"
#endif

#if BUILDFLAG(IS_OZONE)
#include "gpu/command_buffer/client/internal/mappable_buffer_native_pixmap.h"
#include "ui/ozone/public/client_native_pixmap_factory_ozone.h"
#include "ui/ozone/public/ozone_platform.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "gpu/command_buffer/client/internal/mappable_buffer_dxgi.h"
#endif

#if BUILDFLAG(IS_ANDROID)
#include "gpu/command_buffer/client/internal/mappable_buffer_ahb.h"
#endif

namespace gpu {

namespace {

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_ANDROID)
bool GMBIsNative(gfx::GpuMemoryBufferType gmb_type) {
  return gmb_type != gfx::EMPTY_BUFFER && gmb_type != gfx::SHARED_MEMORY_BUFFER;
}
#endif

// Computes the texture target to use for a SharedImage that was created with
// `metadata` and the given type of GpuMemoryBuffer(Handle) supplied by the
// client (which will be gfx::EmptyBuffer if the client did not supply a
// GMB/GMBHandle). Conceptually:
// * On Mac the native buffer target is required if either (1) the client
//   gave a native buffer or (2) the usages require a native buffer. And this
//   matters only when running on ANGLE OpenGL/CGL - in all other cases we use
//   GL_TEXTURE_2D including with Graphite and on iOS (EAGL instead of CGL).
// * On Ozone the native buffer target is required iff external sampling is
//   being used, which is dictated by the format of the SharedImage. Note
//   * Fuchsia does not support import of external images to GL for usage with
//     external sampling.  The ClientSharedImage's texture target must be 0 in
//     the case where external sampling would be used to signal this lack of
//     support to the //media code, which detects the lack of support *based on*
//     on the texture target being 0.
// * On all other platforms GL_TEXTURE_2D is always used (external sampling is
//   supported in Chromium only on Ozone).
uint32_t ComputeTextureTargetForSharedImage(
    SharedImageMetadata metadata,
    gfx::GpuMemoryBufferType client_gmb_type,
    scoped_refptr<SharedImageInterface> sii) {
  CHECK(sii);
#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_OZONE) && !BUILDFLAG(IS_ANDROID)
  return GL_TEXTURE_2D;
#elif BUILDFLAG(IS_MAC)
  // Check for IOSurfaces being used. We infer IOSurface based on scanout or
  // WebGPU usage, but that's not strictly correct e.g. with Graphite, WebGL
  // canvas back buffers will also use IOSurfaces always regardless of scanout.
  // However, in those cases we would be using GL_TEXTURE_2D anyway due to ANGLE
  // Metal (or Swiftshader for tests) being used.
  // Note that iOS uses GL_TEXTURE_2D even though it uses IOSurfaces -
  // GL_TEXTURE_RECTANGLE_ARB is in CGL which is Mac only.
  constexpr gpu::SharedImageUsageSet kUsagesRequiringNativeBuffer =
      SHARED_IMAGE_USAGE_SCANOUT | SHARED_IMAGE_USAGE_WEBGPU_READ |
      SHARED_IMAGE_USAGE_WEBGPU_WRITE;
  const bool uses_native_buffer =
      GMBIsNative(client_gmb_type) ||
      metadata.usage.HasAny(kUsagesRequiringNativeBuffer);
  return uses_native_buffer
             ? sii->GetCapabilities().texture_target_for_io_surfaces
             : GL_TEXTURE_2D;
#else  // Ozone or Android
  // Check for external sampling being used.
  if (!metadata.format.PrefersExternalSampler()) {
    return GL_TEXTURE_2D;
  }
  // The client should configure an SI to use external sampling only if they
  // have provided a native buffer to back that SI.
  CHECK(GMBIsNative(client_gmb_type));
  // See the note at the top of this function wrt Fuchsia.
#if BUILDFLAG(IS_FUCHSIA)
  return 0;
#else
  return GL_TEXTURE_EXTERNAL_OES;
#endif  // BUILDFLAG(IS_FUCHSIA)
#endif  // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_OZONE) && !BUILDFLAG(IS_ANDROID)
}

}  // namespace

SharedImageExportResult::SharedImageExportResult() = default;
SharedImageExportResult::~SharedImageExportResult() = default;
SharedImageExportResult::SharedImageExportResult(SharedImageExportResult&&) =
    default;
SharedImageExportResult& SharedImageExportResult::operator=(
    SharedImageExportResult&&) = default;

SharedImageExportResult::SharedImageExportResult(const SyncToken& sync_token)
    : sync_tokens_{sync_token} {}
SharedImageExportResult::SharedImageExportResult(
    std::vector<SyncToken> sync_tokens)
    : sync_tokens_(std::move(sync_tokens)) {
  CHECK(!sync_tokens_.empty());
}

bool SharedImageExportResult::HasData() const {
  for (const auto& sync_token : sync_tokens_) {
    if (sync_token.HasData()) {
      return true;
    }
  }
  return false;
}

std::string SharedImageExportResult::ToDebugString() const {
  if (sync_tokens_.empty()) {
    return "empty";
  }

  std::vector<std::string> token_strings = base::ToVector(
      sync_tokens_, [](const auto& token) { return token.ToDebugString(); });
  return base::JoinString(token_strings, ",");
}

ClientSharedImage::ScopedMapping::ScopedMapping(const gfx::Size& size,
                                                viz::SharedImageFormat format)
    : size_(size), format_(format) {}

ClientSharedImage::ScopedMapping::~ScopedMapping() {
  if (buffer_) {
    buffer_->Unmap();
  }
}

base::span<uint8_t> ClientSharedImage::ScopedMapping::GetMemoryForPlane(
    const uint32_t plane_index) {
  CHECK(buffer_);

  size_t height_in_pixels = format_.GetPlaneSize(plane_index, Size()).height();
  size_t row_size_in_bytes = viz::SharedMemoryRowSizeForSharedImageFormat(
                                 format_, plane_index, Size().width())
                                 .value();

  CHECK(height_in_pixels);
  CHECK(row_size_in_bytes);
  base::span<uint8_t> memory = buffer_->memory(plane_index);
#if BUILDFLAG(IS_OZONE)
  // We are currently prevented from doing this tightening for
  // NativePixmap-backed MappableBuffers by the fact that
  // VideoFrame requires that the buffer returned from this method be of size
  // that is equal to the size in its internal layout, which for NativePixmap is
  // overridden to be the size of the plane stored in the GMB handle
  // (https://source.chromium.org/chromium/chromium/src/+/main:media/base/video_frame.cc;drc=21e6d1583d1b5683f21556f6125b340d25a6b937;l=527).
  // TODO(crbug.com/404905709): Eliminate that VideoFrame override and do
  // tightening here for NativePixmap.
  return memory;
#else
  // Note that the stride might be larger than the row size.
  // For all rows other than the last, this is legal data for the client to
  // access as it's part of the buffer.  However, the final row is not
  // guaranteed to have padding (it's a system-dependent internal detail).
  // Thus, the data that is legal for the client to access should *not*
  // include any bytes beyond the actual end of the final row.
  size_t span_length =
      Stride(plane_index) * (height_in_pixels - 1) + row_size_in_bytes;
  return memory.first(span_length);
#endif
}

size_t ClientSharedImage::ScopedMapping::Stride(const uint32_t plane_index) {
  CHECK(buffer_);
  return buffer_->stride(plane_index);
}

gfx::Size ClientSharedImage::ScopedMapping::Size() {
  return size_;
}

bool ClientSharedImage::ScopedMapping::IsSharedMemory() {
  CHECK(buffer_);
  return buffer_->GetType() == gfx::GpuMemoryBufferType::SHARED_MEMORY_BUFFER;
}

bool ClientSharedImage::ScopedMapping::Init(MappableBuffer* mappable_buffer,
                                            bool is_already_mapped) {
  if (!mappable_buffer) {
    LOG(ERROR) << "No MappableBuffer.";
    return false;
  }

  if (!is_already_mapped && !mappable_buffer->Map()) {
    LOG(ERROR) << "Failed to map the buffer.";
    return false;
  }
  buffer_ = mappable_buffer;
  return true;
}

std::unique_ptr<MappableBuffer>
ClientSharedImage::CreateMappableBufferFromHandle(
    gfx::GpuMemoryBufferHandle handle,
    const gfx::Size& size,
    viz::SharedImageFormat format,
    gfx::BufferUsage usage,
    gpu::SharedImageUsageSet si_usage,
    scoped_refptr<base::UnsafeSharedMemoryPool> pool) {
  auto copy_native_buffer_to_shmem_callback =
      base::BindRepeating(&ClientSharedImage::CopyNativeGmbToSharedMemoryAsync,
                          base::Unretained(this));
  switch (handle.type) {
    case gfx::EMPTY_BUFFER:
      NOTREACHED();
    case gfx::SHARED_MEMORY_BUFFER:
      return MappableBufferSharedMemory::CreateFromHandle(std::move(handle),
                                                          size, format);
#if BUILDFLAG(IS_APPLE)
    case gfx::IO_SURFACE_BUFFER: {
      bool is_read_only_cpu_usage =
          si_usage.Has(SHARED_IMAGE_USAGE_CPU_READ) &&
          !si_usage.Has(SHARED_IMAGE_USAGE_CPU_WRITE_ONLY);
      return MappableBufferIOSurface::CreateFromHandle(
          std::move(handle), size, format, is_read_only_cpu_usage);
    }
#endif
#if BUILDFLAG(IS_OZONE)
    case gfx::NATIVE_PIXMAP: {
      // NOTE: This is not used beyond the lifetime of CreateFromHandle().
      auto client_native_pixmap_factory =
          ui::CreateClientNativePixmapFactoryOzone();
      return MappableBufferNativePixmap::CreateFromHandle(
          client_native_pixmap_factory.get(), std::move(handle), size, format,
          usage);
    }
#endif
#if BUILDFLAG(IS_WIN)
    case gfx::DXGI_SHARED_HANDLE: {
      // DXGI handles require GPU roundtrip for mapping, so they will wait
      // for event to trigger in async callback.
      // So the copy callback must execute in internal thread otherwise there
      // will be a deadlock: the waiting thread would be used to process the
      // callback reply.
      auto wrapped_callback = base::BindRepeating(
          &ClientSharedImage::RunOnTaskRunner, base::Unretained(this),
          copy_native_buffer_to_shmem_callback);
      return MappableBufferDXGI::CreateFromHandle(
          std::move(handle), size, format, std::move(wrapped_callback),
          std::move(pool));
    }
#endif
#if BUILDFLAG(IS_ANDROID)
    case gfx::ANDROID_HARDWARE_BUFFER: {
      // ANDROID_HARDWARE_BUFFER handles require GPU roundtrip for mapping, so
      // they will wait for event to trigger in async callback.
      // So the copy callback must execute in internal thread otherwise there
      // will be a deadlock: the waiting thread would be used to process the
      // callback reply.
      auto wrapped_callback = base::BindRepeating(
          &ClientSharedImage::RunOnTaskRunner, base::Unretained(this),
          copy_native_buffer_to_shmem_callback);
      return MappableBufferAHB::CreateFromHandle(
          std::move(handle), size, format, std::move(wrapped_callback),
          std::move(pool));
    }
#endif
  }
}

// static
std::unique_ptr<ClientSharedImage::ScopedMapping>
ClientSharedImage::ScopedMapping::Create(SharedImageMetadata metadata,
                                         MappableBuffer* mappable_buffer,
                                         bool is_already_mapped) {
  auto scoped_mapping =
      base::WrapUnique(new ScopedMapping(metadata.size, metadata.format));
  if (!scoped_mapping->Init(mappable_buffer, is_already_mapped)) {
    LOG(ERROR) << "ScopedMapping init failed.";
    return nullptr;
  }
  return scoped_mapping;
}

// static
void ClientSharedImage::ScopedMapping::StartCreateAsync(
    SharedImageMetadata metadata,
    MappableBuffer* mappable_buffer,
    base::OnceCallback<void(std::unique_ptr<ScopedMapping>)> result_cb) {
  uint64_t track_id = reinterpret_cast<uintptr_t>(mappable_buffer);
  TRACE_EVENT_BEGIN(
      "gpu", "ClientSharedImage::MapAsync",
      perfetto::NamedTrack("ClientSharedImage::MapAsync", track_id), "format",
      metadata.format.ToString(), "usage", metadata.usage.ToString(), "size",
      metadata.size.ToString());

  mappable_buffer->MapAsync(
      base::BindOnce(&ClientSharedImage::ScopedMapping::FinishCreateAsync,
                     metadata, mappable_buffer, std::move(result_cb)));
}

// static
void ClientSharedImage::ScopedMapping::FinishCreateAsync(
    SharedImageMetadata metadata,
    MappableBuffer* mappable_buffer,
    base::OnceCallback<void(std::unique_ptr<ScopedMapping>)> result_cb,
    bool success) {
  uint64_t track_id = reinterpret_cast<uintptr_t>(mappable_buffer);
  TRACE_EVENT_END(
      "gpu", perfetto::NamedTrack("ClientSharedImage::MapAsync", track_id));

  std::unique_ptr<ClientSharedImage::ScopedMapping> mapping;
  if (success) {
    mapping = ClientSharedImage::ScopedMapping::Create(
        metadata, mappable_buffer, /*is_already_mapped=*/true);
  }
  std::move(result_cb).Run(std::move(mapping));
}

SkPixmap ClientSharedImage::ScopedMapping::GetSkPixmapForPlane(
    const uint32_t plane_index,
    SkImageInfo sk_image_info) {
  return SkPixmap(sk_image_info, GetMemoryForPlane(plane_index).data(),
                  Stride(plane_index));
}

ClientSharedImage::ClientSharedImage(
    const Mailbox& mailbox,
    const SharedImageInfo& info,
    const SyncToken& sync_token,
    scoped_refptr<SharedImageInterfaceHolder> sii_holder,
    gfx::GpuMemoryBufferType gmb_type)
    : mailbox_(mailbox),
      metadata_(info),
      debug_label_(info.debug_label),
      creation_sync_token_(sync_token),
      sii_holder_(std::move(sii_holder)),
      sii_(base::FeatureList::IsEnabled(
               features::kUseStrongRefToSharedImageInterface)
               ? sii_holder_->Get()
               : nullptr) {
  CHECK(!mailbox.IsZero());
  CHECK(sii_holder_);
  texture_target_ = ComputeTextureTargetForSharedImage(
      metadata_, gmb_type, GetSharedImageInterface());
}

ClientSharedImage::ClientSharedImage(
    const Mailbox& mailbox,
    const SharedImageInfo& info,
    const SyncToken& sync_token,
    scoped_refptr<SharedImageInterfaceHolder> sii_holder,
    base::WritableSharedMemoryMapping mapping)
    : ClientSharedImage(mailbox,
                        info,
                        sync_token,
                        sii_holder,
                        gfx::SHARED_MEMORY_BUFFER) {
  mappable_buffer_ = MappableBufferSharedMemory::CreateFromMapping(
      std::move(mapping), metadata_.size, metadata_.format);
  is_software_ = true;
}

ClientSharedImage::ClientSharedImage(
    const Mailbox& mailbox,
    const SharedImageInfo& info,
    const SyncToken& sync_token,
    scoped_refptr<SharedImageInterfaceHolder> sii_holder,
    uint32_t texture_target)
    : mailbox_(mailbox),
      metadata_(info),
      debug_label_(info.debug_label),
      creation_sync_token_(sync_token),
      sii_holder_(std::move(sii_holder)),
      sii_(base::FeatureList::IsEnabled(
               features::kUseStrongRefToSharedImageInterface)
               ? sii_holder_->Get()
               : nullptr),
      texture_target_(texture_target) {
  // TODO(crbug.com/391788839): Create GpuMemoryBuffer from handle.
  CHECK(!mailbox.IsZero());
  CHECK(sii_holder_);
#if !BUILDFLAG(IS_FUCHSIA)
  CHECK(texture_target);
#endif
}

ClientSharedImage::ClientSharedImage(
    ExportedSharedImage exported_si,
    scoped_refptr<SharedImageInterfaceHolder> sii_holder)
    : ClientSharedImage(std::move(exported_si)) {
  sii_holder_ = std::move(sii_holder);
  CHECK(sii_holder_);

  sii_ = base::FeatureList::IsEnabled(
             features::kUseStrongRefToSharedImageInterface)
             ? sii_holder_->Get()
             : nullptr;
}

ClientSharedImage::ClientSharedImage(ExportedSharedImage exported_si)
    : mailbox_(exported_si.mailbox_),
      metadata_(exported_si.metadata_),
      debug_label_(exported_si.debug_label_),
      creation_sync_token_(exported_si.creation_sync_token_),
      buffer_usage_(exported_si.buffer_usage_),
      texture_target_(exported_si.texture_target_),
      is_software_(exported_si.is_software_) {
  if (exported_si.buffer_handle_) {
    mappable_buffer_ = CreateMappableBufferFromHandle(
        std::move(exported_si.buffer_handle_.value()), metadata_.size,
        metadata_.format, exported_si.buffer_usage_.value(), metadata_.usage);
  }
  CHECK(!mailbox_.IsZero());
#if !BUILDFLAG(IS_FUCHSIA)
  CHECK(texture_target_);
#endif

  for (auto& sync_token : exported_si.managed_sync_tokens_) {
    sync_token_map_.emplace(sync_token.GetClientId(), sync_token);
  }
}

ClientSharedImage::ClientSharedImage(
    const Mailbox& mailbox,
    const SharedImageInfo& info,
    const SyncToken& sync_token,
    GpuMemoryBufferHandleInfo handle_info,
    scoped_refptr<SharedImageInterfaceHolder> sii_holder,
    scoped_refptr<base::UnsafeSharedMemoryPool> shared_memory_pool)
    : mailbox_(mailbox),
      metadata_(info),
      debug_label_(info.debug_label),
      creation_sync_token_(sync_token),
      mappable_buffer_(
          CreateMappableBufferFromHandle(std::move(handle_info.handle),
                                         metadata_.size,
                                         metadata_.format,
                                         handle_info.buffer_usage,
                                         info.usage,
                                         std::move(shared_memory_pool))),
      buffer_usage_(handle_info.buffer_usage),
      sii_holder_(std::move(sii_holder)),
      sii_(base::FeatureList::IsEnabled(
               features::kUseStrongRefToSharedImageInterface)
               ? sii_holder_->Get()
               : nullptr) {
  CHECK(!mailbox.IsZero());
  CHECK(sii_holder_);
  CHECK(mappable_buffer_);
  texture_target_ = ComputeTextureTargetForSharedImage(
      metadata_, mappable_buffer_->GetType(), GetSharedImageInterface());
}

ClientSharedImage::ClientSharedImage(const Mailbox& mailbox,
                                     const SharedImageInfo& info)
    : mailbox_(mailbox), metadata_(info), debug_label_(info.debug_label) {
  CHECK(!mailbox.IsZero());
  texture_target_ = GL_TEXTURE_2D;
}

ClientSharedImage::~ClientSharedImage() {
  if (!HasHolder()) {
    return;
  }

  auto sii = GetSharedImageInterface();
  if (sii) {
    sii->DestroySharedImage(destruction_sync_token_, mailbox_);
  }
}

size_t ClientSharedImage::GetStrideForVideoFrame(uint32_t plane_index) const {
  if (async_map_invoked_callback_for_testing_) {
    return viz::SharedMemoryRowSizeForSharedImageFormat(format(), plane_index,
                                                        size().width())
        .value();
  }
  CHECK(mappable_buffer_);
  return mappable_buffer_->stride(plane_index);
}

// Returns whether the underlying resource is shared memory without needing to
// Map() the shared image. This method is supposed to be used by VideoFrame
// temporarily as mentioned above in ::GetStrideForVideoFrame().
bool ClientSharedImage::IsSharedMemoryForVideoFrame() const {
  if (async_map_invoked_callback_for_testing_) {
    return true;
  }
  CHECK(mappable_buffer_);
  return mappable_buffer_->GetType() ==
         gfx::GpuMemoryBufferType::SHARED_MEMORY_BUFFER;
}

bool ClientSharedImage::AsyncMappingIsNonBlocking() const {
  if (async_map_invoked_callback_for_testing_) {
    return true;
  }
  CHECK(mappable_buffer_);
  return mappable_buffer_->AsyncMappingIsNonBlocking();
}

uint64_t ClientSharedImage::SignalLatestSyncToken(
    std::vector<scoped_refptr<ClientSharedImage>> shared_images,
    std::vector<SyncToken> sync_tokens,
    base::OnceClosure callback,
    SharedImageInterface* sii,
    uint64_t pending_callback_id) {
  CHECK(sii);
  gpu::SyncToken latest_sync_token;
  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    for (const auto& shared_image : shared_images) {
      if (!shared_image) {
        continue;
      }
      base::AutoLock auto_lock(shared_image->lock_);
      CHECK_LE(shared_image->sync_token_map_.size(), 1u);
      for (const auto& [_, sync_token] : shared_image->sync_token_map_) {
        if (sync_token.release_count() > latest_sync_token.release_count()) {
          latest_sync_token = sync_token;
        }
      }
    }
  } else {
    for (const auto& sync_token : sync_tokens) {
      if (sync_token.release_count() > latest_sync_token.release_count()) {
        latest_sync_token = sync_token;
      }
    }
  }
  uint64_t callback_id = latest_sync_token.release_count();
  if (callback_id == 0u) {
    // Run callback immediately if all sync tokens are invalid.
    std::move(callback).Run();
  } else if (callback_id != pending_callback_id) {
    // If the callback is different from the one the caller is already waiting
    // on, pass the callback through to SignalSyncToken. Otherwise the request
    // is redundant.
    sii->SignalSyncToken({latest_sync_token}, std::move(callback));
  }

  return callback_id;
}

void ClientSharedImage::SignalLatestSyncToken(
    std::vector<scoped_refptr<ClientSharedImage>> shared_images,
    std::vector<SyncToken> sync_tokens,
    base::OnceClosure callback,
    SharedImageInterface* sii) {
  CHECK(sii);
  bool has_valid_token = false;
  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    sync_tokens.clear();
    for (const auto& shared_image : shared_images) {
      if (!shared_image) {
        continue;
      }
      base::AutoLock auto_lock(shared_image->lock_);
      for (const auto& [_, sync_token] : shared_image->sync_token_map_) {
        if (sync_token.HasData()) {
          sync_tokens.push_back(sync_token);
        }
      }
    }
    has_valid_token = !sync_tokens.empty();
  } else {
    for (const auto& sync_token : sync_tokens) {
      if (sync_token.HasData()) {
        has_valid_token = true;
        break;
      }
    }
  }

  if (!has_valid_token) {
    std::move(callback).Run();
  } else {
    sii->SignalSyncToken(std::move(sync_tokens), std::move(callback));
  }
}

std::unique_ptr<ClientSharedImage::ScopedMapping> ClientSharedImage::Map() {
  TRACE_EVENT("gpu", "ClientSharedImage::Map", "format",
              metadata_.format.ToString(), "usage", metadata_.usage.ToString(),
              "size", metadata_.size.ToString());
  std::unique_ptr<ClientSharedImage::ScopedMapping> scoped_mapping =
      ScopedMapping::Create(metadata_, mappable_buffer_.get(),
                            /*is_already_mapped=*/false);
  if (!scoped_mapping) {
    std::ostringstream s;
    s << " shared_image Info: {"
      << " format: " << format().ToString() << " usage: " << usage().ToString()
      << " label: " << debug_label() << " }";
    LOG(ERROR) << "Unable to create ScopedMapping.  " << s.str();
  }
  return scoped_mapping;
}

void ClientSharedImage::FinishMapAsyncForTests(
    base::OnceCallback<void(std::unique_ptr<ScopedMapping>)> result_cb,
    bool success) {
  std::unique_ptr<ScopedMapping> mapping;
  if (success) {
    mapping = Map();
  }
  std::move(result_cb).Run(std::move(mapping));
}

void ClientSharedImage::MapAsync(
    base::OnceCallback<void(std::unique_ptr<ScopedMapping>)> result_cb) {
  if (async_map_invoked_callback_for_testing_) {
    if (premapped_for_testing_) {
      FinishMapAsyncForTests(std::move(result_cb), true);
    } else {
      async_map_invoked_callback_for_testing_.Run(
          base::BindOnce(&ClientSharedImage::FinishMapAsyncForTests,
                         base::Unretained(this), std::move(result_cb)));
    }
    return;
  }

  ScopedMapping::StartCreateAsync(metadata_, mappable_buffer_.get(),
                                  std::move(result_cb));
}

gfx::GpuMemoryBufferType ClientSharedImage::GetGpuMemoryBufferType() const {
  return mappable_buffer_ ? mappable_buffer_->GetType() : gfx::EMPTY_BUFFER;
}

bool ClientSharedImage::SupportsZeroCopyWebGPUImport() const {
  return mappable_buffer_ && mappable_buffer_->SupportsZeroCopyWebGPUImport();
}

gfx::GpuMemoryBufferHandle ClientSharedImage::CloneGpuMemoryBufferHandle()
    const {
  // Supported only if this ClientSI is backed by a MappableBuffer that is
  // itself backed by a GMBHandle.
  CHECK(mappable_buffer_ && !is_software_);
  return mappable_buffer_->CloneHandle();
}

uint32_t ClientSharedImage::GetTextureTarget() {
#if !BUILDFLAG(IS_FUCHSIA)
  // Check that `texture_target_` has been initialized (note that on Fuchsia it
  // is possible for `texture_target_` to be initialized to 0: Fuchsia does not
  // support import of external images to GL for usage with external sampling.
  // SetTextureTarget() sets the texture target to 0 in the case where external
  // sampling would be used to signal this lack of support to the //media code,
  // which detects the lack of support *based on* on the texture target being
  // 0).
  CHECK(texture_target_);
#endif
  return texture_target_;
}

scoped_refptr<ClientSharedImage> ClientSharedImage::MakeUnowned() {
  return ClientSharedImage::ImportUnowned(Export());
}

ExportedSharedImage ClientSharedImage::Export(bool with_buffer_handle) {
  if (creation_sync_token_.HasData() &&
      !creation_sync_token_.verified_flush()) {
    auto sii = GetSharedImageInterface();
    // TODO(crbug.com/40286368): We should let ClientSharedImage hold a strong
    // SharedImageInterface reference to ensure `sii` is always valid.
    if (sii) {
      sii->VerifySyncToken(creation_sync_token_);
    } else {
      creation_sync_token_ = gpu::SyncToken();
    }
  }
  std::optional<gfx::GpuMemoryBufferHandle> buffer_handle;
  std::optional<gfx::BufferUsage> buffer_usage;
  if (with_buffer_handle && mappable_buffer_) {
    buffer_handle = mappable_buffer_->CloneHandle();
    buffer_usage = buffer_usage_.value();
  }
  return ExportedSharedImage(mailbox_, metadata_, creation_sync_token_,
                             VerifyAndCollectSyncTokens(), debug_label_,
                             std::move(buffer_handle), buffer_usage,
                             texture_target_, is_software_);
}

scoped_refptr<ClientSharedImage> ClientSharedImage::ImportUnowned(
    ExportedSharedImage exported_shared_image) {
  return base::WrapRefCounted<ClientSharedImage>(
      new ClientSharedImage(std::move(exported_shared_image)));
}

void ClientSharedImage::CreateGpuFenceForSyncTokens(
    std::vector<scoped_refptr<ClientSharedImage>> shared_images,
    std::vector<SyncToken> sync_tokens,
    gles2::GLES2Interface* gl,
    ContextSupport* context_support,
    base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
  CHECK(gl && context_support);

  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    // Ignore the the input `sync_tokens`.
    SyncToken dummy_sync_token;
    for (auto& shared_image : shared_images) {
      shared_image->WaitSyncTokenInternal(gl, dummy_sync_token);
    }
  } else {
    for (auto& sync_token : sync_tokens) {
      gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
    }
  }

  GLuint id = gl->CreateGpuFenceCHROMIUM();
  context_support->GetGpuFence(id, std::move(callback));
  gl->DestroyGpuFenceCHROMIUM(id);
}

gpu::SyncToken ClientSharedImage::BackingWasExternallyUpdated(
    const gpu::SyncToken& sync_token) {
  CHECK(sii_holder_);
  auto sii = GetSharedImageInterface();
  if (!sii) {
    return gpu::SyncToken();
  }

  sii->UpdateSharedImage(sync_token, mailbox());
  return StoreSyncTokenInternal(sii->GenUnverifiedSyncToken());
}

gpu::SyncToken ClientSharedImage::BackingWasExternallyUpdated(
    std::unique_ptr<gfx::GpuFence> acquire_fence) {
  CHECK(sii_holder_);
  auto sii = GetSharedImageInterface();
  if (!sii) {
    return gpu::SyncToken();
  }

  sii->UpdateSharedImage(SyncToken(), std::move(acquire_fence), mailbox());
  return StoreSyncTokenInternal(sii->GenUnverifiedSyncToken());
}

void ClientSharedImage::OnMemoryDump(
    base::trace_event::ProcessMemoryDump* pmd,
    const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
    int importance) {
  auto tracing_guid = GetGUIDForTracing();
  pmd->CreateSharedGlobalAllocatorDump(tracing_guid);
  pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance);
}

scoped_refptr<SharedImageInterface>
ClientSharedImage::GetSharedImageInterface() {
  if (base::FeatureList::IsEnabled(
          features::kUseStrongRefToSharedImageInterface)) {
    return sii_;
  } else {
    return sii_holder_ ? sii_holder_->Get() : nullptr;
  }
}

void ClientSharedImage::BeginAccess(bool readonly) {
  base::AutoLock lock(lock_);
  if (readonly) {
    CHECK(!has_writer_ ||
          usage().Has(SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE));
    num_readers_++;
  } else {
    CHECK(!has_writer_);
    CHECK(num_readers_ == 0 ||
          usage().Has(SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE));
    has_writer_ = true;
  }
}

void ClientSharedImage::EndAccess(bool readonly) {
  base::AutoLock lock(lock_);
  if (readonly) {
    CHECK(num_readers_ > 0);
    num_readers_--;
  } else {
    CHECK(has_writer_);
    has_writer_ = false;
  }
}

std::unique_ptr<SharedImageTexture> ClientSharedImage::CreateGLTexture(
    gles2::GLES2Interface* gl) {
  CHECK(metadata_.usage.Has(SHARED_IMAGE_USAGE_GLES2_READ) ||
        metadata_.usage.Has(SHARED_IMAGE_USAGE_GLES2_WRITE));
  return base::WrapUnique(new SharedImageTexture(gl, this));
}

std::unique_ptr<RasterScopedAccess> ClientSharedImage::BeginRasterAccess(
    InterfaceBase* raster_interface,
    const SyncToken& sync_token,
    bool readonly) {
  bool has_raster_usage =
      metadata_.usage.Has(SHARED_IMAGE_USAGE_RASTER_READ) ||
      metadata_.usage.Has(SHARED_IMAGE_USAGE_RASTER_WRITE) ||
      metadata_.usage.Has(SHARED_IMAGE_USAGE_RASTER_COPY_SOURCE);
  CHECK(has_raster_usage);
  return base::WrapUnique(
      new RasterScopedAccess(raster_interface, this, sync_token, readonly));
}

std::unique_ptr<RasterScopedAccess>
ClientSharedImage::BeginGLAccessForCopySharedImage(InterfaceBase* gl_interface,
                                                   const SyncToken& sync_token,
                                                   bool readonly) {
  return BeginRasterAccess(gl_interface, sync_token, readonly);
}

#if BUILDFLAG(IS_WIN)
void ClientSharedImage::SetUsePreMappedMemory(bool use_premapped_memory) {
  CHECK(mappable_buffer_);
  mappable_buffer_->SetUsePreMappedMemory(use_premapped_memory);
}
#endif

// static
scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting() {
  return CreateForTesting(viz::SinglePlaneFormat::kRGBA_8888, GL_TEXTURE_2D);
}

// static
scoped_refptr<ClientSharedImage> ClientSharedImage::CreateSoftwareForTesting() {
  auto shared_image = CreateForTesting();  // IN-TEST
  shared_image->is_software_ = true;
  return shared_image;
}

// static
scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting(
    const SharedImageMetadata& metadata) {
  return CreateForTesting(metadata, GL_TEXTURE_2D);  // IN-TEST
}

// static
scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting(
    viz::SharedImageFormat format,
    uint32_t texture_target) {
  SharedImageMetadata metadata;
  metadata.format = format;
  metadata.size = gfx::Size(64, 64);
  metadata.color_space = gfx::ColorSpace::CreateSRGB();
  metadata.surface_origin = kTopLeft_GrSurfaceOrigin;
  metadata.alpha_type = kOpaque_SkAlphaType;
  metadata.usage = gpu::SharedImageUsageSet();

  return CreateForTesting(metadata, texture_target);
}

// static
scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting(
    const gfx::ColorSpace& color_space) {
  SharedImageMetadata metadata;
  metadata.format = viz::SinglePlaneFormat::kRGBA_8888;
  metadata.size = gfx::Size(64, 64);
  metadata.color_space = color_space;
  metadata.surface_origin = kTopLeft_GrSurfaceOrigin;
  metadata.alpha_type = kOpaque_SkAlphaType;
  metadata.usage = gpu::SharedImageUsageSet();

  return CreateForTesting(metadata, GL_TEXTURE_2D);  // IN-TEST
}

// static
scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting(
    SharedImageUsageSet usage) {
  SharedImageMetadata metadata;
  metadata.format = viz::SinglePlaneFormat::kRGBA_8888;
  metadata.size = gfx::Size(64, 64);
  metadata.color_space = gfx::ColorSpace::CreateSRGB();
  metadata.surface_origin = kTopLeft_GrSurfaceOrigin;
  metadata.alpha_type = kOpaque_SkAlphaType;
  metadata.usage = usage;

  return CreateForTesting(metadata, GL_TEXTURE_2D);
}

// static
scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting(
    const SharedImageMetadata& metadata,
    uint32_t texture_target) {
  return CreateForTesting(Mailbox::Generate(), metadata,  // IN-TEST
                          SyncToken(), texture_target);
}

// static
scoped_refptr<ClientSharedImage>
ClientSharedImage::CreateForTesting(  // IN-TEST
    const Mailbox& mailbox,
    const SharedImageMetadata& metadata,
    const SyncToken& sync_token,
    uint32_t texture_target,
    bool is_software) {
  gpu::ExportedSharedImage exported_shared_image = gpu::ExportedSharedImage(
      mailbox, metadata, sync_token, /*managed_sync_tokens=*/{},
      "CSICreateForTesting",
      /*buffer_handle=*/std::nullopt, /*buffer_usage=*/std::nullopt,
      texture_target, is_software);
  auto shared_image =
      gpu::ClientSharedImage::ImportUnowned(std::move(exported_shared_image));
  shared_image->is_software_ = is_software;
  return shared_image;
}

// static
scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting(
    const Mailbox& mailbox,
    const SharedImageMetadata& metadata,
    const SyncToken& sync_token,
    bool premapped,
    const AsyncMapInvokedCallback& callback,
    gfx::BufferUsage buffer_usage,
    scoped_refptr<SharedImageInterfaceHolder> sii_holder) {
  SharedImageInfo info(metadata, "CSICreateForTesting");

  gfx::GpuMemoryBufferHandle handle;
  MappableBufferSharedMemory::AllocateForTesting(info.size, info.format,
                                                 buffer_usage, &handle);
  auto mappable_buffer = MappableBufferSharedMemory::CreateFromHandle(
      std::move(handle), info.size, info.format);

  // Since the |mappable_buffer| here is always a shared memory, clear the
  // external sampler prefs if it is already set by client.
  // https://issues.chromium.org/339546249.
  if (info.format.PrefersExternalSampler()) {
    info.format.ClearPrefersExternalSampler();
  }

  auto client_si = base::MakeRefCounted<ClientSharedImage>(
      mailbox, info, sync_token, sii_holder, gfx::SHARED_MEMORY_BUFFER);
  client_si->mappable_buffer_ = std::move(mappable_buffer);
  client_si->async_map_invoked_callback_for_testing_ = callback;
  client_si->premapped_for_testing_ = premapped;
  client_si->buffer_usage_ = buffer_usage;
  return client_si;
}

void ClientSharedImage::CopyNativeGmbToSharedMemoryAsync(
    gfx::GpuMemoryBufferHandle buffer_handle,
    base::UnsafeSharedMemoryRegion memory_region,
    base::OnceCallback<void(bool)> callback) {
  auto sii = GetSharedImageInterface();
  if (!sii) {
    DLOG(WARNING) << "No SharedImageInterface.";
    std::move(callback).Run(false);
    return;
  }
  sii->CopyNativeGmbToSharedMemoryAsync(
      std::move(buffer_handle), std::move(memory_region),
      mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback),
                                                  /*result=*/false));
}

void ClientSharedImage::RunOnTaskRunner(
    MappableBuffer::CopyNativeBufferToShMemCallback callback,
    gfx::GpuMemoryBufferHandle buffer_handle,
    base::UnsafeSharedMemoryRegion memory_region,
    base::OnceCallback<void(bool)> result_cb) {
  // Lazily create the |task_runner_|.
  if (!copy_native_buffer_to_shmem_task_runner_) {
    copy_native_buffer_to_shmem_task_runner_ =
        base::ThreadPool::CreateSingleThreadTaskRunner({base::MayBlock()});
    CHECK(copy_native_buffer_to_shmem_task_runner_);
  }

  if (!copy_native_buffer_to_shmem_task_runner_->BelongsToCurrentThread()) {
    copy_native_buffer_to_shmem_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(callback, std::move(buffer_handle),
                       std::move(memory_region), std::move(result_cb)));
    return;
  }

  callback.Run(std::move(buffer_handle), std::move(memory_region),
               std::move(result_cb));
}

void ClientSharedImage::WaitSyncTokenInternal(InterfaceBase* ib,
                                              const SyncToken& sync_token) {
  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    // Ignore the input `sync_token` since all SyncTokens that we need to wait
    // upon is already inside this ClientSharedImage instance.
    base::AutoLock auto_lock(lock_);
    for (const auto& [_, token] : sync_token_map_) {
      if (token.HasData()) {
        ib->WaitSyncTokenCHROMIUM(token.GetConstData());
      }
    }
  } else {
    ib->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
  }
}

void ClientSharedImage::StoreSyncTokenLocked(const SyncToken& sync_token) {
  if (!sync_token.HasData()) {
    return;
  }
  gpu::SyncPointClientId client_id = sync_token.GetClientId();
  auto [it, inserted] = sync_token_map_.try_emplace(client_id, sync_token);
  if (!inserted) {
    // Entry already exists for this client sequence. Only update if the new
    // SyncToken is newer or has updated flush state.
    if (sync_token.release_count() > it->second.release_count() ||
        (sync_token.release_count() == it->second.release_count() &&
         sync_token.verified_flush())) {
      it->second = sync_token;
    }
  }
}

SyncToken ClientSharedImage::StoreSyncTokenInternal(
    const SyncToken& sync_token) {
  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    base::AutoLock auto_lock(lock_);
    StoreSyncTokenLocked(sync_token);
    return SyncToken();
  } else {
    return sync_token;
  }
}

std::vector<SyncToken> ClientSharedImage::StoreSyncTokenVectorInternal(
    const std::vector<SyncToken>& sync_tokens) {
  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    base::AutoLock auto_lock(lock_);
    for (const auto& sync_token : sync_tokens) {
      StoreSyncTokenLocked(sync_token);
    }
    return std::vector<SyncToken>();
  } else {
    return sync_tokens;
  }
}

SyncToken ClientSharedImage::GenSyncTokenInternal(InterfaceBase* ib) {
  SyncToken sync_token;
  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    if (GetSharedImageInterface()) {
      ib->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
    } else {
      // Without a valid `sii_holder_`, we cannot verify the SyncToken later,
      // so the SyncToken must be verified at generation time.
      ib->GenSyncTokenCHROMIUM(sync_token.GetData());
    }
  } else {
    ib->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
  }
  return StoreSyncTokenInternal(sync_token);
}

std::unique_ptr<WebGPUTextureScopedAccess>
ClientSharedImage::BeginWebGPUTextureAccess(
    webgpu::WebGPUInterface* webgpu,
    const SyncToken& sync_token,
    const wgpu::dawn::wire::client::Device& device,
    const wgpu::dawn::wire::client::TextureDescriptor& desc,
    uint64_t usage,
    webgpu::MailboxFlags mailbox_flags) {
  CHECK(metadata_.usage.Has(SHARED_IMAGE_USAGE_WEBGPU_READ) ||
        metadata_.usage.Has(SHARED_IMAGE_USAGE_WEBGPU_WRITE));
  return base::WrapUnique(new WebGPUTextureScopedAccess(
      webgpu, this, sync_token, device, desc, usage, mailbox_flags));
}

std::vector<SyncToken> ClientSharedImage::VerifyAndCollectSyncTokens() {
  base::AutoLock auto_lock(lock_);

  auto sii = GetSharedImageInterface();
  if (sii) {
    sii->VerifySyncTokens(sync_token_map_, [](auto& entry) -> gpu::SyncToken& {
      return entry.second;
    });
  }

  std::vector<SyncToken> sync_tokens;
  sync_tokens.reserve(sync_token_map_.size());
  for (const auto& [_, sync_token] : sync_token_map_) {
    sync_tokens.push_back(sync_token);
  }
  return sync_tokens;
}

SharedImageExportResult ClientSharedImage::EndImport(
    const SyncToken& sync_token) {
  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    // Ignore the input `sync_token` since it must have been already generated
    // and stored in this ClientSharedImage.
    std::vector<SyncToken> verified_sync_tokens = VerifyAndCollectSyncTokens();
    if (verified_sync_tokens.empty()) {
      return SharedImageExportResult{SyncToken()};
    }
    return SharedImageExportResult{std::move(verified_sync_tokens)};
  } else {
    return SharedImageExportResult{sync_token};
  }
}

SharedImageExportResult ClientSharedImage::EndImport(
    const std::vector<SyncToken>& sync_tokens) {
  if (base::FeatureList::IsEnabled(
          features::kUseAutomaticSyncTokenManagement)) {
    // Ignore the input `sync_tokens` since it must have been already generated
    // and stored in this ClientSharedImage.
    std::vector<SyncToken> verified_sync_tokens = VerifyAndCollectSyncTokens();
    if (verified_sync_tokens.empty()) {
      return SharedImageExportResult{SyncToken()};
    }
    return SharedImageExportResult{std::move(verified_sync_tokens)};
  } else {
    return SharedImageExportResult{sync_tokens};
  }
}

// Unpack the SharedImageExportResult.
// This version expects an empty or a single-SyncToken export result.
SyncToken ClientSharedImage::EndExport(SharedImageExportResult&& result) {
  if (result.sync_tokens_.empty()) {
    return SyncToken();
  }
  CHECK(result.sync_tokens_.size() == 1);
  return StoreSyncTokenInternal(result.sync_tokens_[0]);
}

std::vector<SyncToken> ClientSharedImage::EndExportAsVector(
    SharedImageExportResult&& result) {
  return StoreSyncTokenVectorInternal(std::move(result.sync_tokens_));
}

ExportedSharedImage::ExportedSharedImage() = default;
ExportedSharedImage::~ExportedSharedImage() = default;

ExportedSharedImage::ExportedSharedImage(ExportedSharedImage&& other) = default;
ExportedSharedImage& ExportedSharedImage::operator=(
    ExportedSharedImage&& other) = default;

ExportedSharedImage::ExportedSharedImage(
    const Mailbox& mailbox,
    const SharedImageMetadata& metadata,
    const SyncToken& creation_sync_token,
    const std::vector<SyncToken>& managed_sync_tokens,
    std::string debug_label,
    std::optional<gfx::GpuMemoryBufferHandle> buffer_handle,
    std::optional<gfx::BufferUsage> buffer_usage,
    uint32_t texture_target,
    bool is_software)
    : mailbox_(mailbox),
      metadata_(metadata),
      creation_sync_token_(creation_sync_token),
      managed_sync_tokens_(managed_sync_tokens),
      debug_label_(debug_label),
      buffer_handle_(std::move(buffer_handle)),
      buffer_usage_(buffer_usage),
      texture_target_(texture_target),
      is_software_(is_software) {}

ExportedSharedImage ExportedSharedImage::Clone() const {
  std::optional<gfx::GpuMemoryBufferHandle> handle = std::nullopt;
  if (buffer_handle_.has_value()) {
    handle = buffer_handle_->Clone();
  }
  return ExportedSharedImage(mailbox_, metadata_, creation_sync_token_,
                             managed_sync_tokens_, debug_label_,
                             std::move(handle), buffer_usage_, texture_target_,
                             is_software_);
}

SharedImageTexture::ScopedAccess::ScopedAccess(SharedImageTexture* texture,
                                               const SyncToken& sync_token,
                                               bool readonly)
    : texture_(texture), readonly_(readonly) {
  texture_->shared_image_->WaitSyncTokenInternal(texture_->gl_, sync_token);
  texture_->gl_->BeginSharedImageAccessDirectCHROMIUM(
      texture->id(), (readonly_)
                         ? GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM
                         : GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
}

SharedImageTexture::ScopedAccess::~ScopedAccess() {
  CHECK(is_access_ended_);
}

void SharedImageTexture::ScopedAccess::DidEndAccess() {
  is_access_ended_ = true;
  texture_->DidEndAccess(readonly_);
}

// static
SyncToken SharedImageTexture::ScopedAccess::EndAccess(
    std::unique_ptr<SharedImageTexture::ScopedAccess> scoped_shared_image) {
  gles2::GLES2Interface* gl = scoped_shared_image->texture_->gl_;
  gl->EndSharedImageAccessDirectCHROMIUM(scoped_shared_image->texture_->id());
  scoped_shared_image->DidEndAccess();
  return scoped_shared_image->texture_->shared_image_->GenSyncTokenInternal(gl);
}

SharedImageTexture::SharedImageTexture(gles2::GLES2Interface* gl,
                                       ClientSharedImage* shared_image)
    : gl_(gl), shared_image_(shared_image) {
  CHECK(gl_);
  CHECK(shared_image_);
  shared_image_->WaitSyncTokenInternal(gl_,
                                       shared_image_->creation_sync_token());
  id_ = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(
      shared_image_->mailbox().name);
}

SharedImageTexture::~SharedImageTexture() {
  CHECK(!has_active_access_);
  gl_->DeleteTextures(1, &id_);
}

std::unique_ptr<SharedImageTexture::ScopedAccess>
SharedImageTexture::BeginAccess(const SyncToken& sync_token, bool readonly) {
  CHECK(!has_active_access_);
  if (readonly) {
    CHECK(shared_image_->usage().Has(SHARED_IMAGE_USAGE_GLES2_READ));
  } else {
    CHECK(shared_image_->usage().Has(SHARED_IMAGE_USAGE_GLES2_WRITE));
  }
  has_active_access_ = true;
  shared_image_->BeginAccess(readonly);
  return base::WrapUnique(
      new SharedImageTexture::ScopedAccess(this, sync_token, readonly));
}

void SharedImageTexture::DidEndAccess(bool readonly) {
  has_active_access_ = false;
  shared_image_->EndAccess(readonly);
}

RasterScopedAccess::RasterScopedAccess(InterfaceBase* raster_interface,
                                       ClientSharedImage* shared_image,
                                       const SyncToken& sync_token,
                                       bool readonly)
    : raster_interface_(raster_interface),
      shared_image_(shared_image),
      readonly_(readonly) {
  CHECK(raster_interface_);
  shared_image_->BeginAccess(readonly);
  shared_image_->WaitSyncTokenInternal(raster_interface_, sync_token);
  if (readonly) {
    bool has_read_usage =
        shared_image_->usage().Has(SHARED_IMAGE_USAGE_RASTER_READ) ||
        shared_image_->usage().Has(SHARED_IMAGE_USAGE_RASTER_COPY_SOURCE);
    CHECK(has_read_usage);
  } else {
    bool has_write_usage =
        shared_image_->usage().Has(SHARED_IMAGE_USAGE_RASTER_WRITE);
    CHECK(has_write_usage);
  }
}

// static
SyncToken RasterScopedAccess::EndAccess(
    std::unique_ptr<RasterScopedAccess> scoped_access) {
  InterfaceBase* raster_interface = scoped_access->raster_interface_;
  scoped_access->shared_image_->EndAccess(scoped_access->readonly_);
  return scoped_access->shared_image_->GenSyncTokenInternal(raster_interface);
}

WebGPUTextureScopedAccess::WebGPUTextureScopedAccess(
    webgpu::WebGPUInterface* webgpu,
    ClientSharedImage* shared_image,
    const SyncToken& sync_token,
    const wgpu::dawn::wire::client::Device& device,
    const wgpu::dawn::wire::client::TextureDescriptor& desc,
    uint64_t usage,
    webgpu::MailboxFlags mailbox_flags)
    : webgpu_(webgpu), shared_image_(shared_image) {
  // Wait on any work using the image.
  shared_image_->WaitSyncTokenInternal(webgpu_, sync_token);

  // Produce and inject image to WebGPU texture
  webgpu::ReservedTexture reservation = webgpu_->ReserveTexture(
      device.Get(), &static_cast<const WGPUTextureDescriptor&>(desc));
  DCHECK(reservation.texture);

  // If either |desc.usage| or |usage| contains the following flags, the access
  // is not read-only.
  const wgpu::TextureUsage write_flags = wgpu::TextureUsage::CopyDst |
                                         wgpu::TextureUsage::RenderAttachment |
                                         wgpu::TextureUsage::StorageBinding;
  readonly_ = !((desc.usage | wgpu::TextureUsage{usage}) & write_flags);
  if (readonly_) {
    CHECK(shared_image_->usage().Has(SHARED_IMAGE_USAGE_WEBGPU_READ));
  } else {
    CHECK(shared_image_->usage().Has(SHARED_IMAGE_USAGE_WEBGPU_WRITE));
  }

  shared_image_->BeginAccess(readonly_);
  texture_ = base::WrapUnique(
      new wgpu::Texture(wgpu::Texture::Acquire(reservation.texture)));
  device_id_ = reservation.deviceId;
  device_generation_ = reservation.deviceGeneration;
  texture_id_ = reservation.id;
  texture_generation_ = reservation.generation;

  // This may fail because gl_backing resource cannot produce dawn
  // representation.
  webgpu_->AssociateMailbox(
      device_id_, device_generation_, texture_id_, texture_generation_,
      static_cast<uint64_t>(desc.usage), static_cast<uint64_t>(usage),
      reinterpret_cast<const WGPUTextureFormat*>(desc.viewFormats),
      base::checked_cast<GLuint>(desc.viewFormatCount), mailbox_flags,
      shared_image->mailbox());
}

WebGPUTextureScopedAccess::~WebGPUTextureScopedAccess() = default;

SyncToken WebGPUTextureScopedAccess::EndAccess(
    std::unique_ptr<WebGPUTextureScopedAccess> scoped_access) {
  webgpu::WebGPUInterface* webgpu = scoped_access->webgpu_;
  if (scoped_access->needs_present_) {
    webgpu->DissociateMailboxForPresent(
        scoped_access->device_id_, scoped_access->device_generation_,
        scoped_access->texture_id_, scoped_access->texture_generation_);
  } else {
    webgpu->DissociateMailbox(scoped_access->texture_id_,
                              scoped_access->texture_generation_);
  }

  scoped_access->shared_image_->EndAccess(scoped_access->readonly_);
  return scoped_access->shared_image_->GenSyncTokenInternal(webgpu);
}

const wgpu::dawn::wire::client::Texture& WebGPUTextureScopedAccess::texture() {
  return *texture_.get();
}

void WebGPUTextureScopedAccess::ClearContext() {
  webgpu_ = nullptr;
}

void WebGPUTextureScopedAccess::SetNeedsPresent(bool needs_present) {
  needs_present_ = needs_present;
}

std::unique_ptr<WebGPUBufferScopedAccess>
ClientSharedImage::BeginWebGPUBufferAccess(
    webgpu::WebGPUInterface* webgpu,
    const SyncToken& sync_token,
    const wgpu::dawn::wire::client::Device& device,
    const wgpu::dawn::wire::client::BufferDescriptor& desc,
    uint64_t usage,
    webgpu::MailboxFlags mailbox_flags) {
  CHECK(metadata_.usage.Has(SHARED_IMAGE_USAGE_WEBGPU_SHARED_BUFFER));
  return base::WrapUnique(new WebGPUBufferScopedAccess(
      webgpu, this, sync_token, device, desc, usage, mailbox_flags));
}

WebGPUBufferScopedAccess::WebGPUBufferScopedAccess(
    webgpu::WebGPUInterface* webgpu,
    ClientSharedImage* shared_image,
    const SyncToken& sync_token,
    const wgpu::dawn::wire::client::Device& device,
    const wgpu::dawn::wire::client::BufferDescriptor& desc,
    uint64_t usage,
    webgpu::MailboxFlags mailbox_flags)
    : webgpu_(webgpu), shared_image_(shared_image) {
  // Wait on any work using the buffer.
  shared_image_->WaitSyncTokenInternal(webgpu_, sync_token);

  webgpu::ReservedBuffer reservation = webgpu_->ReserveBuffer(
      device.Get(), &static_cast<const WGPUBufferDescriptor&>(desc));
  DCHECK(reservation.buffer);

  buffer_id_ = reservation.id;
  buffer_generation_ = reservation.generation;

  // We currently only use storage buffers. Which are always read-write.
  shared_image_->BeginAccess(false);

  buffer_ = base::WrapUnique(
      new wgpu::Buffer(wgpu::Buffer::Acquire(reservation.buffer)));

  webgpu_->AssociateMailboxForBuffer(
      reservation.deviceId, reservation.deviceGeneration, buffer_id_,
      buffer_generation_, static_cast<uint64_t>(desc.usage),
      shared_image_->mailbox());
}

WebGPUBufferScopedAccess::~WebGPUBufferScopedAccess() = default;

SyncToken WebGPUBufferScopedAccess::EndAccess(
    std::unique_ptr<WebGPUBufferScopedAccess> scoped_access) {
  webgpu::WebGPUInterface* webgpu = scoped_access->webgpu_;
  SyncToken finished_access_token;
  webgpu->DissociateMailboxForBuffer(scoped_access->buffer_id_,
                                     scoped_access->buffer_generation_);
  scoped_access->shared_image_->EndAccess(false);

  // SyncToken must be verified to allow use from another pipe.
  webgpu->GenSyncTokenCHROMIUM(finished_access_token.GetData());
  return finished_access_token;
}

const wgpu::Buffer& WebGPUBufferScopedAccess::buffer() {
  return *buffer_.get();
}

}  // namespace gpu
