// Copyright 2022 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/service/shared_image/compound_image_backing.h"

#include <ostream>

#include "base/check_is_test.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/memory_allocator_dump_guid.h"
#include "base/trace_event/process_memory_dump.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/shared_image_info.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/shared_image_backing_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_copy_manager.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
#include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
#include "gpu/command_buffer/service/shared_image/shared_memory_image_backing.h"
#include "gpu/command_buffer/service/shared_image/shared_memory_image_backing_factory.h"
#include "gpu/command_buffer/service/shared_memory_region_wrapper.h"
#include "gpu/config/gpu_finch_features.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSurfaceProps.h"
#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
#include "third_party/skia/include/gpu/graphite/BackendTexture.h"
#include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer_handle.h"

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

namespace gpu {
namespace {

constexpr AccessStreamSet kMemoryStreamSet = {SharedImageAccessStream::kMemory};

// Unique GUIDs for child backings.
base::trace_event::MemoryAllocatorDumpGuid GetSubBackingGUIDForTracing(
    const Mailbox& mailbox,
    int backing_index) {
  return base::trace_event::MemoryAllocatorDumpGuid(
      base::StringPrintf("gpu-shared-image/%s/sub-backing/%d",
                         mailbox.ToDebugString().c_str(), backing_index));
}

// LINT.IfChange(ContentSyncReason)
enum class ContentSyncReason { kRead = 0, kWrite = 1, kMaxValue = kWrite };
// LINT.ThenChange(//tools/metrics/histograms/metadata/gpu/enums.xml:ContentSyncReason)

#if BUILDFLAG(IS_WIN)
// Only allow shmem overlays for NV12 on Windows.
// This moves the SCANOUT flag from the GPU backing to the shmem backing in the
// CompoundImageBacking.
constexpr bool kAllowShmOverlays = true;
#else
constexpr bool kAllowShmOverlays = false;
#endif

gpu::SharedImageUsageSet GetShmSharedImageUsage(SharedImageUsageSet usage) {
  gpu::SharedImageUsageSet new_usage = SHARED_IMAGE_USAGE_CPU_WRITE_ONLY;
  if (usage.Has(SHARED_IMAGE_USAGE_CPU_READ)) {
    new_usage |= SHARED_IMAGE_USAGE_CPU_READ;
  }
  if (kAllowShmOverlays && usage.Has(gpu::SHARED_IMAGE_USAGE_SCANOUT)) {
    new_usage |= SHARED_IMAGE_USAGE_SCANOUT;
  }
  return new_usage;
}

// This might need further tweaking in order to be able to choose appropriate
// backing. Note that the backing usually doesnt know at this point if the
// access stream will be used for read or write.
SharedImageUsageSet GetUsageFromAccessStream(SharedImageAccessStream stream) {
  switch (stream) {
    case SharedImageAccessStream::kGL:
      return SHARED_IMAGE_USAGE_GLES2_READ | SHARED_IMAGE_USAGE_GLES2_WRITE;
    case SharedImageAccessStream::kSkia:
      return SHARED_IMAGE_USAGE_RASTER_READ | SHARED_IMAGE_USAGE_RASTER_WRITE |
             SHARED_IMAGE_USAGE_DISPLAY_READ | SHARED_IMAGE_USAGE_DISPLAY_WRITE;
    case SharedImageAccessStream::kDawn:
      return SHARED_IMAGE_USAGE_WEBGPU_READ | SHARED_IMAGE_USAGE_WEBGPU_WRITE;
    case SharedImageAccessStream::kDawnBuffer:
      return SHARED_IMAGE_USAGE_WEBGPU_READ | SHARED_IMAGE_USAGE_WEBGPU_WRITE;
    case SharedImageAccessStream::kOverlay:
      return SHARED_IMAGE_USAGE_SCANOUT;
    case SharedImageAccessStream::kVaapi:
      return SHARED_IMAGE_USAGE_VIDEO_DECODE;
    case SharedImageAccessStream::kWebNNTensor:
      // Note that SHARED_IMAGE_USAGE_WEBNN_SHARED_TENSOR is the main usage, we
      // always need it for WebNN, the two other(*_TENSOR_READ/WRITE) are for
      // additional functionality in webnn (upload/readback of the tensor).
      return SHARED_IMAGE_USAGE_WEBNN_SHARED_TENSOR;
    case SharedImageAccessStream::kVulkan:
      return SHARED_IMAGE_USAGE_RASTER_READ | SHARED_IMAGE_USAGE_RASTER_WRITE |
             SHARED_IMAGE_USAGE_DISPLAY_READ | SHARED_IMAGE_USAGE_DISPLAY_WRITE;
    case SharedImageAccessStream::kMemory:
      // Below usage set ensures that only SharedMemoryImageBacking will be able
      // to support this stream.
      return SHARED_IMAGE_USAGE_CPU_WRITE_ONLY | SHARED_IMAGE_USAGE_CPU_READ |
             SHARED_IMAGE_USAGE_RASTER_COPY_SOURCE;
    default:
      NOTREACHED();
  }
}

viz::SharedImageFormat GetSinglePlaneRGBAFormatForChannelFormat(
    viz::SharedImageFormat::ChannelFormat channel_format) {
  switch (channel_format) {
    case viz::SharedImageFormat::ChannelFormat::k8:
      return viz::SinglePlaneFormat::kRGBA_8888;
    case viz::SharedImageFormat::ChannelFormat::k10:
      return viz::SinglePlaneFormat::kRGBA_1010102;
    case viz::SharedImageFormat::ChannelFormat::k16:
    case viz::SharedImageFormat::ChannelFormat::k16F:
      return viz::SinglePlaneFormat::kRGBA_F16;
  }
}

}  // namespace

// Wrapped representation types are not in the anonymous namespace because they
// need to be friend classes to real representations to access protected
// virtual functions.
class WrappedGLTextureCompoundImageRepresentation
    : public GLTextureImageRepresentation {
 public:
  WrappedGLTextureCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<GLTextureImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : GLTextureImageRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    DCHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  // GLTextureImageRepresentation implementation.
  bool BeginAccess(GLenum mode) final {
    AccessMode access_mode =
        mode == kReadAccessMode ? AccessMode::kRead : AccessMode::kWrite;
    if (!compound_backing()->NotifyBeginAccess(wrapped_->backing(), access_mode,
                                               SharedImageAccessStream::kGL)) {
      return false;
    }
    access_mode_ = access_mode;
    return wrapped_->BeginAccess(mode);
  }

  void EndAccess() override {
    wrapped_->EndAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(), access_mode_);
  }

  gpu::TextureBase* GetTextureBase(size_t plane_index) final {
    return wrapped_->GetTextureBase(plane_index);
  }

  bool SupportsMultipleConcurrentReadAccess() final {
    return wrapped_->SupportsMultipleConcurrentReadAccess();
  }

  gles2::Texture* GetTexture(size_t plane_index) final {
    return wrapped_->GetTexture(plane_index);
  }

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    GLTextureImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<GLTextureImageRepresentation> wrapped_;
  AccessMode access_mode_ = AccessMode::kNone;
};

class WrappedGLTexturePassthroughCompoundImageRepresentation
    : public GLTexturePassthroughImageRepresentation {
 public:
  WrappedGLTexturePassthroughCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<GLTexturePassthroughImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : GLTexturePassthroughImageRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    DCHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  // GLTexturePassthroughImageRepresentation implementation.
  bool BeginAccess(GLenum mode) override {
    AccessMode access_mode =
        mode == kReadAccessMode ? AccessMode::kRead : AccessMode::kWrite;
    if (!compound_backing()->NotifyBeginAccess(wrapped_->backing(), access_mode,
                                               SharedImageAccessStream::kGL)) {
      return false;
    }
    access_mode_ = access_mode;
    return wrapped_->BeginAccess(mode);
  }
  void EndAccess() override {
    wrapped_->EndAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(), access_mode_);
  }

  gpu::TextureBase* GetTextureBase(size_t plane_index) final {
    return wrapped_->GetTextureBase(plane_index);
  }

  bool SupportsMultipleConcurrentReadAccess() final {
    return wrapped_->SupportsMultipleConcurrentReadAccess();
  }

  const scoped_refptr<gles2::TexturePassthrough>& GetTexturePassthrough(
      size_t plane_index) final {
    return wrapped_->GetTexturePassthrough(plane_index);
  }

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    GLTexturePassthroughImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<GLTexturePassthroughImageRepresentation> wrapped_;
  AccessMode access_mode_ = AccessMode::kNone;
};

