// Copyright 2018 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_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_H_

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/containers/circular_deque.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/observer_list.h"
#include "base/sequence_checker.h"
#include "base/timer/timer.h"
#include "base/types/pass_key.h"
#include "build/build_config.h"
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/service/display/display_compositor_memory_and_task_controller.h"
#include "components/viz/service/display/render_pass_alpha_type.h"
#include "components/viz/service/display/skia_output_surface.h"
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/command_buffer/service/task_graph.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/vulkan/vulkan_ycbcr_info.h"
#include "media/gpu/buildflags.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "third_party/skia/include/core/SkOverdrawCanvas.h"
#include "third_party/skia/include/gpu/graphite/GraphiteTypes.h"
#include "third_party/skia/include/private/chromium/GrDeferredDisplayListRecorder.h"
#include "third_party/skia/include/private/chromium/GrSurfaceCharacterization.h"
#include "ui/gfx/presentation_feedback.h"

class SkNoDrawCanvas;

namespace gfx {
namespace mojom {
class DelegatedInkPointRenderer;
}  // namespace mojom
}  // namespace gfx

namespace gpu {
class SharedImageRepresentationFactory;
struct SwapBuffersCompleteParams;
}  // namespace gpu

namespace gpu::raster {
class GraphiteCacheController;
}  // namespace gpu::raster

namespace skgpu::graphite {
class Recorder;
class Recording;
}  // namespace skgpu::graphite

namespace viz {

class ImageContextImpl;
class SkiaOutputSurfaceDependency;
class SkiaOutputSurfaceImplOnGpu;
class SkiaOutputSurfaceSharedImageInterface;

// The SkiaOutputSurface implementation. It is the output surface for
// SkiaRenderer. It lives on the compositor thread, but it will post tasks
// to the GPU thread for initializing. Currently, SkiaOutputSurfaceImpl
// create a SkiaOutputSurfaceImplOnGpu on the GPU thread. It will be used
// for creating a SkSurface from the default framebuffer and providing the
// GrSurfaceCharacterization for the SkSurface. And then SkiaOutputSurfaceImpl
// will create GrDeferredDisplayListRecorder and SkCanvas for SkiaRenderer to
// render into. In SwapBuffers, it detaches a GrDeferredDisplayList from the
// recorder and plays it back on the framebuffer SkSurface on the GPU thread
// through SkiaOutputSurfaceImpleOnGpu.
class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
 public:
  static std::unique_ptr<SkiaOutputSurface> Create(
      DisplayCompositorMemoryAndTaskController* display_controller,
      const RendererSettings& renderer_settings,
      const DebugRendererSettings* debug_settings);

  SkiaOutputSurfaceImpl(
      base::PassKey<SkiaOutputSurfaceImpl> pass_key,
      DisplayCompositorMemoryAndTaskController* display_controller,
      const RendererSettings& renderer_settings,
      const DebugRendererSettings* debug_settings);
  ~SkiaOutputSurfaceImpl() override;

  SkiaOutputSurfaceImpl(const SkiaOutputSurfaceImpl&) = delete;
  SkiaOutputSurfaceImpl& operator=(const SkiaOutputSurfaceImpl&) = delete;

  // OutputSurface implementation:
  gpu::SurfaceHandle GetSurfaceHandle() const override;
  void BindToClient(OutputSurfaceClient* client) override;
  void EnsureBackbuffer() override;
  void DiscardBackbuffer() override;
  void Reshape(const ReshapeParams& params) override;
  void SetUpdateVSyncParametersCallback(
      UpdateVSyncParametersCallback callback) override;
  void SetVSyncDisplayID(int64_t display_id, bool force_update) override;
  void SetDisplayTransformHint(gfx::OverlayTransform transform) override;
  gfx::OverlayTransform GetDisplayTransform() override;
  void SwapBuffers(OutputSurfaceFrame frame) override;
  void SetNeedsSwapSizeNotifications(
      bool needs_swap_size_notifications) override;
#if BUILDFLAG(IS_ANDROID)
  base::ScopedClosureRunner GetCacheBackBufferCb() override;
#endif
  gfx::Rect GetCurrentFramebufferDamage() const override;
#if BUILDFLAG(IS_ANDROID)
  void SetFrameRate(gfx::SurfaceControlFrameRate frame_rate) override;
#endif
  void SetNeedsMeasureNextDrawLatency() override;

