chromium/gpu/command_buffer/service/shared_image/iosurface_image_backing.h

// 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.

#ifndef GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_IOSURFACE_IMAGE_BACKING_H_
#define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_IOSURFACE_IMAGE_BACKING_H_

#include "base/apple/scoped_nsobject.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "gpu/command_buffer/service/shared_image/dawn_shared_texture_holder.h"
#include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/gpu_gles2_export.h"
#include "ui/gl/buildflags.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_fence.h"
#include "ui/gl/gl_surface.h"

namespace gl {
class ScopedEGLSurfaceIOSurface;
}  // namespace gl

namespace gpu {

// The state associated with an EGL texture representation of an IOSurface.
// This is used by the representations GLTextureIRepresentation and
// SkiaGaneshRepresentation (when the underlying GrContext uses GL).
struct IOSurfaceBackingEGLState : base::RefCounted<IOSurfaceBackingEGLState> {
  // The interface through which IOSurfaceBackingEGLState calls into
  // IOSurfaceImageBacking.
  class Client {
   public:
    virtual bool IOSurfaceBackingEGLStateBeginAccess(
        IOSurfaceBackingEGLState* egl_state,
        bool readonly) = 0;
    virtual void IOSurfaceBackingEGLStateEndAccess(
        IOSurfaceBackingEGLState* egl_state,
        bool readonly) = 0;
    virtual void IOSurfaceBackingEGLStateBeingCreated(
        IOSurfaceBackingEGLState* egl_state) = 0;
    virtual void IOSurfaceBackingEGLStateBeingDestroyed(
        IOSurfaceBackingEGLState* egl_state,
        bool have_context) = 0;
  };

  IOSurfaceBackingEGLState(
      Client* client,
      EGLDisplay egl_display,
      gl::GLContext* gl_context,
      gl::GLSurface* gl_surface,
      GLuint gl_target,
      std::vector<scoped_refptr<gles2::TexturePassthrough>> gl_textures);
  GLenum GetGLTarget() const { return gl_target_; }
  GLuint GetGLServiceId(int plane_index) const;
  const scoped_refptr<gles2::TexturePassthrough>& GetGLTexture(
      int plane_index) const {
    return gl_textures_[plane_index];
  }
  bool BeginAccess(bool readonly);
  void EndAccess(bool readonly);
  void WillRelease(bool have_context);

  // Returns true if we need to (re)bind IOSurface to GLTexture before next
  // access.
  bool is_bind_pending() const { return is_bind_pending_; }
  void set_bind_pending() { is_bind_pending_ = true; }
  void clear_bind_pending() { is_bind_pending_ = false; }

 private:
  friend class base::RefCounted<IOSurfaceBackingEGLState>;

  // This class was cleaved off of state from IOSurfaceImageBacking, and so
  // IOSurfaceImageBacking still accesses its internals.
  friend class IOSurfaceImageBacking;

  // The interface through which to call into IOSurfaceImageBacking.
  const raw_ptr<Client> client_;

  // The display for this GL representation.
  const EGLDisplay egl_display_;

  scoped_refptr<gl::GLContext> context_;
  scoped_refptr<gl::GLSurface> surface_;

  // The GL (not EGL) target to which this texture is to be bound.
  const GLuint gl_target_;

  // The EGL and GLES internals for this IOSurface.
  std::vector<std::unique_ptr<gl::ScopedEGLSurfaceIOSurface>> egl_surfaces_;
  std::vector<scoped_refptr<gles2::TexturePassthrough>> gl_textures_;

  // Set to true if the context is known to be lost.
  bool context_lost_ = false;

  bool is_bind_pending_ = false;