class WrappedSkiaGaneshCompoundImageRepresentation
    : public SkiaGaneshImageRepresentation {
 public:
  WrappedSkiaGaneshCompoundImageRepresentation(
      GrDirectContext* gr_context,
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<SkiaGaneshImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : SkiaGaneshImageRepresentation(gr_context, manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    DCHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  // SkiaImageRepresentation implementation.
  bool SupportsMultipleConcurrentReadAccess() final {
    return wrapped_->SupportsMultipleConcurrentReadAccess();
  }

  std::vector<sk_sp<SkSurface>> BeginWriteAccess(
      int final_msaa_count,
      const SkSurfaceProps& surface_props,
      const gfx::Rect& update_rect,
      std::vector<GrBackendSemaphore>* begin_semaphores,
      std::vector<GrBackendSemaphore>* end_semaphores,
      std::unique_ptr<skgpu::MutableTextureState>* end_state) final {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kWrite,
            SharedImageAccessStream::kSkia)) {
      return {};
    }
    return wrapped_->BeginWriteAccess(final_msaa_count, surface_props,
                                      update_rect, begin_semaphores,
                                      end_semaphores, end_state);
  }
  std::vector<sk_sp<GrPromiseImageTexture>> BeginWriteAccess(
      std::vector<GrBackendSemaphore>* begin_semaphores,
      std::vector<GrBackendSemaphore>* end_semaphores,
      std::unique_ptr<skgpu::MutableTextureState>* end_state) final {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kWrite,
            SharedImageAccessStream::kSkia)) {
      return {};
    }
    return wrapped_->BeginWriteAccess(begin_semaphores, end_semaphores,
                                      end_state);
  }
  void EndWriteAccess() final {
    wrapped_->EndWriteAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(),
                                        AccessMode::kWrite);
  }

  std::vector<sk_sp<GrPromiseImageTexture>> BeginReadAccess(
      std::vector<GrBackendSemaphore>* begin_semaphores,
      std::vector<GrBackendSemaphore>* end_semaphores,
      std::unique_ptr<skgpu::MutableTextureState>* end_state) final {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kRead,
            SharedImageAccessStream::kSkia)) {
      return {};
    }
    return wrapped_->BeginReadAccess(begin_semaphores, end_semaphores,
                                     end_state);
  }
  void EndReadAccess() final {
    wrapped_->EndReadAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(), AccessMode::kRead);
  }

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    SkiaGaneshImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<SkiaGaneshImageRepresentation> wrapped_;
};

class WrappedSkiaGraphiteCompoundImageRepresentation
    : public SkiaGraphiteImageRepresentation {
 public:
  WrappedSkiaGraphiteCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<SkiaGraphiteImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : SkiaGraphiteImageRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    CHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  // SkiaGraphiteImageRepresentation implementation.
  bool SupportsMultipleConcurrentReadAccess() final {
    return wrapped_->SupportsMultipleConcurrentReadAccess();
  }
  bool SupportsDeferredGraphiteSubmit() final {
    return wrapped_->SupportsDeferredGraphiteSubmit();
  }

  std::vector<sk_sp<SkSurface>> BeginWriteAccess(
      const SkSurfaceProps& surface_props,
      const gfx::Rect& update_rect) final {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kWrite,
            SharedImageAccessStream::kSkia)) {
      return {};
    }
    return wrapped_->BeginWriteAccess(surface_props, update_rect);
  }
  std::vector<scoped_refptr<GraphiteTextureHolder>> BeginWriteAccess() final {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kWrite,
            SharedImageAccessStream::kSkia)) {
      return {};
    }
    return wrapped_->BeginWriteAccess();
  }
  void EndWriteAccess() final {
    wrapped_->EndWriteAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(),
                                        AccessMode::kWrite);
  }

  std::vector<scoped_refptr<GraphiteTextureHolder>> BeginReadAccess() final {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kRead,
            SharedImageAccessStream::kSkia)) {
      return {};
    }
    return wrapped_->BeginReadAccess();
  }
  void EndReadAccess() final {
    wrapped_->EndReadAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(), AccessMode::kRead);
  }

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    SkiaGraphiteImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<SkiaGraphiteImageRepresentation> wrapped_;
};

class WrappedDawnCompoundImageRepresentation : public DawnImageRepresentation {
 public:
  WrappedDawnCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<DawnImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : DawnImageRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    DCHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  // DawnImageRepresentation implementation.
  wgpu::Texture BeginAccess(wgpu::TextureUsage webgpu_usage,
                            wgpu::TextureUsage internal_usage) override {
    AccessMode access_mode =
        webgpu_usage & kWriteUsage ? AccessMode::kWrite : AccessMode::kRead;
    if (internal_usage & kWriteUsage) {
      access_mode = AccessMode::kWrite;
    }
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), access_mode, SharedImageAccessStream::kDawn)) {
      return nullptr;
    }
    access_mode_ = access_mode;
    return wrapped_->BeginAccess(webgpu_usage, internal_usage);
  }
  void EndAccess() override {
    wrapped_->EndAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(), access_mode_);
  }

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    DawnImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<DawnImageRepresentation> wrapped_;
  AccessMode access_mode_ = AccessMode::kNone;
};

class WrappedDawnBufferCompoundImageRepresentation
    : public DawnBufferRepresentation {
 public:
  WrappedDawnBufferCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<DawnBufferRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : DawnBufferRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    DCHECK(wrapped_);
  }

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

 private:
  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  wgpu::Buffer BeginAccess(wgpu::BufferUsage usage) override {
    AccessMode access_mode = usage & wgpu::BufferUsage::MapWrite
                                 ? AccessMode::kWrite
                                 : AccessMode::kRead;
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), access_mode,
            SharedImageAccessStream::kDawnBuffer)) {
      return nullptr;
    }
    access_mode_ = access_mode;
    return wrapped_->BeginAccess(usage);
  }

  void EndAccess() override {
    wrapped_->EndAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(), access_mode_);
  }

  void OnContextLost() override {
    DawnBufferRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<DawnBufferRepresentation> wrapped_;
  AccessMode access_mode_ = AccessMode::kNone;
};

class WrappedOverlayCompoundImageRepresentation
    : public OverlayImageRepresentation {
 public:
  WrappedOverlayCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<OverlayImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : OverlayImageRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    DCHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  // OverlayImageRepresentation implementation.
  bool BeginReadAccess(gfx::GpuFenceHandle& acquire_fence) final {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kRead,
            SharedImageAccessStream::kOverlay)) {
      return false;
    }
    return wrapped_->BeginReadAccess(acquire_fence);
  }
  void EndReadAccess(gfx::GpuFenceHandle release_fence) final {
    wrapped_->EndReadAccess(std::move(release_fence));
    compound_backing()->NotifyEndAccess(wrapped_->backing(), AccessMode::kRead);
  }
#if BUILDFLAG(IS_ANDROID)
  AHardwareBuffer* GetAHardwareBuffer() final {
    return wrapped_->GetAHardwareBuffer();
  }
  std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
  GetAHardwareBufferFenceSync() final {
    return wrapped_->GetAHardwareBufferFenceSync();
  }
#elif BUILDFLAG(IS_WIN)
  std::optional<gl::DCLayerOverlayImage> GetDCLayerOverlayImage() final {
    return wrapped_->GetDCLayerOverlayImage();
  }
#elif BUILDFLAG(IS_APPLE)
  gfx::ScopedIOSurface GetIOSurface() const final {
    return wrapped_->GetIOSurface();
  }
  std::vector<gfx::MTLSharedEventFence> GetBackpressureFences() const final {
    return wrapped_->GetBackpressureFences();
  }
  bool IsInUseByWindowServer() const final {
    return wrapped_->IsInUseByWindowServer();
  }
#endif

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    OverlayImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<OverlayImageRepresentation> wrapped_;
};

class WrappedWebNNTensorCompoundImageRepresentation
    : public WebNNTensorRepresentation {
 public:
  WrappedWebNNTensorCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<WebNNTensorRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : WebNNTensorRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    DCHECK(wrapped_);
  }

#if BUILDFLAG(IS_WIN)
  scoped_refptr<gfx::D3DSharedFence> GetAcquireFence() const final {
    return wrapped_->GetAcquireFence();
  }

  void SetReleaseFence(scoped_refptr<gfx::D3DSharedFence> release_fence) final {
    wrapped_->SetReleaseFence(std::move(release_fence));
  }

  Microsoft::WRL::ComPtr<ID3D12Resource> GetD3D12Buffer() const final {
    return wrapped_->GetD3D12Buffer();
  }

  base::win::ScopedHandle GetD3D12HeapHandle() const final {
    return wrapped_->GetD3D12HeapHandle();
  }

#endif

#if BUILDFLAG(IS_APPLE)
  IOSurfaceRef GetIOSurface() const final { return wrapped_->GetIOSurface(); }
#endif

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    WebNNTensorRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  bool BeginAccess() override {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kWrite,
            SharedImageAccessStream::kWebNNTensor)) {
      return false;
    }
    return wrapped_->BeginAccess();
  }

  void EndAccess() final {
    wrapped_->EndAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(),
                                        AccessMode::kWrite);
  }

  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<WebNNTensorRepresentation> wrapped_;
};

