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

#ifndef COMPONENTS_VIZ_COMMON_RESOURCES_TRANSFERABLE_RESOURCE_H_
#define COMPONENTS_VIZ_COMMON_RESOURCES_TRANSFERABLE_RESOURCE_H_

#include <stdint.h>

#include <optional>
#include <utility>
#include <vector>

#include "build/build_config.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/common/viz_common_export.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/hdr_metadata.h"

#if BUILDFLAG(IS_ANDROID)
#include "gpu/vulkan/vulkan_ycbcr_info.h"
#endif

namespace base::trace_event {
class TracedValue;
}  // namespace base::trace_event

namespace gpu {
class ClientSharedImage;
}

namespace viz {

struct ReturnedResource;
struct ReturnedResourceViz;

struct VIZ_COMMON_EXPORT TransferableResource {
  struct VIZ_COMMON_EXPORT MetadataOverride {
    std::optional<bool> is_overlay_candidate;
    std::optional<gfx::ColorSpace> color_space;
    std::optional<SkAlphaType> alpha_type;
  };

  enum class SynchronizationType : uint8_t {
    // Commands issued (SyncToken) - a resource can be reused as soon as display
    // compositor issues the latest command on it and SyncToken will be signaled
    // when this happens.
    kSyncToken = 0,
    // Commands completed (aka read lock fence) - If a gpu resource is backed by
    // a GpuMemoryBuffer, then it will be accessed out-of-band, and a gpu fence
    // needs to be waited on before the resource is returned and reused. In
    // other words, the resource will be returned only when gpu commands are
    // completed.
    kGpuCommandsCompleted,
    // Commands submitted (release fence) - a resource will be returned after
    // gpu service submitted commands to the gpu and provide the fence.
    kReleaseFence,
  };

  // Differentiates between the various sources that create a resource. They
  // have different lifetime expectations, and we want to be able to determine
  // which remain after we Evict a Surface.
  //
  // These values are persistent to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class ResourceSource : uint8_t {
    kUnknown = 0,
    kAR = 1,
    kCanvas = 2,
    kDrawingBuffer = 3,
    kExoBuffer = 4,
    kHeadsUpDisplay = 5,
    kImageLayerBridge = 6,
    kPPBGraphics3D = 7,
    kPepperGraphics2D = 8,
    kViewTransition = 9,
    kStaleContent = 10,
    kTest = 11,
    kTileRasterTask = 12,
    kUI = 13,
    kVideo = 14,
    kWebGPUSwapBuffer = 15,
  };

  // Creates transferable resource from the ClientSharedImage. `override` allows
  // to temporary override SharedImage metadata to facilitate current
  // discrepancies until they are fixed. Do not pass it in the new code.
  static TransferableResource Make(
      const scoped_refptr<gpu::ClientSharedImage>& shared_image,
      ResourceSource source,
      const gpu::SyncToken& sync_token,
      const MetadataOverride& override = {});

  static std::vector<ReturnedResource> ReturnResources(
      const std::vector<TransferableResource>& input);

  static std::vector<ReturnedResourceViz> ReturnResourcesViz(
      const std::vector<TransferableResource>& input);

  TransferableResource();
  ~TransferableResource();

  TransferableResource(const TransferableResource& other);
  TransferableResource& operator=(const TransferableResource& other);

  ReturnedResource ToReturnedResource() const;
  ReturnedResourceViz ToReturnedResourceViz() const;

  bool is_empty() const { return shared_image_ == nullptr; }

  void set_sync_token(const gpu::SyncToken& sync_token) {
    sync_token_ = sync_token;
  }

  // For usage only in Mojo serialization/deserialization.
  void set_shared_image(scoped_refptr<gpu::ClientSharedImage> shared_image) {
    shared_image_ = std::move(shared_image);
  }
  void set_metadata_override(const MetadataOverride& metadata_override) {
    metadata_override_ = metadata_override;
  }

  // Returns the Mailbox that this instance is storing. Valid to call only if
  // this instance has been created via MakeSoftwareSharedImage() or MakeGpu().
  const gpu::Mailbox& mailbox() const { return shared_image_->mailbox(); }
  const gpu::SyncToken& sync_token() const { return sync_token_; }
  gpu::SyncToken& mutable_sync_token() { return sync_token_; }

  // When the shared memory buffer is backed by a GPU texture, texture_target()
  // returns that texture's type.
  // See here for OpenGL texture types:
  // https://www.opengl.org/wiki/Texture#Texture_Objects
  uint32_t texture_target() const { return shared_image_->GetTextureTarget(); }

  // For usage only in Mojo serialization/deserialization.
  scoped_refptr<gpu::ClientSharedImage> shared_image() const {
    return shared_image_;
  }
  const MetadataOverride& metadata_override() const {
    return metadata_override_;
  }

  // Indicates if the resource is gpu or software backed.
  bool GetIsSoftware() const {
    return shared_image_ ? shared_image_->is_software() : false;
  }