  ~IOSurfaceBackingEGLState();
};

class GPU_GLES2_EXPORT IOSurfaceImageBacking
    : public SharedImageBacking,
      public IOSurfaceBackingEGLState::Client {
 public:
  IOSurfaceImageBacking(
      gfx::ScopedIOSurface io_surface,
      gfx::GenericSharedMemoryId io_surface_id,
      const Mailbox& mailbox,
      viz::SharedImageFormat format,
      const gfx::Size& size,
      const gfx::ColorSpace& color_space,
      GrSurfaceOrigin surface_origin,
      SkAlphaType alpha_type,
      gpu::SharedImageUsageSet usage,
      std::string debug_label,
      GLenum gl_target,
      bool framebuffer_attachment_angle,
      bool is_cleared,
      GrContextType gr_context_type,
      std::optional<gfx::BufferUsage> buffer_usage = std::nullopt);
  IOSurfaceImageBacking(const IOSurfaceImageBacking& other) = delete;
  IOSurfaceImageBacking& operator=(const IOSurfaceImageBacking& other) = delete;
  ~IOSurfaceImageBacking() override;

  bool UploadFromMemory(const std::vector<SkPixmap>& pixmaps) override;
  bool ReadbackToMemory(const std::vector<SkPixmap>& pixmaps) override;

  bool InitializePixels(base::span<const uint8_t> pixel_data);

  void AddWGPUDeviceWithPendingCommands(wgpu::Device device);
  void WaitForDawnCommandsToBeScheduled(const wgpu::Device& device_to_exclude);

  void AddEGLDisplayWithPendingCommands(gl::GLDisplayEGL* display);
  void WaitForANGLECommandsToBeScheduled();
  void ClearEGLDisplaysWithPendingCommands(gl::GLDisplayEGL* display_to_keep);

  std::unique_ptr<gfx::GpuFence> GetLastWriteGpuFence();
  void SetReleaseFence(gfx::GpuFenceHandle release_fence);

 private:
  class GLTextureIRepresentation;
  class DawnRepresentation;
  class SkiaGaneshRepresentation;
  class SkiaGraphiteRepresentation;
  class OverlayRepresentation;

  // SharedImageBacking:
  base::trace_event::MemoryAllocatorDump* OnMemoryDump(
      const std::string& dump_name,
      base::trace_event::MemoryAllocatorDumpGuid client_guid,
      base::trace_event::ProcessMemoryDump* pmd,
      uint64_t client_tracing_id) override;
  SharedImageBackingType GetType() const override;
  gfx::Rect ClearedRect() const final;
  void SetClearedRect(const gfx::Rect& cleared_rect) final;
  std::unique_ptr<GLTextureImageRepresentation> ProduceGLTexture(
      SharedImageManager* manager,
      MemoryTypeTracker* tracker) final;
  std::unique_ptr<GLTexturePassthroughImageRepresentation>
  ProduceGLTexturePassthrough(SharedImageManager* manager,
                              MemoryTypeTracker* tracker) final;
  std::unique_ptr<OverlayImageRepresentation> ProduceOverlay(
      SharedImageManager* manager,
      MemoryTypeTracker* tracker) final;
  std::unique_ptr<DawnImageRepresentation> ProduceDawn(
      SharedImageManager* manager,
      MemoryTypeTracker* tracker,
      const wgpu::Device& device,
      wgpu::BackendType backend_type,
      std::vector<wgpu::TextureFormat> view_formats,
      scoped_refptr<SharedContextState> context_state) final;
  std::unique_ptr<SkiaGaneshImageRepresentation> ProduceSkiaGanesh(
      SharedImageManager* manager,
      MemoryTypeTracker* tracker,
      scoped_refptr<SharedContextState> context_state) override;
  std::unique_ptr<SkiaGraphiteImageRepresentation> ProduceSkiaGraphite(
      SharedImageManager* manager,
      MemoryTypeTracker* tracker,
      scoped_refptr<SharedContextState> context_state) override;
  void SetPurgeable(bool purgeable) override;
  bool IsPurgeable() const override;
  void Update(std::unique_ptr<gfx::GpuFence> in_fence) override;
  gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() override;

  // IOSurfaceBackingEGLState::Client:
  bool IOSurfaceBackingEGLStateBeginAccess(IOSurfaceBackingEGLState* egl_state,
                                           bool readonly) override;
  void IOSurfaceBackingEGLStateEndAccess(IOSurfaceBackingEGLState* egl_state,
                                         bool readonly) override;
  void IOSurfaceBackingEGLStateBeingCreated(
      IOSurfaceBackingEGLState* egl_state) override;
  void IOSurfaceBackingEGLStateBeingDestroyed(
      IOSurfaceBackingEGLState* egl_state,
      bool have_context) override;

  // Updates the read and write accesses tracker variables on BeginAccess.
  bool BeginAccess(bool readonly);
  // Updates the read and write accesses tracker variables on EndAccess.
  void EndAccess(bool readonly);

  void AddSharedEventForEndAccess(id<MTLSharedEvent> shared_event,
                                  uint64_t signal_value,
                                  bool readonly);
  template <typename Fn>
  void ProcessSharedEventsForBeginAccess(bool readonly, const Fn& fn);

  const gfx::ScopedIOSurface io_surface_;
  const gfx::Size io_surface_size_;
  const uint32_t io_surface_format_;
  const gfx::GenericSharedMemoryId io_surface_id_;

  // DawnSharedTextureHolder that keeps an internal cache of per-device
  // SharedTextureData that vends WebGPU textures for the underlying IOSurface.
  std::unique_ptr<DawnSharedTextureHolder> dawn_texture_holder_;

  DawnSharedTextureHolder* GetDawnTextureHolder();

  // Tracks the number of currently-ongoing accesses to a given WGPU texture.
  base::flat_map<WGPUTexture, int> wgpu_texture_ongoing_accesses_;

  // Tracks the devices to invoke waitUntilScheduled.
  // TODO(dawn:2453): The below comparator should be implemented in
  // wgpu::Device itself.
  struct WGPUDeviceCompare {
    bool operator()(const wgpu::Device& lhs, const wgpu::Device& rhs) const {
      return lhs.Get() < rhs.Get();
    }
  };
  base::flat_set<wgpu::Device, WGPUDeviceCompare> wgpu_devices_pending_flush_;

  // Returns the number of ongoing accesses that were already present on this
  // texture prior to beginning this access.
  int TrackBeginAccessToWGPUTexture(wgpu::Texture texture);

  // Returns the number of ongoing accesses that will still be present on this
  // texture after ending this access.
  int TrackEndAccessToWGPUTexture(wgpu::Texture texture);

  const GLenum gl_target_;
  const bool framebuffer_attachment_angle_;

  // Used to determine whether to release the texture in EndAccess() in use
  // cases that need to ensure IOSurface synchronization.
  uint num_ongoing_read_accesses_ = 0;
  // Used with the above variable to catch cases where clients are performing
  // disallowed concurrent read/write accesses.
  bool ongoing_write_access_ = false;

  scoped_refptr<IOSurfaceBackingEGLState> RetainGLTexture();
  void ReleaseGLTexture(IOSurfaceBackingEGLState* egl_state, bool have_context);

  // This is the cleared rect used by ClearedRect and SetClearedRect when
  // |texture_| is nullptr.
  gfx::Rect cleared_rect_;

  // Whether or not the surface is currently purgeable.
  bool purgeable_ = false;

  // This map tracks all IOSurfaceBackingEGLState instances that exist.
  base::flat_map<EGLDisplay, IOSurfaceBackingEGLState*> egl_state_map_;

  // GrContextType for SharedContextState used to distinguish between Ganesh
  // and Graphite.
  GrContextType gr_context_type_;

  // If Skia is using GL, this object creates a GL texture at construction time
  // for the Skia GL context and reuses it (for that context) for its lifetime.
  scoped_refptr<IOSurfaceBackingEGLState> egl_state_for_skia_gl_context_;

  // Tracks the displays to invoke eglWaitUntilWorkScheduledANGLE().
  base::flat_set<gl::GLDisplayEGL*> egl_displays_pending_flush_;

  using ScopedSharedEvent = base::apple::scoped_nsprotocol<id<MTLSharedEvent>>;
  struct SharedEventCompare {
    bool operator()(const ScopedSharedEvent& lhs,
                    const ScopedSharedEvent& rhs) const {
      return lhs.get() < rhs.get();
    }
  };
  using SharedEventMap =
      base::flat_map<ScopedSharedEvent, uint64_t, SharedEventCompare>;
  // Shared events and signals for exclusive accesses.
  SharedEventMap exclusive_shared_events_;
  // Shared events and signals for non-exclusive accesses.
  SharedEventMap non_exclusive_shared_events_;

  base::WeakPtrFactory<IOSurfaceImageBacking> weak_factory_;
};

}  // namespace gpu

#endif  // GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_IOSURFACE_IMAGE_BACKING_H_