class WrappedMemoryCompoundImageRepresentation
    : public MemoryImageRepresentation {
 public:
  WrappedMemoryCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<MemoryImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : MemoryImageRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    CHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  SkPixmap BeginReadAccess() override {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kRead,
            SharedImageAccessStream::kMemory)) {
      return SkPixmap();
    }
    return wrapped_->BeginReadAccess();
  }

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    MemoryImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<MemoryImageRepresentation> wrapped_;
};

class WrappedVideoCompoundImageRepresentation
    : public VideoImageRepresentation {
 public:
  WrappedVideoCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      std::unique_ptr<VideoImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : VideoImageRepresentation(manager, backing, tracker),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    CHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  bool BeginWriteAccess() override {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kWrite,
            SharedImageAccessStream::kVaapi)) {
      return false;
    }
    return wrapped_->BeginWriteAccess();
  }
  void EndWriteAccess() override {
    wrapped_->EndWriteAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(),
                                        AccessMode::kWrite);
  }
  bool BeginReadAccess() override {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), AccessMode::kRead,
            SharedImageAccessStream::kVaapi)) {
      return false;
    }
    return wrapped_->BeginReadAccess();
  }
  void EndReadAccess() override {
    wrapped_->EndReadAccess();
    compound_backing()->NotifyEndAccess(wrapped_->backing(), AccessMode::kRead);
  }
#if BUILDFLAG(IS_WIN)
  D3D11TextureAndArrayIndex GetD3D11Texture() const override {
    return wrapped_->GetD3D11Texture();
  }
#endif
#if BUILDFLAG(IS_ANDROID)
  AHardwareBuffer* GetAHardwareBuffer() const override {
    return wrapped_->GetAHardwareBuffer();
  }
#endif

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    VideoImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<VideoImageRepresentation> wrapped_;
};

#if BUILDFLAG(ENABLE_VULKAN)
class WrappedVulkanCompoundImageRepresentation
    : public VulkanImageRepresentation {
 public:
  WrappedVulkanCompoundImageRepresentation(
      SharedImageManager* manager,
      SharedImageBacking* backing,
      MemoryTypeTracker* tracker,
      gpu::VulkanDeviceQueue* vulkan_device_queue,
      gpu::VulkanImplementation& vulkan_impl,
      std::unique_ptr<VulkanImageRepresentation> wrapped,
      std::unique_ptr<SharedImageBacking> owned_backing)
      : VulkanImageRepresentation(manager,
                                  backing,
                                  tracker,
                                  nullptr,
                                  vulkan_device_queue,
                                  vulkan_impl),
        owned_backing_(std::move(owned_backing)),
        wrapped_(std::move(wrapped)) {
    DCHECK(wrapped_);
  }

  CompoundImageBacking* compound_backing() {
    return static_cast<CompoundImageBacking*>(backing());
  }

  bool BeginAccess(
      AccessMode access_mode,
      std::vector<base::RawPtrIfPtrT<VkSemaphore, DanglingUntriaged>>&
          begin_semaphores,
      std::vector<base::RawPtrIfPtrT<VkSemaphore, DanglingUntriaged>>&
          end_semaphores) override {
    if (!compound_backing()->NotifyBeginAccess(
            wrapped_->backing(), access_mode,
            SharedImageAccessStream::kVulkan)) {
      return false;
    }
    if (!wrapped_->BeginAccess(access_mode, begin_semaphores, end_semaphores)) {
      return false;
    }
    return true;
  }

  void EndAccess(bool is_read_only, VkSemaphore end_semaphore) override {
    wrapped_->EndAccess(is_read_only, end_semaphore);
    compound_backing()->NotifyEndAccess(
        wrapped_->backing(),
        is_read_only ? AccessMode::kRead : AccessMode::kWrite);
  }

  gpu::VulkanImage& GetVulkanImage() override {
    return wrapped_->GetVulkanImage();
  }

  void SetClearedRect(const gfx::Rect& cleared_rect) override {
    SharedImageRepresentation::SetClearedRect(cleared_rect);
    if (owned_backing_) {
      wrapped_->SetClearedRect(cleared_rect);
    }
  }

  void OnContextLost() override {
    VulkanImageRepresentation::OnContextLost();
    wrapped_->OnContextLost();
  }

 private:
  std::unique_ptr<SharedImageBacking> owned_backing_;
  std::unique_ptr<VulkanImageRepresentation> wrapped_;
};
#endif

// static
bool CompoundImageBacking::IsValidSharedMemoryFormat(
    const gfx::Size& size,
    viz::SharedImageFormat format) {
  if (format.PrefersExternalSampler()) {
    DVLOG(1) << "Unsupported external sampler for format: "
             << format.ToString();
    return false;
  }

  if (!IsSizeForBufferHandleValid(size, format)) {
    DVLOG(1) << "Invalid image size: " << size.ToString()
             << " for format: " << format.ToString();
    return false;
  }

  return true;
}

// static
bool CompoundImageBacking::ComputeIsThreadSafe(
    SharedImageFactoryRef* factory_ref,
    SharedImageUsageSet usage) {
  bool is_thread_safe = false;
  if (factory_ref) {
    factory_ref->Execute([&](SharedImageFactory* factory) {
      is_thread_safe = factory->IsSharedBetweenThreads(usage);

      // If dynamic backing allocation is enabled, the backing must also be
      // thread-safe if the environment is multi-threaded (e.g., DrDC or
      // WebView), as we can't predict all future sharing needs from the
      // initial usage.
      if (!is_thread_safe && base::FeatureList::IsEnabled(
                                 features::kUseDynamicBackingAllocations)) {
        is_thread_safe =
            factory->shared_image_manager_->display_context_on_another_thread();
      }
    });
  }
  return is_thread_safe;
}

// static
SharedImageUsageSet CompoundImageBacking::GetGpuSharedImageUsage(
    SharedImageUsageSet usage) {
  // Add allow copying from the shmem backing to the gpu backing.
  usage |= SHARED_IMAGE_USAGE_CPU_UPLOAD;

  if (kAllowShmOverlays) {
    // Remove SCANOUT usage since it was previously moved to the shmem backing.
    // See: |GetShmSharedImageUsage|
    usage.RemoveAll(SharedImageUsageSet(gpu::SHARED_IMAGE_USAGE_SCANOUT));
  }
  if (usage.Has(SHARED_IMAGE_USAGE_CPU_READ)) {
    // Remove CPU_READ usage since it was previously moved to the shmem backing.
    // See: |GetShmSharedImageUsage|
    usage.RemoveAll(SharedImageUsageSet(gpu::SHARED_IMAGE_USAGE_CPU_READ));
  }
  if (usage.Has(SHARED_IMAGE_USAGE_CPU_WRITE_ONLY)) {
    // Remove CPU_WRITE usage since it was previously moved to the shmem
    // backing. See: |GetShmSharedImageUsage|
    usage.RemoveAll(
        SharedImageUsageSet(gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY));
  }

  return usage;
}

// static
std::unique_ptr<SharedImageBacking> CompoundImageBacking::Create(
    SharedImageFactory* shared_image_factory,
    scoped_refptr<SharedImageCopyManager> copy_manager,
    const Mailbox& mailbox,
    gfx::GpuMemoryBufferHandle handle,
    const SharedImageInfo& si_info) {
  auto format = si_info.format;
  auto size = si_info.size;
  auto usage = si_info.usage;
  if (!IsValidSharedMemoryFormat(size, format)) {
    return nullptr;
  }

  auto* gpu_backing_factory = shared_image_factory->GetFactoryByUsage(
      GetGpuSharedImageUsage(usage), format, size,
      /*pixel_data=*/{}, gfx::EMPTY_BUFFER, /*stream=*/std::nullopt,
      /*params=*/nullptr);
  if (!gpu_backing_factory) {
    return nullptr;
  }

  SharedImageInfo shm_si_info(
      format, size, si_info.color_space, si_info.surface_origin,
      si_info.alpha_type, GetShmSharedImageUsage(usage), si_info.debug_label);
  auto shm_backing = SharedMemoryImageBackingFactory().CreateSharedImage(
      mailbox, shm_si_info, /*is_thread_safe=*/false, std::move(handle));
  if (!shm_backing) {
    return nullptr;
  }
  shm_backing->SetNotRefCounted();

  return base::WrapUnique(new CompoundImageBacking(
      mailbox, si_info, std::move(shm_backing),
      shared_image_factory->GetFactoryRef(), gpu_backing_factory->GetWeakPtr(),
      std::move(copy_manager)));
}