  // The format of the pixels in the gpu mailbox/software bitmap. This should
  // almost always be RGBA_8888 for resources generated by compositor clients,
  // and must be RGBA_8888 always for software resources.
  SharedImageFormat GetFormat() const {
    return shared_image_ ? shared_image_->format()
                         : SinglePlaneFormat::kRGBA_8888;
  }

  // The number of pixels in the gpu mailbox/software bitmap.
  const gfx::Size GetSize() const {
    return shared_image_ ? shared_image_->size() : gfx::Size();
  }

  // A gpu resource may be possible to use directly in an overlay if this is
  // true.
  bool GetIsOverlayCandidate() const {
    return metadata_override_.is_overlay_candidate.value_or(
        shared_image_
            ? shared_image_->usage().Has(gpu::SHARED_IMAGE_USAGE_SCANOUT)
            : false);
  }

  // The color space that is used for pixel path operations (e.g, TexImage,
  // CopyTexImage, DrawPixels) and when displaying as an overlay.
  //
  // TODO(b/220336463): On ChromeOS, the color space for hardware decoded video
  // frames is currently specified at the time of creating the SharedImage.
  // Therefore, for the purposes of that use case and compositing, the
  // color space here is ignored. We should consider using it.
  //
  // TODO(b/233667677): For ChromeOS NV12 hardware overlays, this color space is
  // only used for deciding if an NV12 resource should be promoted to a hardware
  // overlay. Instead, we should plumb this information to DRM/KMS so that if
  // the resource does get promoted to overlay, the display controller knows how
  // to perform the YUV-to-RGB conversion.
  gfx::ColorSpace GetColorSpace() const {
    return metadata_override_.color_space.value_or(
        shared_image_ ? shared_image_->color_space() : gfx::ColorSpace());
  }

  // Origin of the underlying resource.
  GrSurfaceOrigin GetOrigin() const {
    return shared_image_ ? shared_image_->surface_origin()
                         : kTopLeft_GrSurfaceOrigin;
  }

  SkAlphaType GetAlphaType() const {
    return metadata_override_.alpha_type.value_or(
        shared_image_ ? shared_image_->alpha_type() : kPremul_SkAlphaType);
  }

  bool operator==(const TransferableResource& o) const {
    return id == o.id && GetIsSoftware() == o.GetIsSoftware() &&
           GetSize() == o.GetSize() && GetFormat() == o.GetFormat() &&
           mailbox() == o.mailbox() && sync_token_ == o.sync_token_ &&
           texture_target() == o.texture_target() &&
           GetColorSpace() == o.GetColorSpace() &&
           hdr_metadata == o.hdr_metadata &&
           GetIsOverlayCandidate() == o.GetIsOverlayCandidate() &&
#if BUILDFLAG(IS_ANDROID)
           is_backed_by_surface_view == o.is_backed_by_surface_view &&
           wants_promotion_hint == o.wants_promotion_hint &&
#elif BUILDFLAG(IS_WIN)
           wants_promotion_hint == o.wants_promotion_hint &&
#endif
           synchronization_type == o.synchronization_type &&
           resource_source == o.resource_source;
  }

  void AsValueInto(base::trace_event::TracedValue* value) const;

  // TODO(danakj): Some of these fields are only GL, some are only Software,
  // some are both but used for different purposes (like the mailbox name).
  // It would be nice to group things together and make it more clear when
  // they will be used or not, and provide easier access to fields such as the
  // mailbox that also show the intent for software for GL.
  // An |id| field that can be unique to this resource. For resources
  // generated by compositor clients, this |id| may be used for their
  // own book-keeping but need not be set at all.
  ResourceId id = kInvalidResourceId;

  gfx::HDRMetadata hdr_metadata;

  // This defines when the display compositor returns resources. Clients may use
  // different synchronization types based on their needs.
  SynchronizationType synchronization_type = SynchronizationType::kSyncToken;

#if BUILDFLAG(IS_ANDROID)
  // YCbCr info for resources backed by YCbCr Vulkan images.
  std::optional<gpu::VulkanYCbCrInfo> ycbcr_info;

  // Indicates whether this resource may be overlaid on Android via legacy
  // overlay flow, since it's backed by a SurfaceView. It's good to find this
  // out in advance, since one has no fallback path for displaying a
  // SurfaceView except via promoting it to an overlay.
  bool is_backed_by_surface_view = false;
#endif

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
  // Indicates that this resource would like a promotion hint.
  bool wants_promotion_hint = false;
#endif

  // If true, we need to run a detiling image processor on the quad before we
  // can scan it out.
  bool needs_detiling = false;

  // The source that originally allocated this resource. For determining which
  // sources are maintaining lifetime after surface eviction.
  ResourceSource resource_source = ResourceSource::kUnknown;

 private:
  scoped_refptr<gpu::ClientSharedImage> shared_image_;

  // The SyncToken associated with the above buffer. Allows the receiver to wait
  // until the producer has finished using the texture before it begins using
  // the texture.
  gpu::SyncToken sync_token_;

  MetadataOverride metadata_override_;
};

}  // namespace viz

#endif  // COMPONENTS_VIZ_COMMON_RESOURCES_TRANSFERABLE_RESOURCE_H_
