// 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_SKIA_OUTPUT_SURFACE_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_SKIA_OUTPUT_SURFACE_H_

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

#include "build/build_config.h"
#include "components/viz/common/quads/aggregated_render_pass.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/service/display/external_use_client.h"
#include "components/viz/service/display/output_surface.h"
#include "components/viz/service/display/overlay_processor_interface.h"
#include "components/viz/service/display/render_pass_alpha_type.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/vulkan/buildflags.h"
#include "media/gpu/buildflags.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/gfx/gpu_fence_handle.h"

#if BUILDFLAG(IS_WIN)
#include "components/viz/service/display/dc_layer_overlay.h"
#endif

#if BUILDFLAG(IS_APPLE)
#include "components/viz/service/display/ca_layer_overlay.h"
#endif

class SkCanvas;
class SkImage;

namespace gfx {
class ColorSpace;
}  // namespace gfx

namespace viz {

class OverlayCandidate;
class ContextLostObserver;
class CopyOutputRequest;

namespace copy_output {
struct RenderPassGeometry;
}  // namespace copy_output

// This class extends the OutputSurface for SkiaRenderer needs.
class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface,
                                             public ExternalUseClient {
 public:
  using OverlayList = std::vector<OverlayCandidate>;

  SkiaOutputSurface();

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

  ~SkiaOutputSurface() override;

  SkiaOutputSurface* AsSkiaOutputSurface() override;

  // Begin painting the current frame. This method will create a
  // GrDeferredDisplayListRecorder and return a SkCanvas of it.
  // The SkiaRenderer will use this SkCanvas to paint the current
  // frame.
  // And this SkCanvas may become invalid, when FinishPaintCurrentFrame is
  // called.
  virtual SkCanvas* BeginPaintCurrentFrame() = 0;

  // Make a promise SkImage from the given |image_context|. The SkiaRenderer can
  // use the image with SkCanvas returned by |GetSkCanvasForCurrentFrame|, but
  // Skia will not read the content of the resource until the |sync_token| in
  // the |image_context| is satisfied. The SwapBuffers should take care of this
  // by scheduling a GPU task with all resource sync tokens recorded by
  // MakePromiseSkImage for the current frame.
  virtual void MakePromiseSkImage(
      ExternalUseClient::ImageContext* image_context,
      bool force_rgbx) = 0;

  // Called if SwapBuffers() will be skipped.
  virtual void SwapBuffersSkipped(const gfx::Rect root_pass_damage_rect) = 0;

  // Begin painting a render pass. This method will create a
  // GrDeferredDisplayListRecorder and return a SkCanvas of it. The SkiaRenderer
  // will use this SkCanvas to paint the render pass.
  // Note: BeginPaintRenderPass cannot be called without finishing the prior
  // paint render pass.
  virtual SkCanvas* BeginPaintRenderPass(const AggregatedRenderPassId& id,
                                         const gfx::Size& 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) = 0;

  // Create an overdraw recorder for the current paint which will be drawn on
  // top of the current canvas when EndPaint() is called. Returns the new
  // wrapped SkCanvas to be used by SkiaRenderer.
  // This should be called for the root render pass only when
  // debug_settings.show_overdraw_feedback = true.
  virtual SkCanvas* RecordOverdrawForCurrentPaint() = 0;

  // Finish painting the current frame or current render pass, depends on which
  // BeginPaint function is called last. This method will schedule a GPU task to
  // play the DDL back on GPU thread on a cached SkSurface.
  // Optionally the caller may specify |on_finished| callback to be called after
  // the GPU has finished processing all submitted commands. The callback may be
  // called on a different thread. The caller may also specify
  // |return_release_fence_cb| callback to be called after all commands are
  // submitted. The callback will return the release fence which will be
  // signaled once the submitted commands are processed.
  // |update_rect| should be the scissor rect used to clear the render pass
  // backing and cull its draw quads.
  // When finishing painting of a render pass that will be presented as an
  // overlay, |is_overlay| should be true so the GPU thread knows to keep the
  // ScopedWriteAccess open long enough.
  virtual void EndPaint(
      base::OnceClosure on_finished,
      base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb,
      const gfx::Rect& update_rect,
      bool is_overlay) = 0;

  // Make a promise SkImage from a render pass id. The render pass has been
  // painted with BeginPaintRenderPass and FinishPaintRenderPass. The format
  // and mipmap must match arguments used for BeginPaintRenderPass() to paint
  // this render pass.
  virtual sk_sp<SkImage> MakePromiseSkImageFromRenderPass(
      const AggregatedRenderPassId& id,
      const gfx::Size& size,
      SharedImageFormat format,
      bool mipmap,
      const gfx::ColorSpace& color_space,
      const gpu::Mailbox& mailbox) = 0;

  // Remove cached resources generated by BeginPaintRenderPass and
  // FinishPaintRenderPass.
  virtual void RemoveRenderPassResource(
      std::vector<AggregatedRenderPassId> ids) = 0;

  // Copy the output of the current frame if the |mailbox| is zero, otherwise
  // create an SkSurface for the given |mailbox| and copy the output.
  virtual void CopyOutput(const copy_output::RenderPassGeometry& geometry,
                          const gfx::ColorSpace& color_space,
                          std::unique_ptr<CopyOutputRequest> request,
                          const gpu::Mailbox& mailbox) = 0;

  // Schedule drawing overlays at next SwapBuffers() call. Waits on
  // |sync_tokens| for the overlay textures to be ready before scheduling.
  // Optionally the caller may specify |on_finished| callback to be called after
  // the GPU has finished processing all submitted commands. The callback may be
  // called on a different thread.
  virtual void ScheduleOverlays(OverlayList overlays,
                                std::vector<gpu::SyncToken> sync_tokens) = 0;

  // Add context lost observer.
  virtual void AddContextLostObserver(ContextLostObserver* observer) = 0;

  // Remove context lost observer.
  virtual void RemoveContextLostObserver(ContextLostObserver* observer) = 0;

  // Only used for SkiaOutputSurfaceImpl unit tests.
  virtual void ScheduleGpuTaskForTesting(
      base::OnceClosure callback,
      std::vector<gpu::SyncToken> sync_tokens) = 0;
  // TODO(crbug.com/40279197): tests should not need to poll for async work
  // completion.
  virtual void CheckAsyncWorkCompletionForTesting() = 0;

  // Android specific, asks GLSurfaceEGLSurfaceControl to not detach child
  // surface controls during destruction. This is necessary for cases when we
  // switch from chrome to other app, the OS will take care of the cleanup.
  virtual void PreserveChildSurfaceControls() = 0;

  // Flush pending GPU tasks. This method returns a sync token which can be
  // waited on in a command buffer to ensure all pending tasks are executed on
  // the GPU main thread.
  virtual gpu::SyncToken Flush() = 0;

  // Enqueue a GPU task to create a shared image with the specified params and
  // returns the mailbox.
  // Note: |kTopLeft_GrSurfaceOrigin| is used for all images.
  virtual 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) = 0;

  // Enqueue a GPU task to create a 1x1 shared image of the specified color.
  virtual gpu::Mailbox CreateSolidColorSharedImage(
      const SkColor4f& color,
      const gfx::ColorSpace& color_space) = 0;

  // Enqueue a GPU task to delete the specified shared image.
  virtual void DestroySharedImage(const gpu::Mailbox& mailbox) = 0;

  // Enqueue a GPU task to set specified shared image as `purgeable`.
  virtual void SetSharedImagePurgeable(const gpu::Mailbox& mailbox,
                                       bool purgeable) = 0;

  virtual bool SupportsBGRA() const = 0;

#if BUILDFLAG(ENABLE_VULKAN) && BUILDFLAG(IS_CHROMEOS) && \
    BUILDFLAG(USE_V4L2_CODEC)
  virtual 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) = 0;

  virtual void CleanupImageProcessor() = 0;
#endif
};

}  // namespace viz

#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_SKIA_OUTPUT_SURFACE_H_