// static
std::unique_ptr<SharedImageBacking> CompoundImageBacking::Create(
    SharedImageFactory* shared_image_factory,
    scoped_refptr<SharedImageCopyManager> copy_manager,
    const Mailbox& mailbox,
    const SharedImageInfo& si_info,
    gfx::BufferUsage buffer_usage) {
  auto format = si_info.format;
  auto size = si_info.size;
  auto usage = si_info.usage;
  if (!IsValidSharedMemoryFormat(size, format)) {
    return nullptr;
  }

  auto* gpu_backing_factory = shared_image_factory->GetFactoryByUsage(
      GetGpuSharedImageUsage(usage), format, size,
      /*pixel_data=*/{}, gfx::EMPTY_BUFFER, /*stream=*/std::nullopt,
      /*params=*/nullptr);
  if (!gpu_backing_factory) {
    return nullptr;
  }

  SharedImageInfo shm_si_info(
      format, size, si_info.color_space, si_info.surface_origin,
      si_info.alpha_type, GetShmSharedImageUsage(usage), si_info.debug_label);
  auto shm_backing = SharedMemoryImageBackingFactory().CreateSharedImage(
      mailbox, shm_si_info, kNullSurfaceHandle, /*is_thread_safe=*/false,
      buffer_usage);
  if (!shm_backing) {
    return nullptr;
  }
  shm_backing->SetNotRefCounted();

  return base::WrapUnique(new CompoundImageBacking(
      mailbox, si_info, std::move(shm_backing),
      shared_image_factory->GetFactoryRef(), gpu_backing_factory->GetWeakPtr(),
      std::move(copy_manager), std::move(buffer_usage)));
}

// static
void CompoundImageBacking::RecordBackingTypeUMA(
    SharedImageBackingType backing_type) {
  UMA_HISTOGRAM_ENUMERATION("GPU.SharedImage.BackingType", backing_type);
}

std::unique_ptr<SharedImageBacking> CompoundImageBacking::WrapExternalBacking(
    SharedImageFactory* shared_image_factory,
    scoped_refptr<SharedImageCopyManager> copy_manager,
    std::unique_ptr<SharedImageBacking> backing) {
  if (!backing) {
    return nullptr;
  }

  // We don't wrap an existing CompoundImageBacking which consists of a shm and
  // a gpu backing.
  CHECK_NE(backing->GetType(), SharedImageBackingType::kCompound);

  RecordBackingTypeUMA(backing->GetType());

  backing->SetNotRefCounted();

  auto buffer_usage = backing->buffer_usage();
  return base::WrapUnique(new CompoundImageBacking(
      std::move(buffer_usage), std::move(backing), std::move(copy_manager),
      shared_image_factory->GetFactoryRef()));
}

// static
std::unique_ptr<SharedImageBacking>
CompoundImageBacking::CreateSharedMemoryForTesting(
    SharedImageBackingFactory* gpu_backing_factory,
    scoped_refptr<SharedImageCopyManager> copy_manager,
    const Mailbox& mailbox,
    gfx::GpuMemoryBufferHandle handle,
    const SharedImageInfo& si_info) {
  auto format = si_info.format;
  auto size = si_info.size;
  auto usage = si_info.usage;
  DCHECK(IsValidSharedMemoryFormat(size, format));

  SharedImageInfo shm_si_info(
      format, size, si_info.color_space, si_info.surface_origin,
      si_info.alpha_type, GetShmSharedImageUsage(usage), si_info.debug_label);
  auto shm_backing = SharedMemoryImageBackingFactory().CreateSharedImage(
      mailbox, shm_si_info, /*is_thread_safe=*/false, std::move(handle));
  if (!shm_backing) {
    return nullptr;
  }
  shm_backing->SetNotRefCounted();

  return base::WrapUnique(new CompoundImageBacking(
      mailbox, si_info, std::move(shm_backing),
      /*shared_image_factory=*/nullptr, gpu_backing_factory->GetWeakPtr(),
      std::move(copy_manager)));
}

// static
std::unique_ptr<SharedImageBacking>
CompoundImageBacking::CreateSharedMemoryForTesting(
    SharedImageBackingFactory* gpu_backing_factory,
    scoped_refptr<SharedImageCopyManager> copy_manager,
    const Mailbox& mailbox,
    const SharedImageInfo& si_info,
    gfx::BufferUsage buffer_usage) {
  auto format = si_info.format;
  auto size = si_info.size;
  auto usage = si_info.usage;
  DCHECK(IsValidSharedMemoryFormat(size, format));

  SharedImageInfo shm_si_info(
      format, size, si_info.color_space, si_info.surface_origin,
      si_info.alpha_type, GetShmSharedImageUsage(usage), si_info.debug_label);
  auto shm_backing = SharedMemoryImageBackingFactory().CreateSharedImage(
      mailbox, shm_si_info, kNullSurfaceHandle, /*is_thread_safe=*/false,
      buffer_usage);
  if (!shm_backing) {
    return nullptr;
  }
  shm_backing->SetNotRefCounted();

  return base::WrapUnique(new CompoundImageBacking(
      mailbox, si_info, std::move(shm_backing),
      /*shared_image_factory=*/nullptr, gpu_backing_factory->GetWeakPtr(),
      std::move(copy_manager), std::move(buffer_usage)));
}

CompoundImageBacking::CompoundImageBacking(
    const Mailbox& mailbox,
    const SharedImageInfo& si_info,
    std::unique_ptr<SharedImageBacking> shm_backing,
    scoped_refptr<SharedImageFactoryRef> shared_image_factory,
    base::WeakPtr<SharedImageBackingFactory> gpu_backing_factory,
    scoped_refptr<SharedImageCopyManager> copy_manager,
    std::optional<gfx::BufferUsage> buffer_usage)
    : ClearTrackingSharedImageBacking(
          mailbox,
          si_info,
          shm_backing->GetEstimatedSize(),
          ComputeIsThreadSafe(shared_image_factory.get(), si_info.usage),
          std::move(buffer_usage)),
      shared_image_factory_(std::move(shared_image_factory)),
      copy_manager_(std::move(copy_manager)) {
  CHECK(gpu_backing_factory);
  // If the backing is thread-safe, the base class enables an internal lock that
  // protects the |elements_| vector and other metadata from concurrent access.
  DCHECK(shm_backing);
  auto size = si_info.size;
  auto usage = si_info.usage;
  DCHECK_EQ(size, shm_backing->size());

  // Create shared-memory element (stream = kMemory).
  ElementHolder shm_element;
  shm_element.access_streams = kMemoryStreamSet;
  if (kAllowShmOverlays && usage.Has(gpu::SHARED_IMAGE_USAGE_SCANOUT)) {
    shm_element.access_streams.Put(SharedImageAccessStream::kOverlay);
  }
  shm_element.content_id_ = latest_content_id_;
  shm_element.backing = std::move(shm_backing);
  has_shm_backing_ = true;
  elements_.push_back(std::move(shm_element));
  RecordBackingTypeUMA(SharedImageBackingType::kSharedMemory);

  // Whenever CompoundImageBacking is created with a shm backing, mark it as
  // fully cleared.
  SetClearedRectInternal(gfx::Rect(size));

  // Create placeholder for GPU-backed element (streams = all except kMemory).
  ElementHolder gpu_element;
  gpu_element.access_streams =
      base::Difference(AccessStreamSet::All(), kMemoryStreamSet);

  // CreateBackingFromBackingFactory will be called on demand. Hence this is
  // lazy backing creation.
  SharedImageBackingType factory_type = gpu_backing_factory->GetBackingType();
  gpu_element.create_callback = base::BindOnce(
      &CompoundImageBacking::LazyCreateBacking, base::Unretained(this),
      factory_type, std::move(gpu_backing_factory),
      GetGpuSharedImageUsage(usage), si_info.debug_label);
  elements_.push_back(std::move(gpu_element));
  max_elements_allocated_ = 2;
}

CompoundImageBacking::CompoundImageBacking(
    std::optional<gfx::BufferUsage> buffer_usage,
    std::unique_ptr<SharedImageBacking> backing,
    scoped_refptr<SharedImageCopyManager> copy_manager,
    scoped_refptr<SharedImageFactoryRef> shared_image_factory)
    : ClearTrackingSharedImageBacking(
          backing->mailbox(),
          SharedImageInfo(backing->format(),
                          backing->size(),
                          backing->color_space(),
                          backing->surface_origin(),
                          backing->alpha_type(),
                          backing->usage(),
                          backing->debug_label()),
          backing->GetEstimatedSize(),
          ComputeIsThreadSafe(shared_image_factory.get(), backing->usage()),
          std::move(buffer_usage)),
      shared_image_factory_(std::move(shared_image_factory)),
      copy_manager_(std::move(copy_manager)) {
  // Create the element from the backing.
  ElementHolder element;

  // We set the inner backing to support all streams so that we forward all
  // Produce calls to it. This is necessary because:
  // 1. The inner backing might support more than we assume (e.g. SharedMemory
  // supporting Overlay).
  // 2. We want to delegate the decision of "is this supported?" to the inner
  // backing. For example, if ProduceMemory is called on a GPU backing, we want
  // to forward it so the backing can return nullptr (unsupported), rather than
  // CompoundImageBacking logging an error because it thinks no backing exists
  // for that stream.
  // 3. This matches current behavior where we just have one backing handling
  // everything.
  // Future work for dynamic backing allocation will need a way to identify
  // backing support for specific streams based on usage.
  element.access_streams = AccessStreamSet::All();

  // |backing| may have a cleared rect set (e.g. from initial pixel data).
  // Propagate this to the CompoundImageBacking to keep them in sync.
  SetClearedRectInternal(backing->ClearedRect());

  // The backing is already created, so this is not a lazy initialization.
  element.backing = std::move(backing);

  // Mark the backing as having the latest content since it's the only one
  // created so far.
  element.content_id_ = latest_content_id_;
  elements_.push_back(std::move(element));
  CHECK_EQ(elements_.size(), 1u);
  max_elements_allocated_ = 1;
}

