// 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_SERVICE_DISPLAY_OUTPUT_SURFACE_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_OUTPUT_SURFACE_H_

#include <memory>
#include <vector>

#include "base/functional/callback_helpers.h"
#include "base/threading/thread_checker.h"
#include "components/viz/common/display/update_vsync_parameters_callback.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/service/display/pending_swap_params.h"
#include "components/viz/service/display/render_pass_alpha_type.h"
#include "components/viz/service/display/software_output_device.h"
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/service/gpu_task_scheduler_helper.h"
#include "gpu/ipc/common/surface_handle.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "third_party/skia/include/core/SkM44.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/overlay_transform.h"
#include "ui/gfx/surface_origin.h"
#include "ui/latency/latency_info.h"

#if BUILDFLAG(IS_ANDROID)
#include "ui/gfx/android/surface_control_frame_rate.h"
#endif

namespace gfx {
namespace mojom {
class DelegatedInkPointRenderer;
}  // namespace mojom
class ColorSpace;
class Rect;
class Size;
struct SwapResponse;
}  // namespace gfx

namespace viz {
class OutputSurfaceClient;
class OutputSurfaceFrame;
class SkiaOutputSurface;

// This class represents a platform-independent API for presenting
// buffers to display via GPU or software compositing. Implementations
// can provide platform-specific behaviour.
class VIZ_SERVICE_EXPORT OutputSurface {
 public:
  enum Type {
    kSoftware = 0,
    kSkia = 1,
  };

  enum class OrientationMode {
    kLogic,     // The orientation same to logical screen orientation as seen by
                // the user.
    kHardware,  // The orientation same to the hardware.
  };

#if BUILDFLAG(IS_WIN)
  // Level of DComp support. Each value implies support for the features
  // provided by the values before it.
  enum class DCSupportLevel {
    // Direct composition is not supported.
    kNone,
    // Support for presenting `IDXGISwapChain3` and `IDCompositionSurface`.
    kDCLayers,
    // Support for presenting `IDCompositionTexture`.
    kDCompTexture,
    // Support for presenting multiple `IDCompositionTexture` to a persistent
    // surface with incremental damage.
    kDCompDynamicTexture,
  };
#endif

  struct Capabilities {
    Capabilities();
    ~Capabilities();
    Capabilities(const Capabilities& capabilities);
    Capabilities& operator=(const Capabilities& capabilities);

