// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_GPU_DXVA_PICTURE_BUFFER_WIN_H_
#define MEDIA_GPU_DXVA_PICTURE_BUFFER_WIN_H_

#include <d3d11.h>
#include <d3d9.h>
#include <mfidl.h>

#include <memory>

#include "base/memory/linked_ptr.h"
#include "base/win/scoped_comptr.h"
#include "media/video/picture.h"
#include "third_party/angle/include/EGL/egl.h"
#include "third_party/angle/include/EGL/eglext.h"
#include "ui/gl/gl_fence.h"
#include "ui/gl/gl_image.h"

interface IMFSample;

namespace media {
class DXVAVideoDecodeAccelerator;

// Maintains information about a DXVA picture buffer, i.e. whether it is
// available for rendering, the texture information, etc.
class DXVAPictureBuffer {
 public:
  enum State { UNUSED, BOUND, COPYING, IN_CLIENT, WAITING_TO_REUSE };
  static linked_ptr<DXVAPictureBuffer> Create(
      const DXVAVideoDecodeAccelerator& decoder,
      const PictureBuffer& buffer,
      EGLConfig egl_config);
  virtual ~DXVAPictureBuffer();

  virtual bool ReusePictureBuffer() = 0;
  virtual void ResetReuseFence();
  // Copies the output sample data to the picture buffer provided by the
  // client.
  // The dest_surface parameter contains the decoded bits.
  virtual bool CopyOutputSampleDataToPictureBuffer(
      DXVAVideoDecodeAccelerator* decoder,
      IDirect3DSurface9* dest_surface,
      ID3D11Texture2D* dx11_texture,
      int input_buffer_id);

  bool available() const { return state_ == UNUSED; }
  State state() const { return state_; }

  int id() const { return picture_buffer_.id(); }

  gfx::Size size() const { return picture_buffer_.size(); }
  void set_bound();

  scoped_refptr<gl::GLImage> gl_image() { return gl_image_; }

  const gfx::ColorSpace& color_space() const { return color_space_; }
  void set_color_space(const gfx::ColorSpace& color_space) {
    color_space_ = color_space;
  }

  bool waiting_to_reuse() const { return state_ == WAITING_TO_REUSE; }
  virtual gl::GLFence* reuse_fence();

  // Called when the source surface |src_surface| is copied to the destination
  // |dest_surface|
  virtual bool CopySurfaceComplete(IDirect3DSurface9* src_surface,
                                   IDirect3DSurface9* dest_surface);
  virtual bool BindSampleToTexture(base::win::ScopedComPtr<IMFSample> sample);

 protected:
  explicit DXVAPictureBuffer(const PictureBuffer& buffer);

  State state_ = UNUSED;
  PictureBuffer picture_buffer_;
  gfx::ColorSpace color_space_;
  scoped_refptr<gl::GLImage> gl_image_;

  DISALLOW_COPY_AND_ASSIGN(DXVAPictureBuffer);
};

// Copies the video result into an RGBA EGL pbuffer.
class PbufferPictureBuffer : public DXVAPictureBuffer {
 public:
  explicit PbufferPictureBuffer(const PictureBuffer& buffer);
  ~PbufferPictureBuffer() override;

  bool Initialize(const DXVAVideoDecodeAccelerator& decoder,
                  EGLConfig egl_config);
  bool InitializeTexture(const DXVAVideoDecodeAccelerator& decoder,
                         bool use_rgb);

  bool ReusePictureBuffer() override;
  void ResetReuseFence() override;
  bool CopyOutputSampleDataToPictureBuffer(DXVAVideoDecodeAccelerator* decoder,
                                           IDirect3DSurface9* dest_surface,
                                           ID3D11Texture2D* dx11_texture,
                                           int input_buffer_id) override;
  gl::GLFence* reuse_fence() override;
  bool CopySurfaceComplete(IDirect3DSurface9* src_surface,
                           IDirect3DSurface9* dest_surface) override;

 protected:
  EGLSurface decoding_surface_;