CompoundImageBacking::~CompoundImageBacking() {
  UMA_HISTOGRAM_COUNTS_100("GPU.CompoundImageBacking.TotalElementsAllocated",
                           max_elements_allocated_);
  if (pending_copy_to_gmb_callback_) {
    std::move(pending_copy_to_gmb_callback_).Run(/*success=*/false);
  }
}

bool CompoundImageBacking::NotifyBeginAccess(SharedImageBacking* backing,
                                             RepresentationAccessMode mode,
                                             SharedImageAccessStream stream) {
  AutoLock auto_lock(this);

  // Identify if this backing is a permanent element of the container or a
  // transient one allocated for a specific representation.
  ElementHolder* access_element = GetElement(backing);

  // FAST PATH: If this is a permanent element and it already has the latest
  // content, we can skip synchronization for read access.
  if (access_element && access_element->content_id_ == latest_content_id_) {
    if (mode == RepresentationAccessMode::kWrite) {
      // For write access, this backing will become the new "source of truth".
      ++latest_content_id_;
      access_element->content_id_ = latest_content_id_;
    }
    return true;
  }

  // STALE OR TRANSIENT PATH: Either this permanent backing is out-of-date, or
  // it's a transient backing that needs to be initialized. We must find the
  // permanent element that currently holds the latest content and copy from it.
  ElementHolder* latest_content_element = GetElementWithLatestContent();
  if (!latest_content_element) {
    // No permanent element holds the most recent content, so the destination
    // backing cannot be synchronized. This can happen if a transient backing
    // was written to but its content could not be copied back to a permanent
    // element on end access. Do not advance versioning so access can recover if
    // an element re-establishes the latest content (e.g. via Update() for SHM
    // backings). Note that if there is no SHM backing (e.g. GPU-only backings)
    // or no Update() occurs, subsequent access calls (including writes) will
    // continue to fail, requiring client-level context/resource re-creation.
    LOG(ERROR) << "No element with latest content available for sync to "
               << backing->GetName();
    return false;
  }

  bool updated_backing = false;
  bool copy_succeeded = false;

  if (latest_content_element) {
    // Copy data from the latest permanent backing into the current backing
    // (which could be another permanent backing or a transient one).
    copy_succeeded = copy_manager_->CopyImage(
        /*src_backing=*/latest_content_element->GetBacking(),
        /*dst_backing=*/backing);

    if (copy_succeeded) {
      updated_backing = true;

      // When we sync data, we also sync the logical clear state. Propagate the
      // clear rect from the source to the destination, all child backings, and
      // the container itself.
      const gfx::Rect src_cleared_rect =
          latest_content_element->GetBacking()->ClearedRect();
      SetClearedRectInternal(src_cleared_rect);
      for (auto& element : elements_) {
        if (element.backing) {
          element.backing->SetClearedRect(src_cleared_rect);
        }
      }
    } else {
      LOG(ERROR) << "Failed to copy from "
                 << latest_content_element->GetBacking()->GetName() << " to "
                 << backing->GetName();
    }

    UMA_HISTOGRAM_BOOLEAN("GPU.CompoundImageBacking.ContentSync.Success",
                          copy_succeeded);
    UMA_HISTOGRAM_ENUMERATION(
        "GPU.CompoundImageBacking.ContentSync.SourceBackingType",
        latest_content_element->GetBacking()->GetType());
    UMA_HISTOGRAM_ENUMERATION(
        "GPU.CompoundImageBacking.ContentSync.DestBackingType",
        backing->GetType());
    UMA_HISTOGRAM_ENUMERATION("GPU.CompoundImageBacking.ContentSync.Reason",
                              mode == RepresentationAccessMode::kRead
                                  ? ContentSyncReason::kRead
                                  : ContentSyncReason::kWrite);
    UMA_HISTOGRAM_ENUMERATION(
        "GPU.CompoundImageBacking.ContentSync.TriggeringAccessStream", stream);
    UMA_HISTOGRAM_SPARSE(
        "GPU.CompoundImageBacking.ContentSync.InitialSharedImageUsage",
        static_cast<int32_t>(static_cast<uint32_t>(this->usage())));

    if (!copy_succeeded) {
      // The destination backing was not updated so it must not be exposed to
      // the caller. Do not advance versioning so a future access can retry
      // the sync.
      return false;
    }
  }

  // UPDATE VERSIONING:
  // 1. For WRITE access: Increment the global version. If this is a permanent
  //    element, track that it now holds this latest version. Transient backings
  //    don't track their own version locally as they are destroyed after use,
  //    but their write will be synced back to permanent elements in EndAccess.
  // 2. For READ access: If the copy succeeded, mark this permanent element as
  //    being up-to-date with the latest version.
  if (mode == RepresentationAccessMode::kWrite) {
    ++latest_content_id_;
    if (access_element) {
      access_element->content_id_ = latest_content_id_;
    }
  } else if (updated_backing && access_element) {
    access_element->content_id_ = latest_content_id_;
  }
  return true;
}

void CompoundImageBacking::NotifyEndAccess(SharedImageBacking* backing,
                                           RepresentationAccessMode mode) {
  AutoLock auto_lock(this);
  CHECK(backing);

  bool is_transient_backing = !GetElement(backing);

  // If the last access was a write and an underlying backing was accessed,
  // propagate its cleared rect to the compound backing if it's different.
  if (mode == RepresentationAccessMode::kWrite) {
    auto cleared_rect = backing->ClearedRect();
    if (cleared_rect != ClearedRect()) {
      // For transient backings, only update if it's more cleared to avoid
      // overwriting CSI with stale state.
      if (!is_transient_backing || cleared_rect.Contains(ClearedRect())) {
        SetClearedRectInternal(cleared_rect);
      }
    }

    // When is_thread_safe() is true, multiple threads can access the backings.
    // If a backing was written to, we proactively sync its content to all other
    // backings. This is especially critical for transient backings (which are
    // not thread-safe but used in a thread-safe container): we must copy their
    // latest content to the permanent backings before the transient backing is
    // destroyed. This ensures that subsequent accesses on other threads will
    // see the updated data. Note that we are copying back to all the backings
    // here, even to those that will never be read. This can be a performance
    // bottleneck when there are multiple backings.
    if (base::FeatureList::IsEnabled(features::kUseDynamicBackingAllocations) &&
        is_thread_safe() && is_transient_backing) {
      for (auto& element : elements_) {
        auto* dst_backing = element.GetBacking();
        if (dst_backing && dst_backing != backing &&
            element.content_id_ != latest_content_id_) {
          if (copy_manager_->CopyImage(backing, dst_backing)) {
            element.content_id_ = latest_content_id_;
          } else {
            LOG(ERROR) << "DCSI: Proactive copy from " << backing->GetName()
                       << " to " << dst_backing->GetName() << " failed.";
          }
        }
      }
    }
  }
}

void CompoundImageBacking::OnContextLost() {
  ClearTrackingSharedImageBacking::OnContextLost();
  AutoLock auto_lock(this);
  for (const auto& element : elements_) {
    if (element.backing) {
      element.backing->OnContextLost();
    }
  }
}

void CompoundImageBacking::SetPurgeable(bool purgeable) {
  AutoLock auto_lock(this);
  for (auto& element : elements_) {
    if (element.backing) {
      element.backing->SetPurgeable(purgeable);
    }
  }
}

bool CompoundImageBacking::IsPurgeable() const {
  AutoLock auto_lock(this);
  for (const auto& element : elements_) {
    // If any of the elements is purgeable, then return true. The caller may
    // rely on the return value to e.g. recreate data, so needs to be an OR, nor
    // an AND.
    if (element.backing && element.backing->IsPurgeable()) {
      return true;
    }
  }
  return false;
}