    PendingSwapParams pending_swap_params{1};
    // The number of primary plane buffers. This value is only used when
    // `renderer_allocates_images` is true.
    int number_of_buffers = 0;
    // Whether this output surface renders to the default OpenGL zero
    // framebuffer or to an offscreen framebuffer.
    bool uses_default_gl_framebuffer = true;
    // Where (0,0) is on this OutputSurface.
    gfx::SurfaceOrigin output_surface_origin = gfx::SurfaceOrigin::kBottomLeft;
    // Whether this OutputSurface supports post sub buffer or not.
    bool supports_post_sub_buffer = false;
    // Whether this OutputSurface permits scheduling an isothetic sub-rectangle
    // (i.e. viewport) of its contents for display, allowing the DirectRenderer
    // to apply resize optimization by padding to its width/height.
    bool supports_viewporter = false;
    // OutputSurface's orientation mode.
    OrientationMode orientation_mode = OrientationMode::kLogic;
#if BUILDFLAG(IS_WIN)
    // Whether this OutputSurface supports direct composition layers.
    DCSupportLevel dc_support_level = DCSupportLevel::kNone;
    // Whether to 1) clear all drawn areas outside the viewport with a
    // transparent background color when drawing a frame and 2) swap them. This
    // is necessary if the surface clip rect can get out of sync with the
    // viewport size (e.g., due to a race condition).
    bool clear_drawn_areas_outside_viewport = false;
#endif
    // Whether this OutputSurface should skip DrawAndSwap(). This is true for
    // the unified display on Chrome OS. All drawing is handled by the physical
    // displays so the unified display should skip that work.
    bool skips_draw = false;
    // Whether OutputSurface::GetTargetDamageBoundingRect is implemented and
    // will return a bounding rectangle of the target buffer invalidated area.
    bool supports_target_damage = false;
    // Whether the gpu supports surfaceless surface (equivalent of using buffer
    // queue).
    bool supports_surfaceless = false;
    // This is copied over from gpu feature info since there is no easy way to
    // share that out of skia output surface.
    bool android_surface_control_feature_enabled = false;
    // True if the SkiaOutputDevice will set
    // SwapBuffersCompleteParams::frame_buffer_damage_area for every
    // SwapBuffers complete callback.
    bool damage_area_from_skia_output_device = false;
    // This is the maximum size for RenderPass textures. No maximum size is
    // enforced if zero.
    int max_render_target_size = 0;
    // The root surface is rendered using vulkan secondary command buffer.
    bool root_is_vulkan_secondary_command_buffer = false;
    // Maximum number of non-required YUV overlays that will be promoted per
    // frame. Currently only used with DirectComposition.
    // Some new Intel GPUs support two YUV MPO planes. Promoting two videos
    // to hardware overlays in these platforms will benefit power consumption.
    int allowed_yuv_overlay_count = 1;
    // True if the OS supports delegated ink trails.
    // This is currently only implemented on Win10 and Win11 with
    // DirectComposition on the SkiaRenderer.
    bool supports_delegated_ink = false;
    // True if the OutputSurface can resize to match the size of the root
    // surface. E.g. Wayland protocol allows this.
    bool resize_based_on_root_surface = false;
    // Some configuration supports allocating frame buffers on demand.
    // When enabled, `number_of_buffers` should be interpreted as the maximum
    // number of buffers to allocate.
    bool supports_dynamic_frame_buffer_allocation = false;
    // True when SkiaRenderer allocates and maintains a buffer queue of images
    // for the root render pass.
    bool renderer_allocates_images = false;
    // Wayland only: determines whether BufferQueue needs a background image to
    // be stacked below an AcceleratedWidget to make a widget opaque.
    bool needs_background_image = false;
    // Whether the platform supports non-backed solid color overlays. The
    // Wayland backend is able to delegate these overlays without buffer
    // backings depending on the availability of a certain protocol.
    bool supports_non_backed_solid_color_overlays = false;
    // Whether the platform supports single pixel buffer protocol.
    bool supports_single_pixel_buffer = false;
    // Whether make current needs to be called for swap buffers.
    bool present_requires_make_current = true;

    // Map from SharedImageFormat to its associated SkColorType.
    base::flat_map<SharedImageFormat, SkColorType> sk_color_type_map;

    // Max size for textures.
    int max_texture_size = 0;
  };

  // Constructor for skia-based compositing.
  OutputSurface();
  // Constructor for software compositing.
  explicit OutputSurface(std::unique_ptr<SoftwareOutputDevice> software_device);

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

  virtual ~OutputSurface();

  const Capabilities& capabilities() const { return capabilities_; }
  Type type() const { return type_; }

  // Obtain the software device associated with this output surface. This will
  // return non-null for a software output surface and null for skia output
  // surface.
  SoftwareOutputDevice* software_device() const {
    return software_device_.get();
  }

  // Downcasts to SkiaOutputSurface if it is one and returns nullptr otherwise.
  virtual SkiaOutputSurface* AsSkiaOutputSurface();

  void set_color_matrix(const SkM44& color_matrix) {
    color_matrix_ = color_matrix;
  }
  const SkM44& color_matrix() const { return color_matrix_; }

  // Only useful for GPU backend.
  virtual gpu::SurfaceHandle GetSurfaceHandle() const;

  virtual void BindToClient(OutputSurfaceClient* client) = 0;

  virtual void EnsureBackbuffer() = 0;
  virtual void DiscardBackbuffer() = 0;

  // Reshape the output surface.
  struct ReshapeParams {
    gfx::Size size;
    float device_scale_factor = 1.f;
    gfx::ColorSpace color_space;
    SharedImageFormat format = SinglePlaneFormat::kRGBA_8888;
    RenderPassAlphaType alpha_type = RenderPassAlphaType::kPremul;

    friend bool operator==(const ReshapeParams&,
                           const ReshapeParams&) = default;
  };
  virtual void Reshape(const ReshapeParams& params) = 0;