  // SkiaOutputSurface implementation:
  SkCanvas* BeginPaintCurrentFrame() override;
  void SwapBuffersSkipped(const gfx::Rect root_pass_damage_rect) override;

  SkCanvas* BeginPaintRenderPass(const AggregatedRenderPassId& id,
                                 const gfx::Size& surface_size,
                                 SharedImageFormat format,
                                 RenderPassAlphaType alpha_type,
                                 skgpu::Mipmapped mipmap,
                                 bool scanout_dcomp_surface,
                                 const gfx::ColorSpace& color_space,
                                 bool is_overlay,
                                 const gpu::Mailbox& mailbox) override;
  SkCanvas* RecordOverdrawForCurrentPaint() override;
  void EndPaint(
      base::OnceClosure on_finished,
      base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb,
      const gfx::Rect& update_rect,
      bool is_overlay) override;
  void MakePromiseSkImage(ImageContext* image_context,
                          bool force_rgbx) override;
  sk_sp<SkImage> MakePromiseSkImageFromRenderPass(
      const AggregatedRenderPassId& id,
      const gfx::Size& size,
      SharedImageFormat format,
      bool mipmap,
      const gfx::ColorSpace& color_space,
      const gpu::Mailbox& mailbox) override;

  void RemoveRenderPassResource(
      std::vector<AggregatedRenderPassId> ids) override;
  void ScheduleOverlays(OverlayList overlays,
                        std::vector<gpu::SyncToken> sync_tokens) override;

  void CopyOutput(const copy_output::RenderPassGeometry& geometry,
                  const gfx::ColorSpace& color_space,
                  std::unique_ptr<CopyOutputRequest> request,
                  const gpu::Mailbox& mailbox) override;
  void AddContextLostObserver(ContextLostObserver* observer) override;
  void RemoveContextLostObserver(ContextLostObserver* observer) override;
  void PreserveChildSurfaceControls() override;
  gpu::SyncToken Flush() override;
  gpu::Mailbox CreateSharedImage(SharedImageFormat format,
                                 const gfx::Size& size,
                                 const gfx::ColorSpace& color_space,
                                 RenderPassAlphaType alpha_type,
                                 gpu::SharedImageUsageSet usage,
                                 std::string_view debug_label,
                                 gpu::SurfaceHandle surface_handle) override;
  gpu::Mailbox CreateSolidColorSharedImage(
      const SkColor4f& color,
      const gfx::ColorSpace& color_space) override;
  void DestroySharedImage(const gpu::Mailbox& mailbox) override;
  void SetSharedImagePurgeable(const gpu::Mailbox& mailbox,
                               bool purgeable) override;
  bool SupportsBGRA() const override;

  // ExternalUseClient implementation:
  gpu::SyncToken ReleaseImageContexts(
      std::vector<std::unique_ptr<ImageContext>> image_contexts) override;
  std::unique_ptr<ExternalUseClient::ImageContext> CreateImageContext(
      const TransferableResource& resource,
      bool maybe_concurrent_reads,
      bool raw_draw_if_possible,
      uint32_t client_id) override;

  void InitDelegatedInkPointRendererReceiver(
      mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer>
          pending_receiver) override;

  // Set the fields of |capabilities_| and propagates to |impl_on_gpu_|. Should
  // be called after BindToClient().
  void SetCapabilitiesForTesting(gfx::SurfaceOrigin output_surface_origin);