SharedImageBackingType CompoundImageBacking::GetType() const {
  return SharedImageBackingType::kCompound;
}

void CompoundImageBacking::Update(std::unique_ptr<gfx::GpuFence> in_fence) {
  // Update() synchronizes CPU-side writes (from Shared Memory or GMB) with the
  // GPU. Hence it must target the backing which owns the CPU-mappable memory.
  //
  // Per this backing's design:
  // 1. SharedMemoryImageBacking if present is the exclusive owner of CPU
  // memory.
  // 2. A SharedMemoryImageBacking or other CPU-mappable backing is always
  // created only at initialization time. Hence it is guaranteed to be at
  // elements_[0].
  // 3. |elements_| always contains at least one element.
  AutoLock auto_lock(this);
  CHECK(!elements_.empty());
  auto& element = elements_[0];
  CHECK(element.backing);
  element.backing->Update(std::move(in_fence));

  // Incrementing the content ID marks this backing as the new "source
  // of truth." Subsequent access to other backings will trigger an
  // efficient copy from this element to ensure consistency.
  element.content_id_ = ++latest_content_id_;
}

bool CompoundImageBacking::CopyToGpuMemoryBuffer() {
  AutoLock auto_lock(this);
  auto& shm_element = GetShmElement();

  if (HasLatestContent(shm_element)) {
    return true;
  }

  auto* gpu_backing = GetGpuBacking();
  if (!gpu_backing ||
      !copy_manager_->CopyImage(gpu_backing, shm_element.GetBacking())) {
    LOG(ERROR) << "Failed to copy from GPU backing (" << gpu_backing->GetName()
               << ") to shared memory";
    return false;
  }

  shm_element.content_id_ = latest_content_id_;
  return true;
}

void CompoundImageBacking::CopyToGpuMemoryBufferAsync(
    base::OnceCallback<void(bool)> callback) {
  AutoLock auto_lock(this);
  auto& shm_element = GetShmElement();

  if (HasLatestContent(shm_element)) {
    std::move(callback).Run(true);
    return;
  }

  if (pending_copy_to_gmb_callback_) {
    DLOG(ERROR) << "Existing CopyToGpuMemoryBuffer operation pending";
    std::move(callback).Run(false);
    return;
  }

  auto* gpu_backing = GetGpuBacking();
  if (!gpu_backing) {
    LOG(ERROR) << "Failed to copy from GPU backing (" << gpu_backing->GetName()
               << ") to shared memory";
    std::move(callback).Run(false);
    return;
  }

  pending_copy_to_gmb_callback_ = std::move(callback);

  gpu_backing->ReadbackToMemoryAsync(
      GetSharedMemoryPixmaps(),
      base::BindOnce(&CompoundImageBacking::OnCopyToGpuMemoryBufferComplete,
                     base::Unretained(this)));
}

void CompoundImageBacking::OnCopyToGpuMemoryBufferComplete(bool success) {
  AutoLock auto_lock(this);
  if (success) {
    auto& shm_element = GetShmElement();
    shm_element.content_id_ = latest_content_id_;
  }
  std::move(pending_copy_to_gmb_callback_).Run(success);
}

gfx::Rect CompoundImageBacking::ClearedRect() const {
  // If we have a shm_backing, we always copy on access and mark entire backing
  // as cleared.
  return ClearedRectInternal();
}

void CompoundImageBacking::SetClearedRect(const gfx::Rect& cleared_rect) {
  AutoLock auto_lock(this);
  SetClearedRectInternal(cleared_rect);

  // Propagate the cleared rect to all underlying backings. This is important
  // because SetClearedRect can be called on a CompoundImageBacking without a
  // preceding BeginAccess call (e.g. in WebGPU's AssociateMailbox with the
  // WEBGPU_MAILBOX_DISCARD flag). In such cases, it is hard to know which
  // backing is currently being accessed. so we must ensure all potential
  // backings are updated
  for (auto& element : elements_) {
    if (element.backing) {
      element.backing->SetClearedRect(cleared_rect);
    }
  }
}

void CompoundImageBacking::MarkForDestruction() {
  AutoLock auto_lock(this);
  for (const auto& element : elements_) {
    if (element.backing) {
      element.backing->MarkForDestruction();
    }
  }
}

gfx::GpuMemoryBufferHandle CompoundImageBacking::GetGpuMemoryBufferHandle() {
  // A GpuMemoryBufferHandle corresponds to the shared memory or native buffer
  // (like an IOSurface, AHardwareBuffer, or DXGI Handle) that backs the image.
  //
  // Per this backing's design:
  // 1. Any CPU-mappable backing including SharedMemoryImageBacking is always
  // created only at initialization time and never allocated dynamically during
  // runtime. Hence it is guaranteed to be at elements_[0].
  // 2. |elements_| always contains at least one element.
  AutoLock auto_lock(this);
  CHECK(!elements_.empty());
  auto& element = elements_[0];
  CHECK(element.backing);
  return element.backing->GetGpuMemoryBufferHandle();
}

scoped_refptr<gfx::NativePixmap> CompoundImageBacking::GetNativePixmap() {
  // The purpose of this function is to get NativePixmap for overlay testing,
  // so it needs be the same NativePixmap that we would later get from the
  // ProduceOverlay representation. Hence using Overlay stream backing here.
  AutoLock auto_lock(this);
  for (const auto& element : elements_) {
    if (element.access_streams.Has(SharedImageAccessStream::kOverlay) &&
        element.backing) {
      return element.backing->GetNativePixmap();
    }
  }
  return nullptr;
}

#if BUILDFLAG(IS_ANDROID)
std::optional<VulkanYCbCrInfo> CompoundImageBacking::GetVkCbCrInfo(
    SharedContextState* context_state) {
  AutoLock auto_lock(this);
  for (const auto& element : elements_) {
    if (element.backing) {
      auto info = element.backing->GetVkCbCrInfo(context_state);
      if (info) {
        return info;
      }
    }
  }
  return std::nullopt;
}
#endif

