/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef MOZILLA_LAYERS_RENDEREROGL_H
#define MOZILLA_LAYERS_RENDEREROGL_H

#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "mozilla/webrender/webrender_ffi.h"
#include "mozilla/webrender/RendererScreenshotGrabber.h"

namespace mozilla {

namespace gfx {
class DrawTarget;
}

namespace gl {
class GLContext;
}

namespace layers {
class AndroidHardwareBuffer;
class CompositorBridgeParent;
class Fence;
class SyncObjectHost;
}  // namespace layers

namespace widget {
class CompositorWidget;
}

namespace wr {

class RenderCompositor;
class RenderTextureHost;

/// Owns the WebRender renderer and GL context.
///
/// There is one renderer per window, all owned by the render thread.
/// This class is a similar abstraction to CompositorOGL except that it is used
/// on the render thread instead of the compositor thread.
class RendererOGL {
  friend wr::WrExternalImage LockExternalImage(void* aObj,
                                               wr::ExternalImageId aId,
                                               uint8_t aChannelIndex,
                                               wr::ImageRendering);
  friend void UnlockExternalImage(void* aObj, wr::ExternalImageId aId,
                                  uint8_t aChannelIndex);

 public:
  wr::WrExternalImageHandler GetExternalImageHandler();

  void SetFramePublishId(FramePublishId aPublishId);

  /// This can be called on the render thread only.
  void Update();

  /// This can be called on the render thread only.
  RenderedFrameId UpdateAndRender(const Maybe<gfx::IntSize>& aReadbackSize,
                                  const Maybe<wr::ImageFormat>& aReadbackFormat,
                                  const Maybe<Range<uint8_t>>& aReadbackBuffer,
                                  bool* aNeedsYFlip,
                                  const wr::FrameReadyParams& aFrameParams,
                                  RendererStats* aOutStats);

  /// This can be called on the render thread only.
  void WaitForGPU();

  /// This can be called on the render thread only.
  RefPtr<layers::Fence> GetAndResetReleaseFence();

  /// This can be called on the render thread only.
  RenderedFrameId GetLastCompletedFrameId();

  /// This can be called on the render thread only.
  RenderedFrameId UpdateFrameId();

  /// This can be called on the render thread only.
  void SetProfilerEnabled(bool aEnabled);

  /// This can be called on the render thread only.
  void SetFrameStartTime(const TimeStamp& aTime);

  /// These can be called on the render thread only.
  void BeginRecording(const TimeStamp& aRecordingStart,
                      wr::PipelineId aPipelineId);
  void MaybeRecordFrame(const WebRenderPipelineInfo* aPipelineInfo);

  Maybe<layers::FrameRecording> EndRecording();

#ifdef MOZ_WIDGET_ANDROID
  using ScreenPixelsPromise = MozPromise<Ok, nsresult, true>;
  // Captures the pixels for the next rendered frame. Returns a promise that
  // resolves once the pixels are captured.
  RefPtr<ScreenPixelsPromise> RequestScreenPixels(
      gfx::IntRect aSourceRect,
      RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer);
#endif

  /// This can be called on the render thread only.
  ~RendererOGL();

  /// This can be called on the render thread only.
  RendererOGL(RefPtr<RenderThread>&& aThread,
              UniquePtr<RenderCompositor> aCompositor, wr::WindowId aWindowId,
              wr::Renderer* aRenderer, layers::CompositorBridgeParent* aBridge);

  /// This can be called on the render thread only.
  void Pause();

  /// This can be called on the render thread only.
  bool Resume();

  /// This can be called on the render thread only.
  bool IsPaused();

  /// This can be called on the render thread only.
  void CheckGraphicsResetStatus(gfx::DeviceResetDetectPlace aPlace,
                                bool aForce);

  layers::SyncObjectHost* GetSyncObject() const;

  layers::CompositorBridgeParent* GetCompositorBridge() { return mBridge; }

  void FlushPipelineInfo();

  RefPtr<const WebRenderPipelineInfo> GetLastPipelineInfo() const {
    return mLastPipelineInfo;
  }

  RenderTextureHost* GetRenderTexture(wr::ExternalImageId aExternalImageId);

  RenderCompositor* GetCompositor() { return mCompositor.get(); }

  void AccumulateMemoryReport(MemoryReport* aReport);

  void SetProfilerUI(const nsACString& aUI);

  wr::Renderer* GetRenderer() { return mRenderer; }

  gl::GLContext* gl() const;

  void* swgl() const;

  bool EnsureAsyncScreenshot();

 protected:
  /**
   * Determine if any content pipelines updated, and update
   * mContentPipelineEpochs.
   */
  bool DidPaintContent(const wr::WebRenderPipelineInfo* aFrameEpochs);

#ifdef MOZ_WIDGET_ANDROID
  // If mPendingScreenPixelsRequest is set, captures the pixels of the frame
  // that has just been rendered and resolves the request. Must be called after
  // the frame has been rendered but before RenderCompositor::EndFrame() (which
  // swaps buffers).
  void MaybeCaptureScreenPixels();
#endif

  RefPtr<RenderThread> mThread;
  UniquePtr<RenderCompositor> mCompositor;
  UniquePtr<layers::CompositionRecorder> mCompositionRecorder;  // can be null
  wr::Renderer* mRenderer;
  layers::CompositorBridgeParent* mBridge;
  wr::WindowId mWindowId;
  TimeStamp mFrameStartTime;

#ifdef MOZ_WIDGET_ANDROID
  struct ScreenPixelsRequest {
    gfx::IntRect mSourceRect;
    RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
    RefPtr<ScreenPixelsPromise::Private> mPromise;
  };
  Maybe<ScreenPixelsRequest> mPendingScreenPixelsRequest;
#endif

  RendererScreenshotGrabber mScreenshotGrabber;

  // The id of the root WebRender pipeline.
  //
  // All other pipelines are considered content.
  wr::PipelineId mRootPipelineId{};

  // A mapping of wr::PipelineId to the epochs when last they updated.
  //
  // We need to use uint64_t here since wr::PipelineId is not default
  // constructable.
  std::unordered_map<uint64_t, wr::Epoch> mContentPipelineEpochs;

  RefPtr<WebRenderPipelineInfo> mLastPipelineInfo;

  // Tracks whether the last render rasterized any tiles.
  // Used by reftest to verify no rasterization occurred.
  bool mLastFrameDidRasterize = false;

 public:
  bool CheckAndClearDidRasterize();
};

}  // namespace wr
}  // namespace mozilla

#endif