  // Swaps the current backbuffer to the screen. For successful swaps, the
  // implementation must call OutputSurfaceClient::DidReceiveSwapBuffersAck()
  // after returning from this method in order to unblock the next frame.
  virtual void SwapBuffers(OutputSurfaceFrame frame) = 0;

  // Returns a rectangle whose contents may have changed since the current
  // buffer was last submitted and needs to be redrawn. For partial swap,
  // the contents outside this rectangle can be considered valid and do not need
  // to be redrawn.
  // In cases where partial swap is disabled, this method will still be called.
  // The direct renderer will union the returned rect with the rectangle of the
  // surface itself.
  // TODO(dcastagna): Consider making the following pure virtual.
  virtual gfx::Rect GetCurrentFramebufferDamage() const;

  // Sets callback to receive updated vsync parameters after SwapBuffers() if
  // supported.
  virtual void SetUpdateVSyncParametersCallback(
      UpdateVSyncParametersCallback callback) = 0;

  virtual void SetVSyncDisplayID(int64_t display_id, bool force_update) {}

  // When the device is rotated, the scene prepared by the UI is in the logical
  // screen space as seen by the user. However, attempting to scanout a buffer
  // with its content in this logical space may be unsupported or inefficient
  // when rendered by the display hardware.
  //
  // In order to avoid this, this API provides the OutputSurface with the
  // transform/rotation that should be applied to the display compositor's
  // output. This is the same rotation as the physical rotation on the display.
  // In some cases, this is done natively by the graphics backend (
  // For instance, this is already done by GL drivers on Android. See
  // https://source.android.com/devices/graphics/implement#pre-rotation).
  //
  // If not supported natively, the OutputSurface should return the transform
  // needed in GetDisplayTransform for it to explicitly applied by the
  // compositor.
  virtual void SetDisplayTransformHint(gfx::OverlayTransform transform) = 0;
  virtual gfx::OverlayTransform GetDisplayTransform() = 0;

#if BUILDFLAG(IS_ANDROID)
  virtual base::ScopedClosureRunner GetCacheBackBufferCb();
#endif

  // If set to true, the OutputSurface must deliver
  // OutputSurfaceclient::DidSwapWithSize notifications to its client.
  // OutputSurfaces which support delivering swap size notifications should
  // override this.
  virtual void SetNeedsSwapSizeNotifications(
      bool needs_swap_size_notifications);

  // Notifies surface that we want to measure viz-gpu latency for next draw.
  virtual void SetNeedsMeasureNextDrawLatency() {}

  // Updates timing info on the provided LatencyInfo when swap completes.
  static void UpdateLatencyInfoOnSwap(
      const gfx::SwapResponse& response,
      std::vector<ui::LatencyInfo>* latency_info);

#if BUILDFLAG(IS_ANDROID)
  // Notifies the OutputSurface of rate of content updates in frames per second.
  virtual void SetFrameRate(gfx::SurfaceControlFrameRate frame_rate) {}
#endif

  // Sends the pending delegated ink renderer receiver to GPU Main to allow the
  // browser process to send points directly there.
  virtual void InitDelegatedInkPointRendererReceiver(
      mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer>
          pending_receiver);

  // Read back the contents of this output surface. This reads back the root
  // render pass and does not affect rendering in the ways that a copy request
  // might (e.g. damage, overlays, etc). This uses |CopyOutputRequestCallback|
  // to be able to be used with code that consumes copy output responses.
  virtual void ReadbackForTesting(
      CopyOutputRequest::CopyOutputRequestCallback result_callback);

 protected:
  struct OutputSurface::Capabilities capabilities_;

 private:
  const Type type_;
  std::unique_ptr<SoftwareOutputDevice> software_device_;
  SkM44 color_matrix_;
};

#if BUILDFLAG(IS_WIN)
// Helper to check that DComp textures are supported before checking for
// `features::IsDelegatedCompositingEnabled()`.
bool IsDelegatedCompositingSupportedAndEnabled(
    OutputSurface::DCSupportLevel support_level);

bool IsBufferQueueSupportedAndEnabled(
    OutputSurface::DCSupportLevel support_level);
#endif

}  // namespace viz

#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_OUTPUT_SURFACE_H_