std::unique_ptr<DawnImageRepresentation> CompoundImageBacking::ProduceDawn(
    SharedImageManager* manager,
    MemoryTypeTracker* tracker,
    const wgpu::Device& device,
    wgpu::BackendType backend_type,
    std::vector<wgpu::TextureFormat> view_formats,
    scoped_refptr<SharedContextState> context_state) {
  AccessParams access_params;
  access_params.wgpu_device = device;
  access_params.context_state = context_state;
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kDawn,
                                       access_params, transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceDawn(manager, tracker, device, backend_type,
                                       std::move(view_formats), context_state);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedDawnCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<DawnBufferRepresentation>
CompoundImageBacking::ProduceDawnBuffer(
    SharedImageManager* manager,
    MemoryTypeTracker* tracker,
    const wgpu::Device& device,
    wgpu::BackendType backend_type,
    scoped_refptr<SharedContextState> context_state) {
  AccessParams access_params;
  access_params.wgpu_device = device;
  access_params.context_state = context_state;
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kDawnBuffer,
                                       access_params, transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceDawnBuffer(manager, tracker, device,
                                             backend_type, context_state);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedDawnBufferCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<GLTextureImageRepresentation>
CompoundImageBacking::ProduceGLTexture(SharedImageManager* manager,
                                       MemoryTypeTracker* tracker) {
  // For GLTextureImageRepresentation, the SharedImageAccessStream::kGL is
  // specific enough for backing selection. While AccessParams could be extended
  // in the future to include GL context information for stricter correctness
  // checks (e.g., ensuring a backing created on one GL context isn't used on
  // another, unless it's an EglImageBacking), it is not currently needed.
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kGL,
                                       AccessParams(), transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceGLTexture(manager, tracker);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedGLTextureCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<GLTexturePassthroughImageRepresentation>
CompoundImageBacking::ProduceGLTexturePassthrough(SharedImageManager* manager,
                                                  MemoryTypeTracker* tracker) {
  // For GLTexturePassthroughImageRepresentation, the
  // SharedImageAccessStream::kGL is specific enough for backing selection.
  // While AccessParams could be extended in the future to include GL context
  // information for stricter correctness checks, it is not currently needed.
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kGL,
                                       AccessParams(), transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceGLTexturePassthrough(manager, tracker);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<
      WrappedGLTexturePassthroughCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<SkiaGaneshImageRepresentation>
CompoundImageBacking::ProduceSkiaGanesh(
    SharedImageManager* manager,
    MemoryTypeTracker* tracker,
    scoped_refptr<SharedContextState> context_state) {
  AccessParams access_params;
  access_params.context_state = context_state;
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kSkia,
                                       access_params, transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceSkiaGanesh(manager, tracker, context_state);
  if (!real_rep) {
    return nullptr;
  }

  auto* gr_context = context_state ? context_state->gr_context() : nullptr;
  return std::make_unique<WrappedSkiaGaneshCompoundImageRepresentation>(
      gr_context, manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<SkiaGraphiteImageRepresentation>
CompoundImageBacking::ProduceSkiaGraphite(
    SharedImageManager* manager,
    MemoryTypeTracker* tracker,
    scoped_refptr<SharedContextState> context_state) {
  AccessParams access_params;
  access_params.context_state = context_state;
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kSkia,
                                       access_params, transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceSkiaGraphite(manager, tracker, context_state);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedSkiaGraphiteCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<OverlayImageRepresentation>
CompoundImageBacking::ProduceOverlay(SharedImageManager* manager,
                                     MemoryTypeTracker* tracker) {
  // For OverlayImageRepresentation, no specific context information is
  // currently required for backing selection, so AccessParams is empty.
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kOverlay,
                                       AccessParams(), transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceOverlay(manager, tracker);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedOverlayCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<WebNNTensorRepresentation>
CompoundImageBacking::ProduceWebNNTensor(SharedImageManager* manager,
                                         MemoryTypeTracker* tracker) {
  // For WebNNTensorRepresentation, no specific context information is
  // currently required for backing selection, so AccessParams is empty.
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kWebNNTensor,
                                       AccessParams(), transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceWebNNTensor(manager, tracker);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedWebNNTensorCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<MemoryImageRepresentation> CompoundImageBacking::ProduceMemory(
    SharedImageManager* manager,
    MemoryTypeTracker* tracker) {
  // For MemoryImageRepresentation, no specific context information is
  // currently required for backing selection, so AccessParams is empty.
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kMemory,
                                       AccessParams(), transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceMemory(manager, tracker);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedMemoryCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

std::unique_ptr<VideoImageRepresentation> CompoundImageBacking::ProduceVideo(
    SharedImageManager* manager,
    MemoryTypeTracker* tracker,
    VideoDevice device) {
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kGL,
                                       AccessParams(), transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceVideo(manager, tracker, device);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedVideoCompoundImageRepresentation>(
      manager, this, tracker, std::move(real_rep),
      std::move(transient_backing));
}

#if BUILDFLAG(ENABLE_VULKAN)
std::unique_ptr<VulkanImageRepresentation> CompoundImageBacking::ProduceVulkan(
    SharedImageManager* manager,
    MemoryTypeTracker* tracker,
    gpu::VulkanDeviceQueue* vulkan_device_queue,
    gpu::VulkanImplementation& vulkan_impl,
    bool needs_detiling) {
  // For VulkanImageRepresentation, AccessParams is not needed as of now.
  std::unique_ptr<SharedImageBacking> transient_backing;
  auto* backing = GetOrAllocateBacking(SharedImageAccessStream::kVulkan,
                                       AccessParams(), transient_backing);
  if (!backing) {
    return nullptr;
  }

  auto real_rep = backing->ProduceVulkan(manager, tracker, vulkan_device_queue,
                                         vulkan_impl, needs_detiling);
  if (!real_rep) {
    return nullptr;
  }

  return std::make_unique<WrappedVulkanCompoundImageRepresentation>(
      manager, this, tracker, vulkan_device_queue, vulkan_impl,
      std::move(real_rep), std::move(transient_backing));
}
#endif

base::trace_event::MemoryAllocatorDump* CompoundImageBacking::OnMemoryDump(
    const std::string& dump_name,
    base::trace_event::MemoryAllocatorDumpGuid client_guid,
    base::trace_event::ProcessMemoryDump* pmd,
    uint64_t client_tracing_id) {
  // Create dump but don't add scalar size. The size will be inferred from the
  // sizes of the sub-backings.
  AutoLock auto_lock(this);
  base::trace_event::MemoryAllocatorDump* dump =
      pmd->CreateAllocatorDump(dump_name);

  dump->AddString("type", "", GetName());
  dump->AddString("dimensions", "", size().ToString());
  dump->AddString("format", "", format().ToString());
  dump->AddString("usage", "", CreateLabelForSharedImageUsage(usage()));

  // Add ownership edge to `client_guid` which expresses shared ownership with
  // the client process for the top level dump.
  pmd->CreateSharedGlobalAllocatorDump(client_guid);
  pmd->AddOwnershipEdge(dump->guid(), client_guid,
                        static_cast<int>(TracingImportance::kNotOwner));

  // Add dumps nested under `dump_name` for child backings owned by compound
  // image. These get different shared GUIDs to add ownership edges with GPU
  // texture or shared memory.
  for (int i = 0; i < static_cast<int>(elements_.size()); ++i) {
    auto* backing = elements_[i].backing.get();
    if (!backing)
      continue;

    // When CompoundImageBacking wraps a single backing, use the client's global
    // Mailbox GUID instead of sub-backing GUID. This ensures correct effective
    // size attribution to the client process where client claims all of the
    // memory/effective_size(since client usually has higher
    // TracingImportance(2) than service side (0)).
    // This does not work well when CompoundImageBacking has multiple gpu
    // backings. In that case, each child element creates its own sub-backing
    // GUID and links to it rather than linking to global mailbox GUID. However,
    // the client only knows about the single Global Mailbox GUID. As a result,
    // the client claims the Global GUID, but the GPU service claims the memory
    // for the individual Sub-Backing GUIDs. This leads to over-reporting
    // (actual effective_size = Client Size + Sum of Sub-Backings instead of
    // expected effective_size = Sum of Sub-Backings). This is currently a known
    // architectural limitation that requires further design to unify memory
    // attribution (e.g., via dynamic IPC updates to the client about total
    // elements OR by shifting total ownership to the service with higher
    // importance).
    auto element_client_guid =
        elements_.size() == 1 ? client_guid
                              : GetSubBackingGUIDForTracing(mailbox(), i + 1);
    std::string element_dump_name =
        base::StringPrintf("%s/element_%d", dump_name.c_str(), i);
    backing->OnMemoryDump(element_dump_name, element_client_guid, pmd,
                          client_tracing_id);
  }
  return dump;
}

const std::vector<SkPixmap>& CompoundImageBacking::GetSharedMemoryPixmaps() {
  AutoLock auto_lock(this);
  auto* shm_backing = GetShmElement().GetBacking();
  DCHECK(shm_backing);

  // SECURITY: WrapExternalBacking wraps arbitrary backing types and gives them
  // AccessStreamSet::All() (including kMemory). GetShmElement() then returns
  // that wrapped backing here regardless of its actual type. Guard against the
  // resulting type confusion in the static_cast below. Note marking a backing
  // to support all access stream is expected behavior wheres
  // ::GetSharedMemoryPixmaps should only be invoked on
  // SharedImageBackingType::kSharedMemory currently.
  CHECK_EQ(shm_backing->GetType(), SharedImageBackingType::kSharedMemory);

  return static_cast<SharedMemoryImageBacking*>(shm_backing)->pixmaps();
}

CompoundImageBacking::ElementHolder& CompoundImageBacking::GetShmElement() {
  for (auto& element : elements_) {
    // There should be exactly one element where this returns true.
    if (element.access_streams.Has(SharedImageAccessStream::kMemory)) {
      return element;
    }
  }

  NOTREACHED();
}

CompoundImageBacking::ElementHolder* CompoundImageBacking::GetElement(
    const SharedImageBacking* backing) {
  for (auto& element : elements_) {
    if (element.backing.get() == backing) {
      return &element;
    }
  }
  return nullptr;
}

CompoundImageBacking::ElementHolder*
CompoundImageBacking::GetElementWithLatestContent() {
  // Note that for now iterating over all elements should be fine since we would
  // likely not ever had too many backings existing concurrently. We can
  // optimize this code by using better algorithm or more suitable data
  // structure later if needed.
  for (auto& element : elements_) {
    if (element.content_id_ == latest_content_id_ && element.GetBacking()) {
      return &element;
    }
  }
  return nullptr;
}

SharedImageBacking* CompoundImageBacking::GetOrAllocateBacking(
    SharedImageAccessStream stream,
    const AccessParams& params,
    std::unique_ptr<SharedImageBacking>& out_transient_backing) {
  AutoLock auto_lock(this);
  ElementHolder* best_match = nullptr;
  ElementHolder* any_match = nullptr;

  // Note that for now iterating over all elements should be fine since we would
  // likely not ever had too many backings existing concurrently. We can
  // optimize this code by using better algorithm or more suitable data
  // structure later if needed.
  for (auto& element : elements_) {
    if (element.access_streams.Has(stream) && element.GetBacking() &&
        element.GetBacking()->SupportsAccess(stream, params)) {
      if (element.content_id_ == latest_content_id_) {
        best_match = &element;
        break;
      }
      if (!any_match) {
        any_match = &element;
      }
    }
  }

  ElementHolder* target_element = best_match ? best_match : any_match;
  if (target_element) {
    return target_element->GetBacking();
  }

  // If no backing is found, we will try to create a new one. This feature is
  // disabled by default currently until SharedImageCopyManager is fully ready
  // to support all the existing gpu-gpu copy usages.
  if (base::FeatureList::IsEnabled(features::kUseDynamicBackingAllocations) &&
      shared_image_factory_) {
    // When the original format has PrefersExternalSampler() set(which can be
    // only set for multiplanar formats), then per plane texture access is not
    // allowed. It can be only sampled as single RGBA texture on the GPU.
    // When a secondary or transient backings is allocated dynamically, it
    // requires data to be copied from existing backing into this new backing.
    // Since data from existing backing can only be sampled as RGBA, the new
    // backing must also be of RGBA format in order to receive readback data as
    // RGBA. Therefore, map such formats to single-plane RGBA based on channel
    // format for dynamic backing allocation.
    viz::SharedImageFormat backing_format =
        format().PrefersExternalSampler()
            ? GetSinglePlaneRGBAFormatForChannelFormat(
                  format().channel_format())
            : format();
    SharedImageUsageSet usage = GetUsageFromAccessStream(stream);
    std::unique_ptr<SharedImageBacking> new_backing;
    shared_image_factory_->Execute([&](SharedImageFactory* factory) {
      SharedImageBackingFactory* gpu_backing_factory =
          factory->GetFactoryByUsage(usage, backing_format, size(),
                                     /*pixel_data=*/{}, gfx::EMPTY_BUFFER,
                                     stream, &params);
      if (gpu_backing_factory) {
        CreateBackingFromBackingFactory(gpu_backing_factory, debug_label(),
                                        usage, backing_format, new_backing);
      }
    });

    if (new_backing) {
      if (format().PrefersExternalSampler()) {
        CHECK_EQ(new_backing->format(), backing_format);
      }
      UMA_HISTOGRAM_ENUMERATION(
          "GPU.CompoundImageBacking.DynamicAllocation.BackingType",
          new_backing->GetType());
      UMA_HISTOGRAM_ENUMERATION(
          "GPU.CompoundImageBacking.DynamicAllocation.AccessStream", stream);
      UMA_HISTOGRAM_SPARSE(
          "GPU.CompoundImageBacking.DynamicAllocation."
          "InitialSharedImageUsage",
          static_cast<int32_t>(static_cast<uint32_t>(this->usage())));

      // If the CSI container is thread-safe, we treat newly created backings
      // as transient if they are not thread-safe. They will be owned by the
      // representation and destroyed after use. This ensures that a
      // non-thread-safe backing allocated on one thread doesn't persist in
      // the thread-safe container, which could lead to race conditions if
      // accessed from another thread later.
      if (is_thread_safe() && !new_backing->is_thread_safe()) {
        out_transient_backing = std::move(new_backing);
        return out_transient_backing.get();
      }

      // Else we treat the backing as non-transient and add it to the list of
      // alive elements. This is done when either the container is not
      // thread-safe or the new backing itself is thread-safe.
      ElementHolder element;
      element.access_streams.Put(stream);
      element.backing = std::move(new_backing);
      elements_.push_back(std::move(element));
      if (elements_.size() > max_elements_allocated_) {
        max_elements_allocated_ = elements_.size();
      }
      return elements_.back().GetBacking();
    }
  }

  LOG(ERROR) << "Could not find or create a backing for stream " << stream;
  return nullptr;
}

SharedImageBacking* CompoundImageBacking::GetGpuBacking() {
  for (auto& element : elements_) {
    if (!element.access_streams.Has(SharedImageAccessStream::kMemory)) {
      return element.GetBacking();
    }
  }
  LOG(ERROR) << "No GPU backing found.";
  return nullptr;
}

void CompoundImageBacking::CreateBackingFromBackingFactory(
    SharedImageBackingFactory* backing_factory,
    std::string debug_label,
    SharedImageUsageSet usage,
    viz::SharedImageFormat backing_format,
    std::unique_ptr<SharedImageBacking>& backing) {
  // This method assumes the caller has already ensured the factory is alive
  // and synchronized (e.g. by holding the SharedImageFactoryRef lock).
  CHECK(backing_factory);

  SharedImageInfo si_info(backing_format, size(), color_space(),
                          surface_origin(), alpha_type(), usage,
                          std::move(debug_label));
  backing =
      backing_factory->CreateSharedImage(mailbox(), si_info, kNullSurfaceHandle,
                                         /*is_thread_safe=*/false);
  if (!backing) {
    DLOG(ERROR) << "Failed to allocate GPU backing";
    return;
  }

  // Since the owned GPU backing is never registered with SharedImageManager
  // it's not recorded in UMA histogram there.
  RecordBackingTypeUMA(backing->GetType());

  backing->SetNotRefCounted();

  // When a CompoundImageBacking is created with a shared memory backing, it's
  // considered logically cleared from the start, as the shared memory provides
  // the initial content. For consistency, any newly created GPU backings also
  // need to reflect this cleared state, even if their physical memory isn't
  // yet initialized. The system ensures a copy from the shared memory backing
  // to the GPU backing will occur on first access, synchronizing the physical
  // content.
  if (has_shm_backing_) {
    backing->SetCleared();
  }

  // Update peak GPU memory tracking with the new estimated size.
  size_t estimated_size = 0;
  for (auto& element : elements_) {
    if (element.backing) {
      estimated_size += element.backing->GetEstimatedSize();
    }
  }

  UpdateEstimatedSize(estimated_size);
}

void CompoundImageBacking::LazyCreateBacking(
    SharedImageBackingType factory_type,
    base::WeakPtr<SharedImageBackingFactory> test_factory,
    SharedImageUsageSet usage,
    std::string debug_label,
    std::unique_ptr<SharedImageBacking>& backing) {
  // This method provides a thread-safe way to lazily create a backing.
  // It uses the factory type to re-identify the correct factory under a lock,
  // avoiding the use of thread-affine WeakPtrs in production.
  if (shared_image_factory_) {
    shared_image_factory_->Execute([&](SharedImageFactory* factory) {
      // Find the factory by type while holding the lock to ensure its lifetime.
      auto* bf = factory->GetFactoryByType(factory_type);
      if (bf) {
        CreateBackingFromBackingFactory(bf, std::move(debug_label), usage,
                                        format(), backing);
      }
    });
  } else if (test_factory) {
    // Fallback for tests where SharedImageFactoryRef is not present.
    // These tests are currently single-threaded.
    CHECK_IS_TEST();
    CreateBackingFromBackingFactory(test_factory.get(), std::move(debug_label),
                                    usage, format(), backing);
  }
}

bool CompoundImageBacking::HasLatestContent(ElementHolder& element) {
  return element.content_id_ == latest_content_id_;
}

void CompoundImageBacking::OnAddSecondaryReference() {
  // When client adds a reference from another processes it expects this
  // SharedImage can outlive original factory ref and so potentially
  // SharedimageFactory. We should create all backings now as we might not have
  // access to corresponding SharedImageBackingFactories later.
  AutoLock auto_lock(this);
  for (auto& element : elements_) {
    element.CreateBackingIfNecessary();
  }
}

CompoundImageBacking::ElementHolder::ElementHolder() = default;
CompoundImageBacking::ElementHolder::~ElementHolder() = default;
CompoundImageBacking::ElementHolder::ElementHolder(ElementHolder&& other) =
    default;
CompoundImageBacking::ElementHolder&
CompoundImageBacking::ElementHolder::operator=(ElementHolder&& other) = default;

void CompoundImageBacking::ElementHolder::CreateBackingIfNecessary() {
  if (create_callback) {
    std::move(create_callback).Run(backing);
  }
}

SharedImageBacking* CompoundImageBacking::ElementHolder::GetBacking() {
  CreateBackingIfNecessary();
  return backing.get();
}

GPU_GLES2_EXPORT std::ostream& operator<<(
    std::ostream& os,
    gpu::SharedImageAccessStream access_stream) {
  switch (access_stream) {
    case gpu::SharedImageAccessStream::kSkia:
      os << "kSkia";
      break;
    case gpu::SharedImageAccessStream::kOverlay:
      os << "kOverlay";
      break;
    case gpu::SharedImageAccessStream::kGL:
      os << "kGL";
      break;
    case gpu::SharedImageAccessStream::kDawn:
      os << "kDawn";
      break;
    case gpu::SharedImageAccessStream::kDawnBuffer:
      os << "kDawnBuffer";
      break;
    case gpu::SharedImageAccessStream::kMemory:
      os << "kMemory";
      break;
    case gpu::SharedImageAccessStream::kVaapi:
      os << "kVaapi";
      break;
    case gpu::SharedImageAccessStream::kWebNNTensor:
      os << "kWebNNTensor";
      break;
    case gpu::SharedImageAccessStream::kVulkan:
      os << "kVulkan";
      break;
  }
  return os;
}

}  // namespace gpu