  // Used in unit tests.
  void ScheduleGpuTaskForTesting(
      base::OnceClosure callback,
      std::vector<gpu::SyncToken> sync_tokens) override;
  void CheckAsyncWorkCompletionForTesting() override;

#if BUILDFLAG(ENABLE_VULKAN) && BUILDFLAG(IS_CHROMEOS) && \
    BUILDFLAG(USE_V4L2_CODEC)
  void DetileOverlay(gpu::Mailbox input,
                     const gfx::Size& input_visible_size,
                     gpu::SyncToken input_sync_token,
                     gpu::Mailbox output,
                     const gfx::RectF& display_rect,
                     const gfx::RectF& crop_rect,
                     gfx::OverlayTransform transform,
                     bool is_10bit) override;

  void CleanupImageProcessor() override;
#endif

  void ReadbackForTesting(
      CopyOutputRequest::CopyOutputRequestCallback result_callback) override;

 private:
  friend class SkiaOutputSurfaceSharedImageInterface;

  struct InitializeOnGpuThreadResult;
  bool Initialize();
  // Sets `out_result` to null on failure.
  void InitializeOnGpuThread(
      std::optional<InitializeOnGpuThreadResult>* out_result);

  GrSurfaceCharacterization CreateGrSurfaceCharacterizationRenderPass(
      const gfx::Size& surface_size,
      SkColorType color_type,
      SkAlphaType alpha_type,
      skgpu::Mipmapped mipmap,
      sk_sp<SkColorSpace> color_space,
      bool is_overlay,
      bool scanout_dcomp_surface) const;
  GrSurfaceCharacterization CreateGrSurfaceCharacterizationCurrentFrame(
      const gfx::Size& surface_size,
      SkColorType color_type,
      SkAlphaType alpha_type,
      skgpu::Mipmapped mipmap,
      sk_sp<SkColorSpace> color_space) const;
  void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params,
                              const gfx::Size& pixel_size,
                              gfx::GpuFenceHandle release_fence);
  void ReleaseOverlays(const std::vector<gpu::Mailbox> released_overlays);
  void BufferPresented(const gfx::PresentationFeedback& feedback);
  void AddChildWindowToBrowser(gpu::SurfaceHandle child_window);

  using GpuTask = base::OnceClosure;
  void EnqueueGpuTask(GpuTask task,
                      std::vector<gpu::SyncToken> sync_tokens,
                      bool make_current,
                      bool need_framebuffer);
  void ScheduleOrRetainGpuTask(base::OnceClosure callback,
                               std::vector<gpu::SyncToken> tokens);

  enum class SyncMode {
    kNoWait = 0,
    kWaitForTasksStarted = 1,
    kWaitForTasksFinished = 2,
  };
  void FlushGpuTasks(SyncMode sync_mode,
                     const gpu::SyncToken& release = gpu::SyncToken());
  // When flushing the final task to destroy |impl_on_gpu_| we need to pass in a
  // copy of that pointer for any tasks that were already enqueued and will run
  // before the destructor.
  void FlushGpuTasksWithImpl(SyncMode sync_mode,
                             SkiaOutputSurfaceImplOnGpu* impl_on_gpu,
                             const gpu::SyncToken& release);
  GrBackendFormat GetGrBackendFormatForTexture(ImageContextImpl* image_context,
                                               int plane_index);
  skgpu::graphite::TextureInfo GetGraphitePromiseTextureInfo(
      ImageContextImpl* image_context,
      int plane_index,
      bool mipmap);

  void MakePromiseSkImageSinglePlane(ImageContextImpl* image_context,
                                     bool mipmapped,
                                     bool force_rgbx);
  void MakePromiseSkImageMultiPlane(ImageContextImpl* image_context);
  void ContextLost();
  void RecreateRootDDLRecorder();

  raw_ptr<OutputSurfaceClient> client_ = nullptr;
  bool needs_swap_size_notifications_ = false;

  // Images for current frame or render pass.
  std::vector<raw_ptr<ImageContextImpl, VectorExperimental>>
      images_in_current_paint_;

  SEQUENCE_CHECKER(sequence_checker_);

  // Observers for context lost.
  base::ObserverList<ContextLostObserver>::Unchecked observers_;

  scoped_refptr<SkiaOutputSurfaceSharedImageInterface> shared_image_interface_;
  raw_ptr<SkiaOutputSurfaceDependency> dependency_;
  UpdateVSyncParametersCallback update_vsync_parameters_callback_;

  gfx::Size size_;
  SharedImageFormat format_;
  int sample_count_ = 1;
  SkColorType color_type_ = kUnknown_SkColorType;
  SkAlphaType alpha_type_ = kUnknown_SkAlphaType;
  sk_sp<SkColorSpace> sk_color_space_;
  bool reset_ddl_recorder_on_swap_ = false;
  std::optional<GrDeferredDisplayListRecorder> root_ddl_recorder_;

  class ScopedPaint {
   public:
    // Ganesh root surface
    ScopedPaint(GrDeferredDisplayListRecorder* root_ddl_recorder,
                bool skip_draw_for_tests);
    // Ganesh render pass (root or non-root)
    ScopedPaint(const GrSurfaceCharacterization& characterization,
                const gpu::Mailbox& mailbox,
                bool skip_draw_for_tests);
    // Graphite (root or non-root)
    ScopedPaint(skgpu::graphite::Recorder* recorder,
                const SkImageInfo& image_info,
                skgpu::graphite::TextureInfo texture_info,
                const gpu::Mailbox& mailbox = gpu::Mailbox(),
                bool skip_draw_for_tests = false);
    ~ScopedPaint();

    // SkCanvas for the current paint, retrieved from the DDL recorder for
    // Ganesh, or from the Graphite recorder.
    SkCanvas* canvas() const { return canvas_; }

    // Mailbox for the render pass for the current paint (if present).
    const gpu::Mailbox& mailbox() const { return mailbox_; }

    // Detach DDL and reset the SkCanvas pointer.
    sk_sp<GrDeferredDisplayList> DetachDDL();

    // Snap Graphite recording and reset the SkCanvas pointer.
    std::unique_ptr<skgpu::graphite::Recording> SnapRecording();

   private:
    void Initialize(bool skip_draw_for_tests);

    // This is the DDL recorder being used for current paint when using Ganesh.
    raw_ptr<GrDeferredDisplayListRecorder> ddl_recorder_ = nullptr;
    // If we need new recorder for this Paint (i.e. it's not root render pass),
    // it's stored here
    std::optional<GrDeferredDisplayListRecorder> ddl_recorder_storage_;
    // Graphite recorder used for current paint.
    raw_ptr<skgpu::graphite::Recorder> graphite_recorder_ = nullptr;
    // No draw canvas for tests.
    std::unique_ptr<SkNoDrawCanvas> no_draw_canvas_;
    // SkCanvas for the current paint, retrieved from the DDL recorder for
    // Ganesh, or from the Graphite recorder.
    raw_ptr<SkCanvas> canvas_ = nullptr;
    // Mailbox for the render pass for the current paint (if present).
    const gpu::Mailbox mailbox_;
  };

  // This holds current paint info
  std::optional<ScopedPaint> current_paint_;

  // The SkDDL recorder is used for overdraw feedback. It is created by
  // BeginPaintOverdraw, and FinishPaintCurrentFrame will turn it into a SkDDL
  // and play the SkDDL back on the GPU thread.
  std::optional<GrDeferredDisplayListRecorder> overdraw_surface_ddl_recorder_;

  // |overdraw_canvas_| is used to record draw counts.
  std::optional<SkOverdrawCanvas> overdraw_canvas_;

  // |nway_canvas_| contains |overdraw_canvas_| and root canvas.
  std::optional<SkNWayCanvas> nway_canvas_;

  // The cache for promise image created from render passes.
  base::flat_map<AggregatedRenderPassId, std::unique_ptr<ImageContextImpl>>
      render_pass_image_cache_;

  // Sync tokens for resources which are used for the current frame or render
  // pass.
  std::vector<gpu::SyncToken> resource_sync_tokens_;

  const RendererSettings renderer_settings_;

  // Points to the viz-global singleton.
  const raw_ptr<const DebugRendererSettings> debug_settings_;

  // For testing cases we would need to setup a SkiaOutputSurface without
  // OverlayProcessor and Display. For those cases, we hold the gpu task
  // scheduler inside this class by having a unique_ptr.
  // TODO(weiliangc): After changing to proper initialization order for Android
  // WebView, remove this holder.
  const raw_ptr<DisplayCompositorMemoryAndTaskController>
      display_compositor_controller_;

  // |gpu_task_scheduler_| holds a gpu::SingleTaskSequence, and helps schedule
  // tasks on GPU as a single sequence. It is shared with OverlayProcessor so
  // compositing and overlay processing are in order. A gpu::SingleTaskSequence
  // in regular Viz is implemented by SchedulerSequence. In Android WebView
  // gpu::SingleTaskSequence is implemented on top of WebView's task queue.
  const raw_ptr<gpu::GpuTaskSchedulerHelper> gpu_task_scheduler_;

  // True if raw draw is being used.
  const bool is_using_raw_draw_;

  // True if raw draw is using MSAA output surface.
  const bool is_raw_draw_using_msaa_;

  // The display transform relative to the hardware natural orientation,
  // applied to the frame content. The transform can be rotations in 90 degree
  // increments or flips.
  gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE;

  // |impl_on_gpu| is created and destroyed on the GPU thread by a posted task
  // from SkiaOutputSurfaceImpl::Initialize and SkiaOutputSurfaceImpl::dtor. So
  // it's safe to use base::Unretained for posting tasks during life time of
  // SkiaOutputSurfaceImpl.
  std::unique_ptr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu_;

  gpu::ScopedSyncPointClientState sync_point_client_state_;

  gpu::GrContextType gr_context_type_ = gpu::GrContextType::kGL;
  sk_sp<GrContextThreadSafeProxy> gr_context_thread_safe_;
  raw_ptr<skgpu::graphite::Recorder> graphite_recorder_ = nullptr;
  raw_ptr<base::WeakPtr<gpu::raster::GraphiteCacheController>>
      graphite_cache_controller_weak_ptr_;
  scoped_refptr<gpu::raster::GraphiteCacheController>
      graphite_cache_controller_;
  skgpu::graphite::Volatile graphite_use_volatile_promise_images_ =
      skgpu::graphite::Volatile::kYes;

  bool should_measure_next_post_task_ = false;

  // GPU tasks pending for flush.
  std::vector<GpuTask> gpu_tasks_;
  // GPU sync tokens which are depended by |gpu_tasks_|.
  std::vector<gpu::SyncToken> gpu_task_sync_tokens_;
  // True if _any_ of |gpu_tasks_| need a GL context.
  bool make_current_ = false;
  // True if _any_ of |gpu_tasks_| need to access the framebuffer.
  bool need_framebuffer_ = false;

  // Damage area of the current buffer. Differ to the last submit buffer.
  std::optional<gfx::Rect> damage_of_current_buffer_;

  // For accessing tile shared image backings from compositor thread.
  std::unique_ptr<gpu::SharedImageRepresentationFactory>
      representation_factory_;
  // The refresh interval from presentation feedback.
  base::TimeDelta refresh_interval_;
  bool skip_draw_for_tests_;

  base::WeakPtr<SkiaOutputSurfaceImpl> weak_ptr_;
  base::WeakPtrFactory<SkiaOutputSurfaceImpl> weak_ptr_factory_{this};
};

}  // namespace viz

#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_H_