  std::unique_ptr<gl::GLFence> reuse_fence_;

  HANDLE texture_share_handle_;
  base::win::ScopedComPtr<IDirect3DTexture9> decoding_texture_;
  base::win::ScopedComPtr<ID3D11Texture2D> dx11_decoding_texture_;

  base::win::ScopedComPtr<IDXGIKeyedMutex> egl_keyed_mutex_;
  base::win::ScopedComPtr<IDXGIKeyedMutex> dx11_keyed_mutex_;

  // This is the last value that was used to release the keyed mutex.
  uint64_t keyed_mutex_value_;

  // The following |IDirect3DSurface9| interface pointers are used to hold
  // references on the surfaces during the course of a StretchRect operation
  // to copy the source surface to the target. The references are released
  // when the StretchRect operation i.e. the copy completes.
  base::win::ScopedComPtr<IDirect3DSurface9> decoder_surface_;
  base::win::ScopedComPtr<IDirect3DSurface9> target_surface_;

  // This ID3D11Texture2D interface pointer is used to hold a reference to the
  // decoder texture during the course of a copy operation. This reference is
  // released when the copy completes.
  base::win::ScopedComPtr<ID3D11Texture2D> decoder_dx11_texture_;

  // Set to true if RGB is supported by the texture.
  // Defaults to true.
  bool use_rgb_;
};

// Shares the decoded texture with ANGLE without copying by using an EGL stream.
class EGLStreamPictureBuffer : public DXVAPictureBuffer {
 public:
  explicit EGLStreamPictureBuffer(const PictureBuffer& buffer);
  ~EGLStreamPictureBuffer() override;

  bool Initialize();
  bool ReusePictureBuffer() override;
  bool BindSampleToTexture(base::win::ScopedComPtr<IMFSample> sample) override;

 private:
  EGLStreamKHR stream_;

  base::win::ScopedComPtr<IMFSample> current_d3d_sample_;
  base::win::ScopedComPtr<ID3D11Texture2D> dx11_decoding_texture_;
};

// Creates an NV12 texture and copies to it, then shares that with ANGLE.
class EGLStreamCopyPictureBuffer : public DXVAPictureBuffer {
 public:
  explicit EGLStreamCopyPictureBuffer(const PictureBuffer& buffer);
  ~EGLStreamCopyPictureBuffer() override;

  bool Initialize(const DXVAVideoDecodeAccelerator& decoder);
  bool ReusePictureBuffer() override;

  bool CopyOutputSampleDataToPictureBuffer(DXVAVideoDecodeAccelerator* decoder,
                                           IDirect3DSurface9* dest_surface,
                                           ID3D11Texture2D* dx11_texture,
                                           int input_buffer_id) override;
  bool CopySurfaceComplete(IDirect3DSurface9* src_surface,
                           IDirect3DSurface9* dest_surface) override;

 private:
  EGLStreamKHR stream_;

  // This ID3D11Texture2D interface pointer is used to hold a reference to the
  // MFT decoder texture during the course of a copy operation. This reference
  // is released when the copy completes.
  base::win::ScopedComPtr<ID3D11Texture2D> dx11_decoding_texture_;

  base::win::ScopedComPtr<IDXGIKeyedMutex> egl_keyed_mutex_;
  base::win::ScopedComPtr<IDXGIKeyedMutex> dx11_keyed_mutex_;

  HANDLE texture_share_handle_;
  // This is the texture (created on ANGLE's device) that will be put in the
  // EGLStream.
  base::win::ScopedComPtr<ID3D11Texture2D> angle_copy_texture_;
  // This is another copy of that shared resource that will be copied to from
  // the decoder.
  base::win::ScopedComPtr<ID3D11Texture2D> decoder_copy_texture_;

  // This is the last value that was used to release the keyed mutex.
  uint64_t keyed_mutex_value_ = 0;
};

}  // namespace media

#endif  // MEDIA_GPU_DXVA_PICTURE_BUFFER_WIN_H_
